[
  {
    "path": ".github/DISCUSSION_TEMPLATE/questions.yml",
    "content": "labels: [question]\nbody:\n  - type: markdown\n    attributes:\n      value: |\n        Thanks for your interest in Typer! 🚀\n\n        Please follow these instructions, fill every question, and do every step. 🙏\n\n        I'm asking this because answering questions and solving problems in GitHub is what consumes most of the time.\n\n        I end up not being able to add new features, fix bugs, review pull requests, etc. as fast as I wish because I have to spend too much time handling questions.\n\n        All that, on top of all the incredible help provided by a bunch of community members that give a lot of their time to come here and help others.\n\n        If more Typer users came to help others like them just a little bit more, it would be much less effort for them (and you and me 😅).\n\n        By asking questions in a structured way (following this) it will be much easier to help you.\n\n        And there's a high chance that you will find the solution along the way and you won't even have to submit it and wait for an answer. 😎\n\n        As there are too many questions, I'll have to discard and close the incomplete ones. That will allow me (and others) to focus on helping people like you that follow the whole process and help us help you. 🤓\n  - type: checkboxes\n    id: checks\n    attributes:\n      label: First Check\n      description: Please confirm and check all the following options.\n      options:\n        - label: I added a very descriptive title here.\n          required: true\n        - label: I used the GitHub search to find a similar question and didn't find it.\n          required: true\n        - label: I searched the Typer documentation, with the integrated search.\n          required: true\n        - label: I already searched in Google \"How to X in Typer\" and didn't find any information.\n          required: true\n        - label: I already read and followed all the tutorials in the docs and didn't find an answer.\n          required: true\n        - label: I already checked if it is not related to Typer but to [Click](https://github.com/pallets/click).\n          required: true\n  - type: checkboxes\n    id: help\n    attributes:\n      label: Commit to Help\n      description: |\n        After submitting this, I commit to one of:\n\n          * Read open issues with questions until I find 2 issues where I can help someone and add a comment to help there.\n          * I already hit the \"watch\" button in this repository to receive notifications and I commit to help at least 2 people that ask questions in the future.\n          * Review one Pull Request by downloading the code and following all the [review process](https://typer.tiangolo.com/help-typer/#review-pull-requests).\n\n      options:\n        - label: I commit to help with one of those options 👆\n          required: true\n  - type: textarea\n    id: example\n    attributes:\n      label: Example Code\n      description: |\n        Please add a self-contained, [minimal, reproducible, example](https://stackoverflow.com/help/minimal-reproducible-example) with your use case.\n\n        If I (or someone) can copy it, run it, and see it right away, there's a much higher chance I (or someone) will be able to help you.\n\n      placeholder: |\n        import typer\n\n        app = typer.Typer()\n\n        @app.command()\n        def main(name: str):\n            typer.echo(f\"Hello {name}\")\n\n\n        if __name__ == \"__main__\":\n            app()\n      render: python\n    validations:\n      required: true\n  - type: textarea\n    id: description\n    attributes:\n      label: Description\n      description: |\n        What is the problem, question, or error?\n\n        Write a short description telling me what you are doing, what you expect to happen, and what is currently happening.\n      placeholder: |\n        * Create a small Typer script.\n        * Open a Terminal with Ninja-Turtle-Shell.\n        * Trigger autocompletion hitting TAB.\n        * I don't see any completion in the terminal using Ninja-Turtle-Shell.\n        * I expected to see autocompletion there.\n    validations:\n      required: true\n  - type: dropdown\n    id: os\n    attributes:\n      label: Operating System\n      description: What operating system are you on?\n      multiple: true\n      options:\n        - Linux\n        - Windows\n        - macOS\n        - Other\n    validations:\n      required: true\n  - type: textarea\n    id: os-details\n    attributes:\n      label: Operating System Details\n      description: You can add more details about your operating system here, in particular if you chose \"Other\".\n  - type: input\n    id: typer-version\n    attributes:\n      label: Typer Version\n      description: |\n        What Typer version are you using?\n\n        You can find the Typer version with:\n\n        ```bash\n        python -c \"import typer; print(typer.__version__)\"\n        ```\n    validations:\n      required: true\n  - type: input\n    id: python-version\n    attributes:\n      label: Python Version\n      description: |\n        What Python version are you using?\n\n        You can find the Python version with:\n\n        ```bash\n        python --version\n        ```\n    validations:\n      required: true\n  - type: textarea\n    id: context\n    attributes:\n      label: Additional Context\n      description: Add any additional context information or screenshots you think are useful.\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "github: [tiangolo]\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "blank_issues_enabled: false\ncontact_links:\n  - name: Security Contact\n    about: Please report security vulnerabilities to security@tiangolo.com\n  - name: Question or Problem\n    about: Ask a question or ask about a problem in GitHub Discussions.\n    url: https://github.com/fastapi/typer/discussions/categories/questions\n  - name: Feature Request\n    about: To suggest an idea or ask about a feature, please start with a question saying what you would like to achieve. There might be a way to do it already.\n    url: https://github.com/fastapi/typer/discussions/categories/questions\n  - name: Show and tell\n    about: Show what you built with Typer or to be used with Typer.\n    url: https://github.com/fastapi/typer/discussions/categories/show-and-tell\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/privileged.yml",
    "content": "name: Privileged\ndescription: You are @tiangolo or he asked you directly to create an issue here. If not, check the other options. 👇\nbody:\n  - type: markdown\n    attributes:\n      value: |\n        Thanks for your interest in Typer! 🚀\n\n        If you are not @tiangolo or he didn't ask you directly to create an issue here, please start the conversation in a [Question in GitHub Discussions](https://github.com/fastapi/typer/discussions/categories/questions) instead.\n  - type: checkboxes\n    id: privileged\n    attributes:\n      label: Privileged issue\n      description: Confirm that you are allowed to create an issue here.\n      options:\n        - label: I'm @tiangolo or he asked me directly to create an issue here.\n          required: true\n  - type: textarea\n    id: content\n    attributes:\n      label: Issue Content\n      description: Add the content of the issue here.\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "version: 2\nupdates:\n  # GitHub Actions\n  - package-ecosystem: \"github-actions\"\n    directory: \"/\"\n    schedule:\n      interval: \"daily\"\n    commit-message:\n      prefix: ⬆\n  # Python\n  - package-ecosystem: \"uv\"\n    directory: \"/\"\n    schedule:\n      interval: \"daily\"\n    commit-message:\n      prefix: ⬆\n"
  },
  {
    "path": ".github/labeler.yml",
    "content": "docs:\n  - all:\n    - changed-files:\n      - any-glob-to-any-file:\n        - docs/**\n        - docs_src/**\n      - all-globs-to-all-files:\n        - '!typer/**'\n        - '!pyproject.toml'\n\ninternal:\n  - all:\n    - changed-files:\n      - any-glob-to-any-file:\n        - .github/**\n        - scripts/**\n        - .gitignore\n        - .pre-commit-config.yaml\n        - uv.lock\n      - all-globs-to-all-files:\n        - '!docs/**'\n        - '!typer/**'\n        - '!pyproject.toml'\n"
  },
  {
    "path": ".github/workflows/add-to-project.yml",
    "content": "name: Add to Project\n\non:\n  pull_request_target:\n  issues:\n    types:\n      - opened\n      - reopened\n\njobs:\n  add-to-project:\n    name: Add to project\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/add-to-project@v1.0.2\n        with:\n          project-url: https://github.com/orgs/fastapi/projects/2\n          github-token: ${{ secrets.PROJECTS_TOKEN }}\n"
  },
  {
    "path": ".github/workflows/build-docs.yml",
    "content": "name: Build Docs\non:\n  push:\n    branches:\n      - master\n  pull_request:\n    types:\n      - opened\n      - synchronize\n\njobs:\n  changes:\n    runs-on: ubuntu-latest\n    # Required permissions\n    permissions:\n      pull-requests: read\n    # Set job outputs to values from filter step\n    outputs:\n      docs: ${{ steps.filter.outputs.docs }}\n    steps:\n    - uses: actions/checkout@v6\n    # For pull requests it's not necessary to checkout the code but for the main branch it is\n    - uses: dorny/paths-filter@v4\n      id: filter\n      with:\n        filters: |\n          docs:\n            - README.md\n            - docs/**\n            - docs_src/**\n            - pyproject.toml\n            - uv.lock\n            - mkdocs.yml\n            - mkdocs.env.yml\n            - .github/workflows/build-docs.yml\n            - .github/workflows/deploy-docs.yml\n            - data/**\n\n  build-docs:\n    needs:\n      - changes\n    if: ${{ needs.changes.outputs.docs == 'true' }}\n    runs-on: ubuntu-latest\n    steps:\n      - name: Dump GitHub context\n        env:\n          GITHUB_CONTEXT: ${{ toJson(github) }}\n        run: echo \"$GITHUB_CONTEXT\"\n      - uses: actions/checkout@v6\n      - name: Set up Python\n        uses: actions/setup-python@v6\n        with:\n          python-version-file: \".python-version\"\n      - name: Setup uv\n        uses: astral-sh/setup-uv@v7\n        with:\n          enable-cache: true\n          cache-dependency-glob: |\n            pyproject.toml\n            uv.lock\n      - name: Install docs extras\n        run: uv sync --locked --no-dev --group docs\n      - uses: actions/cache@v5\n        with:\n          key: mkdocs-cards-${{ github.ref }}-v1\n          path: .cache\n      - name: Build Docs\n        run: uv run ./scripts/docs.py build\n      - uses: actions/upload-artifact@v7\n        with:\n          name: docs-site\n          path: ./site/**\n          include-hidden-files: true\n\n  # https://github.com/marketplace/actions/alls-green#why\n  docs-all-green:  # This job does nothing and is only used for the branch protection\n    if: always()\n    needs:\n      - build-docs\n    runs-on: ubuntu-latest\n    steps:\n      - name: Decide whether the needed jobs succeeded or failed\n        uses: re-actors/alls-green@release/v1\n        with:\n          jobs: ${{ toJSON(needs) }}\n          allowed-skips: build-docs\n"
  },
  {
    "path": ".github/workflows/conflict.yml",
    "content": "name: \"Conflict detector\"\non:\n  push:\n  pull_request_target:\n    types: [synchronize]\n\njobs:\n  main:\n    permissions:\n      contents: read\n      pull-requests: write\n    runs-on: ubuntu-latest\n    steps:\n      - name: Check if PRs have merge conflicts\n        uses: eps1lon/actions-label-merge-conflict@v3\n        with:\n          dirtyLabel: \"conflicts\"\n          repoToken: \"${{ secrets.GITHUB_TOKEN }}\"\n          commentOnDirty: \"This pull request has a merge conflict that needs to be resolved.\"\n"
  },
  {
    "path": ".github/workflows/deploy-docs.yml",
    "content": "name: Deploy Docs\non:\n  workflow_run:\n    workflows:\n      - Build Docs\n    types:\n      - completed\n\npermissions:\n  deployments: write\n  issues: write\n  pull-requests: write\n  statuses: write\n\njobs:\n  deploy-docs:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Dump GitHub context\n        env:\n          GITHUB_CONTEXT: ${{ toJson(github) }}\n        run: echo \"$GITHUB_CONTEXT\"\n      - uses: actions/checkout@v6\n      - name: Set up Python\n        uses: actions/setup-python@v6\n        with:\n          python-version-file: \".python-version\"\n      - name: Setup uv\n        uses: astral-sh/setup-uv@v7\n        with:\n          enable-cache: true\n          cache-dependency-glob: |\n            pyproject.toml\n            uv.lock\n      - name: Install GitHub Actions dependencies\n        run: uv sync --locked --no-dev --group github-actions\n      - name: Deploy Docs Status Pending\n        run: uv run ./scripts/deploy_docs_status.py\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n          COMMIT_SHA: ${{ github.event.workflow_run.head_sha }}\n          RUN_ID: ${{ github.run_id }}\n          STATE: \"pending\"\n      - name: Clean site\n        run: |\n          rm -rf ./site\n          mkdir ./site\n      - uses: actions/download-artifact@v8\n        with:\n          path: ./site/\n          pattern: docs-site\n          merge-multiple: true\n          github-token: ${{ secrets.GITHUB_TOKEN }}\n          run-id: ${{ github.event.workflow_run.id }}\n      - name: Deploy to Cloudflare Pages\n        # hashFiles returns an empty string if there are no files\n        if: hashFiles('./site/*')\n        id: deploy\n        env:\n          PROJECT_NAME: typertiangolo\n          BRANCH: ${{ ( github.event.workflow_run.head_repository.full_name == github.repository && github.event.workflow_run.head_branch == 'master' && 'main' ) || ( github.event.workflow_run.head_sha ) }}\n        uses: cloudflare/wrangler-action@v3\n        with:\n          apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}\n          accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}\n          command: pages deploy ./site --project-name=${{ env.PROJECT_NAME }} --branch=${{ env.BRANCH }}\n      - name: Deploy Docs Status Error\n        if: failure()\n        run: uv run ./scripts/deploy_docs_status.py\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n          COMMIT_SHA: ${{ github.event.workflow_run.head_sha }}\n          RUN_ID: ${{ github.run_id }}\n          STATE: \"error\"\n      - name: Comment Deploy\n        run: uv run ./scripts/deploy_docs_status.py\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n          DEPLOY_URL: ${{ steps.deploy.outputs.deployment-url }}\n          COMMIT_SHA: ${{ github.event.workflow_run.head_sha }}\n          RUN_ID: ${{ github.run_id }}\n          STATE: \"success\"\n"
  },
  {
    "path": ".github/workflows/issue-manager.yml",
    "content": "name: Issue Manager\n\non:\n  schedule:\n    - cron: \"13 21 * * *\"\n  issue_comment:\n    types:\n      - created\n  issues:\n    types:\n      - labeled\n  pull_request_target:\n    types:\n      - labeled\n  workflow_dispatch:\n\npermissions:\n  issues: write\n  pull-requests: write\n\njobs:\n  issue-manager:\n    if: github.repository_owner == 'fastapi'\n    runs-on: ubuntu-latest\n    steps:\n      - name: Dump GitHub context\n        env:\n          GITHUB_CONTEXT: ${{ toJson(github) }}\n        run: echo \"$GITHUB_CONTEXT\"\n      - uses: tiangolo/issue-manager@0.6.0\n        with:\n          token: ${{ secrets.GITHUB_TOKEN }}\n          config: >\n            {\n              \"answered\": {\n                \"delay\": 864000,\n                \"message\": \"Assuming the original need was handled, this will be automatically closed now. But feel free to add more comments or create new issues or PRs.\"\n              },\n              \"waiting\": {\n                \"delay\": 2628000,\n                \"message\": \"As this PR has been waiting for the original user for a while but seems to be inactive, it's now going to be closed. But if there's anyone interested, feel free to create a new PR.\",\n                \"reminder\": {\n                    \"before\": \"P3D\",\n                    \"message\": \"Heads-up: this will be closed in 3 days unless there's new activity.\"\n                }\n              },\n              \"invalid\": {\n                \"delay\": 0,\n                \"message\": \"This was marked as invalid and will be closed now. If this is an error, please provide additional details.\"\n              },\n              \"maybe-ai\": {\n                \"delay\": 0,\n                \"message\": \"This was marked as potentially AI generated and will be closed now. If this is an error, please provide additional details, make sure to read the docs about contributing and AI.\"\n              }\n            }\n"
  },
  {
    "path": ".github/workflows/labeler.yml",
    "content": "name: Labels\non:\n  pull_request_target:\n    types:\n      - opened\n      - synchronize\n      - reopened\n      # For label-checker\n      - labeled\n      - unlabeled\n\njobs:\n  labeler:\n    permissions:\n      contents: read\n      pull-requests: write\n    runs-on: ubuntu-latest\n    steps:\n    - uses: actions/labeler@v6\n      if: ${{ github.event.action != 'labeled' && github.event.action != 'unlabeled' }}\n    - run: echo \"Done adding labels\"\n  # Run this after labeler applied labels\n  check-labels:\n    needs:\n      - labeler\n    permissions:\n      pull-requests: read\n    runs-on: ubuntu-latest\n    steps:\n      - uses: docker://agilepathway/pull-request-label-checker:latest\n        with:\n          one_of: breaking,security,feature,bug,refactor,upgrade,docs,lang-all,internal\n          repo_token: ${{ secrets.GITHUB_TOKEN }}\n"
  },
  {
    "path": ".github/workflows/latest-changes.yml",
    "content": "name: Latest Changes\n\non:\n  pull_request_target:\n    branches:\n      - master\n    types:\n      - closed\n  workflow_dispatch:\n    inputs:\n      number:\n        description: PR number\n        required: true\n      debug_enabled:\n        description: 'Run the build with tmate debugging enabled (https://github.com/marketplace/actions/debugging-with-tmate)'\n        required: false\n        default: 'false'\n\njobs:\n  latest-changes:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Dump GitHub context\n        env:\n          GITHUB_CONTEXT: ${{ toJson(github) }}\n        run: echo \"$GITHUB_CONTEXT\"\n      - uses: actions/checkout@v6\n        with:\n          # To allow latest-changes to commit to the main branch\n          token: ${{ secrets.TYPER_LATEST_CHANGES }}\n      # Allow debugging with tmate\n      - name: Setup tmate session\n        uses: mxschmitt/action-tmate@v3\n        if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.debug_enabled == 'true' }}\n        with:\n          limit-access-to-actor: true\n      - uses: tiangolo/latest-changes@0.4.1\n        with:\n          token: ${{ secrets.GITHUB_TOKEN }}\n          latest_changes_file: docs/release-notes.md\n          latest_changes_header: '## Latest Changes'\n          end_regex: '^## '\n          debug_logs: true\n          label_header_prefix: '### '\n"
  },
  {
    "path": ".github/workflows/pre-commit.yml",
    "content": "name: pre-commit\n\non:\n  pull_request:\n    types:\n      - opened\n      - synchronize\n\nenv:\n  # Forks and Dependabot don't have access to secrets\n  HAS_SECRETS: ${{ secrets.PRE_COMMIT != '' }}\n\njobs:\n  pre-commit:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Dump GitHub context\n        env:\n          GITHUB_CONTEXT: ${{ toJson(github) }}\n        run: echo \"$GITHUB_CONTEXT\"\n      - uses: actions/checkout@v6\n        name: Checkout PR for own repo\n        if: env.HAS_SECRETS == 'true'\n        with:\n          # To be able to commit it needs to fetch the head of the branch, not the\n          # merge commit\n          ref: ${{ github.head_ref }}\n          # And it needs the full history to be able to compute diffs\n          fetch-depth: 0\n          # A token other than the default GITHUB_TOKEN is needed to be able to trigger CI\n          token: ${{ secrets.PRE_COMMIT }}\n      # pre-commit lite ci needs the default checkout configs to work\n      - uses: actions/checkout@v6\n        name: Checkout PR for fork\n        if: env.HAS_SECRETS == 'false'\n        with:\n        # To be able to commit it needs the head branch of the PR, the remote one\n          ref: ${{ github.event.pull_request.head.sha }}\n          fetch-depth: 0\n      - name: Set up Python\n        uses: actions/setup-python@v6\n        with:\n          python-version-file: \".python-version\"\n      - name: Setup uv\n        uses: astral-sh/setup-uv@v7\n        with:\n          cache-dependency-glob: |\n            pyproject.toml\n            uv.lock\n      - name: Install Dependencies\n        run: uv sync --locked\n      - name: Run prek - pre-commit\n        id: precommit\n        run: uvx prek run --from-ref origin/${GITHUB_BASE_REF} --to-ref HEAD --show-diff-on-failure\n        continue-on-error: true\n      - name: Commit and push changes\n        if: env.HAS_SECRETS == 'true'\n        run: |\n          git config user.name \"github-actions[bot]\"\n          git config user.email \"github-actions[bot]@users.noreply.github.com\"\n          git add -A\n          if git diff --staged --quiet; then\n            echo \"No changes to commit\"\n          else\n            git commit -m \"🎨 Auto format\"\n            git push\n          fi\n      - uses: pre-commit-ci/lite-action@v1.1.0\n        if: env.HAS_SECRETS == 'false'\n        with:\n          msg: 🎨 Auto format\n      - name: Error out on pre-commit errors\n        if: steps.precommit.outcome == 'failure'\n        run: exit 1\n\n  # https://github.com/marketplace/actions/alls-green#why\n  pre-commit-alls-green:  # This job does nothing and is only used for the branch protection\n    if: always()\n    needs:\n      - pre-commit\n    runs-on: ubuntu-latest\n    steps:\n      - name: Dump GitHub context\n        env:\n          GITHUB_CONTEXT: ${{ toJson(github) }}\n        run: echo \"$GITHUB_CONTEXT\"\n      - name: Decide whether the needed jobs succeeded or failed\n        uses: re-actors/alls-green@release/v1\n        with:\n          jobs: ${{ toJSON(needs) }}\n"
  },
  {
    "path": ".github/workflows/publish.yml",
    "content": "name: Publish\n\non:\n  release:\n    types:\n      - created\n\njobs:\n  publish:\n    runs-on: ubuntu-latest\n    permissions:\n      id-token: write\n      contents: read\n    steps:\n      - name: Dump GitHub context\n        env:\n          GITHUB_CONTEXT: ${{ toJson(github) }}\n        run: echo \"$GITHUB_CONTEXT\"\n      - uses: actions/checkout@v6\n      - name: Set up Python\n        uses: actions/setup-python@v6\n        with:\n          python-version-file: \".python-version\"\n      - name: Install uv\n        uses: astral-sh/setup-uv@v7\n      - name: Build distribution\n        run: uv build\n      - name: Publish\n        run: uv publish\n"
  },
  {
    "path": ".github/workflows/smokeshow.yml",
    "content": "name: Smokeshow\n\non:\n  workflow_run:\n    workflows:\n      - Test\n    types:\n      - completed\n\npermissions:\n  statuses: write\n\njobs:\n  smokeshow:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Dump GitHub context\n        env:\n          GITHUB_CONTEXT: ${{ toJson(github) }}\n        run: echo \"$GITHUB_CONTEXT\"\n      - uses: actions/checkout@v6\n      - uses: actions/setup-python@v6\n        with:\n          python-version-file: \".python-version\"\n      - name: Setup uv\n        uses: astral-sh/setup-uv@v7\n        with:\n          cache-dependency-glob: |\n            pyproject.toml\n            uv.lock\n      - run: uv sync --locked --no-dev --group github-actions\n      - uses: actions/download-artifact@v8\n        with:\n          name: coverage-html\n          path: htmlcov\n          github-token: ${{ secrets.GITHUB_TOKEN }}\n          run-id: ${{ github.event.workflow_run.id }}\n      - run: uv run smokeshow upload htmlcov\n        env:\n          SMOKESHOW_GITHUB_STATUS_DESCRIPTION: Coverage {coverage-percentage}\n          SMOKESHOW_GITHUB_COVERAGE_THRESHOLD: 100\n          SMOKESHOW_GITHUB_CONTEXT: coverage\n          SMOKESHOW_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n          SMOKESHOW_GITHUB_PR_HEAD_SHA: ${{ github.event.workflow_run.head_sha }}\n          SMOKESHOW_AUTH_KEY: ${{ secrets.SMOKESHOW_AUTH_KEY }}\n"
  },
  {
    "path": ".github/workflows/test-cpython-nightly.yml",
    "content": "name: Test CPython Nightly\n\non:\n  schedule:\n    - cron: \"0 0 * * *\"\n  workflow_dispatch:\n\nenv:\n  UV_NO_SYNC: true\n\njobs:\n  test-latest-python:\n    runs-on: ubuntu-latest\n    continue-on-error: true\n    steps:\n      - uses: actions/checkout@v6\n      - name: Checkout CPython main\n        uses: actions/checkout@v6\n        with:\n          repository: python/cpython\n          ref: main\n          path: ./.cpython\n      - name: Install CPython build dependencies\n        run: sudo apt-get update && sudo apt-get install -y build-essential pkg-config libssl-dev zlib1g-dev\n      - name: Build CPython\n        run: |\n          cd ./.cpython\n          ./configure && make -j $(nproc)\n      - name: Setup uv\n        uses: astral-sh/setup-uv@v7\n        with:\n          enable-cache: true\n          cache-dependency-glob: |\n            pyproject.toml\n            uv.lock\n      - name: Install Dependencies\n        run: uv sync --locked --no-dev --group tests --python ./.cpython/python\n      - name: Lint\n        run: uv run bash scripts/lint.sh\n      - run: mkdir coverage\n      - run: uv run bash scripts/test-files.sh\n      - name: Test\n        run: uv run bash scripts/test.sh\n        env:\n          COVERAGE_FILE: coverage/.coverage.ubuntu-cpython-main\n          CONTEXT: ubuntu-cpython-main\n      - name: Store coverage files\n        uses: actions/upload-artifact@v7\n        with:\n          name: coverage-ubuntu-cpython-main\n          path: coverage\n          include-hidden-files: true\n"
  },
  {
    "path": ".github/workflows/test-redistribute.yml",
    "content": "name: Test Redistribute\n\non:\n  push:\n    branches:\n      - master\n  pull_request:\n    types:\n      - opened\n      - synchronize\n\njobs:\n  test-redistribute:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Dump GitHub context\n        env:\n          GITHUB_CONTEXT: ${{ toJson(github) }}\n        run: echo \"$GITHUB_CONTEXT\"\n      - uses: actions/checkout@v6\n      - name: Set up Python\n        uses: actions/setup-python@v6\n        with:\n          python-version-file: \".python-version\"\n          # Issue ref: https://github.com/actions/setup-python/issues/436\n          # cache: \"pip\"\n          # cache-dependency-path: pyproject.toml\n      - name: Install build dependencies\n        run: pip install build\n      - name: Build source distribution\n        run: python -m build --sdist\n      - name: Decompress source distribution\n        run: |\n          cd dist\n          tar xvf typer*.tar.gz\n      - name: Install test dependencies\n        run: |\n          cd dist/typer*/\n          pip install --group tests --editable .\n      - name: Run source distribution tests\n        run: |\n          cd dist/typer*/\n          bash scripts/test.sh\n      - name: Build wheel distribution\n        run: |\n          cd dist\n          pip wheel --no-deps typer*.tar.gz\n\n  # https://github.com/marketplace/actions/alls-green#why\n  test-redistribute-alls-green:  # This job does nothing and is only used for the branch protection\n    if: always()\n    needs:\n      - test-redistribute\n    runs-on: ubuntu-latest\n    steps:\n      - name: Decide whether the needed jobs succeeded or failed\n        uses: re-actors/alls-green@release/v1\n        with:\n          jobs: ${{ toJSON(needs) }}\n"
  },
  {
    "path": ".github/workflows/test.yml",
    "content": "name: Test\n\non:\n  push:\n    branches:\n      - master\n  pull_request:\n    types:\n      - opened\n      - synchronize\n  schedule:\n    # cron every week on monday\n    - cron: \"0 0 * * 1\"\n\nenv:\n  UV_NO_SYNC: true\n\njobs:\n  test:\n    strategy:\n      matrix:\n        os: [ ubuntu-latest, windows-latest, macos-latest ]\n        python-version: [ \"3.14\" ]\n        uv-resolution:\n          - highest\n        include:\n          - os: ubuntu-latest\n            python-version: \"3.10\"\n            uv-resolution: lowest-direct\n          - os: macos-latest\n            python-version: \"3.11\"\n            uv-resolution: highest\n          - os: windows-latest\n            python-version: \"3.12\"\n            uv-resolution: lowest-direct\n          - os: ubuntu-latest\n            python-version: \"3.13\"\n            uv-resolution: highest\n      fail-fast: false\n    runs-on: ${{ matrix.os }}\n    env:\n      UV_PYTHON: ${{ matrix.python-version }}\n      UV_RESOLUTION: ${{ matrix.uv-resolution }}\n    steps:\n      - name: Dump GitHub context\n        env:\n          GITHUB_CONTEXT: ${{ toJson(github) }}\n        run: echo \"$GITHUB_CONTEXT\"\n      - uses: actions/checkout@v6\n      - name: Set up Python\n        uses: actions/setup-python@v6\n        with:\n          python-version: ${{ matrix.python-version }}\n      - name: Setup uv\n        uses: astral-sh/setup-uv@v7\n        with:\n          enable-cache: true\n          cache-dependency-glob: |\n            pyproject.toml\n            uv.lock\n      - name: Install Dependencies\n        run: uv sync --no-dev --group tests\n      - run: mkdir coverage\n      - run: uv run bash scripts/test-files.sh\n      - name: Test\n        run: uv run bash scripts/test.sh\n        env:\n          COVERAGE_FILE: coverage/.coverage.${{ runner.os }}-py${{ matrix.python-version }}\n          CONTEXT: ${{ runner.os }}-py${{ matrix.python-version }}\n      - name: Store coverage files\n        uses: actions/upload-artifact@v7\n        with:\n          name: coverage-${{ runner.os }}-${{ matrix.python-version }}\n          path: coverage\n          include-hidden-files: true\n\n  coverage-combine:\n    needs: [test]\n    runs-on: ubuntu-latest\n    steps:\n      - name: Dump GitHub context\n        env:\n          GITHUB_CONTEXT: ${{ toJson(github) }}\n        run: echo \"$GITHUB_CONTEXT\"\n      - uses: actions/checkout@v6\n      - uses: actions/setup-python@v6\n        with:\n          python-version-file: \".python-version\"\n      - name: Setup uv\n        uses: astral-sh/setup-uv@v7\n        with:\n          enable-cache: true\n          cache-dependency-glob: |\n            pyproject.toml\n            uv.lock\n      - name: Get coverage files\n        uses: actions/download-artifact@v8\n        with:\n          pattern: coverage-*\n          path: coverage\n          merge-multiple: true\n      - name: Install Dependencies\n        run: uv sync --locked --no-dev --group tests\n      - run: ls -la coverage\n      - run: uv run coverage combine coverage\n      - run: uv run coverage html --show-contexts --title \"Coverage for ${{ github.sha }}\"\n      - name: Store coverage HTML\n        uses: actions/upload-artifact@v7\n        with:\n          name: coverage-html\n          path: htmlcov\n          include-hidden-files: true\n      - run: uv run coverage report --fail-under=100\n\n  # https://github.com/marketplace/actions/alls-green#why\n  check:  # This job does nothing and is only used for the branch protection\n    if: always()\n    needs:\n      - coverage-combine\n    runs-on: ubuntu-latest\n    steps:\n      - name: Dump GitHub context\n        env:\n          GITHUB_CONTEXT: ${{ toJson(github) }}\n        run: echo \"$GITHUB_CONTEXT\"\n      - name: Decide whether the needed jobs succeeded or failed\n        uses: re-actors/alls-green@release/v1\n        with:\n          jobs: ${{ toJSON(needs) }}\n"
  },
  {
    "path": ".gitignore",
    "content": ".vscode\n*.pyc\n__pycache__\n.venv*\nenv\ndist\n.mypy_cache\n.idea\nsite\nhtmlcov\n.pytest_cache\ncoverage.xml\n.coverage*\n.cache\n"
  },
  {
    "path": ".pre-commit-config.yaml",
    "content": "# See https://pre-commit.com for more information\n# See https://pre-commit.com/hooks.html for more hooks\nrepos:\n-   repo: https://github.com/pre-commit/pre-commit-hooks\n    rev: v6.0.0\n    hooks:\n      - id: check-added-large-files\n      - id: check-toml\n      - id: check-yaml\n        args:\n        -   --unsafe\n      - id: end-of-file-fixer\n      - id: trailing-whitespace\n\n-   repo: local\n    hooks:\n      - id: local-ruff-check\n        name: ruff check\n        entry: uv run ruff check --force-exclude --fix --exit-non-zero-on-fix\n        require_serial: true\n        language: unsupported\n        types: [python]\n\n      - id: local-ruff-format\n        name: ruff format\n        entry: uv run ruff format --force-exclude --exit-non-zero-on-format\n        require_serial: true\n        language: unsupported\n        types: [python]\n\n      - id: local-mypy\n        name: mypy check\n        entry: uv run mypy typer\n        require_serial: true\n        language: unsupported\n        pass_filenames: false\n\n      - id: local-ty\n        name: ty check\n        entry: uv run ty check typer\n        require_serial: true\n        language: unsupported\n        pass_filenames: false\n\n      - id: generate-readme\n        language: unsupported\n        name: generate README.md from index.md\n        entry: uv run ./scripts/docs.py generate-readme\n        files: ^docs/index\\.md|scripts/docs\\.py$\n        pass_filenames: false\n"
  },
  {
    "path": ".python-version",
    "content": "3.10\n"
  },
  {
    "path": "CITATION.cff",
    "content": "# This CITATION.cff file was generated with cffinit.\n# Visit https://bit.ly/cffinit to generate yours today!\n\ncff-version: 1.2.0\ntitle: Typer\nmessage: >-\n  If you use this software, please cite it using the\n  metadata from this file.\ntype: software\nauthors:\n  - given-names: Sebastián\n    family-names: Ramírez\n    email: tiangolo@gmail.com\nidentifiers:\nrepository-code: 'https://github.com/fastapi/typer'\nurl: 'https://typer.tiangolo.com'\nabstract: >-\n  Typer, build great CLIs. Easy to code. Based on Python type hints.\nkeywords:\n  - typer\n  - click\nlicense: MIT\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "Please read the [Development - Contributing](https://typer.tiangolo.com/contributing/) guidelines in the documentation site.\n"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2019 Sebastián Ramírez\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "<p align=\"center\">\n  <a href=\"https://typer.tiangolo.com\"><img src=\"https://typer.tiangolo.com/img/logo-margin/logo-margin-vector.svg#only-light\" alt=\"Typer\"></a>\n\n</p>\n<p align=\"center\">\n    <em>Typer, build great CLIs. Easy to code. Based on Python type hints.</em>\n</p>\n<p align=\"center\">\n<a href=\"https://github.com/fastapi/typer/actions?query=workflow%3ATest+event%3Apush+branch%3Amaster\" target=\"_blank\">\n    <img src=\"https://github.com/fastapi/typer/actions/workflows/test.yml/badge.svg?event=push&branch=master\" alt=\"Test\">\n</a>\n<a href=\"https://github.com/fastapi/typer/actions?query=workflow%3APublish\" target=\"_blank\">\n    <img src=\"https://github.com/fastapi/typer/workflows/Publish/badge.svg\" alt=\"Publish\">\n</a>\n<a href=\"https://coverage-badge.samuelcolvin.workers.dev/redirect/fastapi/typer\" target=\"_blank\">\n    <img src=\"https://coverage-badge.samuelcolvin.workers.dev/fastapi/typer.svg\" alt=\"Coverage\">\n<a href=\"https://pypi.org/project/typer\" target=\"_blank\">\n    <img src=\"https://img.shields.io/pypi/v/typer?color=%2334D058&label=pypi%20package\" alt=\"Package version\">\n</a>\n</p>\n\n---\n\n**Documentation**: <a href=\"https://typer.tiangolo.com\" target=\"_blank\">https://typer.tiangolo.com</a>\n\n**Source Code**: <a href=\"https://github.com/fastapi/typer\" target=\"_blank\">https://github.com/fastapi/typer</a>\n\n---\n\nTyper is a library for building <abbr title=\"command line interface, programs executed from a terminal\">CLI</abbr> applications that users will **love using** and developers will **love creating**. Based on Python type hints.\n\nIt's also a command line tool to run scripts, automatically converting them to CLI applications.\n\nThe key features are:\n\n* **Intuitive to write**: Great editor support. <abbr title=\"also known as auto-complete, autocompletion, IntelliSense\">Completion</abbr> everywhere. Less time debugging. Designed to be easy to use and learn. Less time reading docs.\n* **Easy to use**: It's easy to use for the final users. Automatic help, and automatic completion for all shells.\n* **Short**: Minimize code duplication. Multiple features from each parameter declaration. Fewer bugs.\n* **Start simple**: The simplest example adds only 2 lines of code to your app: **1 import, 1 function call**.\n* **Grow large**: Grow in complexity as much as you want, create arbitrarily complex trees of commands and groups of subcommands, with options and arguments.\n* **Run scripts**: Typer includes a `typer` command/program that you can use to run scripts, automatically converting them to CLIs, even if they don't use Typer internally.\n\n## FastAPI of CLIs\n\n**Typer** is <a href=\"https://fastapi.tiangolo.com\" class=\"external-link\" target=\"_blank\">FastAPI</a>'s little sibling, it's the FastAPI of CLIs.\n\n## Installation\n\nCreate and activate a <a href=\"https://typer.tiangolo.com/virtual-environments/\" class=\"external-link\" target=\"_blank\">virtual environment</a> and then install **Typer**:\n\n<div class=\"termy\">\n\n```console\n$ pip install typer\n---> 100%\nSuccessfully installed typer rich shellingham\n```\n\n</div>\n\n## Example\n\n### The absolute minimum\n\n* Create a file `main.py` with:\n\n```Python\ndef main(name: str):\n    print(f\"Hello {name}\")\n```\n\nThis script doesn't even use Typer internally. But you can use the `typer` command to run it as a CLI application.\n\n### Run it\n\nRun your application with the `typer` command:\n\n<div class=\"termy\">\n\n```console\n// Run your application\n$ typer main.py run\n\n// You get a nice error, you are missing NAME\nUsage: typer [PATH_OR_MODULE] run [OPTIONS] NAME\nTry 'typer [PATH_OR_MODULE] run --help' for help.\n╭─ Error ───────────────────────────────────────────╮\n│ Missing argument 'NAME'.                          │\n╰───────────────────────────────────────────────────╯\n\n\n// You get a --help for free\n$ typer main.py run --help\n\nUsage: typer [PATH_OR_MODULE] run [OPTIONS] NAME\n\nRun the provided Typer app.\n\n╭─ Arguments ───────────────────────────────────────╮\n│ *    name      TEXT  [default: None] [required]   |\n╰───────────────────────────────────────────────────╯\n╭─ Options ─────────────────────────────────────────╮\n│ --help          Show this message and exit.       │\n╰───────────────────────────────────────────────────╯\n\n// Now pass the NAME argument\n$ typer main.py run Camila\n\nHello Camila\n\n// It works! 🎉\n```\n\n</div>\n\nThis is the simplest use case, not even using Typer internally, but it can already be quite useful for simple scripts.\n\n**Note**: auto-completion works when you create a Python package and run it with `--install-completion` or when you use the `typer` command.\n\n## Use Typer in your code\n\nNow let's start using Typer in your own code, update `main.py` with:\n\n```Python\nimport typer\n\n\ndef main(name: str):\n    print(f\"Hello {name}\")\n\n\nif __name__ == \"__main__\":\n    typer.run(main)\n```\n\nNow you could run it with Python directly:\n\n<div class=\"termy\">\n\n```console\n// Run your application\n$ python main.py\n\n// You get a nice error, you are missing NAME\nUsage: main.py [OPTIONS] NAME\nTry 'main.py --help' for help.\n╭─ Error ───────────────────────────────────────────╮\n│ Missing argument 'NAME'.                          │\n╰───────────────────────────────────────────────────╯\n\n\n// You get a --help for free\n$ python main.py --help\n\nUsage: main.py [OPTIONS] NAME\n\n╭─ Arguments ───────────────────────────────────────╮\n│ *    name      TEXT  [default: None] [required]   |\n╰───────────────────────────────────────────────────╯\n╭─ Options ─────────────────────────────────────────╮\n│ --help          Show this message and exit.       │\n╰───────────────────────────────────────────────────╯\n\n// Now pass the NAME argument\n$ python main.py Camila\n\nHello Camila\n\n// It works! 🎉\n```\n\n</div>\n\n**Note**: you can also call this same script with the `typer` command, but you don't need to.\n\n## Example upgrade\n\nThis was the simplest example possible.\n\nNow let's see one a bit more complex.\n\n### An example with two subcommands\n\nModify the file `main.py`.\n\nCreate a `typer.Typer()` app, and create two subcommands with their parameters.\n\n```Python hl_lines=\"3  6  11  20\"\nimport typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef hello(name: str):\n    print(f\"Hello {name}\")\n\n\n@app.command()\ndef goodbye(name: str, formal: bool = False):\n    if formal:\n        print(f\"Goodbye Ms. {name}. Have a good day.\")\n    else:\n        print(f\"Bye {name}!\")\n\n\nif __name__ == \"__main__\":\n    app()\n```\n\nAnd that will:\n\n* Explicitly create a `typer.Typer` app.\n    * The previous `typer.run` actually creates one implicitly for you.\n* Add two subcommands with `@app.command()`.\n* Execute the `app()` itself, as if it was a function (instead of `typer.run`).\n\n### Run the upgraded example\n\nCheck the new help:\n\n<div class=\"termy\">\n\n```console\n$ python main.py --help\n\n Usage: main.py [OPTIONS] COMMAND [ARGS]...\n\n╭─ Options ─────────────────────────────────────────╮\n│ --install-completion          Install completion  │\n│                               for the current     │\n│                               shell.              │\n│ --show-completion             Show completion for │\n│                               the current shell,  │\n│                               to copy it or       │\n│                               customize the       │\n│                               installation.       │\n│ --help                        Show this message   │\n│                               and exit.           │\n╰───────────────────────────────────────────────────╯\n╭─ Commands ────────────────────────────────────────╮\n│ goodbye                                           │\n│ hello                                             │\n╰───────────────────────────────────────────────────╯\n\n// When you create a package you get ✨ auto-completion ✨ for free, installed with --install-completion\n\n// You have 2 subcommands (the 2 functions): goodbye and hello\n```\n\n</div>\n\nNow check the help for the `hello` command:\n\n<div class=\"termy\">\n\n```console\n$ python main.py hello --help\n\n Usage: main.py hello [OPTIONS] NAME\n\n╭─ Arguments ───────────────────────────────────────╮\n│ *    name      TEXT  [default: None] [required]   │\n╰───────────────────────────────────────────────────╯\n╭─ Options ─────────────────────────────────────────╮\n│ --help          Show this message and exit.       │\n╰───────────────────────────────────────────────────╯\n```\n\n</div>\n\nAnd now check the help for the `goodbye` command:\n\n<div class=\"termy\">\n\n```console\n$ python main.py goodbye --help\n\n Usage: main.py goodbye [OPTIONS] NAME\n\n╭─ Arguments ───────────────────────────────────────╮\n│ *    name      TEXT  [default: None] [required]   │\n╰───────────────────────────────────────────────────╯\n╭─ Options ─────────────────────────────────────────╮\n│ --formal    --no-formal      [default: no-formal] │\n│ --help                       Show this message    │\n│                              and exit.            │\n╰───────────────────────────────────────────────────╯\n\n// Automatic --formal and --no-formal for the bool option 🎉\n```\n\n</div>\n\nNow you can try out the new command line application:\n\n<div class=\"termy\">\n\n```console\n// Use it with the hello command\n\n$ python main.py hello Camila\n\nHello Camila\n\n// And with the goodbye command\n\n$ python main.py goodbye Camila\n\nBye Camila!\n\n// And with --formal\n\n$ python main.py goodbye --formal Camila\n\nGoodbye Ms. Camila. Have a good day.\n```\n\n</div>\n\n**Note**: If your app only has one command, by default the command name is **omitted** in usage: `python main.py Camila`. However, when there are multiple commands, you must **explicitly include the command name**: `python main.py hello Camila`. See [One or Multiple Commands](https://typer.tiangolo.com/tutorial/commands/one-or-multiple/) for more details.\n\n### Recap\n\nIn summary, you declare **once** the types of parameters (*CLI arguments* and *CLI options*) as function parameters.\n\nYou do that with standard modern Python types.\n\nYou don't have to learn a new syntax, the methods or classes of a specific library, etc.\n\nJust standard **Python**.\n\nFor example, for an `int`:\n\n```Python\ntotal: int\n```\n\nor for a `bool` flag:\n\n```Python\nforce: bool\n```\n\nAnd similarly for **files**, **paths**, **enums** (choices), etc. And there are tools to create **groups of subcommands**, add metadata, extra **validation**, etc.\n\n**You get**: great editor support, including **completion** and **type checks** everywhere.\n\n**Your users get**: automatic **`--help`**, **auto-completion** in their terminal (Bash, Zsh, Fish, PowerShell) when they install your package or when using the `typer` command.\n\nFor a more complete example including more features, see the <a href=\"https://typer.tiangolo.com/tutorial/\">Tutorial - User Guide</a>.\n\n## Dependencies\n\n**Typer** stands on the shoulders of giants. It has three required dependencies:\n\n* <a href=\"https://click.palletsprojects.com/\" class=\"external-link\" target=\"_blank\">Click</a>: a popular tool for building CLIs in Python. Typer is based on it.\n* <a href=\"https://rich.readthedocs.io/en/stable/index.html\" class=\"external-link\" target=\"_blank\"><code>rich</code></a>: to show nicely formatted errors automatically.\n* <a href=\"https://github.com/sarugaku/shellingham\" class=\"external-link\" target=\"_blank\"><code>shellingham</code></a>: to automatically detect the current shell when installing completion.\n\n### `typer-slim`\n\nThere used to be a slimmed-down version of Typer called `typer-slim`, which didn't include the dependencies `rich` and `shellingham`, nor the `typer` command.\n\nHowever, since version 0.22.0, we have stopped supporting this, and `typer-slim` now simply installs (all of) Typer.\n\nIf you want to disable Rich globally, you can set an environmental variable `TYPER_USE_RICH` to `False` or `0`.\n\n## License\n\nThis project is licensed under the terms of the MIT license.\n"
  },
  {
    "path": "SECURITY.md",
    "content": "# Security Policy\n\nSecurity is very important for Typer and its community. 🔒\n\nLearn more about it below. 👇\n\n## Versions\n\nThe latest versions of Typer are supported.\n\nYou are encouraged to [write tests](https://typer.tiangolo.com/tutorial/testing/) for your application and update your Typer version frequently after ensuring that your tests are passing. This way you will benefit from the latest features, bug fixes, and **security fixes**.\n\n## Reporting a Vulnerability\n\nIf you think you found a vulnerability, and even if you are not sure about it, please report it right away by sending an email to: security@tiangolo.com. Please try to be as explicit as possible, describing all the steps and example code to reproduce the security issue.\n\nI (the author, [@tiangolo](https://twitter.com/tiangolo)) will review it thoroughly and get back to you.\n\n## Public Discussions\n\nPlease restrain from publicly discussing a potential security vulnerability. 🙊\n\nIt's better to discuss privately and try to find a solution first, to limit the potential impact as much as possible.\n\n---\n\nThanks for your help!\n\nThe Typer community and I thank you for that. 🙇\n"
  },
  {
    "path": "data/members.yml",
    "content": "members:\n- login: tiangolo\n- login: svlandeg\n- login: patrick91\n"
  },
  {
    "path": "docs/about/index.md",
    "content": "# About\n\nAbout **Typer**, its design, inspiration, and more. 🤓\n"
  },
  {
    "path": "docs/alternatives.md",
    "content": "# Alternatives, Inspiration and Comparisons\n\nWhat inspired **Typer**, how it compares to other alternatives and what it learned from them.\n\n## Intro\n\n**Typer** wouldn't exist if not for the previous work of others.\n\nThere have been many tools created before that have helped inspire its creation.\n\n## Previous tools\n\n### <a href=\"https://docs.python.org/3/library/argparse.html\" class=\"external-link\" target=\"_blank\">`argparse`</a>\n\n`argparse` is the Python standard library's module to write CLIs.\n\nIt provides a better alternative than reading the *CLI Parameters* as a `list` of `str` and parsing everything by hand.\n\n/// check | Inspired **Typer** to\n\nProvide a better development experience than just reading *CLI Parameters* by hand.\n\n///\n\n### <a href=\"https://hugapi.github.io/hug/\" class=\"external-link\" target=\"_blank\">Hug</a>\n\nHug is a library to create APIs and CLIs, it uses parameters in functions to declare the required data.\n\nIt inspired a lot of the ideas in **FastAPI** and **Typer**.\n\n/// check | Inspired **Typer** to\n\nUse function parameters to declare *CLI arguments* and *CLI options* as it simplifies a lot the development experience.\n\n///\n\n### <a href=\"https://plac.readthedocs.io/en/latest/\" class=\"external-link\" target=\"_blank\">Plac</a>\n\nPlac is another library to create CLIs using parameters in functions, similar to Hug.\n\n/// check | Inspired **Typer** to\n\nProvide a simple way to use a function as a command line app, without having to create a complete app, with `typer.run(some_function)`.\n\n///\n\n### <a href=\"https://pydantic-docs.helpmanual.io/\" class=\"external-link\" target=\"_blank\">Pydantic</a>\n\nPydantic is a library to handle data validation using standard modern Python type annotations.\n\nIt powers **FastAPI** underneath.\n\nIt is not used by **Typer**, but it inspired a lot of the design (through **FastAPI**).\n\n/// check | Inspired **Typer** to\n\nUse standard Python type annotations to declare types instead of library-specific types or classes and use them for data validation and documentation.\n\n///\n\n### <a href=\"https://click.palletsprojects.com\" class=\"external-link\" target=\"_blank\">Click</a>\n\nClick is one of the most widely used libraries to create CLIs in Python.\n\nIt's a very powerful tool and there are many CLIs built with it. It is what powers **Typer** underneath.\n\nIt also uses functions with parameters for *CLI arguments* and *CLI options*, but the declaration of the specific *CLI arguments*, *CLI options*, types, etc, is done in decorators on top of the function. This requires some code repetition (e.g. a *CLI Option* name `--verbose` and a variable name `verbose`) and synchronization between two places related to the same information (the decorator and the parameter function).\n\nIt uses decorators on top of functions to modify the actual value of those functions, converting them to instances of a specific class. This is a clever trick, but code editors can't provide great support for autocompletion that way.\n\nIt was built with some great ideas and design using the features available in the language at the time (Python 2.x).\n\n/// check | **Typer** uses it for\n\nEverything. 🚀\n\n**Typer** mainly adds a layer on top of Click, making the code simpler and easier to use, with autocompletion everywhere, etc, but providing all the powerful features of Click underneath.\n\nAs someone pointed out: <em><a href=\"https://twitter.com/fishnets88/status/1210126833745838080\" class=\"external-link\" target=\"_blank\">\"Nice to see it is built on Click but adds the type stuff. Me gusta!\"</a></em>\n\n///\n\n### <a href=\"https://github.com/click-contrib/click-completion\" class=\"external-link\" target=\"_blank\">`click-completion`</a>\n\n`click-completion` is a plug-in for Click. It was created to extend completion support for shells when Click only had support for Bash completion.\n\nPrevious versions of **Typer** had deep integrations with `click-completion` and used it as an optional dependency. But now all the completion logic is implemented internally in **Typer** itself, the internal logic was heavily inspired and using some parts of `click-completion`.\n\nAnd now **Typer** improved it to have new features, tests, some bug fixes (for issues in plain `click-completion` and Click), and better support for shells, including modern versions of PowerShell (e.g. the default versions that come with Windows 10).\n\n/// check | Inspired **Typer** to\n\nProvide auto completion for all the shells.\n\n///\n\n### <a href=\"https://fastapi.tiangolo.com/\" class=\"external-link\" target=\"_blank\">FastAPI</a>\n\nI created **FastAPI** to provide an easy way to build APIs with autocompletion for everything in the code (and some other <a href=\"https://fastapi.tiangolo.com/features/\" class=\"external-link\" target=\"_blank\">features</a>).\n\n**Typer** is the \"FastAPI of CLIs\".\n\nIt uses the same design and usage of FastAPI as much as possible. So, if you have used FastAPI, you know how to use Typer.\n"
  },
  {
    "path": "docs/contributing.md",
    "content": "# Development - Contributing\n\nFirst, you might want to see the basic ways to [help Typer and get help](help-typer.md){.internal-link target=_blank}.\n\n## Developing\n\nIf you already cloned the <a href=\"https://github.com/fastapi/typer\" class=\"external-link\" target=\"_blank\">typer repository</a> and you want to deep dive in the code, here are some guidelines to set up your environment.\n\n### Install Requirements Using `uv`\n\nCreate a virtual environment and install the required packages in one command:\n\n<div class=\"termy\">\n\n```console\n$ uv sync\n\n---> 100%\n```\n\n</div>\n\nIt will install all the dependencies and your local Typer in your local environment.\n\n### Using your Local Typer\n\nIf you create a Python file that imports and uses Typer, and run it with the Python from your local environment, it will use your cloned local Typer source code.\n\nAnd if you update that local Typer source code when you run that Python file again, it will use the fresh version of Typer you just edited.\n\nThat way, you don't have to \"install\" your local version to be able to test every change.\n\n/// note | \"Technical Details\"\n\nThis only happens when you install using this included `requirements.txt` instead of running `pip install typer` directly.\n\nThat is because inside the `requirements.txt` file, the local version of Typer is marked to be installed in \"editable\" mode, with the `-e` option.\n\n///\n\n### Format\n\nThere is a script that you can run that will format and clean all your code:\n\n<div class=\"termy\">\n\n```console\n$ bash scripts/format.sh\n```\n\n</div>\n\nIt will also auto-sort all your imports.\n\n## Tests\n\nThere is a script that you can run locally to test all the code and generate coverage reports in HTML:\n\n<div class=\"termy\">\n\n```console\n$ bash scripts/test-cov-html.sh\n```\n\n</div>\n\nThis command generates a directory `./htmlcov/`, if you open the file `./htmlcov/index.html` in your browser, you can explore interactively the regions of code that are covered by the tests, and notice if there is any region missing.\n\n## Completion\n\nTo try and test the completion for different shells and check that they are working you can use a Docker container.\n\nThere's a `Dockerfile` and a Docker Compose file `compose.yaml` at `./scripts/docker/`.\n\nIt has installed `bash`, `zsh`, `fish`, and `pwsh` (PowerShell for Linux).\n\nIt also has installed `nano` and `vim`, so that you can check the modified configuration files for the shells (for example `.bashrc`, `.zshrc`, etc).\n\nIt also has `uv` installed, so you can install the dependencies and the project quickly.\n\nThe Docker Compose file mounts the main directory as `/code` inside the container, so you can change things and try them out.\n\nGo to the `./scripts/docker/` directory:\n\n```console\n$ cd scripts/docker/\n```\n\nThen run an interactive session with `bash` inside the container:\n\n```console\n$ docker compose run typer bash\n\nroot@79c4b9b70cbe:/code#\n```\n\nThen inside the container, you can install `typer` with:\n\n```console\n$ uv pip install -r requirements.txt\n```\n\nThen, you can start the shell you want to use, the one where you want to try out completion:\n\n* `bash`\n* `fish`\n* `pwsh`\n* `zsh`\n\nFor example:\n\n```console\n$ zsh\n```\n\nThen install `typer` completion:\n\n```console\n$ typer --install-completion\n```\n\n/// info\n\nIn `pwsh` you will probably get a warning of:\n\n```plaintext\nSet-ExecutionPolicy: Operation is not supported on this platform.\n```\n\nthis is because that configuration is only available in Windows (and needed there), not in PowerShell for Linux.\n\n///\n\nFor completion to take effect, you need to restart the shell. So, exit the current shell:\n\n```console\n$ exit\n```\n\nand start a new shell (for the same shell you installed completion in) again. For example:\n\n```console\n$ zsh\n```\n\nNow you could create a demo file on the same Typer directory in your editor, for example `demo.py`:\n\n```python\nimport typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef hello():\n    print(\"Hello\")\n\n\n@app.command()\ndef goodbye():\n    print(\"Goodbye\")\n\n\nif __name__ == \"__main__\":\n    app()\n```\n\nBecause the directory is mounted as a volume, you will be able to access the file from inside the container.\n\nSo, you can try running it with the `typer` command, that will use the installed shell completion:\n\n```console\n$ typer demo.py <TAB>\n```\n\nAnd you should see the completion working:\n\n```console\nrun    -- Run the provided Typer app.\nutils  -- Extra utility commands for Typer apps.\n```\n\nAnd the same for the commands in your `demo.py` file:\n\n```console\n$ typer demo.py run <TAB>\n\nhello    goodbye\n```\n\nYou can also check the configuration file using `nano` or `vim`, for example:\n\n```bash\nnano ~/.zshrc\n```\n\nIt will show some content like:\n\n```bash\nfpath+=~/.zfunc; autoload -Uz compinit; compinit\n\n\nzstyle ':completion:*' menu select\n```\n\nIf you exit from the container, you can start a new one, you will probably have to install the packages again and install completion again.\n\nUsing this process, you can test all the shells, with their completions, being able to start from scratch quickly in a fresh container, and verifying that everything works as expected.\n\n## Docs\n\nFirst, make sure you set up your environment as described above, that will install all the requirements.\n\n### Docs live\n\nDuring local development, there is a script that builds the site and checks for any changes, live-reloading:\n\n<div class=\"termy\">\n\n```console\n$ python ./scripts/docs.py live\n\n<span style=\"color: green;\">[INFO]</span> Serving on http://127.0.0.1:8008\n<span style=\"color: green;\">[INFO]</span> Start watching changes\n<span style=\"color: green;\">[INFO]</span> Start detecting changes\n```\n\n</div>\n\nIt will serve the documentation on `http://127.0.0.1:8008`.\n\nThat way, you can edit the documentation/source files and see the changes live.\n\n/// tip\n\nAlternatively, you can perform the same steps that script does manually.\n\nGo into the docs directory at `docs/`:\n\n```console\n$ cd docs/\n```\n\nThen run `mkdocs` in that directory:\n\n```console\n$ mkdocs serve --dev-addr 8008\n```\n\n///\n\n#### Typer CLI (optional)\n\nThe instructions here show you how to use the script at `./scripts/docs.py` with the `python` program directly.\n\nBut you can also use <a href=\"https://typer.tiangolo.com/typer-cli/\" class=\"external-link\" target=\"_blank\">Typer CLI</a>, and you will get autocompletion in your terminal for the commands after installing completion.\n\nIf you install Typer CLI, you can install completion with:\n\n<div class=\"termy\">\n\n```console\n$ typer --install-completion\n\nzsh completion installed in /home/user/.bashrc.\nCompletion will take effect once you restart the terminal.\n```\n\n</div>\n\n### Docs Structure\n\nThe documentation uses <a href=\"https://www.mkdocs.org/\" class=\"external-link\" target=\"_blank\">MkDocs</a>.\n\nAnd there are extra tools/scripts in place in `./scripts/docs.py`.\n\n/// tip\n\nYou don't need to see the code in `./scripts/docs.py`, you just use it in the command line.\n\n///\n\nAll the documentation is in Markdown format in the directory `./docs`.\n\nMany of the tutorials have blocks of code.\n\nIn most of the cases, these blocks of code are actual complete applications that can be run as is.\n\nIn fact, those blocks of code are not written inside the Markdown, they are Python files in the `./docs_src/` directory.\n\nAnd those Python files are included/injected in the documentation when generating the site.\n\n### Docs for Tests\n\nMost of the tests actually run against the example source files in the documentation.\n\nThis helps to make sure that:\n\n* The documentation is up-to-date.\n* The documentation examples can be run as is.\n* Most of the features are covered by the documentation, ensured by test coverage.\n\n## Automated Code and AI\n\nYou are encouraged to use all the tools you want to do your work and contribute as efficiently as possible, this includes AI (LLM) tools, etc. Nevertheless, contributions should have meaningful human intervention, judgement, context, etc.\n\nIf the **human effort** put in a PR, e.g. writing LLM prompts, is **less** than the **effort we would need to put** to **review it**, please **don't** submit the PR.\n\nThink of it this way: we can already write LLM prompts or run automated tools ourselves, and that would be faster than reviewing external PRs.\n\n### Closing Automated and AI PRs\n\nIf we see PRs that seem AI generated or automated in similar ways, we'll flag them and close them.\n\nThe same applies to comments and descriptions, please don't copy paste the content generated by an LLM.\n\n### Human Effort Denial of Service\n\nUsing automated tools and AI to submit PRs or comments that we have to carefully review and handle would be the equivalent of a <a href=\"https://en.wikipedia.org/wiki/Denial-of-service_attack\" class=\"external-link\" target=\"_blank\">Denial-of-service attack</a> on our human effort.\n\nIt would be very little effort from the person submitting the PR (an LLM prompt) that generates a large amount of effort on our side (carefully reviewing code).\n\nPlease don't do that.\n\nWe'll need to block accounts that spam us with repeated automated PRs or comments.\n\n### Use Tools Wisely\n\nAs Uncle Ben said:\n\n<blockquote>\nWith great <strike>power</strike> <strong>tools</strong> comes great responsibility.\n</blockquote>\n\nAvoid inadvertently doing harm.\n\nYou have amazing tools at hand, use them wisely to help effectively.\n"
  },
  {
    "path": "docs/css/custom.css",
    "content": "/* Fira Code, including characters used by Rich output, like the \"heavy right-pointing angle bracket ornament\", not included in Google Fonts */\n@import url(https://cdn.jsdelivr.net/npm/firacode@6.2.0/distr/fira_code.css);\n/* Noto Color Emoji for emoji support with the same font everywhere */\n@import url(https://fonts.googleapis.com/css2?family=Noto+Color+Emoji&display=swap);\n\n/* Override default code font in Material for MkDocs to Fira Code */\n:root {\n    --md-code-font: \"Fira Code\", monospace, \"Noto Color Emoji\";\n}\n\n/* Override default regular font in Material for MkDocs to include Noto Color Emoji */\n:root {\n    --md-text-font: \"Roboto\", \"Noto Color Emoji\";\n}\n\n.termynal-comment {\n    color: #4a968f;\n    font-style: italic;\n    display: block;\n}\n\n.termy [data-termynal] {\n    white-space: pre-wrap;\n}\n\n.termy .linenos {\n    display: none;\n}\n\n/* External links: detected by JS comparing origin to site origin\n   JS sets data-external-link on links pointing outside the site\n   Skip image links, .no-link-icon, and .announce-link */\na[data-external-link]:not(:has(img)):not(.no-link-icon):not(.announce-link) {\n  /* For right to left languages */\n  direction: ltr;\n  display: inline-block;\n}\n\na[data-external-link]:not(:has(img)):not(.no-link-icon):not(.announce-link)::after {\n  content: \"\";\n  display: inline-block;\n  width: 0.75em;\n  height: 0.75em;\n  margin-left: 0.25em;\n  vertical-align: middle;\n  opacity: 0.55;\n  background: currentColor;\n  -webkit-mask-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6'/%3E%3Cpolyline points='15 3 21 3 21 9'/%3E%3Cline x1='10' y1='14' x2='21' y2='3'/%3E%3C/svg%3E\");\n  -webkit-mask-size: contain;\n  -webkit-mask-repeat: no-repeat;\n  mask-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6'/%3E%3Cpolyline points='15 3 21 3 21 9'/%3E%3Cline x1='10' y1='14' x2='21' y2='3'/%3E%3C/svg%3E\");\n  mask-size: contain;\n  mask-repeat: no-repeat;\n}\n\na[data-external-link]:not(:has(img)):not(.no-link-icon):not(.announce-link):hover::after {\n  opacity: 0.85;\n}\n\n/* Internal links opening in new tab: same-origin links with target=_blank\n   JS sets data-internal-link on links pointing to the same site origin\n   Skip image links, .no-link-icon, and .announce-link */\na[data-internal-link][target=\"_blank\"]:not(:has(img)):not(.no-link-icon):not(.announce-link) {\n  /* For right to left languages */\n  direction: ltr;\n  display: inline-block;\n}\n\na[data-internal-link][target=\"_blank\"]:not(:has(img)):not(.no-link-icon):not(.announce-link)::after {\n  content: \"\";\n  display: inline-block;\n  width: 0.75em;\n  height: 0.75em;\n  margin-left: 0.25em;\n  vertical-align: middle;\n  opacity: 0.55;\n  background: currentColor;\n  -webkit-mask-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='7' width='14' height='14' rx='2'/%3E%3Cpath d='M7 3h14v14'/%3E%3C/svg%3E\");\n  -webkit-mask-size: contain;\n  -webkit-mask-repeat: no-repeat;\n  mask-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='7' width='14' height='14' rx='2'/%3E%3Cpath d='M7 3h14v14'/%3E%3C/svg%3E\");\n  mask-size: contain;\n  mask-repeat: no-repeat;\n}\n\na[data-internal-link][target=\"_blank\"]:not(:has(img)):not(.no-link-icon):not(.announce-link):hover::after {\n  opacity: 0.85;\n}\n\n/* Disable link icons in footer and header nav */\n.md-footer a::after,\n.md-header a::after {\n  content: none !important;\n}\n\n.shadow {\n    box-shadow: 5px 5px 10px #999;\n}\n\n.user-list {\n    display: flex;\n    flex-wrap: wrap;\n    margin-bottom: 2rem;\n}\n\n.user-list-center {\n    justify-content: space-evenly;\n}\n\n.user {\n    margin: 1em;\n    min-width: 7em;\n}\n\n.user .avatar-wrapper {\n    width: 80px;\n    height: 80px;\n    margin: 10px auto;\n    overflow: hidden;\n    border-radius: 50%;\n    position: relative;\n}\n\n.user .avatar-wrapper img {\n    position: absolute;\n    top: 50%;\n    left: 50%;\n    transform: translate(-50%, -50%);\n}\n\n.user .title {\n    text-align: center;\n}\n\n.user .count {\n    font-size: 80%;\n    text-align: center;\n}\n"
  },
  {
    "path": "docs/css/termynal.css",
    "content": "/**\n * termynal.js\n *\n * @author Ines Montani <ines@ines.io>\n * @version 0.0.1\n * @license MIT\n */\n\n:root {\n    --color-bg: #252a33;\n    --color-text: #eee;\n    --color-text-subtle: #a2a2a2;\n}\n\n[data-termynal] {\n    width: 750px;\n    max-width: 100%;\n    background: var(--color-bg);\n    color: var(--color-text);\n    /* font-size: 18px; */\n    font-size: 15px;\n    /* font-family: 'Fira Mono', Consolas, Menlo, Monaco, 'Courier New', Courier, monospace; */\n    font-family: var(--md-code-font-family), 'Roboto Mono', 'Fira Mono', Consolas, Menlo, Monaco, 'Courier New', Courier, monospace;\n    border-radius: 4px;\n    padding: 75px 45px 35px;\n    position: relative;\n    -webkit-box-sizing: border-box;\n            box-sizing: border-box;\n    /* Custom line-height */\n    line-height: 1.2;\n}\n\n[data-termynal]:before {\n    content: '';\n    position: absolute;\n    top: 15px;\n    left: 15px;\n    display: inline-block;\n    width: 15px;\n    height: 15px;\n    border-radius: 50%;\n    /* A little hack to display the window buttons in one pseudo element. */\n    background: #d9515d;\n    -webkit-box-shadow: 25px 0 0 #f4c025, 50px 0 0 #3ec930;\n            box-shadow: 25px 0 0 #f4c025, 50px 0 0 #3ec930;\n}\n\n[data-termynal]:after {\n    content: 'bash';\n    position: absolute;\n    color: var(--color-text-subtle);\n    top: 5px;\n    left: 0;\n    width: 100%;\n    text-align: center;\n}\n\na[data-terminal-control] {\n    text-align: right;\n    display: block;\n    color: #aebbff;\n}\n\n[data-ty] {\n    display: block;\n    line-height: 2;\n}\n\n[data-ty]:before {\n    /* Set up defaults and ensure empty lines are displayed. */\n    content: '';\n    display: inline-block;\n    vertical-align: middle;\n}\n\n[data-ty=\"input\"]:before,\n[data-ty-prompt]:before {\n    margin-right: 0.75em;\n    color: var(--color-text-subtle);\n}\n\n[data-ty=\"input\"]:before {\n    content: '$';\n}\n\n[data-ty][data-ty-prompt]:before {\n    content: attr(data-ty-prompt);\n}\n\n[data-ty-cursor]:after {\n    content: attr(data-ty-cursor);\n    font-family: monospace;\n    margin-left: 0.5em;\n    -webkit-animation: blink 1s infinite;\n            animation: blink 1s infinite;\n}\n\n\n/* Cursor animation */\n\n@-webkit-keyframes blink {\n    50% {\n        opacity: 0;\n    }\n}\n\n@keyframes blink {\n    50% {\n        opacity: 0;\n    }\n}\n"
  },
  {
    "path": "docs/environment-variables.md",
    "content": "# Environment Variables\n\nBefore we jump into **Typer** code, let's cover a bit some of the **basics** that we'll need to understand how to work with Python (and programming) in general. Let's check a bit about **environment variables**.\n\n/// tip\n\nIf you already know what \"environment variables\" are and how to use them, feel free to skip this.\n\n///\n\nAn environment variable (also known as \"**env var**\") is a variable that lives **outside** of the Python code, in the **operating system**, and could be read by your Python code (or by other programs as well).\n\nEnvironment variables could be useful for handling application **settings**, as part of the **installation** of Python, etc.\n\n## Create and Use Env Vars\n\nYou can **create** and use environment variables in the **shell (terminal)**, without needing Python:\n\n//// tab | Linux, macOS, Windows Bash\n\n<div class=\"termy\">\n\n```console\n// You could create an env var MY_NAME with\n$ export MY_NAME=\"Wade Wilson\"\n\n// Then you could use it with other programs, like\n$ echo \"Hello $MY_NAME\"\n\nHello Wade Wilson\n```\n\n</div>\n\n////\n\n//// tab | Windows PowerShell\n\n<div class=\"termy\">\n\n```console\n// Create an env var MY_NAME\n$ $Env:MY_NAME = \"Wade Wilson\"\n\n// Use it with other programs, like\n$ echo \"Hello $Env:MY_NAME\"\n\nHello Wade Wilson\n```\n\n</div>\n\n////\n\n## Read env vars in Python\n\nYou could also create environment variables **outside** of Python, in the terminal (or with any other method), and then **read them in Python**.\n\nFor example you could have a file `main.py` with:\n\n```Python hl_lines=\"3\"\nimport os\n\nname = os.getenv(\"MY_NAME\", \"World\")\nprint(f\"Hello {name} from Python\")\n```\n\n/// tip\n\nThe second argument to <a href=\"https://docs.python.org/3.8/library/os.html#os.getenv\" class=\"external-link\" target=\"_blank\">`os.getenv()`</a> is the default value to return.\n\nIf not provided, it's `None` by default, here we provide `\"World\"` as the default value to use.\n\n///\n\nThen you could call that Python program:\n\n//// tab | Linux, macOS, Windows Bash\n\n<div class=\"termy\">\n\n```console\n// Here we don't set the env var yet\n$ python main.py\n\n// As we didn't set the env var, we get the default value\n\nHello World from Python\n\n// But if we create an environment variable first\n$ export MY_NAME=\"Wade Wilson\"\n\n// And then call the program again\n$ python main.py\n\n// Now it can read the environment variable\n\nHello Wade Wilson from Python\n```\n\n</div>\n\n////\n\n//// tab | Windows PowerShell\n\n<div class=\"termy\">\n\n```console\n// Here we don't set the env var yet\n$ python main.py\n\n// As we didn't set the env var, we get the default value\n\nHello World from Python\n\n// But if we create an environment variable first\n$ $Env:MY_NAME = \"Wade Wilson\"\n\n// And then call the program again\n$ python main.py\n\n// Now it can read the environment variable\n\nHello Wade Wilson from Python\n```\n\n</div>\n\n////\n\nAs environment variables can be set outside of the code, but can be read by the code, and don't have to be stored (committed to `git`) with the rest of the files, it's common to use them for configurations or **settings**.\n\nYou can also create an environment variable only for a **specific program invocation**, that is only available to that program, and only for its duration.\n\nTo do that, create it right before the program itself, on the same line:\n\n<div class=\"termy\">\n\n```console\n// Create an env var MY_NAME in line for this program call\n$ MY_NAME=\"Wade Wilson\" python main.py\n\n// Now it can read the environment variable\n\nHello Wade Wilson from Python\n\n// The env var no longer exists afterwards\n$ python main.py\n\nHello World from Python\n```\n\n</div>\n\n/// tip\n\nYou can read more about it at <a href=\"https://12factor.net/config\" class=\"external-link\" target=\"_blank\">The Twelve-Factor App: Config</a>.\n\n///\n\n## Types and Validation\n\nThese environment variables can only handle **text strings**, as they are external to Python and have to be compatible with other programs and the rest of the system (and even with different operating systems, as Linux, Windows, macOS).\n\nThat means that **any value** read in Python from an environment variable **will be a `str`**, and any conversion to a different type or any validation has to be done in code.\n\nYou will learn more about using environment variables for your <abbr title=\"command line interface\">CLI</abbr> applications later in the section about [CLI Arguments with Environment Variables](./tutorial/arguments/envvar.md){.internal-link target=_blank}.\n\n## `PATH` Environment Variable\n\nThere is a **special** environment variable called **`PATH`** that is used by the operating systems (Linux, macOS, Windows) to find programs to run.\n\nThe value of the variable `PATH` is a long string that is made of directories separated by a colon `:` on Linux and macOS, and by a semicolon `;` on Windows.\n\nFor example, the `PATH` environment variable could look like this:\n\n//// tab | Linux, macOS\n\n```plaintext\n/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin\n```\n\nThis means that the system should look for programs in the directories:\n\n* `/usr/local/bin`\n* `/usr/bin`\n* `/bin`\n* `/usr/sbin`\n* `/sbin`\n\n////\n\n//// tab | Windows\n\n```plaintext\nC:\\Program Files\\Python312\\Scripts;C:\\Program Files\\Python312;C:\\Windows\\System32\n```\n\nThis means that the system should look for programs in the directories:\n\n* `C:\\Program Files\\Python312\\Scripts`\n* `C:\\Program Files\\Python312`\n* `C:\\Windows\\System32`\n\n////\n\nWhen you type a **command** in the terminal, the operating system **looks for** the program in **each of those directories** listed in the `PATH` environment variable.\n\nFor example, when you type `python` in the terminal, the operating system looks for a program called `python` in the **first directory** in that list.\n\nIf it finds it, then it will **use it**. Otherwise it keeps looking in the **other directories**.\n\n### Installing Python and Updating the `PATH`\n\nWhen you install Python, you might be asked if you want to update the `PATH` environment variable.\n\n//// tab | Linux, macOS\n\nLet's say you install Python and it ends up in a directory `/opt/custompython/bin`.\n\nIf you say yes to update the `PATH` environment variable, then the installer will add `/opt/custompython/bin` to the `PATH` environment variable.\n\nIt could look like this:\n\n```plaintext\n/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/custompython/bin\n```\n\n////\n\n//// tab | Windows\n\nLet's say you install Python and it ends up in a directory `C:\\opt\\custompython\\bin`.\n\nIf you say yes to update the `PATH` environment variable, then the installer will add `C:\\opt\\custompython\\bin` to the `PATH` environment variable.\n\n```plaintext\nC:\\Program Files\\Python312\\Scripts;C:\\Program Files\\Python312;C:\\Windows\\System32;C:\\opt\\custompython\\bin\n```\n\n////\n\nSo, if you type:\n\n<div class=\"termy\">\n\n```console\n$ python\n```\n\n</div>\n\n//// tab | Linux, macOS\n\nThe system will **find** the `python` program in `/opt/custompython/bin` (the last directory) and run it.\n\nIt would be roughly equivalent to typing:\n\n<div class=\"termy\">\n\n```console\n$ /opt/custompython/bin/python\n```\n\n</div>\n\n////\n\n//// tab | Windows\n\nThe system will **find** the `python` program in `C:\\opt\\custompython\\bin\\python` (the last directory) and run it.\n\nIt would be roughly equivalent to typing:\n\n<div class=\"termy\">\n\n```console\n$ C:\\opt\\custompython\\bin\\python\n```\n\n</div>\n\n////\n\nThis information will be useful when learning about [Virtual Environments](virtual-environments.md){.internal-link target=_blank}.\n\nIt will also be useful when you **create your own CLI programs** as, for them to be available for your users, they will need to be somewhere in the `PATH` environment variable.\n\n## Conclusion\n\nWith this you should have a basic understanding of what **environment variables** are and how to use them in Python.\n\nYou can also read more about them in the <a href=\"https://en.wikipedia.org/wiki/Environment_variable\" class=\"external-link\" target=\"_blank\">Wikipedia for Environment Variable</a>.\n\nIn many cases it's not very obvious how environment variables would be useful and applicable right away. But they keep showing up in many different scenarios when you are developing, so it's good to know about them.\n\nFor example, you will need this information in the next section, about [Virtual Environments](virtual-environments.md).\n"
  },
  {
    "path": "docs/features.md",
    "content": "# Features\n\n## Design based on **FastAPI**\n\n<a href=\"https://fastapi.tiangolo.com\" target=\"_blank\"><img src=\"https://fastapi.tiangolo.com/img/logo-margin/logo-teal.png\" style=\"width: 20%;\"></a>\n\n**Typer** is <a href=\"https://fastapi.tiangolo.com\" class=\"external-link\" target=\"_blank\">FastAPI</a>'s little sibling.\n\nIt follows the same design and ideas. If you know **FastAPI**, you already know **Typer**... more or less.\n\n## Just Modern Python\n\nIt's all based on standard **Python type** declarations. No new syntax to learn. Just standard modern Python.\n\nIf you need a 2 minute refresher of how to use Python types (even if you don't use FastAPI or Typer), check the FastAPI tutorial section: <a href=\"https://fastapi.tiangolo.com/python-types/\" class=\"external-link\" target=\"_blank\">Python types intro</a>.\n\nYou will also see a 20 seconds refresher on the section [Tutorial - User Guide: First Steps](tutorial/first-steps.md){.internal-link target=_blank}.\n\n## Editor support\n\n**Typer** was designed to be easy and intuitive to use, to ensure the best development experience. With autocompletion everywhere.\n\nYou will rarely need to come back to the docs.\n\nHere's how your editor might help you:\n\n* in <a href=\"https://code.visualstudio.com/\" class=\"external-link\" target=\"_blank\">Visual Studio Code</a>:\n\n![editor support](img/vscode-completion.png)\n\n* in <a href=\"https://www.jetbrains.com/pycharm/\" class=\"external-link\" target=\"_blank\">PyCharm</a>:\n\n![editor support](img/pycharm-completion.png)\n\nYou will get completion for everything. That's something no other CLI library provides right now.\n\nNo more guessing what type was that variable, if it could be `None`, etc.\n\n### Short\n\nIt has sensible **defaults** for everything, with optional configurations everywhere. All the parameters can be fine-tuned to do what you need, customize the help, callbacks per parameter, make them required or not, etc.\n\nBut by default, it all **\"just works\"**.\n\n## User friendly CLI apps\n\nThe resulting CLI apps created with **Typer** have the nice features of many \"pro\" command line programs you probably already love.\n\n* Automatic help options for the main CLI program and all its subcommands.\n* Automatic command and subcommand structure handling (you will see more about subcommands in the Tutorial - User Guide).\n* Automatic completion for the CLI app in all operating systems, in all the shells (Bash, Zsh, Fish, PowerShell), so that the final user of your app can just hit <kbd>TAB</kbd> and get the available options or subcommands. *\n\n/// note | * Auto completion\n\nAuto completion works when you create a package (installable with `pip`). Or when using the `typer` command.\n\n**Typer** uses `shellingham` to auto-detect the current shell when installing completion.\n\n**Typer** will automatically create 2 *CLI options*:\n\n* `--install-completion`: Install completion for the current shell.\n* `--show-completion`: Show completion for the current shell, to copy it or customize the installation.\n\n///\n\n/// tip\n\n**Typer**'s completion is implemented internally, it uses ideas and components from Click and ideas from `click-completion`, but it doesn't use `click-completion` and re-implements some of the relevant parts of Click.\n\nThen it extends those ideas with features and bug fixes. For example, **Typer** programs also support modern versions of PowerShell (e.g. in Windows 10) among all the other shells.\n\n///\n\n## Tested\n\n* 100% <abbr title=\"The amount of code that is automatically tested\">test coverage</abbr>.\n* 100% <abbr title=\"Python type annotations, with this your editor and external tools can give you better support\">type annotated</abbr> code base.\n* Used in production applications.\n"
  },
  {
    "path": "docs/help-typer.md",
    "content": "# Help Typer - Get Help\n\nAre you liking **Typer**?\n\nWould you like to help Typer, other users, and the author?\n\nOr would you like to get help with **Typer**?\n\nThere are very simple ways to help (several involve just one or two clicks).\n\nAnd there are several ways to get help too.\n\n## Subscribe to the newsletter\n\nYou can subscribe to the (infrequent) [**FastAPI and friends** newsletter](https://fastapi.tiangolo.com/newsletter/){.internal-link target=_blank} to stay updated about:\n\n* News about FastAPI and friends, including Typer 🚀\n* Guides 📝\n* Features ✨\n* Breaking changes 🚨\n* Tips and tricks ✅\n\n## Star **Typer** in GitHub\n\nYou can \"star\" Typer in GitHub (clicking the star button at the top right): <a href=\"https://github.com/fastapi/typer\" class=\"external-link\" target=\"_blank\">https://github.com/fastapi/typer</a>.\n\nBy adding a star, other users will be able to find it more easily and see that it has been already useful for others.\n\n## Watch the GitHub repository for releases\n\nYou can \"watch\" Typer in GitHub (clicking the \"watch\" button at the top right): <a href=\"https://github.com/fastapi/typer\" class=\"external-link\" target=\"_blank\">https://github.com/fastapi/typer</a>.\n\nThere you can select \"Releases only\".\n\nBy doing it, you will receive notifications (in your email) whenever there's a new release (a new version) of **Typer** with bug fixes and new features.\n\n## Connect with the author\n\nYou can connect with <a href=\"https://tiangolo.com\" class=\"external-link\" target=\"_blank\">me (Sebastián Ramírez / `tiangolo`)</a>, the author.\n\nYou can:\n\n* <a href=\"https://github.com/tiangolo\" class=\"external-link\" target=\"_blank\">Follow me on **GitHub**</a>.\n    * See other Open Source projects I have created that could help you.\n    * Follow me to see when I create a new Open Source project.\n* <a href=\"https://twitter.com/tiangolo\" class=\"external-link\" target=\"_blank\">Follow me on **Twitter**</a>.\n    * Tell me how you use Typer (I love to hear that).\n    * Hear when I make announcements or release new tools.\n* <a href=\"https://www.linkedin.com/in/tiangolo/\" class=\"external-link\" target=\"_blank\">Connect with me on **Linkedin**</a>.\n    * Hear when I make announcements or release new tools (although I use Twitter more often 🤷‍♂).\n* Read what I write (or follow me) on <a href=\"https://dev.to/tiangolo\" class=\"external-link\" target=\"_blank\">**Dev.to**</a> or <a href=\"https://medium.com/@tiangolo\" class=\"external-link\" target=\"_blank\">**Medium**</a>.\n    * Read other ideas, articles, and read about tools I have created.\n    * Follow me to read when I publish something new.\n\n## Tweet about **Typer**\n\n<a href=\"https://twitter.com/compose/tweet?text=I'm loving Typer because... https://github.com/fastapi/typer cc @tiangolo\" class=\"external-link\" target=\"_blank\">Tweet about **Typer**</a> and let me and others know why you like it.\n\nI love to hear about how **Typer** is being used, what have you liked in it, in which project/company you are using it, etc.\n\n## Help others with questions in GitHub\n\nYou can try and help others with their questions in:\n\n* <a href=\"https://github.com/fastapi/typer/discussions/categories/questions?discussions_q=category%3AQuestions+is%3Aunanswered\" class=\"external-link\" target=\"_blank\">GitHub Discussions</a>\n* <a href=\"https://github.com/fastapi/typer/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3Aquestion+-label%3Aanswered+\" class=\"external-link\" target=\"_blank\">GitHub Issues</a>\n\nIn many cases you might already know the answer for those questions. 🤓\n\nJust remember, the most important point is: try to be kind. People come with their frustrations and in many cases don't ask in the best way, but try as best as you can to be kind. 🤗\n\nThe idea is for the **Typer** community to be kind and welcoming. At the same time, don't accept bullying or disrespectful behavior towards others. We have to take care of each other.\n\n---\n\nHere's how to help others with questions (in discussions or issues):\n\n### Understand the question\n\n* Check if you can understand what is the **purpose** and use case of the person asking.\n\n* Then check if the question (the vast majority are questions) is **clear**.\n\n* In many cases the question asked is about an imaginary solution from the user, but there might be a **better** one. If you can understand the problem and use case better, you might be able to suggest a better **alternative solution**.\n\n* If you can't understand the question, ask for more **details**.\n\n### Reproduce the problem\n\nFor most of the cases and most of the questions there's something related to the person's **original code**.\n\nIn many cases they will only copy a fragment of the code, but that's not enough to **reproduce the problem**.\n\n* You can ask them to provide a <a href=\"https://stackoverflow.com/help/minimal-reproducible-example\" class=\"external-link\" target=\"_blank\">minimal, reproducible, example</a>, that you can **copy-paste** and run locally to see the same error or behavior they are seeing, or to understand their use case better.\n\n* If you are feeling too generous, you can try to **create an example** like that yourself, just based on the description of the problem. Just have in mind that this might take a lot of time and it might be better to ask them to clarify the problem first.\n\n### Suggest solutions\n\n* After being able to understand the question, you can give them a possible **answer**.\n\n* In many cases, it's better to understand their **underlying problem or use case**, because there might be a better way to solve it than what they are trying to do.\n\n### Ask to close\n\nIf they reply, there's a high chance you would have solved their problem, congrats, **you're a hero**! 🦸\n\n* Now, if that solved their problem, you can ask them to:\n\n    * In GitHub Discussions: mark the comment as the **answer**.\n    * In GitHub Issues: **close** the issue**.\n\n## Watch the GitHub repository\n\nYou can \"watch\" Typer in GitHub (clicking the \"watch\" button at the top right): <a href=\"https://github.com/fastapi/typer\" class=\"external-link\" target=\"_blank\">https://github.com/fastapi/typer</a>.\n\nIf you select \"Watching\" instead of \"Releases only\" you will receive notifications when someone creates a new issue or question. You can also specify that you only want to be notified about new issues, or discussions, or PRs, etc.\n\nThen you can try and help them solve those questions.\n\n## Ask Questions\n\nYou can <a href=\"https://github.com/fastapi/typer/discussions/new?category=questions\" class=\"external-link\" target=\"_blank\">create a new question</a> in the GitHub repository, for example to:\n\n* Ask a **question** or ask about a **problem**.\n* Suggest a new **feature**.\n\n**Note**: if you do it, then I'm going to ask you to also help others. 😉\n\n## Review Pull Requests\n\nYou can help me review pull requests from others.\n\nAgain, please try your best to be kind. 🤗\n\n---\n\nHere's what to have in mind and how to review a pull request:\n\n### Understand the problem\n\n* First, make sure you **understand the problem** that the pull request is trying to solve. It might have a longer discussion in a GitHub Discussion or issue.\n\n* There's also a good chance that the pull request is not actually needed because the problem can be solved in a **different way**. Then you can suggest or ask about that.\n\n### Don't worry about style\n\n* Don't worry too much about things like commit message styles, I will squash and merge customizing the commit manually.\n\n* Also don't worry about style rules, there are already automated tools checking that.\n\nAnd if there's any other style or consistency need, I'll ask directly for that, or I'll add commits on top with the needed changes.\n\n### Check the code\n\n* Check and read the code, see if it makes sense, **run it locally** and see if it actually solves the problem.\n\n* Then **comment** saying that you did that, that's how I will know you really checked it.\n\n/// info\n\nUnfortunately, I can't simply trust PRs that just have several approvals.\n\nSeveral times it has happened that there are PRs with 3, 5 or more approvals, probably because the description is appealing, but when I check the PRs, they are actually broken, have a bug, or don't solve the problem they claim to solve. 😅\n\nSo, it's really important that you actually read and run the code, and let me know in the comments that you did. 🤓\n\n///\n\n* If the PR can be simplified in a way, you can ask for that, but there's no need to be too picky, there might be a lot of subjective points of view (and I will have my own as well 🙈), so it's better if you can focus on the fundamental things.\n\n### Tests\n\n* Help me check that the PR has **tests**.\n\n* Check that the tests **fail** before the PR. 🚨\n\n* Then check that the tests **pass** after the PR. ✅\n\n* Many PRs don't have tests, you can **remind** them to add tests, or you can even **suggest** some tests yourself. That's one of the things that consume most time and you can help a lot with that.\n\n* Then also comment what you tried, that way I'll know that you checked it. 🤓\n\n## Create a Pull Request\n\nYou can [contribute](contributing.md){.internal-link target=_blank} to the source code with Pull Requests, for example:\n\n* To fix a typo you found on the documentation.\n* To propose new documentation sections.\n* To fix an existing issue/bug.\n    * Make sure to add tests.\n* To add a new feature.\n    * Make sure to add tests.\n    * Make sure to add documentation if it's relevant.\n\n## Help Maintain Typer\n\nHelp me maintain **Typer**! 🤓\n\nThere's a lot of work to do, and for most of it, **YOU** can do it.\n\nThe main tasks that you can do right now are:\n\n* [Help others with questions in GitHub](#help-others-with-questions-in-github){.internal-link target=_blank} (see the section above).\n* [Review Pull Requests](#review-pull-requests){.internal-link target=_blank} (see the section above).\n\nThose two tasks are what **consume time the most**. That's the main work of maintaining Typer.\n\nIf you can help me with that, **you are helping me maintain Typer** and making sure it keeps **advancing faster and better**. 🚀\n\n## Join the chat\n\nJoin the 👥 <a href=\"https://discord.gg/VQjSZaeJmf\" class=\"external-link\" target=\"_blank\">FastAPI and Friends Discord chat server</a> 👥 and hang out with others in the community. There's a `#typer` channel.\n\n/// tip\n\nFor questions, ask them in <a href=\"https://github.com/fastapi/typer/discussions/new?category=questions\" class=\"external-link\" target=\"_blank\">GitHub Discussions</a>, there's a much better chance you will receive help there.\n\nUse the chat only for other general conversations.\n\n///\n\n### Don't use the chat for questions\n\nHave in mind that as chats allow more \"free conversation\", it's easy to ask questions that are too general and more difficult to answer, so, you might not receive answers.\n\nIn GitHub, the template will guide you to write the right question so that you can more easily get a good answer, or even solve the problem yourself even before asking. And in GitHub I can make sure I always answer everything, even if it takes some time. I can't personally do that with the chat. 😅\n\nConversations in the chat are also not as easily searchable as in GitHub, so questions and answers might get lost in the conversation.\n\nOn the other side, there are thousands of users in the chat, so there's a high chance you'll find someone to talk to there, almost all the time. 😄\n\n## Sponsor the author\n\nYou can also financially support the author (me) through <a href=\"https://github.com/sponsors/tiangolo\" class=\"external-link\" target=\"_blank\">GitHub sponsors</a>.\n\nThere you could buy me a coffee ☕️ to say thanks. 😄\n\n## Sponsor the tools that power Typer\n\nAs you have seen in the documentation, Typer is built on top of Click.\n\nYou can also sponsor:\n\n* Pallets Project (Click maintainers) <a href=\"https://palletsprojects.com/donate\" class=\"external-link\" target=\"_blank\">via the PSF</a> or <a href=\"https://tidelift.com/subscription/pkg/pypi-click\" class=\"external-link\" target=\"_blank\">via Tidelift</a>\n\n---\n\nThanks! 🚀\n"
  },
  {
    "path": "docs/index.md",
    "content": "<style>\n.md-content .md-typeset h1 { display: none; }\n</style>\n\n<p align=\"center\">\n  <a href=\"https://typer.tiangolo.com\"><img src=\"https://typer.tiangolo.com/img/logo-margin/logo-margin-vector.svg#only-light\" alt=\"Typer\"></a>\n<!-- only-mkdocs -->\n  <a href=\"https://typer.tiangolo.com\"><img src=\"img/logo-margin/logo-margin-white-vector.svg#only-dark\" alt=\"Typer\"></a>\n<!-- /only-mkdocs -->\n</p>\n<p align=\"center\">\n    <em>Typer, build great CLIs. Easy to code. Based on Python type hints.</em>\n</p>\n<p align=\"center\">\n<a href=\"https://github.com/fastapi/typer/actions?query=workflow%3ATest+event%3Apush+branch%3Amaster\" target=\"_blank\">\n    <img src=\"https://github.com/fastapi/typer/actions/workflows/test.yml/badge.svg?event=push&branch=master\" alt=\"Test\">\n</a>\n<a href=\"https://github.com/fastapi/typer/actions?query=workflow%3APublish\" target=\"_blank\">\n    <img src=\"https://github.com/fastapi/typer/workflows/Publish/badge.svg\" alt=\"Publish\">\n</a>\n<a href=\"https://coverage-badge.samuelcolvin.workers.dev/redirect/fastapi/typer\" target=\"_blank\">\n    <img src=\"https://coverage-badge.samuelcolvin.workers.dev/fastapi/typer.svg\" alt=\"Coverage\">\n<a href=\"https://pypi.org/project/typer\" target=\"_blank\">\n    <img src=\"https://img.shields.io/pypi/v/typer?color=%2334D058&label=pypi%20package\" alt=\"Package version\">\n</a>\n</p>\n\n---\n\n**Documentation**: <a href=\"https://typer.tiangolo.com\" target=\"_blank\">https://typer.tiangolo.com</a>\n\n**Source Code**: <a href=\"https://github.com/fastapi/typer\" target=\"_blank\">https://github.com/fastapi/typer</a>\n\n---\n\nTyper is a library for building <abbr title=\"command line interface, programs executed from a terminal\">CLI</abbr> applications that users will **love using** and developers will **love creating**. Based on Python type hints.\n\nIt's also a command line tool to run scripts, automatically converting them to CLI applications.\n\nThe key features are:\n\n* **Intuitive to write**: Great editor support. <abbr title=\"also known as auto-complete, autocompletion, IntelliSense\">Completion</abbr> everywhere. Less time debugging. Designed to be easy to use and learn. Less time reading docs.\n* **Easy to use**: It's easy to use for the final users. Automatic help, and automatic completion for all shells.\n* **Short**: Minimize code duplication. Multiple features from each parameter declaration. Fewer bugs.\n* **Start simple**: The simplest example adds only 2 lines of code to your app: **1 import, 1 function call**.\n* **Grow large**: Grow in complexity as much as you want, create arbitrarily complex trees of commands and groups of subcommands, with options and arguments.\n* **Run scripts**: Typer includes a `typer` command/program that you can use to run scripts, automatically converting them to CLIs, even if they don't use Typer internally.\n\n## FastAPI of CLIs\n\n**Typer** is <a href=\"https://fastapi.tiangolo.com\" class=\"external-link\" target=\"_blank\">FastAPI</a>'s little sibling, it's the FastAPI of CLIs.\n\n## Installation\n\nCreate and activate a <a href=\"https://typer.tiangolo.com/virtual-environments/\" class=\"external-link\" target=\"_blank\">virtual environment</a> and then install **Typer**:\n\n<div class=\"termy\">\n\n```console\n$ pip install typer\n---> 100%\nSuccessfully installed typer rich shellingham\n```\n\n</div>\n\n## Example\n\n### The absolute minimum\n\n* Create a file `main.py` with:\n\n```Python\ndef main(name: str):\n    print(f\"Hello {name}\")\n```\n\nThis script doesn't even use Typer internally. But you can use the `typer` command to run it as a CLI application.\n\n### Run it\n\nRun your application with the `typer` command:\n\n<div class=\"termy\">\n\n```console\n// Run your application\n$ typer main.py run\n\n// You get a nice error, you are missing NAME\nUsage: typer [PATH_OR_MODULE] run [OPTIONS] NAME\nTry 'typer [PATH_OR_MODULE] run --help' for help.\n╭─ Error ───────────────────────────────────────────╮\n│ Missing argument 'NAME'.                          │\n╰───────────────────────────────────────────────────╯\n\n\n// You get a --help for free\n$ typer main.py run --help\n\nUsage: typer [PATH_OR_MODULE] run [OPTIONS] NAME\n\nRun the provided Typer app.\n\n╭─ Arguments ───────────────────────────────────────╮\n│ *    name      TEXT  [default: None] [required]   |\n╰───────────────────────────────────────────────────╯\n╭─ Options ─────────────────────────────────────────╮\n│ --help          Show this message and exit.       │\n╰───────────────────────────────────────────────────╯\n\n// Now pass the NAME argument\n$ typer main.py run Camila\n\nHello Camila\n\n// It works! 🎉\n```\n\n</div>\n\nThis is the simplest use case, not even using Typer internally, but it can already be quite useful for simple scripts.\n\n**Note**: auto-completion works when you create a Python package and run it with `--install-completion` or when you use the `typer` command.\n\n## Use Typer in your code\n\nNow let's start using Typer in your own code, update `main.py` with:\n\n```Python\nimport typer\n\n\ndef main(name: str):\n    print(f\"Hello {name}\")\n\n\nif __name__ == \"__main__\":\n    typer.run(main)\n```\n\nNow you could run it with Python directly:\n\n<div class=\"termy\">\n\n```console\n// Run your application\n$ python main.py\n\n// You get a nice error, you are missing NAME\nUsage: main.py [OPTIONS] NAME\nTry 'main.py --help' for help.\n╭─ Error ───────────────────────────────────────────╮\n│ Missing argument 'NAME'.                          │\n╰───────────────────────────────────────────────────╯\n\n\n// You get a --help for free\n$ python main.py --help\n\nUsage: main.py [OPTIONS] NAME\n\n╭─ Arguments ───────────────────────────────────────╮\n│ *    name      TEXT  [default: None] [required]   |\n╰───────────────────────────────────────────────────╯\n╭─ Options ─────────────────────────────────────────╮\n│ --help          Show this message and exit.       │\n╰───────────────────────────────────────────────────╯\n\n// Now pass the NAME argument\n$ python main.py Camila\n\nHello Camila\n\n// It works! 🎉\n```\n\n</div>\n\n**Note**: you can also call this same script with the `typer` command, but you don't need to.\n\n## Example upgrade\n\nThis was the simplest example possible.\n\nNow let's see one a bit more complex.\n\n### An example with two subcommands\n\nModify the file `main.py`.\n\nCreate a `typer.Typer()` app, and create two subcommands with their parameters.\n\n```Python hl_lines=\"3  6  11  20\"\nimport typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef hello(name: str):\n    print(f\"Hello {name}\")\n\n\n@app.command()\ndef goodbye(name: str, formal: bool = False):\n    if formal:\n        print(f\"Goodbye Ms. {name}. Have a good day.\")\n    else:\n        print(f\"Bye {name}!\")\n\n\nif __name__ == \"__main__\":\n    app()\n```\n\nAnd that will:\n\n* Explicitly create a `typer.Typer` app.\n    * The previous `typer.run` actually creates one implicitly for you.\n* Add two subcommands with `@app.command()`.\n* Execute the `app()` itself, as if it was a function (instead of `typer.run`).\n\n### Run the upgraded example\n\nCheck the new help:\n\n<div class=\"termy\">\n\n```console\n$ python main.py --help\n\n Usage: main.py [OPTIONS] COMMAND [ARGS]...\n\n╭─ Options ─────────────────────────────────────────╮\n│ --install-completion          Install completion  │\n│                               for the current     │\n│                               shell.              │\n│ --show-completion             Show completion for │\n│                               the current shell,  │\n│                               to copy it or       │\n│                               customize the       │\n│                               installation.       │\n│ --help                        Show this message   │\n│                               and exit.           │\n╰───────────────────────────────────────────────────╯\n╭─ Commands ────────────────────────────────────────╮\n│ goodbye                                           │\n│ hello                                             │\n╰───────────────────────────────────────────────────╯\n\n// When you create a package you get ✨ auto-completion ✨ for free, installed with --install-completion\n\n// You have 2 subcommands (the 2 functions): goodbye and hello\n```\n\n</div>\n\nNow check the help for the `hello` command:\n\n<div class=\"termy\">\n\n```console\n$ python main.py hello --help\n\n Usage: main.py hello [OPTIONS] NAME\n\n╭─ Arguments ───────────────────────────────────────╮\n│ *    name      TEXT  [default: None] [required]   │\n╰───────────────────────────────────────────────────╯\n╭─ Options ─────────────────────────────────────────╮\n│ --help          Show this message and exit.       │\n╰───────────────────────────────────────────────────╯\n```\n\n</div>\n\nAnd now check the help for the `goodbye` command:\n\n<div class=\"termy\">\n\n```console\n$ python main.py goodbye --help\n\n Usage: main.py goodbye [OPTIONS] NAME\n\n╭─ Arguments ───────────────────────────────────────╮\n│ *    name      TEXT  [default: None] [required]   │\n╰───────────────────────────────────────────────────╯\n╭─ Options ─────────────────────────────────────────╮\n│ --formal    --no-formal      [default: no-formal] │\n│ --help                       Show this message    │\n│                              and exit.            │\n╰───────────────────────────────────────────────────╯\n\n// Automatic --formal and --no-formal for the bool option 🎉\n```\n\n</div>\n\nNow you can try out the new command line application:\n\n<div class=\"termy\">\n\n```console\n// Use it with the hello command\n\n$ python main.py hello Camila\n\nHello Camila\n\n// And with the goodbye command\n\n$ python main.py goodbye Camila\n\nBye Camila!\n\n// And with --formal\n\n$ python main.py goodbye --formal Camila\n\nGoodbye Ms. Camila. Have a good day.\n```\n\n</div>\n\n**Note**: If your app only has one command, by default the command name is **omitted** in usage: `python main.py Camila`. However, when there are multiple commands, you must **explicitly include the command name**: `python main.py hello Camila`. See [One or Multiple Commands](https://typer.tiangolo.com/tutorial/commands/one-or-multiple/) for more details.\n\n### Recap\n\nIn summary, you declare **once** the types of parameters (*CLI arguments* and *CLI options*) as function parameters.\n\nYou do that with standard modern Python types.\n\nYou don't have to learn a new syntax, the methods or classes of a specific library, etc.\n\nJust standard **Python**.\n\nFor example, for an `int`:\n\n```Python\ntotal: int\n```\n\nor for a `bool` flag:\n\n```Python\nforce: bool\n```\n\nAnd similarly for **files**, **paths**, **enums** (choices), etc. And there are tools to create **groups of subcommands**, add metadata, extra **validation**, etc.\n\n**You get**: great editor support, including **completion** and **type checks** everywhere.\n\n**Your users get**: automatic **`--help`**, **auto-completion** in their terminal (Bash, Zsh, Fish, PowerShell) when they install your package or when using the `typer` command.\n\nFor a more complete example including more features, see the <a href=\"https://typer.tiangolo.com/tutorial/\">Tutorial - User Guide</a>.\n\n## Dependencies\n\n**Typer** stands on the shoulders of giants. It has three required dependencies:\n\n* <a href=\"https://click.palletsprojects.com/\" class=\"external-link\" target=\"_blank\">Click</a>: a popular tool for building CLIs in Python. Typer is based on it.\n* <a href=\"https://rich.readthedocs.io/en/stable/index.html\" class=\"external-link\" target=\"_blank\"><code>rich</code></a>: to show nicely formatted errors automatically.\n* <a href=\"https://github.com/sarugaku/shellingham\" class=\"external-link\" target=\"_blank\"><code>shellingham</code></a>: to automatically detect the current shell when installing completion.\n\n### `typer-slim`\n\nThere used to be a slimmed-down version of Typer called `typer-slim`, which didn't include the dependencies `rich` and `shellingham`, nor the `typer` command.\n\nHowever, since version 0.22.0, we have stopped supporting this, and `typer-slim` now simply installs (all of) Typer.\n\nIf you want to disable Rich globally, you can set an environmental variable `TYPER_USE_RICH` to `False` or `0`.\n\n## License\n\nThis project is licensed under the terms of the MIT license.\n"
  },
  {
    "path": "docs/js/custom.js",
    "content": "function setupTermynal() {\n    document.querySelectorAll(\".use-termynal\").forEach(node => {\n        node.style.display = \"block\";\n        new Termynal(node, {\n            lineDelay: 500\n        });\n    });\n    const progressLiteralStart = \"---> 100%\";\n    const promptLiteralStart = \"$ \";\n    const customPromptLiteralStart = \"# \";\n    const termynalActivateClass = \"termy\";\n    let termynals = [];\n\n    function createTermynals() {\n        document\n            .querySelectorAll(`.${termynalActivateClass} .highlight code`)\n            .forEach(node => {\n                const text = node.textContent;\n                const lines = text.split(\"\\n\");\n                const useLines = [];\n                let buffer = [];\n                function saveBuffer() {\n                    if (buffer.length) {\n                        let isBlankSpace = true;\n                        buffer.forEach(line => {\n                            if (line) {\n                                isBlankSpace = false;\n                            }\n                        });\n                        dataValue = {};\n                        if (isBlankSpace) {\n                            dataValue[\"delay\"] = 0;\n                        }\n                        if (buffer[buffer.length - 1] === \"\") {\n                            // A last single <br> won't have effect\n                            // so put an additional one\n                            buffer.push(\"\");\n                        }\n                        const bufferValue = buffer.join(\"<br>\");\n                        dataValue[\"value\"] = bufferValue;\n                        useLines.push(dataValue);\n                        buffer = [];\n                    }\n                }\n                for (let line of lines) {\n                    if (line === progressLiteralStart) {\n                        saveBuffer();\n                        useLines.push({\n                            type: \"progress\"\n                        });\n                    } else if (line.startsWith(promptLiteralStart)) {\n                        saveBuffer();\n                        const value = line.replace(promptLiteralStart, \"\").trimEnd();\n                        useLines.push({\n                            type: \"input\",\n                            value: value\n                        });\n                    } else if (line.startsWith(\"// \")) {\n                        saveBuffer();\n                        const value = \"💬 \" + line.replace(\"// \", \"\").trimEnd();\n                        useLines.push({\n                            value: value,\n                            class: \"termynal-comment\",\n                            delay: 0\n                        });\n                    } else if (line.startsWith(customPromptLiteralStart)) {\n                        saveBuffer();\n                        const promptStart = line.indexOf(promptLiteralStart);\n                        if (promptStart === -1) {\n                            console.error(\"Custom prompt found but no end delimiter\", line)\n                        }\n                        const prompt = line.slice(0, promptStart).replace(customPromptLiteralStart, \"\")\n                        let value = line.slice(promptStart + promptLiteralStart.length);\n                        useLines.push({\n                            type: \"input\",\n                            value: value,\n                            prompt: prompt\n                        });\n                    } else {\n                        buffer.push(line);\n                    }\n                }\n                saveBuffer();\n                const inputCommands = useLines.filter(line => line.type === \"input\").map(line => line.value).join(\"\\n\");\n                node.textContent = inputCommands;\n                const div = document.createElement(\"div\");\n                node.style.display = \"none\";\n                node.after(div);\n                const termynal = new Termynal(div, {\n                    lineData: useLines,\n                    noInit: true,\n                    lineDelay: 500\n                });\n                termynals.push(termynal);\n            });\n    }\n\n    function loadVisibleTermynals() {\n        termynals = termynals.filter(termynal => {\n            if (termynal.container.getBoundingClientRect().top - innerHeight <= 0) {\n                termynal.init();\n                return false;\n            }\n            return true;\n        });\n    }\n    window.addEventListener(\"scroll\", loadVisibleTermynals);\n    createTermynals();\n    loadVisibleTermynals();\n}\n\nfunction openLinksInNewTab() {\n    const siteUrl = document.querySelector(\"link[rel='canonical']\")?.href\n        || window.location.origin;\n    const siteOrigin = new URL(siteUrl).origin;\n    document.querySelectorAll(\".md-content a[href]\").forEach(a => {\n        if (a.getAttribute(\"target\") === \"_self\") return;\n        const href = a.getAttribute(\"href\");\n        if (!href) return;\n        try {\n            const url = new URL(href, window.location.href);\n            // Skip same-page anchor links (only the hash differs)\n            if (url.origin === window.location.origin\n                && url.pathname === window.location.pathname\n                && url.search === window.location.search) return;\n            if (!a.hasAttribute(\"target\")) {\n                a.setAttribute(\"target\", \"_blank\");\n                a.setAttribute(\"rel\", \"noopener\");\n            }\n            if (url.origin !== siteOrigin) {\n                a.dataset.externalLink = \"\";\n            } else {\n                a.dataset.internalLink = \"\";\n            }\n        } catch (_) {}\n    });\n}\n\nasync function main() {\n    setupTermynal();\n    openLinksInNewTab();\n}\n\ndocument$.subscribe(() => {\n    main()\n})\n"
  },
  {
    "path": "docs/js/termynal.js",
    "content": "/**\n * termynal.js\n * A lightweight, modern and extensible animated terminal window, using\n * async/await.\n *\n * @author Ines Montani <ines@ines.io>\n * @version 0.0.1\n * @license MIT\n */\n\n'use strict';\n\n/** Generate a terminal widget. */\nclass Termynal {\n    /**\n     * Construct the widget's settings.\n     * @param {(string|Node)=} container - Query selector or container element.\n     * @param {Object=} options - Custom settings.\n     * @param {string} options.prefix - Prefix to use for data attributes.\n     * @param {number} options.startDelay - Delay before animation, in ms.\n     * @param {number} options.typeDelay - Delay between each typed character, in ms.\n     * @param {number} options.lineDelay - Delay between each line, in ms.\n     * @param {number} options.progressLength - Number of characters displayed as progress bar.\n     * @param {string} options.progressChar – Character to use for progress bar, defaults to █.\n\t * @param {number} options.progressPercent - Max percent of progress.\n     * @param {string} options.cursor – Character to use for cursor, defaults to ▋.\n     * @param {Object[]} lineData - Dynamically loaded line data objects.\n     * @param {boolean} options.noInit - Don't initialise the animation.\n     */\n    constructor(container = '#termynal', options = {}) {\n        this.container = (typeof container === 'string') ? document.querySelector(container) : container;\n        this.pfx = `data-${options.prefix || 'ty'}`;\n        this.originalStartDelay = this.startDelay = options.startDelay\n            || parseFloat(this.container.getAttribute(`${this.pfx}-startDelay`)) || 600;\n        this.originalTypeDelay = this.typeDelay = options.typeDelay\n            || parseFloat(this.container.getAttribute(`${this.pfx}-typeDelay`)) || 90;\n        this.originalLineDelay = this.lineDelay = options.lineDelay\n            || parseFloat(this.container.getAttribute(`${this.pfx}-lineDelay`)) || 1500;\n        this.progressLength = options.progressLength\n            || parseFloat(this.container.getAttribute(`${this.pfx}-progressLength`)) || 40;\n        this.progressChar = options.progressChar\n            || this.container.getAttribute(`${this.pfx}-progressChar`) || '█';\n\t\tthis.progressPercent = options.progressPercent\n            || parseFloat(this.container.getAttribute(`${this.pfx}-progressPercent`)) || 100;\n        this.cursor = options.cursor\n            || this.container.getAttribute(`${this.pfx}-cursor`) || '▋';\n        this.lineData = this.lineDataToElements(options.lineData || []);\n        this.loadLines()\n        if (!options.noInit) this.init()\n    }\n\n    loadLines() {\n        // Load all the lines and create the container so that the size is fixed\n        // Otherwise it would be changing and the user viewport would be constantly\n        // moving as she/he scrolls\n        const finish = this.generateFinish()\n        finish.style.visibility = 'hidden'\n        this.container.appendChild(finish)\n        // Appends dynamically loaded lines to existing line elements.\n        this.lines = [...this.container.querySelectorAll(`[${this.pfx}]`)].concat(this.lineData);\n        for (let line of this.lines) {\n            line.style.visibility = 'hidden'\n            this.container.appendChild(line)\n        }\n        const restart = this.generateRestart()\n        restart.style.visibility = 'hidden'\n        this.container.appendChild(restart)\n        this.container.setAttribute('data-termynal', '');\n    }\n\n    /**\n     * Initialise the widget, get lines, clear container and start animation.\n     */\n    init() {\n        /**\n         * Calculates width and height of Termynal container.\n         * If container is empty and lines are dynamically loaded, defaults to browser `auto` or CSS.\n         */\n        const containerStyle = getComputedStyle(this.container);\n        this.container.style.width = containerStyle.width !== '0px' ?\n            containerStyle.width : undefined;\n        this.container.style.minHeight = containerStyle.height !== '0px' ?\n            containerStyle.height : undefined;\n\n        this.container.setAttribute('data-termynal', '');\n        this.container.innerHTML = '';\n        for (let line of this.lines) {\n            line.style.visibility = 'visible'\n        }\n        this.start();\n    }\n\n    /**\n     * Start the animation and rener the lines depending on their data attributes.\n     */\n    async start() {\n        this.addFinish()\n        await this._wait(this.startDelay);\n\n        for (let line of this.lines) {\n            const type = line.getAttribute(this.pfx);\n            const delay = line.getAttribute(`${this.pfx}-delay`) || this.lineDelay;\n\n            if (type == 'input') {\n                line.setAttribute(`${this.pfx}-cursor`, this.cursor);\n                await this.type(line);\n                await this._wait(delay);\n            }\n\n            else if (type == 'progress') {\n                await this.progress(line);\n                await this._wait(delay);\n            }\n\n            else {\n                this.container.appendChild(line);\n                await this._wait(delay);\n            }\n\n            line.removeAttribute(`${this.pfx}-cursor`);\n        }\n        this.addRestart()\n        this.finishElement.style.visibility = 'hidden'\n        this.lineDelay = this.originalLineDelay\n        this.typeDelay = this.originalTypeDelay\n        this.startDelay = this.originalStartDelay\n    }\n\n    generateRestart() {\n        const restart = document.createElement('a')\n        restart.onclick = (e) => {\n            e.preventDefault()\n            this.container.innerHTML = ''\n            this.init()\n        }\n        restart.href = '#'\n        restart.setAttribute('data-terminal-control', '')\n        restart.innerHTML = \"restart ↻\"\n        return restart\n    }\n\n    generateFinish() {\n        const finish = document.createElement('a')\n        finish.onclick = (e) => {\n            e.preventDefault()\n            this.lineDelay = 0\n            this.typeDelay = 0\n            this.startDelay = 0\n        }\n        finish.href = '#'\n        finish.setAttribute('data-terminal-control', '')\n        finish.innerHTML = \"fast →\"\n        this.finishElement = finish\n        return finish\n    }\n\n    addRestart() {\n        const restart = this.generateRestart()\n        this.container.appendChild(restart)\n    }\n\n    addFinish() {\n        const finish = this.generateFinish()\n        this.container.appendChild(finish)\n    }\n\n    /**\n     * Animate a typed line.\n     * @param {Node} line - The line element to render.\n     */\n    async type(line) {\n        const chars = [...line.textContent];\n        line.textContent = '';\n        this.container.appendChild(line);\n\n        for (let char of chars) {\n            const delay = line.getAttribute(`${this.pfx}-typeDelay`) || this.typeDelay;\n            await this._wait(delay);\n            line.textContent += char;\n        }\n    }\n\n    /**\n     * Animate a progress bar.\n     * @param {Node} line - The line element to render.\n     */\n    async progress(line) {\n        const progressLength = line.getAttribute(`${this.pfx}-progressLength`)\n            || this.progressLength;\n        const progressChar = line.getAttribute(`${this.pfx}-progressChar`)\n            || this.progressChar;\n        const chars = progressChar.repeat(progressLength);\n\t\tconst progressPercent = line.getAttribute(`${this.pfx}-progressPercent`)\n\t\t\t|| this.progressPercent;\n        line.textContent = '';\n        this.container.appendChild(line);\n\n        for (let i = 1; i < chars.length + 1; i++) {\n            await this._wait(this.typeDelay);\n            const percent = Math.round(i / chars.length * 100);\n            line.textContent = `${chars.slice(0, i)} ${percent}%`;\n\t\t\tif (percent>progressPercent) {\n\t\t\t\tbreak;\n\t\t\t}\n        }\n    }\n\n    /**\n     * Helper function for animation delays, called with `await`.\n     * @param {number} time - Timeout, in ms.\n     */\n    _wait(time) {\n        return new Promise(resolve => setTimeout(resolve, time));\n    }\n\n    /**\n     * Converts line data objects into line elements.\n     *\n     * @param {Object[]} lineData - Dynamically loaded lines.\n     * @param {Object} line - Line data object.\n     * @returns {Element[]} - Array of line elements.\n     */\n    lineDataToElements(lineData) {\n        return lineData.map(line => {\n            let div = document.createElement('div');\n            div.innerHTML = `<span ${this._attributes(line)}>${line.value || ''}</span>`;\n\n            return div.firstElementChild;\n        });\n    }\n\n    /**\n     * Helper function for generating attributes string.\n     *\n     * @param {Object} line - Line data object.\n     * @returns {string} - String of attributes.\n     */\n    _attributes(line) {\n        let attrs = '';\n        for (let prop in line) {\n            // Custom add class\n            if (prop === 'class') {\n                attrs += ` class=${line[prop]} `\n                continue\n            }\n            if (prop === 'type') {\n                attrs += `${this.pfx}=\"${line[prop]}\" `\n            } else if (prop !== 'value') {\n                attrs += `${this.pfx}-${prop}=\"${line[prop]}\" `\n            }\n        }\n        return attrs;\n    }\n}\n\n/**\n* HTML API: If current script has container(s) specified, initialise Termynal.\n*/\nif (document.currentScript.hasAttribute('data-termynal-container')) {\n    const containers = document.currentScript.getAttribute('data-termynal-container');\n    containers.split('|')\n        .forEach(container => new Termynal(container))\n}\n"
  },
  {
    "path": "docs/management-tasks.md",
    "content": "# Repository Management Tasks\n\nThese are the tasks that can be performed to manage the Typer repository by [team members](./management.md#team){.internal-link target=_blank}.\n\n/// tip\n\nThis section is useful only to a handful of people, team members with permissions to manage the repository. You can probably skip it. 😉\n\n///\n\n...so, you are a [team member of Typer](./management.md#team){.internal-link target=_blank}? Wow, you are so cool! 😎\n\nYou can help with everything on [Help Typer - Get Help](./help-typer.md){.internal-link target=_blank} the same ways as external contributors. But additionally, there are some tasks that only you (as part of the team) can perform.\n\nHere are the general instructions for the tasks you can perform.\n\nThanks a lot for your help. 🙇\n\n## Be Nice\n\nFirst of all, be nice. 😊\n\nYou probably are super nice if you were added to the team, but it's worth mentioning it. 🤓\n\n### When Things are Difficult\n\nWhen things are great, everything is easier, so that doesn't need much instructions. But when things are difficult, here are some guidelines.\n\nTry to find the good side. In general, if people are not being unfriendly, try to thank their effort and interest, even if you disagree with the main subject (discussion, PR), just thank them for being interested in the project, or for having dedicated some time to try to do something.\n\nIt's difficult to convey emotion in text, use emojis to help. 😅\n\nIn discussions and PRs, in many cases, people bring their frustration and show it without filter, in many cases exaggerating, complaining, being entitled, etc. That's really not nice, and when it happens, it lowers our priority to solve their problems. But still, try to breath, and be gentle with your answers.\n\nTry to avoid using bitter sarcasm or potentially passive-aggressive comments. If something is wrong, it's better to be direct (try to be gentle) than sarcastic.\n\nTry to be as specific and objective as possible, avoid generalizations.\n\nFor conversations that are more difficult, for example to reject a PR, you can ask me (@tiangolo) to handle it directly.\n\n## Edit PR Titles\n\n* Edit the PR title to start with an emoji from <a href=\"https://gitmoji.dev/\" class=\"external-link\" target=\"_blank\">gitmoji</a>.\n    * Use the emoji character, not the GitHub code. So, use `🐛` instead of `:bug:`. This is so that it shows up correctly outside of GitHub, for example in the release notes.\n* Start the title with a verb. For example `Add`, `Refactor`, `Fix`, etc. This way the title will say the action that the PR does. Like `Add support for teleporting`, instead of `Teleporting wasn't working, so this PR fixes it`.\n* Edit the text of the PR title to start in \"imperative\", like giving an order. So, instead of `Adding support for teleporting` use `Add support for teleporting`.\n* Try to make the title descriptive about what it achieves. If it's a feature, try to describe it, for example `Add support for teleporting` instead of `Create TeleportAdapter class`.\n* Do not finish the title with a period (`.`).\n\nOnce the PR is merged, a GitHub Action (<a href=\"https://github.com/tiangolo/latest-changes\" class=\"external-link\" target=\"_blank\">latest-changes</a>) will use the PR title to update the latest changes automatically.\n\nSo, having a nice PR title will not only look nice in GitHub, but also in the release notes. 📝\n\n## Add Labels to PRs\n\nThe same GitHub Action <a href=\"https://github.com/tiangolo/latest-changes\" class=\"external-link\" target=\"_blank\">latest-changes</a> uses one label in the PR to decide the section in the release notes to put this PR in.\n\nMake sure you use a supported label from the <a href=\"https://github.com/tiangolo/latest-changes#using-labels\" class=\"external-link\" target=\"_blank\">latest-changes list of labels</a>:\n\n* `breaking`: Breaking Changes\n    * Existing code will break if they update the version without changing their code. This rarely happens, so this label is not frequently used.\n* `security`: Security Fixes\n    * This is for security fixes, like vulnerabilities. It would almost never be used.\n* `feature`: Features\n    * New features, adding support for things that didn't exist before.\n* `bug`: Fixes\n    * Something that was supported didn't work, and this fixes it. There are many PRs that claim to be bug fixes because the user is doing something in an unexpected way that is not supported, but they considered it what should be supported by default. Many of these are actually features or refactors. But in some cases there's an actual bug.\n* `refactor`: Refactors\n    * This is normally for changes to the internal code that don't change the behavior. Normally it improves maintainability, or enables future features, etc.\n* `upgrade`: Upgrades\n    * This is for upgrades to direct dependencies from the project, or extra optional dependencies, normally in `pyproject.toml`. So, things that would affect final users, they would end up receiving the upgrade in their code base once they update. But this is not for upgrades to internal dependencies used for development, testing, docs, etc. Those internal dependencies or GitHub Action versions should be marked as `internal`, not `upgrade`.\n* `docs`: Docs\n    * Changes in docs. This includes updating the docs, fixing typos. But it doesn't include changes to translations.\n    * You can normally quickly detect it by going to the \"Files changed\" tab in the PR and checking if the updated file(s) starts with `docs/en/docs`. The original version of the docs is always in English, so in `docs/en/docs`.\n* `internal`: Internal\n    * Use this for changes that only affect how the repo is managed. For example upgrades to internal dependencies, changes in GitHub Actions or scripts, etc.\n\n/// tip\n\nSome tools like Dependabot, will add some labels, like `dependencies`, but have in mind that this label is not used by the `latest-changes` GitHub Action, so it won't be used in the release notes. Please make sure one of the labels above is added.\n\n///\n\n## Review PRs\n\n* If a PR doesn't explain what it does or why, if it seems like it could be useful, ask for more information. Otherwise, feel free to close it.\n\n* If a PR seems to be spam, meaningless, only to change statistics (to appear as \"contributor\") or similar, you can simply mark it as `invalid`, and it will be automatically closed.\n\n* If a PR seems to be AI generated, and seems like reviewing it would take more time from you than the time it took to write the prompt, mark it as `maybe-ai`, and it will be automatically closed.\n\n* A PR should have a specific use case that it is solving.\n\n* If the PR is for a feature, it should have docs.\n    * Unless it's a feature we want to discourage, like support for a corner case that we don't want users to use.\n* The docs should include a source example file, not write Python directly in Markdown.\n* If the source example(s) file can have different syntax for different Python versions, there should be different versions of the file, and they should be shown in tabs in the docs.\n* There should be tests testing the source example.\n* Before the PR is applied, the new tests should fail.\n* After applying the PR, the new tests should pass.\n* Coverage should stay at 100%.\n* If you see the PR makes sense, or we discussed it and considered it should be accepted, you can add commits on top of the PR to tweak it, to add docs, tests, format, refactor, remove extra files, etc.\n* Feel free to comment in the PR to ask for more information, to suggest changes, etc.\n* Once you think the PR is ready, move it in the internal GitHub project for me to review it.\n\n## Dependabot PRs\n\nDependabot will create PRs to update dependencies for several things, and those PRs all look similar, but some are way more delicate than others.\n\n* If the PR is for a direct dependency, so, Dependabot is modifying `pyproject.toml` in the main dependencies, **don't merge it**. 😱 Let me check it first. There's a good chance that some additional tweaks or updates are needed.\n* If the PR updates one of the internal dependencies, for example the group `dev` in `pyproject.toml`, or GitHub Action versions, if the tests are passing, the release notes (shown in a summary in the PR) don't show any obvious potential breaking change, you can merge it. 😎\n\n## Mark GitHub Discussions Answers\n\nWhen a question in GitHub Discussions has been answered, mark the answer by clicking \"Mark as answer\".\n\nYou can filter discussions by <a href=\"https://github.com/fastapi/typer/discussions/categories/questions?discussions_q=category:Questions+is:open+is:unanswered\" class=\"external-link\" target=\"_blank\">`Questions` that are `Unanswered`</a>.\n"
  },
  {
    "path": "docs/management.md",
    "content": "# Repository Management\n\nHere's a short description of how the Typer repository is managed and maintained.\n\n## Owner\n\nI, <a href=\"https://github.com/tiangolo\" target=\"_blank\">@tiangolo</a>, am the creator and owner of the Typer repository. 🤓\n\nI normally give the final review to each PR before merging them. I make the final decisions on the project, I'm the <a href=\"https://en.wikipedia.org/wiki/Benevolent_dictator_for_life\" class=\"external-link\" target=\"_blank\"><abbr title=\"Benevolent Dictator For Life\">BDFL</abbr></a>. 😅\n\n## Team\n\nThere's a team of people that help manage and maintain the project. 😎\n\nThey have different levels of permissions and [specific instructions](./management-tasks.md){.internal-link target=_blank}.\n\nSome of the tasks they can perform include:\n\n* Adding labels to PRs.\n* Editing PR titles.\n* Adding commits on top of PRs to tweak them.\n* Mark answers in GitHub Discussions questions, etc.\n* Merge some specific types of PRs.\n\nJoining the team is by invitation only, and I could update or remove permissions, instructions, or membership.\n\n### Team Members\n\nThis is the current list of team members. 😎\n\n<div class=\"user-list user-list-center\">\n{% for user in members[\"members\"] %}\n\n<div class=\"user\"><a href=\"https://github.com/{{ user.login }}\" target=\"_blank\"><div class=\"avatar-wrapper\"><img src=\"https://github.com/{{ user.login }}.png\"/></div><div class=\"title\">@{{ user.login }}</div></a></div>\n{% endfor %}\n\n</div>\n\nAdditional to them, there's a large community of people helping each other and getting involved in the projects in different ways.\n\n## External Contributions\n\nExternal contributions are very welcome and appreciated, including answering questions, submitting PRs, etc. 🙇‍♂️\n\nThere are many ways to [help maintain Typer](./help-typer.md){.internal-link target=_blank}.\n"
  },
  {
    "path": "docs/overrides/main.html",
    "content": "{% extends \"base.html\" %}\n"
  },
  {
    "path": "docs/reference/context.md",
    "content": "# Context\n\nEvery app has a special internal object that keeps track of state relevant to the script's execution.\nFor some advanced use-cases, you may want to access it directly.\nThis can be done by declaring a function parameter of type `typer.Context`.\n\nSimilarly, you can also declare a function parameter with type `typer.CallbackParam` in case a callback could be used\nby several CLI parameters, and you need to figure out which one it was.\n\n\n```python\nfrom typing import Annotated\n\nimport typer\n\napp = typer.Typer()\n\n\ndef name_callback(ctx: typer.Context, param: typer.CallbackParam, value: str):\n    if ctx.resilient_parsing:\n        return\n    print(f\"Validating param: {param.name}\")\n    if value != \"Rick\":\n        raise typer.BadParameter(\"Only Rick is allowed\")\n    return value\n\n\n@app.command()\ndef main(name: Annotated[str | None, typer.Option(callback=name_callback)] = None):\n    print(f\"Hello {name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n```\n\n::: typer.Context\n\n::: typer.CallbackParam\n"
  },
  {
    "path": "docs/reference/file_objects.md",
    "content": "# File objects\n\nWhen you want to declare some types of files, you can use `Path`.\nHowever, in some cases you may need to have access to a file-like object, and then you can use these [special File types](https://typer.tiangolo.com/tutorial/parameter-types/file/).\n\nThese objects can be imported from `typer` directly:\n\n```python\nfrom typer import FileBinaryRead, FileBinaryWrite, FileText, FileTextWrite\n```\n\n::: typer.FileText\n\n::: typer.FileTextWrite\n\n::: typer.FileBinaryRead\n\n::: typer.FileBinaryWrite\n"
  },
  {
    "path": "docs/reference/index.md",
    "content": "# Reference\n\nHere's the reference or code API, the classes, functions, parameters, attributes, and\nall the Typer parts you can use in your applications.\n\nIf you want to **learn Typer** you are much better off reading the\n[Typer Tutorial](https://typer.tiangolo.com/tutorial/).\n"
  },
  {
    "path": "docs/reference/parameters.md",
    "content": "# Parameters\n\nParameters to our command line interface may be both [CLI Options](https://typer.tiangolo.com/tutorial/options/) and [CLI Arguments](https://typer.tiangolo.com/tutorial/arguments/):\n\n```python\nfrom typing import Annotated\nimport typer\n\napp = typer.Typer()\n\n@app.command()\ndef register(\n    user: Annotated[str, typer.Argument()],\n    age: Annotated[int, typer.Option(min=18)],\n    score: Annotated[float, typer.Option(max=100)] = 0,\n):\n    print(f\"User is {user}\")\n    print(f\"--age is {age}\")\n    print(f\"--score is {score}\")\n\nif __name__ == \"__main__\":\n    app()\n```\n\n::: typer.Argument\n    options:\n      show_overloads: false\n\n::: typer.Option\n    options:\n      show_overloads: false\n"
  },
  {
    "path": "docs/reference/run_launch.md",
    "content": "# `run` and `launch`\n\nThese two functions can be imported from `typer` directly:\n\n```python\nfrom typer import launch, run\n```\n\n::: typer.run\n\n::: typer.launch\n"
  },
  {
    "path": "docs/reference/typer.md",
    "content": "# `Typer` class\n\nHere's the reference information for the `Typer` class, with all its parameters, attributes and methods.\n\nYou can import the `Typer` class directly from `typer`:\n\n```python\nfrom typer import Typer\n```\n\n::: typer.Typer\n    options:\n        members:\n            - callback\n            - command\n            - add_typer\n"
  },
  {
    "path": "docs/release-notes.md",
    "content": "# Release Notes\n\n## Latest Changes\n\n### Refactors\n\n* 🎨 Ensure `ty` runs without errors. PR [#1628](https://github.com/fastapi/typer/pull/1628) by [@svlandeg](https://github.com/svlandeg).\n\n### Docs\n\n* 📝 Fix broken link to FastAPI and Friends newsletter. PR [#1540](https://github.com/fastapi/typer/pull/1540) by [@Karlemami](https://github.com/Karlemami).\n* 🔨 Handle external links `target=_blank` and CSS automatically in JS and CSS. PR [#1622](https://github.com/fastapi/typer/pull/1622) by [@tiangolo](https://github.com/tiangolo).\n* 📝 Remove link to Typer developer survey. PR [#1609](https://github.com/fastapi/typer/pull/1609) by [@svlandeg](https://github.com/svlandeg).\n* ✏️ Clean up documentation in `install.md` file. PR [#1606](https://github.com/fastapi/typer/pull/1606) by [@Johandielangman](https://github.com/Johandielangman).\n\n### Internal\n\n* ⬆ Bump prek from 0.3.5 to 0.3.6. PR [#1638](https://github.com/fastapi/typer/pull/1638) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump ty from 0.0.22 to 0.0.23. PR [#1639](https://github.com/fastapi/typer/pull/1639) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump pyjwt from 2.10.1 to 2.12.0. PR [#1636](https://github.com/fastapi/typer/pull/1636) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump cairosvg from 2.8.2 to 2.9.0. PR [#1635](https://github.com/fastapi/typer/pull/1635) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump ruff from 0.15.5 to 0.15.6. PR [#1633](https://github.com/fastapi/typer/pull/1633) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump ty from 0.0.21 to 0.0.22. PR [#1634](https://github.com/fastapi/typer/pull/1634) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump dorny/paths-filter from 3 to 4. PR [#1632](https://github.com/fastapi/typer/pull/1632) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump mkdocs-material from 9.7.4 to 9.7.5. PR [#1629](https://github.com/fastapi/typer/pull/1629) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump prek from 0.3.4 to 0.3.5. PR [#1627](https://github.com/fastapi/typer/pull/1627) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump ty from 0.0.20 to 0.0.21. PR [#1624](https://github.com/fastapi/typer/pull/1624) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump ruff from 0.15.4 to 0.15.5. PR [#1625](https://github.com/fastapi/typer/pull/1625) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump mkdocs-material from 9.7.3 to 9.7.4. PR [#1621](https://github.com/fastapi/typer/pull/1621) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump ty from 0.0.19 to 0.0.20. PR [#1618](https://github.com/fastapi/typer/pull/1618) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump prek from 0.3.3 to 0.3.4. PR [#1617](https://github.com/fastapi/typer/pull/1617) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump ruff from 0.15.2 to 0.15.4. PR [#1616](https://github.com/fastapi/typer/pull/1616) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump ty from 0.0.18 to 0.0.19. PR [#1615](https://github.com/fastapi/typer/pull/1615) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump actions/download-artifact from 7 to 8. PR [#1614](https://github.com/fastapi/typer/pull/1614) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump actions/upload-artifact from 6 to 7. PR [#1613](https://github.com/fastapi/typer/pull/1613) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump mkdocs-material from 9.7.2 to 9.7.3. PR [#1611](https://github.com/fastapi/typer/pull/1611) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump mkdocstrings-python from 2.0.1 to 2.0.3. PR [#1605](https://github.com/fastapi/typer/pull/1605) by [@svlandeg](https://github.com/svlandeg).\n* ⬆ Bump ty from 0.0.17 to 0.0.18. PR [#1602](https://github.com/fastapi/typer/pull/1602) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump griffe-warnings-deprecated from 1.1.0 to 1.1.1. PR [#1603](https://github.com/fastapi/typer/pull/1603) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump griffe-typingdoc from 0.3.0 to 0.3.1. PR [#1604](https://github.com/fastapi/typer/pull/1604) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* 🔧 Upgrade pytest version and config. PR [#1570](https://github.com/fastapi/typer/pull/1570) by [@tiangolo](https://github.com/tiangolo).\n\n## 0.24.1\n\n### Internal\n\n* 👷 Fix CI, do not attempt to build `typer-slim`, nor `typer-cli`. PR [#1569](https://github.com/fastapi/typer/pull/1569) by [@tiangolo](https://github.com/tiangolo).\n* ➖ Drop support for `typer-slim` and `typer-cli`, no more versions will be released, use only `typer`. PR [#1568](https://github.com/fastapi/typer/pull/1568) by [@tiangolo](https://github.com/tiangolo).\n* ⬆ Bump rich from 14.3.2 to 14.3.3. PR [#1565](https://github.com/fastapi/typer/pull/1565) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump pydantic-settings from 2.13.0 to 2.13.1. PR [#1566](https://github.com/fastapi/typer/pull/1566) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump ruff from 0.15.1 to 0.15.2. PR [#1567](https://github.com/fastapi/typer/pull/1567) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump mkdocs-material from 9.7.1 to 9.7.2. PR [#1561](https://github.com/fastapi/typer/pull/1561) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump pillow from 11.3.0 to 12.1.1. PR [#1550](https://github.com/fastapi/typer/pull/1550) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump pytest from 8.4.2 to 9.0.2. PR [#1551](https://github.com/fastapi/typer/pull/1551) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump pydantic-settings from 2.12.0 to 2.13.0. PR [#1552](https://github.com/fastapi/typer/pull/1552) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* 🎨 Upgrade types for Python 3.10. PR [#1549](https://github.com/fastapi/typer/pull/1549) by [@tiangolo](https://github.com/tiangolo).\n* 🔨 Add internal scripts to migrate docs from Python 3.9 to 3.10. PR [#1547](https://github.com/fastapi/typer/pull/1547) by [@tiangolo](https://github.com/tiangolo).\n* ⬆ Bump ty from 0.0.16 to 0.0.17. PR [#1544](https://github.com/fastapi/typer/pull/1544) by [@dependabot[bot]](https://github.com/apps/dependabot).\n\n## 0.24.0\n\n### Breaking Changes\n\n* ➖ Drop support for Python 3.9. PR [#1546](https://github.com/fastapi/typer/pull/1546) by [@tiangolo](https://github.com/tiangolo).\n\n## 0.23.2\n\n### Features\n\n* ✅ Monkeypatch console width to allow running `pytest` directly. PR [#1542](https://github.com/fastapi/typer/pull/1542) by [@SwaatiR](https://github.com/SwaatiR).\n\n### Internal\n\n* 👷 Run tests with lower bound uv sync, update minimum dependencies. PR [#1526](https://github.com/fastapi/typer/pull/1526) by [@YuriiMotov](https://github.com/YuriiMotov).\n* ⬆ Bump prek from 0.3.2 to 0.3.3. PR [#1545](https://github.com/fastapi/typer/pull/1545) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump ruff from 0.15.0 to 0.15.1. PR [#1541](https://github.com/fastapi/typer/pull/1541) by [@dependabot[bot]](https://github.com/apps/dependabot).\n\n## 0.23.1\n\n### Fixes\n\n* 🐛 Fix `TYPER_USE_RICH` parsing to allow disabling Rich completely. PR [#1539](https://github.com/fastapi/typer/pull/1539) by [@bckohan](https://github.com/bckohan).\n\n### Docs\n\n* 📝 Remove documentation pages that reference using Click directly. PR [#1538](https://github.com/fastapi/typer/pull/1538) by [@svlandeg](https://github.com/svlandeg).\n\n### Internal\n\n* ⬆ Bump ty from 0.0.15 to 0.0.16. PR [#1533](https://github.com/fastapi/typer/pull/1533) by [@dependabot[bot]](https://github.com/apps/dependabot).\n\n## 0.23.0\n\n### Breaking Changes\n\n* ♻️ When printing error tracebacks with Rich, default to not showing locals, which are sometimes verbose. PR [#1072](https://github.com/fastapi/typer/pull/1072) by [@tiangolo](https://github.com/tiangolo).\n\n### Docs\n\n* 📝 Add more explicit deprecation note in shell packages. PR [#1534](https://github.com/fastapi/typer/pull/1534) by [@tiangolo](https://github.com/tiangolo).\n\n### Internal\n\n* ⬆ Bump cryptography from 46.0.4 to 46.0.5. PR [#1532](https://github.com/fastapi/typer/pull/1532) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* 🔨 Tweak PDM hook script, remove unnecessary default. PR [#1536](https://github.com/fastapi/typer/pull/1536) by [@tiangolo](https://github.com/tiangolo).\n* ♻️ Simplify build setup scripts and configs for deprecated wrapper packages. PR [#1535](https://github.com/fastapi/typer/pull/1535) by [@tiangolo](https://github.com/tiangolo).\n\n## 0.22.0\n\n### Breaking Changes\n\n* 💥 Make `typer-slim` a shallow wrapper around `typer`, always requiring `rich` and `shellingham`. PR [#1522](https://github.com/fastapi/typer/pull/1522) by [@svlandeg](https://github.com/svlandeg).\n\n## 0.21.2\n\n### Fixes\n\n* 🐛 Fix highlighting of optional variadic argument metavars. PR [#1508](https://github.com/fastapi/typer/pull/1508) by [@BenjyWiener](https://github.com/BenjyWiener).\n* 🐛 Fix `--help` text alignment when using `typer.style()` in option descriptions. PR [#1356](https://github.com/fastapi/typer/pull/1356) by [@mahimairaja](https://github.com/mahimairaja).\n\n### Refactors\n\n* 🎨 Update types and errors, comment ty for now. PR [#1531](https://github.com/fastapi/typer/pull/1531) by [@tiangolo](https://github.com/tiangolo).\n\n### Upgrades\n\n* ➖ Drop `typing-extensions` as external dependency. PR [#1467](https://github.com/fastapi/typer/pull/1467) by [@svlandeg](https://github.com/svlandeg).\n\n### Docs\n\n* 📝 Add reference (code API) docs. PR [#1504](https://github.com/fastapi/typer/pull/1504) by [@svlandeg](https://github.com/svlandeg).\n* 📝 Update the \"Building a Package\" tutorial to use `uv` instead of `poetry`. PR [#1474](https://github.com/fastapi/typer/pull/1474) by [@svlandeg](https://github.com/svlandeg).\n* 📝 Update `management-tasks.md` to be in line with `management-tasks.md` in FastAPI repo. PR [#1519](https://github.com/fastapi/typer/pull/1519) by [@YuriiMotov](https://github.com/YuriiMotov).\n* 📝 Add link to Typer developer survey. PR [#1514](https://github.com/fastapi/typer/pull/1514) by [@tiangolo](https://github.com/tiangolo).\n* 📝 Add contribution instructions about LLM generated code and comments and automated tools for PRs. PR [#1489](https://github.com/fastapi/typer/pull/1489) by [@alejsdev](https://github.com/alejsdev).\n* 🐛 Fix copy button in `custom.js`. PR [#1488](https://github.com/fastapi/typer/pull/1488) by [@alejsdev](https://github.com/alejsdev).\n\n### Internal\n\n* 🔧 Update config for labeler. PR [#1530](https://github.com/fastapi/typer/pull/1530) by [@tiangolo](https://github.com/tiangolo).\n* 🔧 Remove no longer used requirements.txt file. PR [#1528](https://github.com/fastapi/typer/pull/1528) by [@tiangolo](https://github.com/tiangolo).\n* 📌 Update internal dependency limits. PR [#1529](https://github.com/fastapi/typer/pull/1529) by [@tiangolo](https://github.com/tiangolo).\n* 🔧 Add ty to pre-commit. PR [#1527](https://github.com/fastapi/typer/pull/1527) by [@tiangolo](https://github.com/tiangolo).\n* 👷 Add `ty` to the CI `lint` check. PR [#1477](https://github.com/fastapi/typer/pull/1477) by [@svlandeg](https://github.com/svlandeg).\n* ⬆ Bump prek from 0.3.1 to 0.3.2. PR [#1524](https://github.com/fastapi/typer/pull/1524) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump ruff from 0.14.14 to 0.15.0. PR [#1516](https://github.com/fastapi/typer/pull/1516) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* 🔧 Add generate-readme to pre-commit. PR [#1515](https://github.com/fastapi/typer/pull/1515) by [@tiangolo](https://github.com/tiangolo).\n* 👷 Run mypy in pre-commit instead of `Lint` step in `test.yml` workflow. PR [#1511](https://github.com/fastapi/typer/pull/1511) by [@YuriiMotov](https://github.com/YuriiMotov).\n* ⬆ Bump prek from 0.3.0 to 0.3.1. PR [#1510](https://github.com/fastapi/typer/pull/1510) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump rich from 14.3.1 to 14.3.2. PR [#1509](https://github.com/fastapi/typer/pull/1509) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ✅ Update test to use `mod` fixture. PR [#1506](https://github.com/fastapi/typer/pull/1506) by [@svlandeg](https://github.com/svlandeg).\n* ⬆ Bump rich from 14.2.0 to 14.3.1. PR [#1502](https://github.com/fastapi/typer/pull/1502) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump ruff from 0.14.13 to 0.14.14. PR [#1499](https://github.com/fastapi/typer/pull/1499) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* 🔧 Ensure that an edit to `uv.lock` gets the `internal` label. PR [#1497](https://github.com/fastapi/typer/pull/1497) by [@svlandeg](https://github.com/svlandeg).\n* ⬆ Bump ruff from 0.14.10 to 0.14.13. PR [#1493](https://github.com/fastapi/typer/pull/1493) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump urllib3 from 2.6.2 to 2.6.3. PR [#1496](https://github.com/fastapi/typer/pull/1496) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump prek from 0.2.24 to 0.3.0. PR [#1495](https://github.com/fastapi/typer/pull/1495) by [@dependabot[bot]](https://github.com/apps/dependabot).\n\n## 0.21.1\n\n### Fixes\n\n* 🐛 Fix escaping in help text when `rich` is installed but not used. PR [#1089](https://github.com/fastapi/typer/pull/1089) by [@svlandeg](https://github.com/svlandeg).\n\n### Internal\n\n* ⬆️  Migrate to uv. PR [#1472](https://github.com/fastapi/typer/pull/1472) by [@DoctorJohn](https://github.com/DoctorJohn).\n* ⬆ Bump mypy from 1.18.2 to 1.19.1. PR [#1469](https://github.com/fastapi/typer/pull/1469) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump actions/checkout from 5 to 6. PR [#1456](https://github.com/fastapi/typer/pull/1456) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump actions/download-artifact from 6 to 7. PR [#1444](https://github.com/fastapi/typer/pull/1444) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump ruff from 0.14.8 to 0.14.10. PR [#1449](https://github.com/fastapi/typer/pull/1449) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump mkdocs-material from 9.7.0 to 9.7.1. PR [#1446](https://github.com/fastapi/typer/pull/1446) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump actions/upload-artifact from 5 to 6. PR [#1443](https://github.com/fastapi/typer/pull/1443) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump actions/cache from 4 to 5. PR [#1441](https://github.com/fastapi/typer/pull/1441) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* 👷 Update secrets check. PR [#1471](https://github.com/fastapi/typer/pull/1471) by [@YuriiMotov](https://github.com/YuriiMotov).\n* ✅ Add missing tests for code examples. PR [#1465](https://github.com/fastapi/typer/pull/1465) by [@YuriiMotov](https://github.com/YuriiMotov).\n* 🔧 Update pre-commit to use local Ruff instead of hook, unpin `prek`. PR [#1466](https://github.com/fastapi/typer/pull/1466) by [@YuriiMotov](https://github.com/YuriiMotov).\n* ⬆ Bump mypy from 1.14.1 to 1.18.2. PR [#1382](https://github.com/fastapi/typer/pull/1382) by [@dependabot[bot]](https://github.com/apps/dependabot).\n\n## 0.21.0\n\n### Breaking Changes\n\n* ➖ Drop support for Python 3.8. PR [#1464](https://github.com/fastapi/typer/pull/1464) by [@tiangolo](https://github.com/tiangolo).\n* ➖ Drop support for Python 3.8 in CI. PR [#1463](https://github.com/fastapi/typer/pull/1463) by [@YuriiMotov](https://github.com/YuriiMotov) and [@tiangolo](https://github.com/tiangolo).\n\n### Docs\n\n* 📝 Update code examples to Python 3.9. PR [#1459](https://github.com/fastapi/typer/pull/1459) by [@YuriiMotov](https://github.com/YuriiMotov).\n\n### Internal\n\n* 💚 Move `ruff` dependency to shared `requirements-docs-tests.txt` to fix \"Build docs\" workflow in CI. PR [#1458](https://github.com/fastapi/typer/pull/1458) by [@YuriiMotov](https://github.com/YuriiMotov).\n* ⬆ Bump markdown-include-variants from 0.0.5 to 0.0.8. PR [#1442](https://github.com/fastapi/typer/pull/1442) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* 👷 Add pre-commit workflow. PR [#1453](https://github.com/fastapi/typer/pull/1453) by [@tiangolo](https://github.com/tiangolo).\n* 👷 Configure coverage, error on main tests, don't wait for Smokeshow. PR [#1448](https://github.com/fastapi/typer/pull/1448) by [@YuriiMotov](https://github.com/YuriiMotov).\n* 👷 Run Smokeshow always, even on test failures. PR [#1447](https://github.com/fastapi/typer/pull/1447) by [@YuriiMotov](https://github.com/YuriiMotov).\n* 🔨 Add Typer script to generate example variants for Python files. PR [#1452](https://github.com/fastapi/typer/pull/1452) by [@tiangolo](https://github.com/tiangolo).\n\n## 0.20.1\n\n### Features\n\n* ✨ Add support for standard tracebacks via the env `TYPER_STANDARD_TRACEBACK`. PR [#1299](https://github.com/fastapi/typer/pull/1299) by [@colin-nolan](https://github.com/colin-nolan).\n\n### Fixes\n\n* 🐛 Ensure that `options_metavar` is passed through correctly. PR [#816](https://github.com/fastapi/typer/pull/816) by [@gar1t](https://github.com/gar1t).\n* 🐛 Ensure an optional argument is shown in brackets, even when `metavar` is set. PR [#1409](https://github.com/fastapi/typer/pull/1409) by [@svlandeg](https://github.com/svlandeg).\n* 🐛 Ensure that the default `rich_markup_mode` is interpreted correctly. PR [#1304](https://github.com/fastapi/typer/pull/1304) by [@svlandeg](https://github.com/svlandeg).\n\n### Refactors\n\n* ♻️ Refactor the handling of `shellingham`. PR [#1347](https://github.com/fastapi/typer/pull/1347) by [@nathanjmcdougall](https://github.com/nathanjmcdougall).\n\n### Docs\n\n* 📝 Ensure that bold letters are rendered correctly in `printing.md`. PR [#1365](https://github.com/fastapi/typer/pull/1365) by [@svlandeg](https://github.com/svlandeg).\n* 🩺 Update test badge to only reflect pushes to `master`. PR [#1414](https://github.com/fastapi/typer/pull/1414) by [@svlandeg](https://github.com/svlandeg).\n* 📝 Update console output on the Rich help formatting page. PR [#1430](https://github.com/fastapi/typer/pull/1430) by [@svlandeg](https://github.com/svlandeg).\n* 📝 Update emoji used in Rich help formatting tutorial. PR [#1429](https://github.com/fastapi/typer/pull/1429) by [@svlandeg](https://github.com/svlandeg).\n* 📝 Remove duplicate explanation how the path is resolved. PR [#956](https://github.com/fastapi/typer/pull/956) by [@dennis-rall](https://github.com/dennis-rall).\n* 📝 Update docs to use `Typer()` more prominently. PR [#1418](https://github.com/fastapi/typer/pull/1418) by [@svlandeg](https://github.com/svlandeg).\n* 💄 Use font 'Fira Code' to fix display of Rich panels in docs in Windows. PR [#1419](https://github.com/fastapi/typer/pull/1419) by [@tiangolo](https://github.com/tiangolo).\n\n### Internal\n\n* 🔨  Add `--showlocals` to `test.sh`. PR [#1169](https://github.com/fastapi/typer/pull/1169) by [@rickwporter](https://github.com/rickwporter).\n* ⬆ Bump ruff from 0.14.6 to 0.14.8. PR [#1436](https://github.com/fastapi/typer/pull/1436) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#1434](https://github.com/fastapi/typer/pull/1434) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci).\n* ✅ Update tests to use `mod.app` . PR [#1427](https://github.com/fastapi/typer/pull/1427) by [@svlandeg](https://github.com/svlandeg).\n* ⬆ Bump actions/checkout from 5 to 6. PR [#1426](https://github.com/fastapi/typer/pull/1426) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#1425](https://github.com/fastapi/typer/pull/1425) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci).\n* ⬆ Bump ruff from 0.14.5 to 0.14.6. PR [#1423](https://github.com/fastapi/typer/pull/1423) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump actions/checkout from 5 to 6. PR [#1417](https://github.com/fastapi/typer/pull/1417) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* 👷 Upgrade `latest-changes` GitHub Action and pin `actions/checkout@v5`. PR [#1424](https://github.com/fastapi/typer/pull/1424) by [@tiangolo](https://github.com/tiangolo).\n* 🔧 Upgrade Material for MkDocs and remove insiders. PR [#1416](https://github.com/fastapi/typer/pull/1416) by [@tiangolo](https://github.com/tiangolo).\n* ⬆ Bump mkdocs-material from 9.6.23 to 9.7.0. PR [#1404](https://github.com/fastapi/typer/pull/1404) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump mkdocs-macros-plugin from 1.4.1 to 1.5.0. PR [#1406](https://github.com/fastapi/typer/pull/1406) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump ruff from 0.14.4 to 0.14.5. PR [#1407](https://github.com/fastapi/typer/pull/1407) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#1413](https://github.com/fastapi/typer/pull/1413) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci).\n* ⬆ Bump ruff from 0.14.3 to 0.14.4. PR [#1402](https://github.com/fastapi/typer/pull/1402) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#1403](https://github.com/fastapi/typer/pull/1403) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci).\n* ⬆ Bump ruff from 0.14.2 to 0.14.3. PR [#1396](https://github.com/fastapi/typer/pull/1396) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#1399](https://github.com/fastapi/typer/pull/1399) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci).\n* ⬆ Bump mkdocs-material from 9.6.22 to 9.6.23. PR [#1398](https://github.com/fastapi/typer/pull/1398) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#1394](https://github.com/fastapi/typer/pull/1394) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci).\n* ⬆ Bump ruff from 0.14.1 to 0.14.2. PR [#1383](https://github.com/fastapi/typer/pull/1383) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump actions/upload-artifact from 4 to 5. PR [#1388](https://github.com/fastapi/typer/pull/1388) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump mkdocs-macros-plugin from 1.4.0 to 1.4.1. PR [#1389](https://github.com/fastapi/typer/pull/1389) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump actions/download-artifact from 5 to 6. PR [#1391](https://github.com/fastapi/typer/pull/1391) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* 🔧 Add PEP-639 license metadata. PR [#1387](https://github.com/fastapi/typer/pull/1387) by [@svlandeg](https://github.com/svlandeg).\n* ⬆ Bump mypy from 1.11.2 to 1.14.1. PR [#1375](https://github.com/fastapi/typer/pull/1375) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#1380](https://github.com/fastapi/typer/pull/1380) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci).\n* ⬆ Bump ruff from 0.14.0 to 0.14.1. PR [#1379](https://github.com/fastapi/typer/pull/1379) by [@dependabot[bot]](https://github.com/apps/dependabot).\n\n## 0.20.0\n\n### Features\n\n* ✨ Enable command suggestions on typo by default. PR [#1371](https://github.com/fastapi/typer/pull/1371) by [@savannahostrowski](https://github.com/savannahostrowski).\n\n### Upgrades\n\n* ⬆️ Add support for Python 3.14. PR [#1372](https://github.com/fastapi/typer/pull/1372) by [@svlandeg](https://github.com/svlandeg).\n\n### Internal\n\n* 👷 Add nightly workflow to run tests against CPython main branch. PR [#1374](https://github.com/fastapi/typer/pull/1374) by [@savannahostrowski](https://github.com/savannahostrowski).\n* ⬆ Bump mkdocs-material from 9.6.21 to 9.6.22. PR [#1377](https://github.com/fastapi/typer/pull/1377) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* 🔧 Configure reminder for `waiting` label in `issue-manager`. PR [#1378](https://github.com/fastapi/typer/pull/1378) by [@YuriiMotov](https://github.com/YuriiMotov).\n* ⬆ Bump ruff from 0.13.3 to 0.14.0. PR [#1368](https://github.com/fastapi/typer/pull/1368) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#1376](https://github.com/fastapi/typer/pull/1376) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci).\n* ⬆ Bump mkdocs-macros-plugin from 1.3.9 to 1.4.0. PR [#1354](https://github.com/fastapi/typer/pull/1354) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump mkdocs-material from 9.6.20 to 9.6.21. PR [#1360](https://github.com/fastapi/typer/pull/1360) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump mypy from 1.4.1 to 1.11.2. PR [#957](https://github.com/fastapi/typer/pull/957) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump astral-sh/setup-uv from 6 to 7. PR [#1369](https://github.com/fastapi/typer/pull/1369) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump ruff from 0.13.2 to 0.13.3. PR [#1366](https://github.com/fastapi/typer/pull/1366) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#1367](https://github.com/fastapi/typer/pull/1367) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci).\n* ⬆ Bump tiangolo/issue-manager from 0.5.1 to 0.6.0. PR [#1361](https://github.com/fastapi/typer/pull/1361) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump ruff from 0.13.1 to 0.13.2. PR [#1357](https://github.com/fastapi/typer/pull/1357) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#1358](https://github.com/fastapi/typer/pull/1358) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci).\n* 👷 Update docs previews comment, single comment, add failure status. PR [#1359](https://github.com/fastapi/typer/pull/1359) by [@tiangolo](https://github.com/tiangolo).\n\n## 0.19.2\n\n### Fixes\n\n* 🐛  Fix list convertor with an empty list default factory . PR [#1350](https://github.com/fastapi/typer/pull/1350) by [@svlandeg](https://github.com/svlandeg).\n\n### Refactors\n\n* 🔥 Drop support for Python 3.7. PR [#830](https://github.com/fastapi/typer/pull/830) by [@kinuax](https://github.com/kinuax).\n\n### Internal\n\n* ⬆ Bump ruff from 0.13.0 to 0.13.1. PR [#1339](https://github.com/fastapi/typer/pull/1339) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#1351](https://github.com/fastapi/typer/pull/1351) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci).\n* ⬆ Bump markdown-include-variants from 0.0.4 to 0.0.5. PR [#1348](https://github.com/fastapi/typer/pull/1348) by [@dependabot[bot]](https://github.com/apps/dependabot).\n\n## 0.19.1\n\n**Note**: this is the last version to support Python 3.7, going forward Typer will require Python 3.8+. And soon Python 3.8 will also be dropped as [Python 3.8 reached it's end of life](https://devguide.python.org/versions/) almost a year ago.\n\n### Fixes\n\n* 🐛 Ensure that `Optional[list]` values work correctly with callbacks. PR [#1018](https://github.com/fastapi/typer/pull/1018) by [@solesensei](https://github.com/solesensei).\n\n## 0.19.0\n\n### Features\n\n* ✨ Support `typing.Literal` to define a set of predefined choices. PR [#429](https://github.com/fastapi/typer/pull/429) by [@blackary](https://github.com/blackary).\n* ✨ Allow setting an environment variable to `None` in `CliRunner.invoke`. PR [#1303](https://github.com/fastapi/typer/pull/1303) by [@arjenzorgdoc](https://github.com/arjenzorgdoc).\n\n### Refactors\n\n* ✅ Use Ruff rules to ensure safe lazy-loading of `rich`. PR [#1297](https://github.com/fastapi/typer/pull/1297) by [@nathanjmcdougall](https://github.com/nathanjmcdougall).\n* ✅ Avoid rich formatting in number test. PR [#1305](https://github.com/fastapi/typer/pull/1305) by [@svlandeg](https://github.com/svlandeg).\n\n### Docs\n\n* 📝 Clarify single-command vs multi-command behaviour in README. PR [#1268](https://github.com/fastapi/typer/pull/1268) by [@MorgenPronk](https://github.com/MorgenPronk).\n\n## 0.18.0\n\n### Fixes\n\n* 👽️ Ensure compatibility with Click 8.3.0 by restoring the original `value_is_missing` function. PR [#1333](https://github.com/fastapi/typer/pull/1333) by [@svlandeg](https://github.com/svlandeg).\n\n### Upgrades\n\n* 📌 Remove pin for Click < 8.3.0 now that there's a fix for the changes. PR [#1346](https://github.com/fastapi/typer/pull/1346) by [@tiangolo](https://github.com/tiangolo).\n\n## 0.17.5\n\n### Fixes\n\n* ⬇️ Restrict Click to below 8.3.0 to handle changes in the new version. PR [#1336](https://github.com/fastapi/typer/pull/1336) by [@svlandeg](https://github.com/svlandeg).\n\n### Internal\n\n* ⬆ Bump mkdocs-material from 9.6.14 to 9.6.20. PR [#1308](https://github.com/fastapi/typer/pull/1308) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump mkdocs-material from 9.5.50 to 9.6.14. PR [#1223](https://github.com/fastapi/typer/pull/1223) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump actions/download-artifact from 4 to 5. PR [#1269](https://github.com/fastapi/typer/pull/1269) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump ruff from 0.12.12 to 0.13.0. PR [#1302](https://github.com/fastapi/typer/pull/1302) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#1307](https://github.com/fastapi/typer/pull/1307) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci).\n* ⬆ Update pytest-cov requirement from <7.0.0,>=2.10.0 to >=2.10.0,<8.0.0. PR [#1301](https://github.com/fastapi/typer/pull/1301) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump actions/setup-python from 5 to 6. PR [#1291](https://github.com/fastapi/typer/pull/1291) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump ruff from 0.12.11 to 0.12.12. PR [#1295](https://github.com/fastapi/typer/pull/1295) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#1300](https://github.com/fastapi/typer/pull/1300) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci).\n* ⬆ Bump actions/labeler from 5 to 6. PR [#1296](https://github.com/fastapi/typer/pull/1296) by [@dependabot[bot]](https://github.com/apps/dependabot).\n\n## 0.17.4\n\n### Fixes\n\n* 🐛 Make sure `rich.markup` is imported when rendering help text. PR [#1290](https://github.com/fastapi/typer/pull/1290) by [@g-arjones](https://github.com/g-arjones).\n\n### Internal\n\n* ⬆ Bump pypa/gh-action-pypi-publish from 1.12.4 to 1.13.0. PR [#1292](https://github.com/fastapi/typer/pull/1292) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump ruff from 0.12.10 to 0.12.11. PR [#1283](https://github.com/fastapi/typer/pull/1283) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#1288](https://github.com/fastapi/typer/pull/1288) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci).\n* 👷 Set permissions for conflict detector workflow. PR [#1287](https://github.com/fastapi/typer/pull/1287) by [@svlandeg](https://github.com/svlandeg).\n* 👷 Detect and label merge conflicts on PRs automatically. PR [#1286](https://github.com/fastapi/typer/pull/1286) by [@svlandeg](https://github.com/svlandeg).\n\n## 0.17.3\n\n### Features\n\n* ✨ Allow annotated parsing with a subclass of `Path`. PR [#1183](https://github.com/fastapi/typer/pull/1183) by [@emfdavid](https://github.com/emfdavid).\n\n## 0.17.2\n\n### Fixes\n\n* 🐛 Avoid printing `default: None` in the help section when using Rich. PR [#1120](https://github.com/fastapi/typer/pull/1120) by [@mattmess1221](https://github.com/mattmess1221).\n\n## 0.17.1\n\n### Fixes\n\n* 🐛 Fix markdown formatting in `--help` output. PR [#815](https://github.com/fastapi/typer/pull/815) by [@gar1t](https://github.com/gar1t).\n\n## 0.17.0\n\n### Features\n\n* ⚡️ Lazy-load `rich_utils` to reduce startup time. PR [#1128](https://github.com/fastapi/typer/pull/1128) by [@oefe](https://github.com/oefe).\n\n### Internal\n\n* ⬆ Bump ruff from 0.12.9 to 0.12.10. PR [#1280](https://github.com/fastapi/typer/pull/1280) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#1281](https://github.com/fastapi/typer/pull/1281) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci).\n* ⬆ Update pytest-sugar requirement from <1.1.0,>=0.9.4 to >=0.9.4,<1.2.0. PR [#1279](https://github.com/fastapi/typer/pull/1279) by [@dependabot[bot]](https://github.com/apps/dependabot).\n\n## 0.16.1\n\n### Fixes\n\n* 🐛 Avoid printing additional output with `no_args_is_help=True` and Click 8.2. PR [#1278](https://github.com/fastapi/typer/pull/1278) by [@svlandeg](https://github.com/svlandeg).\n\n### Docs\n\n* 📝 Remove duplicated line in `environment-variables.md`. PR [#1277](https://github.com/fastapi/typer/pull/1277) by [@neirzhei](https://github.com/neirzhei).\n* 📝 Fix reference to `count` parameter in the documentation. PR [#1201](https://github.com/fastapi/typer/pull/1201) by [@PokkaKiyo](https://github.com/PokkaKiyo).\n\n### Internal\n\n* ⬆ Bump ruff from 0.11.13 to 0.12.9. PR [#1276](https://github.com/fastapi/typer/pull/1276) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#1247](https://github.com/fastapi/typer/pull/1247) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci).\n* ⬆ Bump actions/checkout from 4 to 5. PR [#1271](https://github.com/fastapi/typer/pull/1271) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump mkdocs-macros-plugin from 1.3.7 to 1.3.9. PR [#1272](https://github.com/fastapi/typer/pull/1272) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump tiangolo/latest-changes from 0.3.2 to 0.4.0. PR [#1265](https://github.com/fastapi/typer/pull/1265) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump pillow from 11.2.1 to 11.3.0. PR [#1249](https://github.com/fastapi/typer/pull/1249) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#1206](https://github.com/fastapi/typer/pull/1206) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci).\n* ⬆ Bump pillow from 11.1.0 to 11.2.1. PR [#1198](https://github.com/fastapi/typer/pull/1198) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump cairosvg from 2.7.1 to 2.8.2. PR [#1226](https://github.com/fastapi/typer/pull/1226) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump ruff from 0.11.6 to 0.11.13. PR [#1241](https://github.com/fastapi/typer/pull/1241) by [@dependabot[bot]](https://github.com/apps/dependabot).\n\n## 0.16.0\n\n### Upgrades\n\n* ⬆️ Add compatibility with Click 8.2. PR [#1222](https://github.com/fastapi/typer/pull/1222) by [@tiangolo](https://github.com/tiangolo).\n\nWhen using the `CliRunner` with Click < 8.2, to be able to access the `stderr` output, you needed to set the `mix_stderr` parameter to `True`. Since Click 8.2 (and Typer 0.160 this release supporting it) this is no longer necessary, so this parameter has been removed.\n\n### Refactors\n\n* ✅ Refactor tests for compatibility with Click 8.2. PR [#1230](https://github.com/fastapi/typer/pull/1230) by [@tiangolo](https://github.com/tiangolo).\n\n### Internal\n\n* 🔧 Remove Google Analytics. PR [#1229](https://github.com/fastapi/typer/pull/1229) by [@tiangolo](https://github.com/tiangolo).\n\n## 0.15.4\n\n### Upgrades\n\n* 📌 Pin Click to < 8.2, compatibility for Click >= 8.2 will be added in a future version. PR [#1225](https://github.com/fastapi/typer/pull/1225) by [@tiangolo](https://github.com/tiangolo).\n\n## 0.15.3\n\n### Fixes\n\n* 🐛 Ensure that autocompletion works for `Path` arguments/options. PR [#1138](https://github.com/fastapi/typer/pull/1138) by [@svlandeg](https://github.com/svlandeg).\n* 🐛 Fix newline after header in help text, and add more tests for the behaviour of `rich_markup_mode` . PR [#964](https://github.com/fastapi/typer/pull/964) by [@svlandeg](https://github.com/svlandeg).\n\n### Internal\n\n* ⬆ Bump astral-sh/setup-uv from 5 to 6. PR [#1203](https://github.com/fastapi/typer/pull/1203) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump ruff from 0.11.2 to 0.11.6. PR [#1200](https://github.com/fastapi/typer/pull/1200) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#1196](https://github.com/fastapi/typer/pull/1196) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci).\n* ⬆ Bump ruff from 0.11.1 to 0.11.2. PR [#1186](https://github.com/fastapi/typer/pull/1186) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#1187](https://github.com/fastapi/typer/pull/1187) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci).\n* ⬆ Bump ruff from 0.11.0 to 0.11.1. PR [#1185](https://github.com/fastapi/typer/pull/1185) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump ruff from 0.9.10 to 0.11.0. PR [#1180](https://github.com/fastapi/typer/pull/1180) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#1181](https://github.com/fastapi/typer/pull/1181) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci).\n* ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#1176](https://github.com/fastapi/typer/pull/1176) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci).\n* ⬆ Bump ruff from 0.9.9 to 0.9.10. PR [#1175](https://github.com/fastapi/typer/pull/1175) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#1171](https://github.com/fastapi/typer/pull/1171) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci).\n* ⬆ Bump ruff from 0.9.7 to 0.9.9. PR [#1166](https://github.com/fastapi/typer/pull/1166) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ✏️ Fix typo in test name. PR [#1165](https://github.com/fastapi/typer/pull/1165) by [@svlandeg](https://github.com/svlandeg).\n\n## 0.15.2\n\n### Features\n\n* ✨ Allow custom styles for commands in help output. PR [#1103](https://github.com/fastapi/typer/pull/1103) by [@TheTechromancer](https://github.com/TheTechromancer).\n* ✨ Avoid the unnecessary import of `typing_extensions` in newer Python versions. PR [#1048](https://github.com/fastapi/typer/pull/1048) by [@horta](https://github.com/horta).\n\n### Fixes\n\n* 🐛 Fix shell completions for the fish shell. PR [#1069](https://github.com/fastapi/typer/pull/1069) by [@goraje](https://github.com/goraje).\n\n### Refactors\n\n* 🚚 Rename test to corner-cases to make it more explicit. PR [#1083](https://github.com/fastapi/typer/pull/1083) by [@tiangolo](https://github.com/tiangolo).\n\n### Docs\n\n* ✏️ Fix small typos in the tutorial documentation. PR [#1137](https://github.com/fastapi/typer/pull/1137) by [@svlandeg](https://github.com/svlandeg).\n* 📝 Update optional CLI argument section in tutorial with `Annotated`. PR [#983](https://github.com/fastapi/typer/pull/983) by [@gkeuccsr](https://github.com/gkeuccsr).\n* 📝 Clarify the need for `mix_stderr` when accessing the output of `stderr` in tests. PR [#1045](https://github.com/fastapi/typer/pull/1045) by [@mrchrisadams](https://github.com/mrchrisadams).\n\n### Internal\n\n* 🔧 Add support for Python 3.13, tests in CI and add PyPI trove classifier. PR [#1091](https://github.com/fastapi/typer/pull/1091) by [@edgarrmondragon](https://github.com/edgarrmondragon).\n* ⬆ Bump ruff from 0.9.6 to 0.9.7. PR [#1161](https://github.com/fastapi/typer/pull/1161) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#1162](https://github.com/fastapi/typer/pull/1162) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci).\n* ⬆ Bump ruff from 0.9.5 to 0.9.6. PR [#1153](https://github.com/fastapi/typer/pull/1153) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#1151](https://github.com/fastapi/typer/pull/1151) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci).\n* ⬆ Bump ruff from 0.9.4 to 0.9.5. PR [#1146](https://github.com/fastapi/typer/pull/1146) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#1142](https://github.com/fastapi/typer/pull/1142) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci).\n* ⬆ Bump ruff from 0.9.3 to 0.9.4. PR [#1139](https://github.com/fastapi/typer/pull/1139) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#1135](https://github.com/fastapi/typer/pull/1135) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci).\n* ⬆ Bump ruff from 0.9.1 to 0.9.3. PR [#1136](https://github.com/fastapi/typer/pull/1136) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#1130](https://github.com/fastapi/typer/pull/1130) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci).\n* ⬆ Bump ruff from 0.8.6 to 0.9.1. PR [#1118](https://github.com/fastapi/typer/pull/1118) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump pypa/gh-action-pypi-publish from 1.12.3 to 1.12.4. PR [#1132](https://github.com/fastapi/typer/pull/1132) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump mkdocs-material from 9.5.49 to 9.5.50. PR [#1129](https://github.com/fastapi/typer/pull/1129) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* 💚 Fix test matrix for Python 3.7. PR [#1116](https://github.com/fastapi/typer/pull/1116) by [@svlandeg](https://github.com/svlandeg).\n* ⬆ Bump ruff from 0.8.4 to 0.8.6. PR [#1107](https://github.com/fastapi/typer/pull/1107) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#1109](https://github.com/fastapi/typer/pull/1109) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci).\n* ⬆ Bump pillow from 11.0.0 to 11.1.0. PR [#1104](https://github.com/fastapi/typer/pull/1104) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#1102](https://github.com/fastapi/typer/pull/1102) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci).\n* ⬆ Bump ruff from 0.8.3 to 0.8.4. PR [#1097](https://github.com/fastapi/typer/pull/1097) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump astral-sh/setup-uv from 4 to 5. PR [#1098](https://github.com/fastapi/typer/pull/1098) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump markdown-include-variants from 0.0.3 to 0.0.4. PR [#1100](https://github.com/fastapi/typer/pull/1100) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump ruff from 0.8.2 to 0.8.3. PR [#1090](https://github.com/fastapi/typer/pull/1090) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#1093](https://github.com/fastapi/typer/pull/1093) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci).\n* ⬆ Bump mkdocs-material from 9.5.48 to 9.5.49. PR [#1092](https://github.com/fastapi/typer/pull/1092) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump pypa/gh-action-pypi-publish from 1.12.2 to 1.12.3. PR [#1088](https://github.com/fastapi/typer/pull/1088) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#1087](https://github.com/fastapi/typer/pull/1087) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci).\n* ⬆ Bump ruff from 0.8.1 to 0.8.2. PR [#1084](https://github.com/fastapi/typer/pull/1084) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump mkdocs-material from 9.5.47 to 9.5.48. PR [#1086](https://github.com/fastapi/typer/pull/1086) by [@dependabot[bot]](https://github.com/apps/dependabot).\n\n## 0.15.1\n\n### Features\n\n* 🗑️ Deprecate `shell_complete` and continue to use `autocompletion` for CLI parameters. PR [#974](https://github.com/fastapi/typer/pull/974) by [@svlandeg](https://github.com/svlandeg).\n\n### Docs\n\n* ✏️ Fix a few typos in the source and documentation. PR [#1028](https://github.com/fastapi/typer/pull/1028) by [@kkirsche](https://github.com/kkirsche).\n* 📝 Fix minor inconsistencies and typos in tutorial. PR [#1067](https://github.com/fastapi/typer/pull/1067) by [@tvoirand](https://github.com/tvoirand).\n* ✏️ Fix a few small typos in the documentation. PR [#1077](https://github.com/fastapi/typer/pull/1077) by [@svlandeg](https://github.com/svlandeg).\n\n### Internal\n\n* 🔧 Update build-docs filter patterns. PR [#1080](https://github.com/fastapi/typer/pull/1080) by [@tiangolo](https://github.com/tiangolo).\n* 🔨 Update deploy docs preview script. PR [#1079](https://github.com/fastapi/typer/pull/1079) by [@tiangolo](https://github.com/tiangolo).\n* 🔧 Update members. PR [#1078](https://github.com/fastapi/typer/pull/1078) by [@tiangolo](https://github.com/tiangolo).\n* ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#1071](https://github.com/fastapi/typer/pull/1071) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci).\n* ⬆ Update httpx requirement from <0.28.0,>=0.27.0 to >=0.27.0,<0.29.0. PR [#1065](https://github.com/fastapi/typer/pull/1065) by [@dependabot[bot]](https://github.com/apps/dependabot).\n\n## 0.15.0\n\n### Features\n\n* ✨ Add support for extending typer apps without passing a name, add commands to the top level. PR [#1037](https://github.com/fastapi/typer/pull/1037) by [@patrick91](https://github.com/patrick91).\n    * New docs: [One File Per Command](https://typer.tiangolo.com/tutorial/one-file-per-command/).\n\n### Internal\n\n* ⬆ Bump mkdocs-material from 9.5.46 to 9.5.47. PR [#1070](https://github.com/fastapi/typer/pull/1070) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump ruff from 0.8.0 to 0.8.1. PR [#1066](https://github.com/fastapi/typer/pull/1066) by [@dependabot[bot]](https://github.com/apps/dependabot).\n\n## 0.14.0\n\n### Breaking Changes\n\n* 🔥 Remove auto naming of groups added via `add_typer` based on the group's callback function name. PR [#1052](https://github.com/fastapi/typer/pull/1052) by [@patrick91](https://github.com/patrick91).\n\nBefore, it was supported to infer the name of a command group from the callback function name in the sub-app, so, in this code:\n\n```python\nimport typer\n\napp = typer.Typer()\nusers_app = typer.Typer()\n\napp.add_typer(users_app)\n\n\n@users_app.callback()\ndef users():  # <-- This was the inferred command group name\n    \"\"\"\n    Manage users in the app.\n    \"\"\"\n\n\n@users_app.command()\ndef create(name: str):\n    print(f\"Creating user: {name}\")\n```\n\n...the command group would be named `users`, based on the name of the function `def users()`.\n\nNow you need to set it explicitly:\n\n```python\nimport typer\n\napp = typer.Typer()\nusers_app = typer.Typer()\n\napp.add_typer(users_app, name=\"users\")  # <-- Explicitly set the command group name\n\n\n@users_app.callback()\ndef users():\n    \"\"\"\n    Manage users in the app.\n    \"\"\"\n\n\n@users_app.command()\ndef create(name: str):\n    print(f\"Creating user: {name}\")\n```\n\nUpdated docs [SubCommand Name and Help](https://typer.tiangolo.com/tutorial/subcommands/name-and-help/).\n\n**Note**: this change will enable important features in the next release. 🤩\n\n### Internal\n\n* ⬆ Bump pypa/gh-action-pypi-publish from 1.10.3 to 1.12.2. PR [#1043](https://github.com/fastapi/typer/pull/1043) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump mkdocs-material from 9.5.44 to 9.5.46. PR [#1062](https://github.com/fastapi/typer/pull/1062) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump ruff from 0.7.4 to 0.8.0. PR [#1059](https://github.com/fastapi/typer/pull/1059) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump astral-sh/setup-uv from 3 to 4. PR [#1061](https://github.com/fastapi/typer/pull/1061) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#1053](https://github.com/fastapi/typer/pull/1053) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci).\n\n## 0.13.1\n\n### Features\n\n* ✨ Remove Rich tags when showing completion text. PR [#877](https://github.com/fastapi/typer/pull/877) by [@svlandeg](https://github.com/svlandeg).\n* ✨ Render Rich markup as HTML in Markdown docs. PR [#847](https://github.com/fastapi/typer/pull/847) by [@svlandeg](https://github.com/svlandeg).\n* ✨ Support cp850 encoding for auto-completion in PowerShell. PR [#808](https://github.com/fastapi/typer/pull/808) by [@svlandeg](https://github.com/svlandeg).\n* ✨ Allow gettext translation of help message. PR [#886](https://github.com/fastapi/typer/pull/886) by [@svlandeg](https://github.com/svlandeg).\n\n### Refactors\n\n* 🐛 Fix printing HTML from Rich output. PR [#1055](https://github.com/fastapi/typer/pull/1055) by [@tiangolo](https://github.com/tiangolo).\n\n### Docs\n\n* 📝 Update markdown includes to use the new simpler format. PR [#1054](https://github.com/fastapi/typer/pull/1054) by [@tiangolo](https://github.com/tiangolo).\n\n### Internal\n\n* ⬆ Bump ruff from 0.7.3 to 0.7.4. PR [#1051](https://github.com/fastapi/typer/pull/1051) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#1047](https://github.com/fastapi/typer/pull/1047) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci).\n* ⬆ Bump ruff from 0.7.2 to 0.7.3. PR [#1046](https://github.com/fastapi/typer/pull/1046) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump tiangolo/latest-changes from 0.3.1 to 0.3.2. PR [#1044](https://github.com/fastapi/typer/pull/1044) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Update pytest-cov requirement from <6.0.0,>=2.10.0 to >=2.10.0,<7.0.0. PR [#1033](https://github.com/fastapi/typer/pull/1033) by [@dependabot[bot]](https://github.com/apps/dependabot).\n\n## 0.13.0\n\n### Features\n\n* ✨ Handle `KeyboardInterrupt` separately from other exceptions. PR [#1039](https://github.com/fastapi/typer/pull/1039) by [@patrick91](https://github.com/patrick91).\n* ✨ Update `launch` to not print anything when opening urls. PR [#1035](https://github.com/fastapi/typer/pull/1035) by [@patrick91](https://github.com/patrick91).\n* ✨ Show help items in order of definition. PR [#944](https://github.com/fastapi/typer/pull/944) by [@svlandeg](https://github.com/svlandeg).\n\n### Fixes\n\n* 🐛 Fix equality check for custom classes. PR [#979](https://github.com/fastapi/typer/pull/979) by [@AryazE](https://github.com/AryazE).\n* 🐛 Allow colon in zsh autocomplete values and descriptions. PR [#988](https://github.com/fastapi/typer/pull/988) by [@snapbug](https://github.com/snapbug).\n\n### Refactors\n\n* 🗑️ Deprecate support for `is_flag` and `flag_value` parameters. PR [#987](https://github.com/fastapi/typer/pull/987) by [@svlandeg](https://github.com/svlandeg).\n* 🔥 Remove unused functionality from `_typing.py` file. PR [#805](https://github.com/fastapi/typer/pull/805) by [@ivantodorovich](https://github.com/ivantodorovich).\n* ✏️ Fix typo in function name `_make_rich_text`. PR [#959](https://github.com/fastapi/typer/pull/959) by [@svlandeg](https://github.com/svlandeg).\n\n### Internal\n\n* ✅ Only run completion installation tests when the env var `_TYPER_RUN_INSTALL_COMPLETION_TESTS` is set. PR [#995](https://github.com/fastapi/typer/pull/995) by [@svlandeg](https://github.com/svlandeg).\n* 📝 Update the docstring of the `_make_rich_text` method. PR [#972](https://github.com/fastapi/typer/pull/972) by [@svlandeg](https://github.com/svlandeg).\n* ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#1040](https://github.com/fastapi/typer/pull/1040) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci).\n* ⬆ Bump mkdocs-material from 9.5.42 to 9.5.44. PR [#1042](https://github.com/fastapi/typer/pull/1042) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump ruff from 0.7.1 to 0.7.2. PR [#1038](https://github.com/fastapi/typer/pull/1038) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump mkdocs-macros-plugin from 1.3.6 to 1.3.7. PR [#1031](https://github.com/fastapi/typer/pull/1031) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#1032](https://github.com/fastapi/typer/pull/1032) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci).\n* ⬆ Bump ruff from 0.7.0 to 0.7.1. PR [#1029](https://github.com/fastapi/typer/pull/1029) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump pillow from 10.4.0 to 11.0.0. PR [#1023](https://github.com/fastapi/typer/pull/1023) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump mkdocs-material from 9.5.35 to 9.5.42. PR [#1027](https://github.com/fastapi/typer/pull/1027) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump ruff from 0.6.5 to 0.7.0. PR [#1026](https://github.com/fastapi/typer/pull/1026) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump mkdocs-macros-plugin from 1.2.0 to 1.3.6. PR [#1025](https://github.com/fastapi/typer/pull/1025) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Update pre-commit requirement from <4.0.0,>=2.17.0 to >=2.17.0,<5.0.0. PR [#1012](https://github.com/fastapi/typer/pull/1012) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump pypa/gh-action-pypi-publish from 1.10.1 to 1.10.3. PR [#1009](https://github.com/fastapi/typer/pull/1009) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#1001](https://github.com/fastapi/typer/pull/1001) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci).\n* 👷 Update Deploy docs CI to use uv. PR [#1021](https://github.com/fastapi/typer/pull/1021) by [@tiangolo](https://github.com/tiangolo).\n* 👷 Fix smokeshow, checkout files on CI. PR [#1020](https://github.com/fastapi/typer/pull/1020) by [@tiangolo](https://github.com/tiangolo).\n* 👷 Use uv in CI. PR [#1019](https://github.com/fastapi/typer/pull/1019) by [@tiangolo](https://github.com/tiangolo).\n* 👷 Update `labeler.yml`. PR [#1014](https://github.com/fastapi/typer/pull/1014) by [@tiangolo](https://github.com/tiangolo).\n* 👷 Update worfkow deploy-docs-notify URL. PR [#1011](https://github.com/fastapi/typer/pull/1011) by [@tiangolo](https://github.com/tiangolo).\n* 👷 Upgrade Cloudflare GitHub Action. PR [#1010](https://github.com/fastapi/typer/pull/1010) by [@tiangolo](https://github.com/tiangolo).\n* ⬆ Bump mkdocs-macros-plugin from 1.0.5 to 1.2.0. PR [#992](https://github.com/fastapi/typer/pull/992) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump ruff from 0.6.4 to 0.6.5. PR [#991](https://github.com/fastapi/typer/pull/991) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump mkdocs-material from 9.5.34 to 9.5.35. PR [#996](https://github.com/fastapi/typer/pull/996) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#993](https://github.com/fastapi/typer/pull/993) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci).\n* ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#982](https://github.com/fastapi/typer/pull/982) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci).\n* ⬆ Bump tiangolo/issue-manager from 0.5.0 to 0.5.1. PR [#980](https://github.com/fastapi/typer/pull/980) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* 👷 Update `issue-manager.yml`. PR [#978](https://github.com/fastapi/typer/pull/978) by [@tiangolo](https://github.com/tiangolo).\n* ⬆ Bump ruff from 0.6.3 to 0.6.4. PR [#975](https://github.com/fastapi/typer/pull/975) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump mkdocs-material from 9.5.33 to 9.5.34. PR [#963](https://github.com/fastapi/typer/pull/963) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump pypa/gh-action-pypi-publish from 1.9.0 to 1.10.1. PR [#973](https://github.com/fastapi/typer/pull/973) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#966](https://github.com/fastapi/typer/pull/966) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci).\n* 💚 Set `include-hidden-files` to `True` when using the `upload-artifact` GH action. PR [#967](https://github.com/fastapi/typer/pull/967) by [@svlandeg](https://github.com/svlandeg).\n* ⬆ Bump ruff from 0.6.1 to 0.6.3. PR [#961](https://github.com/fastapi/typer/pull/961) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#689](https://github.com/fastapi/typer/pull/689) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci).\n* ⬆ Bump ruff from 0.2.0 to 0.6.1. PR [#938](https://github.com/fastapi/typer/pull/938) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* 👷 Update `latest-changes` GitHub Action. PR [#955](https://github.com/fastapi/typer/pull/955) by [@tiangolo](https://github.com/tiangolo).\n\n## 0.12.5\n\n### Features\n\n* 💄 Unify the width of the Rich console for help and errors. PR [#788](https://github.com/fastapi/typer/pull/788) by [@racinmat](https://github.com/racinmat).\n* 🚸 Improve assertion error message if a group is not a valid subclass. PR [#425](https://github.com/fastapi/typer/pull/425) by [@chrisburr](https://github.com/chrisburr).\n\n### Fixes\n\n* 🐛 Ensure `rich_markup_mode=None` disables Rich formatting. PR [#859](https://github.com/fastapi/typer/pull/859) by [@svlandeg](https://github.com/svlandeg).\n* 🐛  Fix sourcing of completion path for Git Bash. PR [#801](https://github.com/fastapi/typer/pull/801) by [@svlandeg](https://github.com/svlandeg).\n* 🐛 Fix PowerShell completion with incomplete word. PR [#360](https://github.com/fastapi/typer/pull/360) by [@patricksurry](https://github.com/patricksurry).\n\n### Refactors\n\n* 🔥 Remove Python 3.6 specific code paths. PR [#850](https://github.com/fastapi/typer/pull/850) by [@svlandeg](https://github.com/svlandeg).\n* 🔥 Clean up redundant code. PR [#858](https://github.com/fastapi/typer/pull/858) by [@svlandeg](https://github.com/svlandeg).\n\n### Docs\n\n* ♻️ Use F-strings in Click examples in docs. PR [#891](https://github.com/fastapi/typer/pull/891) by [@svlandeg](https://github.com/svlandeg).\n* 📝Add missing `main.py` in tutorial on CLI option names. PR [#868](https://github.com/fastapi/typer/pull/868) by [@fsramalho](https://github.com/fsramalho).\n* 📝 Fix broken link. PR [#835](https://github.com/fastapi/typer/pull/835) by [@OhioDschungel6](https://github.com/OhioDschungel6).\n* 📝 Update package docs with the latest versions of Typer and Poetry. PR [#781](https://github.com/fastapi/typer/pull/781) by [@kinuax](https://github.com/kinuax).\n* 📝 Update the Progress Bar tutorial with correct output. PR [#199](https://github.com/fastapi/typer/pull/199) by [@n1ckdm](https://github.com/n1ckdm).\n* 📝 Add docs and scripts to test completion in different shells. PR [#953](https://github.com/fastapi/typer/pull/953) by [@tiangolo](https://github.com/tiangolo).\n* ✏️ Fix a typo in `docs/virtual-environments.md`. PR [#952](https://github.com/fastapi/typer/pull/952) by [@tiangolo](https://github.com/tiangolo).\n* ✏️ Fix typo in `docs/contributing.md`. PR [#947](https://github.com/fastapi/typer/pull/947) by [@tiangolo](https://github.com/tiangolo).\n* 📝 Add docs for virtual environments, environment variables, and update contributing. PR [#946](https://github.com/fastapi/typer/pull/946) by [@tiangolo](https://github.com/tiangolo).\n\n### Internal\n\n* 🔨 Pre-install dependencies in Docker so that testing in Docker is faster. PR [#954](https://github.com/fastapi/typer/pull/954) by [@tiangolo](https://github.com/tiangolo).\n* ✅ Add `needs_bash` test fixture. PR [#888](https://github.com/fastapi/typer/pull/888) by [@svlandeg](https://github.com/svlandeg).\n* ⬆ Bump mkdocs-material from 9.5.18 to 9.5.33. PR [#945](https://github.com/fastapi/typer/pull/945) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump pillow from 10.3.0 to 10.4.0. PR [#939](https://github.com/fastapi/typer/pull/939) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* 👷 Fix issue-manager. PR [#948](https://github.com/fastapi/typer/pull/948) by [@tiangolo](https://github.com/tiangolo).\n* 🙈 Remove extra line in .gitignore. PR [#936](https://github.com/fastapi/typer/pull/936) by [@tiangolo](https://github.com/tiangolo).\n* ⬆ Update pytest-cov requirement from <5.0.0,>=2.10.0 to >=2.10.0,<6.0.0. PR [#844](https://github.com/fastapi/typer/pull/844) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump pypa/gh-action-pypi-publish from 1.8.11 to 1.9.0. PR [#865](https://github.com/fastapi/typer/pull/865) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Update pytest requirement from <8.0.0,>=4.4.0 to >=4.4.0,<9.0.0. PR [#915](https://github.com/fastapi/typer/pull/915) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Update pytest-sugar requirement from <0.10.0,>=0.9.4 to >=0.9.4,<1.1.0. PR [#841](https://github.com/fastapi/typer/pull/841) by [@dependabot[bot]](https://github.com/apps/dependabot).\n\n## 0.12.4\n\n### Features\n\n* ✨ Add support for Python 3.12, tests in CI and official marker. PR [#807](https://github.com/tiangolo/typer/pull/807) by [@ivantodorovich](https://github.com/ivantodorovich).\n\n### Fixes\n\n* 🐛 Fix support for `UnionType` (e.g. `str | None`) with Python 3.11. PR [#548](https://github.com/fastapi/typer/pull/548) by [@jonaslb](https://github.com/jonaslb).\n* 🐛 Fix `zsh` autocompletion installation. PR [#237](https://github.com/fastapi/typer/pull/237) by [@alexjurkiewicz](https://github.com/alexjurkiewicz).\n* 🐛 Fix usage of `Annotated` with future annotations in Python 3.7+. PR [#814](https://github.com/fastapi/typer/pull/814) by [@ivantodorovich](https://github.com/ivantodorovich).\n* 🐛 Fix `shell_complete` not working for Arguments. PR [#737](https://github.com/fastapi/typer/pull/737) by [@bckohan](https://github.com/bckohan).\n\n### Docs\n\n* 📝 Update docs links, from tiangolo to new fastapi org. PR [#919](https://github.com/fastapi/typer/pull/919) by [@tiangolo](https://github.com/tiangolo).\n* 📝 Add docs for team and repo management. PR [#917](https://github.com/tiangolo/typer/pull/917) by [@tiangolo](https://github.com/tiangolo).\n\n### Internal\n\n* 🔧 Add URLs to `pyproject.toml`, show up in PyPI. PR [#931](https://github.com/fastapi/typer/pull/931) by [@tiangolo](https://github.com/tiangolo).\n* 👷 Do not sync labels as it overrides manually added labels. PR [#930](https://github.com/fastapi/typer/pull/930) by [@tiangolo](https://github.com/tiangolo).\n* 👷 Update labeler GitHub Action to add only one label. PR [#927](https://github.com/fastapi/typer/pull/927) by [@tiangolo](https://github.com/tiangolo).\n* 👷 Update labeler GitHub Actions permissions and dependencies. PR [#926](https://github.com/fastapi/typer/pull/926) by [@tiangolo](https://github.com/tiangolo).\n* 👷 Add GitHub Action label-checker. PR [#925](https://github.com/fastapi/typer/pull/925) by [@tiangolo](https://github.com/tiangolo).\n* 👷 Add GitHub Action labeler. PR [#924](https://github.com/fastapi/typer/pull/924) by [@tiangolo](https://github.com/tiangolo).\n* 👷 Add GitHub Action add-to-project. PR [#922](https://github.com/fastapi/typer/pull/922) by [@tiangolo](https://github.com/tiangolo).\n* 🔨 Update docs.py script to enable dirty reload conditionally. PR [#918](https://github.com/tiangolo/typer/pull/918) by [@tiangolo](https://github.com/tiangolo).\n* 🔧 Update MkDocs previews. PR [#916](https://github.com/tiangolo/typer/pull/916) by [@tiangolo](https://github.com/tiangolo).\n* 👷 Upgrade build docs configs. PR [#914](https://github.com/tiangolo/typer/pull/914) by [@tiangolo](https://github.com/tiangolo).\n* 🔧 Update MkDocs to have titles in Markdown files instead of config. PR [#913](https://github.com/tiangolo/typer/pull/913) by [@tiangolo](https://github.com/tiangolo).\n* 👷 Add alls-green for test-redistribute. PR [#911](https://github.com/tiangolo/typer/pull/911) by [@tiangolo](https://github.com/tiangolo).\n* 👷 Update docs-previews to handle no docs changes. PR [#912](https://github.com/tiangolo/typer/pull/912) by [@tiangolo](https://github.com/tiangolo).\n* 👷🏻 Show docs deployment status and preview URLs in comment. PR [#910](https://github.com/tiangolo/typer/pull/910) by [@tiangolo](https://github.com/tiangolo).\n* 🔧 Enable auto dark mode from system. PR [#908](https://github.com/tiangolo/typer/pull/908) by [@tiangolo](https://github.com/tiangolo).\n* 💄 Add dark mode logo. PR [#907](https://github.com/tiangolo/typer/pull/907) by [@tiangolo](https://github.com/tiangolo).\n* 🔧 Update tabs and admonitions with new syntax and new MkDocs features. PR [#906](https://github.com/tiangolo/typer/pull/906) by [@tiangolo](https://github.com/tiangolo).\n* 🔧 Enable MkDocs Material features. PR [#905](https://github.com/tiangolo/typer/pull/905) by [@tiangolo](https://github.com/tiangolo).\n* 🔧 Enable dark mode for docs. PR [#904](https://github.com/tiangolo/typer/pull/904) by [@tiangolo](https://github.com/tiangolo).\n* ➖ Do not install jieba for MkDocs Material as there are no chinese translations. PR [#903](https://github.com/tiangolo/typer/pull/903) by [@tiangolo](https://github.com/tiangolo).\n* 🙈 Add MkDocs Material cache to gitignore. PR [#902](https://github.com/tiangolo/typer/pull/902) by [@tiangolo](https://github.com/tiangolo).\n* 🔨 Update lint script. PR [#901](https://github.com/tiangolo/typer/pull/901) by [@tiangolo](https://github.com/tiangolo).\n* 🔧 Update MkDocs configs and docs build setup. PR [#900](https://github.com/tiangolo/typer/pull/900) by [@tiangolo](https://github.com/tiangolo).\n* ⬆ Bump actions/cache from 3 to 4. PR [#839](https://github.com/tiangolo/typer/pull/839) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* 🍱 Update Typer icon and logo. PR [#899](https://github.com/tiangolo/typer/pull/899) by [@tiangolo](https://github.com/tiangolo).\n* 👷 Update issue-manager.yml GitHub Action permissions. PR [#897](https://github.com/tiangolo/typer/pull/897) by [@tiangolo](https://github.com/tiangolo).\n* 👷 Refactor GitHub Action to comment docs deployment URLs and update token, preparing for GitHub org. PR [#896](https://github.com/tiangolo/typer/pull/896) by [@tiangolo](https://github.com/tiangolo).\n* 🔨 Update docs Termynal scripts to not include line nums for local dev. PR [#882](https://github.com/tiangolo/typer/pull/882) by [@tiangolo](https://github.com/tiangolo).\n* ⬆ Bump black from 23.3.0 to 24.3.0. PR [#837](https://github.com/tiangolo/typer/pull/837) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump pillow from 10.1.0 to 10.3.0. PR [#836](https://github.com/tiangolo/typer/pull/836) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ✅ Add CI configs to run tests on Windows and MacOS. PR [#824](https://github.com/tiangolo/typer/pull/824) by [@svlandeg](https://github.com/svlandeg).\n* 👷 Update GitHub Actions to upload and download artifacts. PR [#829](https://github.com/tiangolo/typer/pull/829) by [@tiangolo](https://github.com/tiangolo).\n* 👷 Tweak CI for test-redistribute, add needed env vars for slim. PR [#827](https://github.com/tiangolo/typer/pull/827) by [@tiangolo](https://github.com/tiangolo).\n* ✅ Generalize test suite to run on Windows. PR [#810](https://github.com/tiangolo/typer/pull/810) by [@svlandeg](https://github.com/svlandeg).\n* ✅ Add `__init__.py` files to fix test suite. PR [#809](https://github.com/tiangolo/typer/pull/809) by [@svlandeg](https://github.com/svlandeg).\n* 🔧 Update MkDocs Material, enable plugins. PR [#813](https://github.com/tiangolo/typer/pull/813) by [@tiangolo](https://github.com/tiangolo).\n* 🔧 Tweak development scripts and configs after migration to PDM, Ruff, etc.. PR [#797](https://github.com/tiangolo/typer/pull/797) by [@tiangolo](https://github.com/tiangolo).\n\n## 0.12.3\n\n### Fixes\n\n* 🐛 Fix Rich formatting with no commands. PR [#796](https://github.com/tiangolo/typer/pull/796) by [@svlandeg](https://github.com/svlandeg).\n\n## 0.12.2\n\n### Features\n\n* ✨ Improve column help display, ensure commands column width is the same on all panels. PR [#567](https://github.com/tiangolo/typer/pull/567) by [@ssbarnea](https://github.com/ssbarnea).\n\n### Fixes\n\n* 🐛 Add support for an argument of type `Optional[Tuple]` and default value `None`. PR [#757](https://github.com/tiangolo/typer/pull/757) by [@Asthestarsfalll](https://github.com/Asthestarsfalll).\n\n### Docs\n\n* 🔧 Fix typo in Github template. PR [#793](https://github.com/tiangolo/typer/pull/793) by [@svlandeg](https://github.com/svlandeg).\n* 📝 Fix typos in documentation. PR [#761](https://github.com/tiangolo/typer/pull/761) by [@svlandeg](https://github.com/svlandeg).\n* 📝 Update console output with Click 8 messages. PR [#789](https://github.com/tiangolo/typer/pull/789) by [@svlandeg](https://github.com/svlandeg).\n* 📝 Remove references to a .rst README generated by poetry new. PR [#632](https://github.com/tiangolo/typer/pull/632) by [@jonasmmiguel](https://github.com/jonasmmiguel).\n\n## 0.12.1\n\nNow you don't need to install `typer[all]`. When you install `typer` it comes with the default optional dependencies and the `typer` command.\n\nIf you don't want the extra optional dependencies (`rich` and `shellingham`), you can install `typer-slim` instead.\n\nYou can also install `typer-slim[standard]`, which includes the default optional dependencies, but not the `typer` command.\n\nNow the package `typer-cli` doesn't add anything on top of what `typer` has, it only depends on `typer`, and is there only for backwards compatibility, so that projects that depend on `typer-cli` can get the latest features of the `typer` command while they upgrade their dependencies to require `typer` directly.\n\n### Features\n\n* ✨ Add support for `typer ./someprogram.py utils docs --title`. PR [#782](https://github.com/tiangolo/typer/pull/782) by [@tiangolo](https://github.com/tiangolo).\n\n### Fixes\n\n* 🐛 Fix broken installation when upgrading from `typer <0.12.0` to `typer >=0.12.0`, make `typer` independent of `typer-slim`, include `typer` command in `typer` package. PR [#791](https://github.com/tiangolo/typer/pull/791) by [@tiangolo](https://github.com/tiangolo).\n\nThis fixes a problem that would break the `typer` installation directory when upgrading from `typer <0.12.0` to `typer >=0.12.0`, see issue [#790](https://github.com/tiangolo/typer/issues/790).\n\nBy installing the latest version (`0.12.1`) it fixes it, for any previous version, even if the installation directory was already broken by the previous upgrade.\n\n### Internal\n\n* 👷 Add cron to run test once a week on monday. PR [#783](https://github.com/tiangolo/typer/pull/783) by [@estebanx64](https://github.com/estebanx64).\n\n## 0.12.0\n\nIn version `0.12.0`, the `typer` package depends on `typer-slim[standard]` which includes the default dependencies (instead of `typer[all]`) and `typer-cli` (that provides the `typer` command).\n\nIf you don't want the extra optional dependencies (`rich` and `shellingham`), you can install `typer-slim` instead.\n\nYou can also install `typer-slim[standard]`, which includes the default optional dependencies, but not the `typer` command.\n\nIn version `0.12.0` the `typer-cli` package only provides the `typer` command, but the code is still in the main code, so even without installing `typer-cli`, it can be called with `python -m typer`.\n\nThis approach of having `typer` depend on `typer-slim[standard]` instead of including the whole code and dependencies itself caused an issue when upgrading from `typer <0.12.0` to `typer >=0.12.0`, see issue [#790](https://github.com/tiangolo/typer/issues/790). This is fixed in version `0.12.1`.\n\n### Features\n\n* ✨ Add `typer-slim` package without extras, make `typer` include `typer-slim[default]` and integrate Typer CLI (`typer` command) into Typer. PR [#780](https://github.com/tiangolo/typer/pull/780) by [@tiangolo](https://github.com/tiangolo).\n\n### Internal\n\n* 🔧 Temporarily disable social plugin while a MkDocs issue is handled. PR [#779](https://github.com/tiangolo/typer/pull/779) by [@tiangolo](https://github.com/tiangolo).\n* 👷 Fix install MkDocs Insiders only when available. PR [#778](https://github.com/tiangolo/typer/pull/778) by [@tiangolo](https://github.com/tiangolo).\n\n## 0.11.1\n\n### Fixes\n\n* 🔧 Explicitly include testing files in sdist for redistributors (e.g. OpenSUSE) and add CI to test redistribution. PR [#773](https://github.com/tiangolo/typer/pull/773) by [@tiangolo](https://github.com/tiangolo).\n\n### Internal\n\n* 👷 Do not use the cache for dependencies when publishing to PyPI. PR [#774](https://github.com/tiangolo/typer/pull/774) by [@tiangolo](https://github.com/tiangolo).\n\n## 0.11.0\n\n### Breaking Changes\n\n* 🔧 Refactor package manager, move from Flit to PDM, remove private pip extras for `test`, `doc`, `dev`. PR [#764](https://github.com/tiangolo/typer/pull/764) by [@tiangolo](https://github.com/tiangolo).\n* 🔥 Remove support for Click 7, require Click 8+. PR [#760](https://github.com/tiangolo/typer/pull/760) by [@tiangolo](https://github.com/tiangolo).\n* 🔥 Remove support for Python 3.6. PR [#758](https://github.com/tiangolo/typer/pull/758) by [@tiangolo](https://github.com/tiangolo).\n\n### Refactors\n\n* 🔧 Migrate from Black, isort, flake8, autoflake, pyupgrade to Ruff. PR [#763](https://github.com/tiangolo/typer/pull/763) by [@tiangolo](https://github.com/tiangolo).\n\n### Internal\n\n* ⬆️ Upgrade coverage and configs. PR [#769](https://github.com/tiangolo/typer/pull/769) by [@tiangolo](https://github.com/tiangolo).\n* 🔧 Upgrade mypy and config. PR [#768](https://github.com/tiangolo/typer/pull/768) by [@tiangolo](https://github.com/tiangolo).\n* 👷 Upgrade Smokeshow GitHub action. PR [#767](https://github.com/tiangolo/typer/pull/767) by [@tiangolo](https://github.com/tiangolo).\n* 👷 Upgrade latest-changes GitHub Action. PR [#766](https://github.com/tiangolo/typer/pull/766) by [@tiangolo](https://github.com/tiangolo).\n* 👷 Upgrade issue-manager GitHub Action. PR [#765](https://github.com/tiangolo/typer/pull/765) by [@tiangolo](https://github.com/tiangolo).\n* 👷 Add alls-green to CI. PR [#759](https://github.com/tiangolo/typer/pull/759) by [@tiangolo](https://github.com/tiangolo).\n\n## 0.10.0\n\n### Fixes\n\n* 🐛 Fix default value of `None` for CLI Parameters when the type is `list | None` and the default value is `None`. PR [#664](https://github.com/tiangolo/typer/pull/664) by [@theowisear](https://github.com/theowisear).\n\n## 0.9.4\n\n### Features\n\n* ✨ Improve support for CLI translations using gettext. PR [#417](https://github.com/tiangolo/typer/pull/417) by [@mjodmj](https://github.com/mjodmj).\n\n## 0.9.3\n\n### Fixes\n\n* 🐛 Fix evaluating stringified annotations in Python 3.10 (also `from __future__ import annotations`). PR [#721](https://github.com/tiangolo/typer/pull/721) by [@heckad](https://github.com/heckad).\n\n## 0.9.2\n\n### Fixes\n\n* 🐛 Fix display of default value for Enum parameters inside of a list, include docs and tests. PR [#473](https://github.com/tiangolo/typer/pull/473) by [@asieira](https://github.com/asieira).\n* 🐛 Update type annotations for `show_default` parameter and update docs for setting a \"Custom default string\". PR [#501](https://github.com/tiangolo/typer/pull/501) by [@plannigan](https://github.com/plannigan).\n\n### Docs\n\n* 📝 Add docs and test for `no_args_is_help` feature. PR [#751](https://github.com/tiangolo/typer/pull/751) by [@svlandeg](https://github.com/svlandeg).\n\n## 0.9.1\n\n### Fixes\n\n* 🐛 Add missing `default_factory` in `Argument` overloads. PR [#750](https://github.com/tiangolo/typer/pull/750) by [@m9810223](https://github.com/m9810223).\n* 🐛 Fix preserving case in enum values. PR [#571](https://github.com/tiangolo/typer/pull/571) by [@avaldebe](https://github.com/avaldebe).\n\n### Docs\n\n* 📝 Remove obsolete references to `--install-completion` for `typer.run()` scripts. PR [#595](https://github.com/tiangolo/typer/pull/595) by [@tiangolo](https://github.com/tiangolo).\n\n* 📝 Update docs example for a Typer/Click group to make new subcommands explicit. PR [#755](https://github.com/tiangolo/typer/pull/755) by [@svlandeg](https://github.com/svlandeg).\n* 📝 Update docs for building a package, file structure example. PR [#683](https://github.com/tiangolo/typer/pull/683) by [@davidbgk](https://github.com/davidbgk).\n* 📝 Update link in docs to the newest stable version of click. PR [#675](https://github.com/tiangolo/typer/pull/675) by [@javier171188](https://github.com/javier171188).\n* 🔧 Add `CITATION.cff` file for academic citations. PR [#681](https://github.com/tiangolo/typer/pull/681) by [@tiangolo](https://github.com/tiangolo).\n* ✏ Fix typo in `docs/tutorial/exceptions.md`. PR [#702](https://github.com/tiangolo/typer/pull/702) by [@menzenski](https://github.com/menzenski).\n* ✏ Fix typo in `docs/tutorial/options/name.md`. PR [#725](https://github.com/tiangolo/typer/pull/725) by [@bwagner](https://github.com/bwagner).\n* ✏ Fix typo in `docs/tutorial/arguments/optional.md`. PR [#602](https://github.com/tiangolo/typer/pull/602) by [@tadasgedgaudas](https://github.com/tadasgedgaudas).\n\n### Internal\n\n* ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#606](https://github.com/tiangolo/typer/pull/606) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci).\n* 👷 Install MkDocs Material Insiders only when secrets are available, for Dependabot. PR [#685](https://github.com/tiangolo/typer/pull/685) by [@tiangolo](https://github.com/tiangolo).\n* ⚒️ Update build-docs.yml, do not zip docs. PR [#645](https://github.com/tiangolo/typer/pull/645) by [@tiangolo](https://github.com/tiangolo).\n* 👷 Deploy docs to Cloudflare. PR [#644](https://github.com/tiangolo/typer/pull/644) by [@tiangolo](https://github.com/tiangolo).\n* 👷 Upgrade CI for docs. PR [#642](https://github.com/tiangolo/typer/pull/642) by [@tiangolo](https://github.com/tiangolo).\n* 👷 Update token for latest changes. PR [#635](https://github.com/tiangolo/typer/pull/635) by [@tiangolo](https://github.com/tiangolo).\n* 👷 Update CI workflow dispatch for latest changes. PR [#643](https://github.com/tiangolo/typer/pull/643) by [@tiangolo](https://github.com/tiangolo).\n* 👷 Update token for Material for MkDocs Insiders. PR [#636](https://github.com/tiangolo/typer/pull/636) by [@tiangolo](https://github.com/tiangolo).\n* 🐛 Fix internal type annotations and bump mypy version. PR [#638](https://github.com/tiangolo/typer/pull/638) by [@paulo-raca](https://github.com/paulo-raca).\n* 💡 Add comments to document overload definitions in code. PR [#752](https://github.com/tiangolo/typer/pull/752) by [@svlandeg](https://github.com/svlandeg).\n* 🔥 Remove Jina QA Bot as it has been discontinued. PR [#749](https://github.com/tiangolo/typer/pull/749) by [@tiangolo](https://github.com/tiangolo).\n* 👷 Update build docs CI cache paths. PR [#707](https://github.com/tiangolo/typer/pull/707) by [@tiangolo](https://github.com/tiangolo).\n* 👷 Upgrade latest-changes GitHub Action. PR [#691](https://github.com/tiangolo/typer/pull/691) by [@tiangolo](https://github.com/tiangolo).\n\n## 0.9.0\n\n### Features\n\n* ✨ Add support for PEP-593 `Annotated` for specifying options and arguments. Initial PR [#584](https://github.com/tiangolo/typer/pull/584) by [@ryangalamb](https://github.com/ryangalamb).\n    * New docs: [Optional CLI arguments](https://typer.tiangolo.com/tutorial/arguments/optional/#an-alternative-cli-argument-declaration).\n    * It is no longer required to pass a default value of `...` to mark a *CLI Argument* or *CLI Option* as required.\n    * It is now recommended to use `Annotated` for `typer.Option()` and `typer.Argument()`.\n    * All the docs have been updated to recommend `Annotated`.\n\n### Docs\n\n* 📝 Update docs examples for custom param types using `Annotated`, fix overloads for `typer.Argument`. PR [#594](https://github.com/tiangolo/typer/pull/594) by [@tiangolo](https://github.com/tiangolo).\n\n### Internal\n\n* ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#592](https://github.com/tiangolo/typer/pull/592) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci).\n\n## 0.8.0\n\n### Features\n\n* ✨ Add support for custom types and parsers. Initial PR [#583](https://github.com/tiangolo/typer/pull/583) by [@jpurviance](https://github.com/jpurviance). Based on original PR [#443](https://github.com/tiangolo/typer/pull/443) by [@paulo-raca](https://github.com/paulo-raca).\n    * New docs: [CLI Parameter Types: Custom Types](https://typer.tiangolo.com/tutorial/parameter-types/custom-types/).\n\n### Upgrades\n\n* ⬆ Upgrade Rich, support 13.x. PR [#524](https://github.com/tiangolo/typer/pull/524) by [@musicinmybrain](https://github.com/musicinmybrain).\n\n### Docs\n\n* 📝 Tweak docs, Custom Types path, main page and READAME colors, broken links. PR [#588](https://github.com/tiangolo/typer/pull/588) by [@tiangolo](https://github.com/tiangolo).\n* ✏ Fix spelling (shinny -> shiny). PR [#586](https://github.com/tiangolo/typer/pull/586) by [@runofthemill](https://github.com/runofthemill).\n* 📝 Update docs about helping Typer. PR [#547](https://github.com/tiangolo/typer/pull/547) by [@tiangolo](https://github.com/tiangolo).\n* ✏️ Fix typo in datetime docs. PR [#495](https://github.com/tiangolo/typer/pull/495) by [@huxuan](https://github.com/huxuan).\n* ✏️ Add quotes to package name that includes brackets in docs. PR [#475](https://github.com/tiangolo/typer/pull/475) by [@gjolga](https://github.com/gjolga).\n\n### Internal\n\n* ⬆ Bump dawidd6/action-download-artifact from 2.24.2 to 2.26.0. PR [#558](https://github.com/tiangolo/typer/pull/558) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#549](https://github.com/tiangolo/typer/pull/549) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci).\n* 🔧 Add `exclude_lines` to coverage configuration. PR [#585](https://github.com/tiangolo/typer/pull/585) by [@dmontagu](https://github.com/dmontagu).\n* ⬆️ Upgrade analytics. PR [#557](https://github.com/tiangolo/typer/pull/557) by [@tiangolo](https://github.com/tiangolo).\n* 🔧 Update new issue chooser to suggest GitHub Discussions. PR [#544](https://github.com/tiangolo/typer/pull/544) by [@tiangolo](https://github.com/tiangolo).\n* 🔧 Add GitHub Discussion templates for questions. PR [#541](https://github.com/tiangolo/typer/pull/541) by [@tiangolo](https://github.com/tiangolo).\n* 🔧 Update pre-commit, Python version, isort version. PR [#542](https://github.com/tiangolo/typer/pull/542) by [@tiangolo](https://github.com/tiangolo).\n* ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#512](https://github.com/tiangolo/typer/pull/512) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci).\n* ⬆ Bump nwtgck/actions-netlify from 1.2.4 to 2.0.0. PR [#513](https://github.com/tiangolo/typer/pull/513) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* 👷 Refactor CI artifact upload/download for docs previews. PR [#516](https://github.com/tiangolo/typer/pull/516) by [@tiangolo](https://github.com/tiangolo).\n* ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#500](https://github.com/tiangolo/typer/pull/500) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci).\n* ⬆ Bump actions/cache from 2 to 3. PR [#496](https://github.com/tiangolo/typer/pull/496) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump dawidd6/action-download-artifact from 2.24.1 to 2.24.2. PR [#494](https://github.com/tiangolo/typer/pull/494) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump dawidd6/action-download-artifact from 2.9.0 to 2.24.1. PR [#491](https://github.com/tiangolo/typer/pull/491) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump actions/setup-python from 2 to 4. PR [#492](https://github.com/tiangolo/typer/pull/492) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* 👷‍♂️ Consistently use `sys.executable` to run subprocesses, needed by OpenSUSE. PR [#408](https://github.com/tiangolo/typer/pull/408) by [@theMarix](https://github.com/theMarix).\n* 👷‍♂️ Ensure the `PYTHONPATH` is set properly when testing the tutorial scripts. PR [#407](https://github.com/tiangolo/typer/pull/407) by [@theMarix](https://github.com/theMarix).\n\n## 0.7.0\n\n### Features\n\n* ✨ Make `typer.run()` not add completion scripts by default, it only makes sense in installed apps. Also update docs for handling [autocompletion in CLI options](https://typer.tiangolo.com/tutorial/options-autocompletion/). PR [#488](https://github.com/tiangolo/typer/pull/488) by [@tiangolo](https://github.com/tiangolo).\n* ✨ Add support for Python 3.11, tests in CI and official marker. PR [#487](https://github.com/tiangolo/typer/pull/487) by [@tiangolo](https://github.com/tiangolo).\n* 👷 Add CI for Python 3.10. PR [#384](https://github.com/tiangolo/typer/pull/384) by [@tiangolo](https://github.com/tiangolo).\n\n### Fixes\n\n* 🎨 Fix type annotation of `typer.run()`. PR [#284](https://github.com/tiangolo/typer/pull/284) by [@yassu](https://github.com/yassu).\n* 🎨 Fix type annotations for `get_group`. PR [#430](https://github.com/tiangolo/typer/pull/430) by [@tiangolo](https://github.com/tiangolo).\n\n### Docs\n\n* 📝 Add note about how subcommands with function names using underscores are converted to dashes. PR [#403](https://github.com/tiangolo/typer/pull/403) by [@targhs](https://github.com/targhs).\n* 📝 Fix typo in docs at `docs/tutorial/commands/help.md`. PR [#466](https://github.com/tiangolo/typer/pull/466) by [@fepegar](https://github.com/fepegar).\n* ✏ Fix link in docs to `datetime.strptime()`. PR [#464](https://github.com/tiangolo/typer/pull/464) by [@Kobu](https://github.com/Kobu).\n* ✏ Update `first-steps.md`, clarify distinction between parameter and argument. PR [#176](https://github.com/tiangolo/typer/pull/176) by [@mccarthysean](https://github.com/mccarthysean).\n* ✏ Fix broken plac link. PR [#275](https://github.com/tiangolo/typer/pull/275) by [@mgielda](https://github.com/mgielda).\n\n### Internal\n\n* ✅ Add extra tests just for coverage because monkeypatching with strange imports confuses coverage. PR [#490](https://github.com/tiangolo/typer/pull/490) by [@tiangolo](https://github.com/tiangolo).\n* 🔧 Tweak pytest coverage. PR [#485](https://github.com/tiangolo/typer/pull/485) by [@tiangolo](https://github.com/tiangolo).\n* ➕ Bring back pytest-cov because coverage can't detect pytest-xdist. PR [#484](https://github.com/tiangolo/typer/pull/484) by [@tiangolo](https://github.com/tiangolo).\n* ⬆ Bump actions/upload-artifact from 2 to 3. PR [#477](https://github.com/tiangolo/typer/pull/477) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump actions/checkout from 2 to 3. PR [#478](https://github.com/tiangolo/typer/pull/478) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#411](https://github.com/tiangolo/typer/pull/411) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci).\n* ⬆ Bump nwtgck/actions-netlify from 1.1.5 to 1.2.4. PR [#479](https://github.com/tiangolo/typer/pull/479) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* ⬆ Bump tiangolo/issue-manager from 0.2.0 to 0.4.0. PR [#481](https://github.com/tiangolo/typer/pull/481) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* 👷 Move from pytest-cov to coverage and Codecov to Smokeshow. PR [#483](https://github.com/tiangolo/typer/pull/483) by [@tiangolo](https://github.com/tiangolo).\n* ➕ Add extra Material for MkDocs deps for docs. PR [#482](https://github.com/tiangolo/typer/pull/482) by [@tiangolo](https://github.com/tiangolo).\n* 🔧 Update Dependabot config. PR [#476](https://github.com/tiangolo/typer/pull/476) by [@tiangolo](https://github.com/tiangolo).\n\n## 0.6.1\n\n### Fixes\n\n* 🐛 Fix setting `FORCE_TERMINAL` with colors 2. PR [#424](https://github.com/tiangolo/typer/pull/424) by [@tiangolo](https://github.com/tiangolo).\n* 🐛 Fix setting `FORCE_TERMINAL` with colors. PR [#423](https://github.com/tiangolo/typer/pull/423) by [@tiangolo](https://github.com/tiangolo).\n\n## 0.6.0\n\nThis release adds deep integrations with [Rich](https://rich.readthedocs.io/en/stable/). ✨\n\n`rich` is an optional dependency, you can install it directly or it will be included when you install with:\n\n```console\n$ pip install \"typer[all]\"\n```\n\nIf Rich is available, it will be used to show the content from `--help` options, validation errors, and even errors in your app (exception tracebacks).\n\nThere are new options to group commands, *CLI arguments*, and *CLI options*, support for [Rich Console Markup](https://rich.readthedocs.io/en/stable/markup.html), and more! 🎉\n\n### Features\n\n* ✨ Richify, add integrations with Rich everywhere. PR [#419](https://github.com/tiangolo/typer/pull/419) by [@tiangolo](https://github.com/tiangolo).\n    * Recommend Rich as the main information displaying tool, new docs: [Printing and Colors](https://typer.tiangolo.com/tutorial/printing/).\n    * For most use cases not using Rich, use plain `print()` instead of `typer.echo()` in the docs, to simplify the concepts and avoid confusions. New docs: [Printing and Colors - typer Echo](https://typer.tiangolo.com/tutorial/printing/#typer-echo).\n    * Define help panels for *CLI arguments*, new docs: [CLI Arguments with Help - CLI Argument help panels](https://typer.tiangolo.com/tutorial/arguments/help/#cli-argument-help-panels).\n    * Define help panels for *CLI options*, new docs: [CLI Options with Help - CLI Options help panels](https://typer.tiangolo.com/tutorial/options/help/#cli-options-help-panels).\n    * New docs for deprecating commands: [Commands - Command Help - Deprecate a Command](https://typer.tiangolo.com/tutorial/commands/help/#deprecate-a-command).\n    * Support for Rich Markdown in docstrings, *CLI parameters* `help`, and `epilog` with the new parameter `typer.Typer(rich_markup_mode=\"markdown\")`, new docs: [Commands - Command Help - Rich Markdown and Markup](https://typer.tiangolo.com/tutorial/commands/help/#rich-markdown-and-markup).\n    * Support for Rich Markup (different from Markdown) in docstrings, *CLI parameters* `help`, and `epilog` with the new parameter `typer.Typer(rich_markup_mode=\"rich\")`, new docs: [Commands - Command Help - Rich Markdown and Markup](https://typer.tiangolo.com/tutorial/commands/help/#rich-markdown-and-markup).\n    * Define help panels for *commands*, new docs: [Commands - Command Help - Help Panels](https://typer.tiangolo.com/tutorial/commands/help/#help-panels).\n    * New docs for setting an `epilog`, with support for Rich Markdown and Console Markup, new docs: [Commands - Command Help - Epilog](https://typer.tiangolo.com/tutorial/commands/help/#epilog).\n* ✨ Refactor and document handling pretty exceptions. PR [#422](https://github.com/tiangolo/typer/pull/422) by [@tiangolo](https://github.com/tiangolo).\n    * Add support for customizing pretty short errors, new docs: [Exceptions and Errors](https://typer.tiangolo.com/tutorial/exceptions/).\n* ✨ Allow configuring pretty errors when creating the Typer instance. PR [#416](https://github.com/tiangolo/typer/pull/416) by [@tiangolo](https://github.com/tiangolo).\n\n### Docs\n\n* 📝 Add docs for using Rich with Typer. PR [#421](https://github.com/tiangolo/typer/pull/421) by [@tiangolo](https://github.com/tiangolo).\n    * Add new docs: [Ask with Prompt - Prompt with Rich](https://typer.tiangolo.com/tutorial/prompt/#prompt-with-rich).\n    * Add new docs to handle progress bars and spinners with Rich: [Progress Par](https://typer.tiangolo.com/tutorial/progressbar/).\n\n### Internal\n\n* ⬆️ Upgrade codecov GitHub Action. PR [#420](https://github.com/tiangolo/typer/pull/420) by [@tiangolo](https://github.com/tiangolo).\n\n## 0.5.0\n\n### Features\n\n* ✨ Add pretty error tracebacks for user errors and support for Rich. PR [#412](https://github.com/tiangolo/typer/pull/412) by [@tiangolo](https://github.com/tiangolo).\n\n### Docs\n\n* ✏ Fix typo, \"ASCII codes\" to \"ANSI escape sequences\". PR [#308](https://github.com/tiangolo/typer/pull/308) by [@septatrix](https://github.com/septatrix).\n\n## 0.4.2\n\n### Fixes\n\n* 🐛 Fix type conversion for `List` and `Tuple` and their internal types. PR [#143](https://github.com/tiangolo/typer/pull/143) by [@hellowhistler](https://github.com/hellowhistler).\n* 🐛 Fix `context_settings` for a Typer app with a single command. PR [#210](https://github.com/tiangolo/typer/pull/210) by [@daddycocoaman](https://github.com/daddycocoaman).\n\n### Docs\n\n* 📝 Clarify testing documentation about checking `stderr`. PR [#335](https://github.com/tiangolo/typer/pull/335) by [@cgabard](https://github.com/cgabard).\n* ✏ Fix typo in docs for CLI Option autocompletion. PR [#288](https://github.com/tiangolo/typer/pull/288) by [@graue70](https://github.com/graue70).\n* 🎨 Fix header format for \"Standard Input\" in `docs/tutorial/printing.md`. PR [#386](https://github.com/tiangolo/typer/pull/386) by [@briancohan](https://github.com/briancohan).\n* ✏ Fix typo in `docs/tutorial/terminating.md`. PR [#382](https://github.com/tiangolo/typer/pull/382) by [@kianmeng](https://github.com/kianmeng).\n* ✏ Fix syntax typo in `docs/tutorial/package.md`. PR [#333](https://github.com/tiangolo/typer/pull/333) by [@ryanstreur](https://github.com/ryanstreur).\n* ✏ Fix typo, duplicated word in `docs/tutorial/options/required.md`.. PR [#316](https://github.com/tiangolo/typer/pull/316) by [@michaelriri](https://github.com/michaelriri).\n* ✏ Fix minor typo in `index.md`. PR [#274](https://github.com/tiangolo/typer/pull/274) by [@RmStorm](https://github.com/RmStorm).\n* ✏ Fix double \"and\" typo in first-steps tutorial. PR [#225](https://github.com/tiangolo/typer/pull/225) by [@softwarebloat](https://github.com/softwarebloat).\n* 🎨 Fix format in docs explaining `datetime` parameter type. PR [#220](https://github.com/tiangolo/typer/pull/220) by [@DiegoPiloni](https://github.com/DiegoPiloni).\n\n### Internal\n\n* ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#404](https://github.com/tiangolo/typer/pull/404) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci).\n* 👷 Fix Material for MkDocs install in CI. PR [#395](https://github.com/tiangolo/typer/pull/395) by [@tiangolo](https://github.com/tiangolo).\n* 👷 Add pre-commit CI config. PR [#394](https://github.com/tiangolo/typer/pull/394) by [@tiangolo](https://github.com/tiangolo).\n* 👷 Clear MkDocs Insiders cache. PR [#393](https://github.com/tiangolo/typer/pull/393) by [@tiangolo](https://github.com/tiangolo).\n* 🔧 Add pre-commit config and formatting. PR [#392](https://github.com/tiangolo/typer/pull/392) by [@tiangolo](https://github.com/tiangolo).\n* 👷 Disable installing MkDocs Insiders in forks. PR [#391](https://github.com/tiangolo/typer/pull/391) by [@tiangolo](https://github.com/tiangolo).\n* ⬆️ Upgrade Codecov GitHub Action. PR [#383](https://github.com/tiangolo/typer/pull/383) by [@tiangolo](https://github.com/tiangolo).\n\n## 0.4.1\n\n### Fixes\n\n* 🐛 Fix import of `get_terminal_size` for Click 8.1.0 support and upgrade Black to fix CI. PR [#380](https://github.com/tiangolo/typer/pull/380) by [@tiangolo](https://github.com/tiangolo) based on original PR [#375](https://github.com/tiangolo/typer/pull/375) by [@madkinsz](https://github.com/madkinsz).\n\n### Internal\n\n* 📝 Add Jina's QA Bot to the docs to help people that want to ask quick questions. PR [#368](https://github.com/tiangolo/typer/pull/368) by [@tiangolo](https://github.com/tiangolo).\n* 💚 Only test on push when on master, avoid duplicate CI runs from PRs. PR [#358](https://github.com/tiangolo/typer/pull/358) by [@tiangolo](https://github.com/tiangolo).\n* ✨ Add support for previewing docs in PRs from forks and enable MkDocs Insiders. PR [#357](https://github.com/tiangolo/typer/pull/357) by [@tiangolo](https://github.com/tiangolo).\n* ⬆️ Upgrade MkDocs Material, MDX-Include, and MkDocs structure. PR [#356](https://github.com/tiangolo/typer/pull/356) by [@tiangolo](https://github.com/tiangolo).\n* 👷 Update publish GitHub action. PR [#325](https://github.com/tiangolo/typer/pull/325) by [@tiangolo](https://github.com/tiangolo).\n\n## 0.4.0\n\n### Features\n\n* ✨ Add support for Click 8 while keeping compatibility with Click 7. PR [#317](https://github.com/tiangolo/typer/pull/317) by [@tiangolo](https://github.com/tiangolo).\n\n### Internal\n\n* 📝 Add Security policy. PR [#324](https://github.com/tiangolo/typer/pull/324) by [@tiangolo](https://github.com/tiangolo).\n* 🔧 Add updated issue templates. PR [#323](https://github.com/tiangolo/typer/pull/323) by [@tiangolo](https://github.com/tiangolo).\n* 👷 Enable tests for Python 3.9. PR [#322](https://github.com/tiangolo/typer/pull/322) by [@tiangolo](https://github.com/tiangolo).\n* 👷 Add GitHub Action Latest Changes. PR [#321](https://github.com/tiangolo/typer/pull/321) by [@tiangolo](https://github.com/tiangolo).\n* 👷 Update docs CI name. PR [#320](https://github.com/tiangolo/typer/pull/320) by [@tiangolo](https://github.com/tiangolo).\n* 🔧 Add sponsors docs and badge. PR [#319](https://github.com/tiangolo/typer/pull/319) by [@tiangolo](https://github.com/tiangolo).\n\n## 0.3.2\n\n### Features\n\n* Add support for `mypy --strict`. Original PR [#147](https://github.com/tiangolo/typer/pull/147) by [@victorphoenix3](https://github.com/victorphoenix3).\n\n### Docs\n\n* Update docs with new `--help` showing default values. PR [#135](https://github.com/tiangolo/typer/pull/135) by [@victorphoenix3](https://github.com/victorphoenix3).\n* Add `Optional` to docs for *CLI Arguments and Options* with a default of `None`. PR [#131](https://github.com/tiangolo/typer/pull/131) by [@rkbeatss](https://github.com/rkbeatss).\n* Add valid date formats to docs. PR [#122](https://github.com/tiangolo/typer/pull/122) by [@IamCathal](https://github.com/IamCathal).\n\n### Internal\n\n* Report coverage in XML to support GitHub Actions. PR [#146](https://github.com/tiangolo/typer/pull/146).\n* Update badges and remove Travis, now that GitHub Actions is the main CI. PR [#145](https://github.com/tiangolo/typer/pull/145).\n\n## 0.3.1\n\n* Add GitHub Actions, move from Travis. PR [#144](https://github.com/tiangolo/typer/pull/144).\n* Pin dependencies. PR [#138](https://github.com/tiangolo/typer/pull/138).\n* Add Dependabot. PR [#136](https://github.com/tiangolo/typer/pull/136).\n* Upgrade Isort to version 5.x.x. PR [#137](https://github.com/tiangolo/typer/pull/137).\n\n## 0.3.0\n\n* Add support for `help` parameter in *CLI arguments*:\n    * As `help` in *CLI arguments* is not supported by Click, there are two new internal classes (Click sub-classes) to support it:\n        * `typer.core.TyperArgument`\n        * `typer.core.TyperCommand`\n    * This includes a new auto-generated help text section `Arguments` for *CLI arguments*, showing defaults, required arguments, etc.\n    * It's also possible to disable it and keep the previous behavior, not showing automatic help for *CLI arguments* (Click's default) using the `hidden` parameter.\n    * Now `show_default` is `True` by default.\n    * And now `show_envvar` is `True` by default.\n    * So, default values and env vars are shown in the help text by default, without having to manually enable them, for both *CLI arguments* and *CLI options*.\n    * New docs:\n        * [CLI Arguments Intro](https://typer.tiangolo.com/tutorial/arguments/).\n        * [Optional CLI Arguments](https://typer.tiangolo.com/tutorial/arguments/optional/).\n        * [CLI Arguments with Default](https://typer.tiangolo.com/tutorial/arguments/default/).\n        * [CLI Arguments with Help](https://typer.tiangolo.com/tutorial/arguments/help/).\n        * [CLI Arguments with Environment Variables](https://typer.tiangolo.com/tutorial/arguments/envvar/).\n        * [CLI Arguments: Other uses](https://typer.tiangolo.com/tutorial/arguments/other-uses/).\n        * [CLI arguments with tuples](https://typer.tiangolo.com/tutorial/multiple-values/arguments-with-multiple-values/#cli-arguments-with-tuples).\n    * Lot's of tests for all the new examples in the new docs, keeping coverage at 100%.\n    * PR [#123](https://github.com/tiangolo/typer/pull/123).\n* Add docs for calling packages with `python -m some_package` using `__main__.py`: [Building a Package: Support `python -m`](https://typer.tiangolo.com/tutorial/package/#support-python-m-optional). PR [#121](https://github.com/tiangolo/typer/pull/121).\n* Add support for `*args` and `**kwargs` when calling the Typer app, just like in Click. PR [#120](https://github.com/tiangolo/typer/pull/120) by [@teymour-aldridge](https://github.com/teymour-aldridge).\n* Fix typos in README and main docs [#103](https://github.com/tiangolo/typer/pull/103) by [@mrcartoonster](https://github.com/mrcartoonster).\n* Fix typo in docs. PR [#98](https://github.com/tiangolo/typer/pull/98) by [@mrcartoonster](https://github.com/mrcartoonster).\n* Fix typos and rewording in docs. PR [#97](https://github.com/tiangolo/typer/pull/97) by [@mrcartoonster](https://github.com/mrcartoonster).\n* Update GitHub Action issue-manager. PR [#114](https://github.com/tiangolo/typer/pull/114).\n\n## 0.2.1\n\n* Add support for forward references (types declared inside of strings). PR [#93](https://github.com/tiangolo/typer/pull/93).\n\n## 0.2.0\n\n* Add support for completion for commands/programs not available on startup.\n    * This allows installing a Typer program/script in a virtual environment and still have completion globally installed.\n    * PR [#92](https://github.com/tiangolo/typer/pull/92).\n* Add note about `typer.echo()` and `print()` for colors in Windows. PR [#89](https://github.com/tiangolo/typer/pull/89).\n* Upgrade Mkdocs-Material version, update contributing guide style. PR [#90](https://github.com/tiangolo/typer/pull/90).\n\n## 0.1.1\n\n* Fix completion evaluation for Bash and Zsh when the program is not installed/found. PR [#83](https://github.com/tiangolo/typer/pull/83).\n* Fix completion script for Fish. PR [#82](https://github.com/tiangolo/typer/pull/82).\n* Fix shell installation for Bash to `~/.bashrc` and update Windows development docs. PR [#81](https://github.com/tiangolo/typer/pull/81).\n* Update coverage badge. PR [#78](https://github.com/tiangolo/typer/pull/78).\n\n## 0.1.0\n\n* Fix coverage instructions. PR [#72](https://github.com/tiangolo/typer/pull/72).\n* Add docs for [Building a Package](https://typer.tiangolo.com/tutorial/package/). PR [#71](https://github.com/tiangolo/typer/pull/71).\n* Add docs for [Using Click (with Typer)](https://typer.tiangolo.com/tutorial/using-click/). PR [#70](https://github.com/tiangolo/typer/pull/70).\n* Add support for type-based callbacks and autocompletion functions, extra tests and docs:\n    * Extra tests, raising coverage to 100%.\n    * New docs: [Printing and Colors: \"Standard Output\" and \"Standard Error\"](https://typer.tiangolo.com/tutorial/printing/#standard-output-and-standard-error).\n    * New docs: [Password CLI Option and Confirmation Prompt](https://typer.tiangolo.com/tutorial/options/password/).\n    * Support for callbacks based on type annotations. New docs: [CLI Option Callback and Context](https://typer.tiangolo.com/tutorial/options/callback-and-context/).\n    * New docs: [Version CLI Option, is_eager](https://typer.tiangolo.com/tutorial/options/version/).\n    * Support for autocompletion functions based on type annotations. New docs: [CLI Option autocompletion](https://typer.tiangolo.com/tutorial/options/autocompletion/).\n    * New docs: [Commands: Using the Context](https://typer.tiangolo.com/tutorial/commands/context/).\n    * New docs: [Testing](https://typer.tiangolo.com/tutorial/testing/).\n    * PR [#68](https://github.com/tiangolo/typer/pull/68).\n* Fix Zsh completion install script. PR [#69](https://github.com/tiangolo/typer/pull/69).\n* Fix typo in progressbar example. PR [#63](https://github.com/tiangolo/typer/pull/63) by [@ValentinCalomme](https://github.com/ValentinCalomme).\n\n## 0.0.11\n\n* Re-implement completion system:\n    * Remove optional dependency `click-completion` (with its sub-dependencies, like Jinja).\n    * Add optional dependency `shellingham` to auto detect shell to install (it was used by `click-completion`).\n    * Completion now doesn't require a third party library.\n        * If `shellingham` is not installed/added as a dependency, `--install-completion` and `--show-completion` take a value with the name of the shell.\n    * Fix support for user provided completion in *CLI Parameters*.\n    * Fix completion for files in Bash, Zsh, and Fish.\n    * Add support for modern versions of PowerShell, 5, 6, and 7 (e.g. in Windows 10).\n    * Add support for `pwsh` (PowerShell Core).\n        * PowerShell support includes help strings for commands and *CLI Parameters*.\n    * Several bug fixes.\n    * Tests for the completion logic/code.\n    * Tested in all the shells in Linux and Windows.\n    * PR [#66](https://github.com/tiangolo/typer/pull/66).\n* Fix format in docs with highlighted lines. PR [#65](https://github.com/tiangolo/typer/pull/65).\n* Add docs about [Typer CLI - completion for small scripts](https://typer.tiangolo.com/typer-cli/). PR [#64](https://github.com/tiangolo/typer/pull/64).\n* Add docs about [Alternatives, Inspiration and Comparisons](https://typer.tiangolo.com/alternatives/). PR [#62](https://github.com/tiangolo/typer/pull/62).\n* Add [Development - Contributing Guide](https://typer.tiangolo.com/contributing/). PR [#61](https://github.com/tiangolo/typer/pull/61).\n\n## 0.0.10\n\n* Add support for Click version 7.1.1. PR [#60](https://github.com/tiangolo/typer/pull/60).\n\n## 0.0.9\n\n* Add support for PEP 561, to allow `mypy` to type check applications built with **Typer**. PR [#58](https://github.com/tiangolo/typer/pull/58).\n* Upgrade deploy docs to Netlify GitHub action. PR [#57](https://github.com/tiangolo/typer/pull/57).\n* Add support for Mermaid JS for visualizations. PR [#56](https://github.com/tiangolo/typer/pull/56).\n* Update CI to run docs deployment in GitHub actions. PR [#50](https://github.com/tiangolo/typer/pull/50).\n* Update format for internal links. PR [#38](https://github.com/tiangolo/typer/pull/38).\n* Tweak external links' format. PR [#36](https://github.com/tiangolo/typer/pull/36).\n\n## 0.0.8\n\n* Update docs and add latest changes to MkDocs/website. PR [#33](https://github.com/tiangolo/typer/pull/33).\n* Add extra tests for edge cases that don't belong in docs' examples. PR [#32](https://github.com/tiangolo/typer/pull/32).\n* Add docs for CLI Parameters with [Multiple Values](https://typer.tiangolo.com/tutorial/multiple-values/). Includes tests for all the examples and bug fixes. PR [#31](https://github.com/tiangolo/typer/pull/31).\n* Add docs for extra *CLI parameter* types: [CLI Parameter Types: Number](https://typer.tiangolo.com/tutorial/parameter-types/number/) and [CLI Parameter Types: Boolean CLI Options](https://typer.tiangolo.com/tutorial/parameter-types/bool/). PR [#30](https://github.com/tiangolo/typer/pull/30).\n* Extend docs for Commands, add [Commands: Typer Callback](https://typer.tiangolo.com/tutorial/commands/callback/) and [Commands: One or Multiple](https://typer.tiangolo.com/tutorial/commands/one-or-multiple/). This includes tests for all the examples and bug fixes. PR [#29](https://github.com/tiangolo/typer/pull/29).\n* Add docs for [SubCommands - Command Groups](https://typer.tiangolo.com/tutorial/subcommands/). This includes tests for all the examples and bug fixes. PR [#28](https://github.com/tiangolo/typer/pull/28).\n* Remove unneeded code for argument handling. PR [#26](https://github.com/tiangolo/typer/pull/26).\n* Add docs for [Launching Applications](https://typer.tiangolo.com/tutorial/launch/). PR [#25](https://github.com/tiangolo/typer/pull/25).\n* Add docs for getting the [CLI Application Directory](https://typer.tiangolo.com/tutorial/app-dir/). PR [#24](https://github.com/tiangolo/typer/pull/24).\n* Add docs for [Progress Bars](https://typer.tiangolo.com/tutorial/progressbar/). PR [#23](https://github.com/tiangolo/typer/pull/23).\n* Add docs for [Asking with Interactive Prompts](). PR [#22](https://github.com/tiangolo/typer/pull/22).\n* Update docs for path *CLI option*. PR [#21](https://github.com/tiangolo/typer/pull/21).\n* Add colors module and docs for [Printing and Colors](https://typer.tiangolo.com/tutorial/printing/) and for [Terminating](https://typer.tiangolo.com/tutorial/terminating/), including tests. PR [#20](https://github.com/tiangolo/typer/pull/20).\n* Refactor docs to make each individual page/section \"bite-sized\" / small. Add docs for [CLI option names](https://typer.tiangolo.com/tutorial/options/name/). Update `typer.Argument()` to remove invalid positional `param_decls`. PR [#19](https://github.com/tiangolo/typer/pull/19).\n\n## 0.0.7\n\n* Add docs for [*CLI parameter* types](https://typer.tiangolo.com/tutorial/parameter-types/). Includes tests and file classes refactor. PR [#17](https://github.com/tiangolo/typer/pull/17).\n* Add tests for completion. PR [#15](https://github.com/tiangolo/typer/pull/15) and [#16](https://github.com/tiangolo/typer/pull/16).\n\n## 0.0.6\n\n* Add docs for [Commands](https://typer.tiangolo.com/tutorial/commands/). Includes a bug fix for handling default values set in `typer.Typer()` parameters. PR [#14](https://github.com/tiangolo/typer/pull/14).\n* Add docs for [CLI Arguments](https://typer.tiangolo.com/tutorial/arguments/). PR [#13](https://github.com/tiangolo/typer/pull/13).\n* Add docs for [CLI Options](https://typer.tiangolo.com/tutorial/options/). PR [#12](https://github.com/tiangolo/typer/pull/12).\n\n## 0.0.5\n\n* Clean exports from Typer. Remove unneeded components from Click and add needed `Exit` exception. PR [#11](https://github.com/tiangolo/typer/pull/11).\n* Fix and document extracting help from a function's docstring [First Steps: Document your CLI app](https://typer.tiangolo.com/tutorial/first-steps/#document-your-cli-app). PR [#10](https://github.com/tiangolo/typer/pull/10).\n* Update references to `--install-completion` and `--show-completion` in docs. PR [#9](https://github.com/tiangolo/typer/pull/9).\n* Fix testing utilities, add tests for First Steps examples. PR [#8](https://github.com/tiangolo/typer/pull/8).\n* Add auto completion options by default when [click-completion](https://github.com/click-contrib/click-completion) is installed: `--install-completion` and `--show-completion`. PR [#7](https://github.com/tiangolo/typer/pull/7).\n* Update Termynal to have fixed sizes, add \"fast\" button, and use it in [First Steps](https://typer.tiangolo.com/tutorial/first-steps/). PR [#6](https://github.com/tiangolo/typer/pull/6).\n* Add custom automatic [Termynal](https://github.com/tiangolo/termynal) for docs. PR [#5](https://github.com/tiangolo/typer/pull/5).\n\n## 0.0.4\n\n* Update short descriptions and assets.\n* Docs rewording and fix typos. PR [#1](https://github.com/tiangolo/typer/pull/1) by [@mariacamilagl](https://github.com/mariacamilagl).\n\n## 0.0.3\n\n* Fix group creation without name.\n\n## 0.0.2\n\n* Add initial version of code, docs, etc.\n\n## 0.0.1\n\n* First commit. Publish to PyPI to reserve package name.\n"
  },
  {
    "path": "docs/resources/index.md",
    "content": "# Resources\n\nAdditional resources, how to **help** and get help, how to **contribute**, and more. ✈️\n"
  },
  {
    "path": "docs/tutorial/app-dir.md",
    "content": "# CLI Application Directory\n\nYou can get the application directory where you can, for example, save configuration files with `typer.get_app_dir()`:\n\n{* docs_src/app_dir/tutorial001_py310.py hl[12] *}\n\nIt will give you a directory for storing configurations appropriate for your CLI program for the current user in each operating system.\n\nCheck it:\n\n<div class=\"termy\">\n\n```console\n$ python main.py\n\nConfig file doesn't exist yet\n```\n\n</div>\n\n## About `Path`\n\nIf you hadn't seen something like that:\n\n```Python\nPath(app_dir) / \"config.json\"\n```\n\nA `Path` object can be used with `/` and it will convert it to the separator for the current system (`/` for Unix systems and `\\` for Windows).\n\nIf the first element is a `Path` object the next ones (after the `/`) can be `str`.\n\nAnd it will create a new `Path` object from that.\n\nIf you want a quick guide on using `Path()` you can check <a href=\"https://realpython.com/python-pathlib/\" class=\"external-link\" target=\"_blank\">this post on Real Python</a> or <a href=\"https://treyhunner.com/2018/12/why-you-should-be-using-pathlib/\" class=\"external-link\" target=\"_blank\">this post by Trey Hunner</a>.\n\nIn the code above, we are also explicitly declaring `config_path` as having type `Path` to help the editor provide completion and type checks:\n\n```Python\nconfig_path: Path = Path(app_dir) / \"config.json\"\n```\n\nOtherwise it could think it's a sub-type (a `PurePath`) and stop providing completion for some methods.\n"
  },
  {
    "path": "docs/tutorial/arguments/default.md",
    "content": "# CLI Arguments with Default\n\nWe can also use the same `typer.Argument()` to set a default value.\n\nThat way the *CLI argument* will be optional *and also* have a default value.\n\n## An optional *CLI argument* with a default\n\nWe can also use `typer.Argument()` to make a *CLI argument* have a default value other than `None`:\n\n{* docs_src/arguments/default/tutorial001_an_py310.py hl[9] *}\n\n/// tip\n\nBecause now the value will be a `str` passed by the user or the default value of `\"Wade Wilson\"` which is also a `str`, we know the value will never be `None`, so we don't have to (and shouldn't) use `Optional[str]`.\n\nHave in mind that the `Optional[something]` tells Python that a value \"could be `None`\". But the use of `Optional` doesn't affect Typer in any way, e.g. it doesn't tell Typer if a value is required or not.\n\n///\n\nCheck it:\n\n<div class=\"termy\">\n\n```console\n// Check the help\n$ python main.py --help\n\n// Notice the [default: Wade Wilson] ✨\nUsage: main.py [OPTIONS] [NAME]\n\nArguments:\n  [NAME]  [default: Wade Wilson]\n\nOptions:\n  --help                Show this message and exit.\n\n// With no optional CLI argument\n$ python main.py\n\nHello Wade Wilson\n\n// With one CLI argument\n$ python main.py Camila\n\nHello Camila\n```\n\n</div>\n\n## Dynamic default value\n\nAnd we can even make the default value be dynamically generated by passing a function as the `default_factory` argument:\n\n{* docs_src/arguments/default/tutorial002_an_py310.py hl[9:10,14] *}\n\nIn this case, we created the function `get_name` that will just return a random `str` each time.\n\nAnd we pass it as the first function argument to `typer.Argument()`.\n\n/// tip\n\nThe word \"factory\" in `default_factory` is just a fancy way of saying \"function that will create the default value\".\n\n///\n\nCheck it:\n\n<div class=\"termy\">\n\n```console\n// Check the help\n$ python main.py --help\n\nUsage: main.py [OPTIONS] [NAME]\n\nArguments:\n  [NAME]  [default: (dynamic)]\n\nOptions:\n  --help                Show this message and exit.\n\n// Try it several times, it will use a random default each time\n$ python main.py\n\nHello Deadpool\n\n$ python main.py\n\nHello Hiro\n\n$ python main.py\n\nHello Rick\n\n// Now pass a value for the CLI argument\n$ python main.py Camila\n\nHello Camila\n```\n\n</div>\n"
  },
  {
    "path": "docs/tutorial/arguments/envvar.md",
    "content": "# CLI Arguments with Environment Variables\n\nYou can also configure a *CLI argument* to read a value from an environment variable if it is not provided in the command line as a *CLI argument*.\n\n/// tip\n\nYou can learn more about environment variables in the [Environment Variables](../../environment-variables.md){.internal-link target=_blank} page.\n\n///\n\nTo do that, use the `envvar` parameter for `typer.Argument()`:\n\n{* docs_src/arguments/envvar/tutorial001_an_py310.py hl[9] *}\n\nIn this case, the *CLI argument* `name` will have a default value of `\"World\"`, but will also read any value passed to the environment variable `AWESOME_NAME` if no value is provided in the command line:\n\n<div class=\"termy\">\n\n```console\n// Check the help\n$ python main.py --help\n\nUsage: main.py [OPTIONS] [NAME]\n\nArguments:\n  [NAME]  [env var: AWESOME_NAME;default: World]\n\nOptions:\n  --help                Show this message and exit.\n\n// Call it without a CLI argument\n$ python main.py\n\nHello Mr. World\n\n// Now pass a value for the CLI argument\n$ python main.py Czernobog\n\nHello Mr. Czernobog\n\n// And now use the environment variable\n$ AWESOME_NAME=Wednesday python main.py\n\nHello Mr. Wednesday\n\n// CLI arguments take precedence over env vars\n$ AWESOME_NAME=Wednesday python main.py Czernobog\n\nHello Mr. Czernobog\n```\n\n</div>\n\n## Multiple environment variables\n\nYou are not restricted to a single environment variable, you can declare a list of environment variables that could be used to get a value if it was not passed in the command line:\n\n{* docs_src/arguments/envvar/tutorial002_an_py310.py hl[10] *}\n\nCheck it:\n\n<div class=\"termy\">\n\n```console\n// Check the help\n$ python main.py --help\n\nUsage: main.py [OPTIONS] [NAME]\n\nArguments:\n  [NAME]  [env var: AWESOME_NAME, GOD_NAME;default: World]\n\nOptions:\n  --help                Show this message and exit.\n\n// Try the first env var\n$ AWESOME_NAME=Wednesday python main.py\n\nHello Mr. Wednesday\n\n// Try the second env var\n$ GOD_NAME=Anubis python main.py\n\nHello Mr. Anubis\n```\n\n</div>\n\n## Hide an env var from the help text\n\nBy default, environment variables used will be shown in the help text, but you can disable them with `show_envvar=False`:\n\n{* docs_src/arguments/envvar/tutorial003_an_py310.py hl[11] *}\n\nCheck it:\n\n<div class=\"termy\">\n\n```console\n//Check the help\n$ python main.py --help\n\n// It won't show the env var\nUsage: main.py [OPTIONS] [NAME]\n\nArguments:\n  [NAME]  [default: World]\n\nOptions:\n  --help                Show this message and exit.\n\n// But it will still be able to use it\n$ AWESOME_NAME=Wednesday python main.py\n\nHello Mr. Wednesday\n```\n\n</div>\n"
  },
  {
    "path": "docs/tutorial/arguments/help.md",
    "content": "# CLI Arguments with Help\n\nIn the *First Steps* section you saw how to add help for a CLI app/command by adding it to a function's <abbr title=\"a multi-line string as the first expression inside a function (not assigned to any variable) used for documentation\">docstring</abbr>.\n\nHere's how that last example looked like:\n\n{* docs_src/first_steps/tutorial006_py310.py *}\n\nNow that you also know how to use `typer.Argument()`, let's use it to add documentation specific for a *CLI argument*.\n\n## Add a `help` text for a *CLI argument*\n\nYou can use the `help` parameter to add a help text for a *CLI argument*:\n\n{* docs_src/arguments/help/tutorial001_an_py310.py hl[9] *}\n\nAnd it will be used in the automatic `--help` option:\n\n<div class=\"termy\">\n\n```console\n$ python main.py --help\n\n// Check the section with Arguments below 🚀\nUsage: main.py [OPTIONS] NAME\n\nArguments:\n  NAME  The name of the user to greet  [required]\n\nOptions:\n  --help                Show this message and exit.\n```\n\n</div>\n\n## Combine help text and docstrings\n\nAnd of course, you can also combine that `help` with the <abbr title=\"a multi-line string as the first expression inside a function (not assigned to any variable) used for documentation\">docstring</abbr>:\n\n{* docs_src/arguments/help/tutorial002_an_py310.py hl[9:12] *}\n\nAnd the `--help` option will combine all the information:\n\n<div class=\"termy\">\n\n```console\n$ python main.py --help\n\n// Notice that we have the help text from the docstring and also the Arguments 📝\nUsage: main.py [OPTIONS] NAME\n\n  Say hi to NAME very gently, like Dirk.\n\nArguments:\n  NAME  The name of the user to greet  [required]\n\nOptions:\n  --help                Show this message and exit.\n```\n\n</div>\n\n## Help with defaults\n\nIf you have a *CLI argument* with a default value, like `\"World\"`:\n\n{* docs_src/arguments/help/tutorial003_an_py310.py hl[9] *}\n\nIt will show that default value in the help text:\n\n<div class=\"termy\">\n\n```console\n$ python main.py --help\n\n// Notice the [default: World] 🔍\nUsage: main.py [OPTIONS] [NAME]\n\n  Say hi to NAME very gently, like Dirk.\n\nArguments:\n  [NAME]  Who to greet  [default: World]\n\nOptions:\n  --help                Show this message and exit.\n```\n\n</div>\n\nBut you can disable that if you want to, with `show_default=False`:\n\n{* docs_src/arguments/help/tutorial004_an_py310.py hl[11] *}\n\nAnd then it won't show the default value:\n\n<div class=\"termy\">\n\n```console\n$ python main.py --help\n\n// Notice the there's no [default: World] now 🔥\nUsage: main.py [OPTIONS] [NAME]\n\n  Say hi to NAME very gently, like Dirk.\n\nArguments:\n  [NAME]  Who to greet\n\nOptions:\n  --help                Show this message and exit.\n```\n\n</div>\n\n## Custom default string\n\nYou can use the same `show_default` to pass a custom string (instead of a `bool`) to customize the default value to be shown in the help text:\n\n{* docs_src/arguments/help/tutorial005_an_py310.py hl[13] *}\n\nAnd it will be used in the help text:\n\n<div class=\"termy\">\n\n```console\n$ python main.py --help\n\nUsage: main.py [OPTIONS] [NAME]\n\nArguments:\n  [NAME]  Who to greet  [default: (Deadpoolio the amazing's name)]\n\n\nOptions:\n  --help                Show this message and exit.\n\n// See it shows \"(Deadpoolio the amazing's name)\" instead of the actual default of \"Wade Wilson\"\n```\n\n</div>\n\n## Custom help name (`metavar`)\n\nYou can also customize the text used in the generated help text to represent a *CLI argument*.\n\nBy default, it will be the same name you declared, in uppercase letters.\n\nSo, if you declare it as:\n\n```Python\nname: str\n```\n\nIt will be shown as:\n\n```\nNAME\n```\n\nBut you can customize it with the `metavar` parameter for `typer.Argument()`.\n\nFor example, let's say you don't want to have the default of `NAME`, you want to have `username`, in lowercase, and you really want ✨ emojis ✨ everywhere:\n\n{* docs_src/arguments/help/tutorial006_an_py310.py hl[9] *}\n\nNow the generated help text will have `✨username✨` instead of `NAME`:\n\n<div class=\"termy\">\n\n```console\n$ python main.py --help\n\nUsage: main.py [OPTIONS] [✨username✨]\n\nArguments:\n  [✨username✨]  [default: World]\n\nOptions:\n  --help                Show this message and exit.\n```\n\n</div>\n\n## *CLI Argument* help panels\n\nYou might want to show the help information for *CLI arguments* in different panels when using the `--help` option.\n\nIf you have installed Rich as described in the docs for [Printing and Colors](../printing.md){.internal-link target=_blank}, you can set the `rich_help_panel` parameter to the name of the panel where you want this *CLI argument* to be shown:\n\n{* docs_src/arguments/help/tutorial007_an_py310.py hl[12,16] *}\n\nThen, if you check the `--help` option, you will see a default panel named \"`Arguments`\" for the *CLI arguments* that don't have a custom `rich_help_panel`.\n\nAnd next you will see other panels for the *CLI arguments* that have a custom panel set in the `rich_help_panel` parameter:\n\n<div class=\"termy\">\n\n```console\n$ python main.py --help\n\n<b> </b><font color=\"#F4BF75\"><b>Usage: </b></font><b>main.py [OPTIONS] NAME [LASTNAME] [AGE]               </b>\n<b>                                                                     </b>\n Say hi to NAME very gently, like Dirk.\n\n<font color=\"#A5A5A1\">╭─ Arguments ───────────────────────────────────────────────────────╮</font>\n<font color=\"#A5A5A1\">│ </font><font color=\"#F92672\">*</font>    name      <font color=\"#F4BF75\"><b>TEXT</b></font>  Who to greet [default: None] <font color=\"#A6194C\">[required]</font>      │\n<font color=\"#A5A5A1\">╰───────────────────────────────────────────────────────────────────╯</font>\n<font color=\"#A5A5A1\">╭─ Secondary Arguments ─────────────────────────────────────────────╮</font>\n<font color=\"#A5A5A1\">│   lastname      </font><font color=\"#A37F4E\"><b>[LASTNAME]</b></font>  The last name                         │\n<font color=\"#A5A5A1\">│   age           </font><font color=\"#A37F4E\"><b>[AGE]     </b></font>  The user&apos;s age                        │\n<font color=\"#A5A5A1\">╰───────────────────────────────────────────────────────────────────╯</font>\n<font color=\"#A5A5A1\">╭─ Options ─────────────────────────────────────────────────────────╮</font>\n<font color=\"#A5A5A1\">│ </font><font color=\"#A1EFE4\"><b>--help</b></font>                        Show this message and exit.         │\n<font color=\"#A5A5A1\">╰───────────────────────────────────────────────────────────────────╯</font>\n```\n\n</div>\n\nIn this example we have a custom *CLI arguments* panel named \"`Secondary Arguments`\".\n\n## Help with style using Rich\n\nIn a future section you will see how to use custom markup in the `help` for *CLI arguments* when reading about [Commands - Command Help](../commands/help.md#rich-markdown-and-markup){.internal-link target=_blank}.\n\nIf you are in a hurry you can jump there, but otherwise, it would be better to continue reading here and following the tutorial in order.\n\n## Hide a *CLI argument* from the help text\n\nIf you want, you can make a *CLI argument* **not** show up in the `Arguments` section in the help text.\n\nYou will probably not want to do this normally, but it's possible:\n\n{* docs_src/arguments/help/tutorial008_an_py310.py hl[9] *}\n\nCheck it:\n\n<div class=\"termy\">\n\n```console\n$ python main.py --help\n\n// Notice there's no Arguments section at all 🔥\nUsage: main.py [OPTIONS] [NAME]\n\n  Say hi to NAME very gently, like Dirk.\n\nOptions:\n  --help                Show this message and exit.\n```\n\n</div>\n\n/// info\n\nHave in mind that the *CLI argument* will still show up in the first line with `Usage`.\n\nBut it won't show up in the main help text under the `Arguments` section.\n\n///\n\n### Help text for *CLI arguments*\n\n**Typer supports `help` for *CLI arguments*** to make it easier to have consistent help texts with a consistent format for your CLI programs. 🎨\n\nThis is also to help you create CLI programs that are ✨ awesome ✨ *by default*. With very little code.\n\nIf you don't want the CLI Argument to be shown in help outputs, you can set the `hidden` parameter to `True`.\n"
  },
  {
    "path": "docs/tutorial/arguments/index.md",
    "content": "# CLI Arguments\n\nIn the next few sections we'll see some ways to modify how *CLI arguments* work.\n\nWe'll create optional *CLI arguments*, we'll add integrated help for *CLI arguments*, etc.\n"
  },
  {
    "path": "docs/tutorial/arguments/optional.md",
    "content": "# Optional CLI Arguments\n\nWe said before that *by default*:\n\n* *CLI options* are **optional**\n* *CLI arguments* are **required**\n\nAgain, that's how they work *by default*, and that's the convention in many CLI programs and systems.\n\nBut you can change that.\n\nIn fact, it's very common to have **optional** *CLI arguments*, it's way more common than having **required** *CLI options*.\n\nAs an example of how it could be useful, let's see how the `ls` CLI program works.\n\n<div class=\"termy\">\n\n```console\n// If you just type\n$ ls\n\n// ls will \"list\" the files and directories in the current directory\ntyper  tests  README.md  LICENSE\n\n// But it also receives an optional CLI argument\n$ ls ./tests/\n\n// And then ls will list the files and directories inside of that directory from the CLI argument\n__init__.py  test_tutorial\n```\n\n</div>\n\n## An alternative *CLI argument* declaration\n\nIn the [First Steps](../first-steps.md#add-a-cli-argument){.internal-link target=_blank} you saw how to add a *CLI argument*:\n\n{* docs_src/first_steps/tutorial002_py310.py hl[4] *}\n\nNow let's see an alternative way to create the same *CLI argument*:\n\n{* docs_src/arguments/optional/tutorial000_an_py310.py hl[6] *}\n\nOr, using an explicit `Typer()` instance creation:\n\n{* docs_src/arguments/optional/tutorial001_an_py310.py hl[9] *}\n\n/// info\n\nTyper added support for `Annotated` (and started recommending it) in version 0.9.0.\n\nIf you have an older version, you would get errors when trying to use `Annotated`.\n\nMake sure you upgrade the Typer version to at least 0.9.0 before using `Annotated`.\n\n///\n\nBefore, you had this function parameter:\n\n```Python\nname: str\n```\n\nAnd now we wrap it with `Annotated`:\n\n```Python\nname: Annotated[str]\n```\n\nBoth of these versions mean the same thing, `Annotated` is part of standard Python and is there for this.\n\nBut the second version using `Annotated` allows us to pass additional metadata that can be used by **Typer**:\n\n```Python\nname: Annotated[str, typer.Argument()]\n```\n\nNow we are being explicit that `name` is a *CLI argument*. It's still a `str` and it's still required (it doesn't have a default value).\n\nAll we did there achieves the same thing as before, a **required** *CLI argument*:\n\n<div class=\"termy\">\n\n```console\n$ python main.py\n\nUsage: main.py [OPTIONS] NAME\nTry \"main.py --help\" for help.\n\nError: Missing argument 'NAME'.\n```\n\n</div>\n\nIt's still not very useful, but it works correctly.\n\nAnd being able to declare a **required** *CLI argument* using\n\n```Python\nname: Annotated[str, typer.Argument()]\n```\n\n...that works exactly the same as\n\n```Python\nname: str\n```\n\n...will come handy later.\n\n## Make an optional *CLI argument*\n\nNow, finally what we came for, an optional *CLI argument*.\n\nTo make a *CLI argument* optional, use `typer.Argument()` and make sure to provide a \"default\" value, for example `\"World\"`:\n\n{* docs_src/arguments/optional/tutorial002_an_py310.py hl[9] *}\n\nNow we have:\n\n```Python\nname: Annotated[str, typer.Argument()] = \"World\"\n```\n\nBecause we are using `typer.Argument()` **Typer** will know that this is a *CLI argument* (no matter if *required* or *optional*).\n\nCheck the help:\n\n<div class=\"termy\">\n\n```console\n// First check the help\n$ python main.py --help\n\nUsage: main.py [OPTIONS] [NAME]\n\nArguments:\n  [NAME]\n\nOptions:\n  --help                Show this message and exit.\n```\n\n</div>\n\n/// tip\n\nNotice that `NAME` is still a *CLI argument*, it's shown up there in the \"`Usage: main.py` ...\".\n\nAlso notice that now `[NAME]` has brackets (\"`[`\" and \"`]`\") around (before it was just `NAME`) to denote that it's **optional**, not **required**.\n\n///\n\nNow run it and test it:\n\n<div class=\"termy\">\n\n```console\n// With no CLI argument\n$ python main.py\n\nHello World!\n\n// With one optional CLI argument\n$ python main.py Camila\n\nHello Camila\n```\n\n</div>\n\n/// tip\n\nNotice that \"`Camila`\" here is an optional *CLI argument*, not a *CLI option*, because we didn't use something like \"`--name Camila`\", we just passed \"`Camila`\" directly to the program.\n\n///\n\n## Alternative (old) `typer.Argument()` as the default value\n\n**Typer** also supports another older alternative syntax for declaring *CLI arguments* with additional metadata.\n\nInstead of using `Annotated`, you can use `typer.Argument()` as the default value:\n\n{* docs_src/arguments/optional/tutorial001_py310.py hl[7] *}\n\n/// tip\n\nPrefer to use the `Annotated` version if possible.\n\n///\n\nBefore, because `name` didn't have any default value it would be a **required parameter** for the Python function, in Python terms.\n\nWhen using `typer.Argument()` as the default value **Typer** does the same and makes it a **required** *CLI argument*.\n\nWe changed it to:\n\n```Python\nname: str = typer.Argument()\n```\n\nBut now as `typer.Argument()` is the \"default value\" of the function's parameter, it would mean that \"it is no longer required\" (in Python terms).\n\nAs we no longer have the Python function default value (or its absence) to tell if something is required or not and what is the default value, `typer.Argument()` receives a first parameter `default` that serves the same purpose of defining that default value, or making it required.\n\nNot passing any value to the `default` argument is the same as marking it as required. But you can also explicitly mark it as *required* by passing `...` as the `default` argument, passed to `typer.Argument(default=...)`.\n\n```Python\nname: str = typer.Argument(default=...)\n```\n\n/// info\n\nIf you hadn't seen that `...` before: it is a special single value, it is <a href=\"https://docs.python.org/3/library/constants.html#Ellipsis\" class=\"external-link\" target=\"_blank\">part of Python and is called \"Ellipsis\"</a>.\n\n///\n\n{* docs_src/arguments/optional/tutorial003_py310.py hl[7] *}\n\nAnd the same way, you can make it optional by passing a different `default` value, for example `\"World\"`:\n\n{* docs_src/arguments/optional/tutorial002_py310.py hl[7] *}\n\nBecause the first parameter passed to `typer.Argument(default=\"World\")` (the new \"default\" value) is `\"World\"`, **Typer** knows that this is an **optional** *CLI argument*, if no value is provided when calling it in the command line, it will have that default value of `\"World\"`.\n\nThe `default` argument is the first one, so it's possible that you see code that passes the value without explicitly using `default=`, like:\n\n```Python\nname: str = typer.Argument(...)\n```\n\n...or like:\n\n```Python\nname: str = typer.Argument(\"World\")\n```\n\n...but again, try to use `Annotated` if possible, that way your code in terms of Python will mean the same thing as with **Typer** and you won't have to remember any of these details.\n"
  },
  {
    "path": "docs/tutorial/arguments/other-uses.md",
    "content": "# Other uses\n\n`typer.Argument()` has several other use cases. Such as for data validation, to enable other features, etc.\n\nYou will see about these use cases later in the docs.\n"
  },
  {
    "path": "docs/tutorial/commands/arguments.md",
    "content": "# Command CLI Arguments\n\nThe same way as with a CLI application with a single command, subcommands (or just \"commands\") can also have their own *CLI arguments*:\n\n{* docs_src/commands/arguments/tutorial001_py310.py hl[7,12] *}\n\n<div class=\"termy\">\n\n```console\n// Check the help for create\n$ python main.py create --help\n\nUsage: main.py create [OPTIONS] USERNAME\n\nOptions:\n  --help  Show this message and exit.\n\n// Call it with a CLI argument\n$ python main.py create Camila\n\nCreating user: Camila\n\n// The same for delete\n$ python main.py delete Camila\n\nDeleting user: Camila\n```\n\n</div>\n\n/// tip\n\nEverything to the *right* of the *command* are *CLI parameters* (*CLI arguments* and *CLI options*) for that command.\n\n///\n\n/// note | Technical Details\n\nActually, it's everything to the right of that command, *before any subcommand*.\n\nIt's possible to have groups of *subcommands*, it's like if one *command* also had *subcommands*. And then those *subcommands* could have their own *CLI parameters*, taking their own *CLI parameters*.\n\nYou will see about them later in another section.\n\n///\n"
  },
  {
    "path": "docs/tutorial/commands/callback.md",
    "content": "# Typer Callback\n\nWhen you create an `app = typer.Typer()` it works as a group of commands.\n\nAnd you can create multiple commands with it.\n\nEach of those commands can have their own *CLI parameters*.\n\nBut as those *CLI parameters* are handled by each of those commands, they don't allow us to create *CLI parameters* for the main CLI application itself.\n\nBut we can use `@app.callback()` for that.\n\nIt's very similar to `@app.command()`, but it declares the *CLI parameters* for the main CLI application (before the commands):\n\n{* docs_src/commands/callback/tutorial001_py310.py hl[25,26,27,28,29,30,31,32] *}\n\nHere we create a `callback` with a `--verbose` *CLI option*.\n\n/// tip\n\nAfter getting the `--verbose` flag, we modify a global `state`, and we use it in the other commands.\n\nThere are other ways to achieve the same, but this will suffice for this example.\n\n///\n\nAnd as we added a docstring to the callback function, by default it will be extracted and used as the help text.\n\nCheck it:\n\n<div class=\"termy\">\n\n```console\n// Check the help\n$ python main.py --help\n\n// Notice the main help text, extracted from the callback function: \"Manage users in the awesome CLI app.\"\nUsage: main.py [OPTIONS] COMMAND [ARGS]...\n\n  Manage users in the awesome CLI app.\n\nOptions:\n  --verbose / --no-verbose  [default: False]\n  --install-completion      Install completion for the current shell.\n  --show-completion         Show completion for the current shell, to copy it or customize the installation.\n  --help                    Show this message and exit.\n\nCommands:\n  create\n  delete\n\n// Check the new top level CLI option --verbose\n\n// Try it normally\n$ python main.py create Camila\n\nCreating user: Camila\n\n// And now with --verbose\n$ python main.py --verbose create Camila\n\nWill write verbose output\nAbout to create a user\nCreating user: Camila\nJust created a user\n\n// Notice that --verbose belongs to the callback, it has to go before create or delete ⛔️\n$ python main.py create --verbose Camila\n\nUsage: main.py create [OPTIONS] USERNAME\nTry \"main.py create --help\" for help.\n\nError: No such option: --verbose\n```\n\n</div>\n\n## Adding a callback on creation\n\nIt's also possible to add a callback when creating the `typer.Typer()` app:\n\n{* docs_src/commands/callback/tutorial002_py310.py hl[4,5,8] *}\n\nThat achieves the same as with `@app.callback()`.\n\nCheck it:\n\n<div class=\"termy\">\n\n```console\n$ python main.py create Camila\n\nRunning a command\nCreating user: Camila\n```\n\n</div>\n\n## Overriding a callback\n\nIf you added a callback when creating the `typer.Typer()` app, it's possible to override it with `@app.callback()`:\n\n{* docs_src/commands/callback/tutorial003_py310.py hl[11,12,13] *}\n\nNow `new_callback()` will be the one used.\n\nCheck it:\n\n<div class=\"termy\">\n\n```console\n$ python main.py create Camila\n\n// Notice that the message is the one from new_callback()\nOverride callback, running a command\nCreating user: Camila\n```\n\n</div>\n\n## Adding a callback only for documentation\n\nYou can also add a callback just to add the documentation in the docstring.\n\nIt can be convenient especially if you have several lines of text, as the indentation will be automatically handled for you:\n\n{* docs_src/commands/callback/tutorial004_py310.py hl[8,9,10,11,12,13,14,15,16] *}\n\nNow the callback will be used mainly to extract the docstring for the help text.\n\nCheck it:\n\n<div class=\"termy\">\n\n```console\n$ python main.py --help\n\n// Notice all the help text extracted from the callback docstring\nUsage: main.py [OPTIONS] COMMAND [ARGS]...\n\n  Manage users CLI app.\n\n  Use it with the create command.\n\n  A new user with the given NAME will be created.\n\nOptions:\n  --install-completion  Install completion for the current shell.\n  --show-completion     Show completion for the current shell, to copy it or customize the installation.\n  --help                Show this message and exit.\n\nCommands:\n  create\n\n// And it just works as normally\n$ python main.py create Camila\n\nCreating user: Camila\n```\n\n</div>\n"
  },
  {
    "path": "docs/tutorial/commands/context.md",
    "content": "# Using the Context\n\nWhen you create a **Typer** application it always has a special, hidden object underneath called the \"Context\".\n\nBut you can access the context by declaring a function parameter of type `typer.Context`.\n\nYou might have read it in [CLI Option Callback and Context](../options/callback-and-context.md){.internal-link target=_blank}.\n\nThe same way, in commands or in the main `Typer` callback you can access the context by declaring a function parameter of type `typer.Context`.\n\n## Getting the context\n\nFor example, let's say that you want to execute some logic in a `Typer` callback depending on the subcommand that is being called.\n\nYou can get the name of the subcommand from the context:\n\n{* docs_src/commands/context/tutorial001_py310.py hl[17,21] *}\n\nCheck it:\n\n<div class=\"termy\">\n\n```console\n$ python main.py create Camila\n\n// We get the message from the callback\nAbout to execute command: create\nCreating user: Camila\n\n$ python main.py delete Camila\n\n// We get the message from the callback, this time with delete\nAbout to execute command: delete\nDeleting user: Camila\n```\n\n</div>\n\n## Executable callback\n\nBy default, the callback is only executed right before executing a command.\n\nAnd if no command is provided, the help message is shown.\n\nBut we could make it run even without a subcommand with `invoke_without_command=True`:\n\n{* docs_src/commands/context/tutorial002_py310.py hl[16] *}\n\nCheck it:\n\n<div class=\"termy\">\n\n```console\n$ python main.py\n\n// The callback is executed, we don't get the default help message\nInitializing database\n\n// Try with a command\n$ python main.py create Camila\n\n// The callback is still executed\nInitializing database\nCreating user: Camila\n```\n\n</div>\n\n## Exclusive executable callback\n\nWe might not want the callback to be executed if there's already other command that will be executed.\n\nFor that, we can get the `typer.Context` and check if there's an invoked command in `ctx.invoked_subcommand`.\n\nIf it's `None`, it means that we are not calling a subcommand but the main program (the callback) directly:\n\n{* docs_src/commands/context/tutorial003_py310.py hl[17,21] *}\n\nCheck it:\n\n<div class=\"termy\">\n\n```console\n$ python main.py\n\n// The callback is executed\nInitializing database\n\n// Check it with a subcommand\n$ python main.py create Camila\n\n// This time the callback is not executed\nCreating user: Camila\n```\n\n</div>\n\n## Configuring the context\n\nYou can pass configurations for the context when creating a command or callback.\n\nFor example, you could keep additional *CLI parameters* not declared in your CLI program with `ignore_unknown_options` and `allow_extra_args`.\n\nThen you can access those extra raw *CLI parameters* as a `list` of `str` in `ctx.args`:\n\n{* docs_src/commands/context/tutorial004_py310.py hl[7,9,10] *}\n\n<div class=\"termy\">\n\n```console\n$ python main.py --name Camila --city Berlin\n\nGot extra arg: --name\nGot extra arg: Camila\nGot extra arg: --city\nGot extra arg: Berlin\n```\n\n</div>\n\n/// tip\n\nNotice that it saves all the extra *CLI parameters* as a raw `list` of `str`, including the *CLI option* names and values, everything together.\n\n///\n"
  },
  {
    "path": "docs/tutorial/commands/help.md",
    "content": "# Command Help\n\nThe same as before, you can add help for the commands in the docstrings and the *CLI options*.\n\nAnd the `typer.Typer()` application receives a parameter `help` that you can pass with the main help text for your CLI program:\n\n{* docs_src/commands/help/tutorial001_an_py310.py hl[5,10:12,23,27:31,44,48:52,61:63] *}\n\nCheck it:\n\n<div class=\"termy\">\n\n```console\n// Check the new help\n$ python main.py --help\n\nUsage: main.py [OPTIONS] COMMAND [ARGS]...\n\n  Awesome CLI user manager.\n\nOptions:\n  --install-completion  Install completion for the current shell.\n  --show-completion     Show completion for the current shell, to copy it or customize the installation.\n  --help                Show this message and exit.\n\nCommands:\n  create      Create a new user with USERNAME.\n  delete      Delete a user with USERNAME.\n  delete-all  Delete ALL users in the database.\n  init        Initialize the users database.\n\n// Now the commands have inline help 🎉\n\n// Check the help for create\n$ python main.py create --help\n\nUsage: main.py create [OPTIONS] USERNAME\n\n  Create a new user with USERNAME.\n\nOptions:\n  --help  Show this message and exit.\n\n// Check the help for delete\n$ python main.py delete --help\n\nUsage: main.py delete [OPTIONS] USERNAME\n\n  Delete a user with USERNAME.\n\n  If --force is not used, will ask for confirmation.\n\nOptions:\n  --force / --no-force  Force deletion without confirmation.  [required]\n  --help                Show this message and exit.\n\n// Check the help for delete-all\n$ python main.py delete-all --help\n\nUsage: main.py delete-all [OPTIONS]\n\n  Delete ALL users in the database.\n\n  If --force is not used, will ask for confirmation.\n\nOptions:\n  --force / --no-force  Force deletion without confirmation.  [required]\n  --help                Show this message and exit.\n\n// Check the help for init\n$ python main.py init --help\n\nUsage: main.py init [OPTIONS]\n\n  Initialize the users database.\n\nOptions:\n  --help  Show this message and exit.\n```\n\n</div>\n\n/// tip\n\n`typer.Typer()` receives several other parameters for other things, we'll see that later.\n\nYou will also see how to use \"Callbacks\" later, and those include a way to add this same help message in a function docstring.\n\n///\n\n## Overwrite command help\n\nYou will probably be better adding the help text as a docstring to your functions, but if for some reason you wanted to overwrite it, you can use the `help` function argument passed to `@app.command()`:\n\n{* docs_src/commands/help/tutorial002_py310.py hl[6,14] *}\n\nCheck it:\n\n<div class=\"termy\">\n\n```console\n// Check the help\n$ python main.py --help\n\n// Notice it uses the help passed to @app.command()\nUsage: main.py [OPTIONS] COMMAND [ARGS]...\n\nOptions:\n  --install-completion  Install completion for the current shell.\n  --show-completion     Show completion for the current shell, to copy\n                        it or customize the installation.\n  --help                Show this message and exit.\n\nCommands:\n  create  Create a new user with USERNAME.\n  delete  Delete a user with USERNAME.\n\n// It uses \"Create a new user with USERNAME.\" instead of \"Some internal utility function to create.\"\n```\n\n</div>\n\n## Deprecate a Command\n\nThere could be cases where you have a command in your app that you need to deprecate, so that your users stop using it, even while it's still supported for a while.\n\nYou can mark it with the parameter `deprecated=True`:\n\n{* docs_src/commands/help/tutorial003_py310.py hl[14] *}\n\nAnd when you show the `--help` option you will see it's marked as \"`deprecated`\":\n\n<div class=\"termy\">\n\n```console\n$ python main.py --help\n\n<b> </b><font color=\"#F4BF75\"><b>Usage: </b></font><b>main.py [OPTIONS] COMMAND [ARGS]...                  </b>\n<b>                                                                     </b>\n<font color=\"#A5A5A1\">╭─ Options ─────────────────────────────────────────────────────────╮</font>\n<font color=\"#A5A5A1\">│ </font><font color=\"#A1EFE4\"><b>--install-completion</b></font>          Install completion for the current  │\n<font color=\"#A5A5A1\">│                               shell.                              │</font>\n<font color=\"#A5A5A1\">│ </font><font color=\"#A1EFE4\"><b>--show-completion</b></font>             Show completion for the current     │\n<font color=\"#A5A5A1\">│                               shell, to copy it or customize the  │</font>\n<font color=\"#A5A5A1\">│                               installation.                       │</font>\n<font color=\"#A5A5A1\">│ </font><font color=\"#A1EFE4\"><b>--help</b></font>                        Show this message and exit.         │\n<font color=\"#A5A5A1\">╰───────────────────────────────────────────────────────────────────╯</font>\n<font color=\"#A5A5A1\">╭─ Commands ────────────────────────────────────────────────────────╮</font>\n<font color=\"#A5A5A1\">│ </font><font color=\"#A1EFE4\"><b>create       </b></font> Create a user.                                      │\n<font color=\"#A5A5A1\">│ </font><font color=\"#6B9F98\"><b>delete       </b></font> Delete a user.              <font color=\"#F92672\">(deprecated)           </font> │\n<font color=\"#A5A5A1\">╰───────────────────────────────────────────────────────────────────╯</font>\n```\n\n</div>\n\nAnd if you check the `--help` for the deprecated command (in this example, the command `delete`), it also shows it as deprecated:\n\n<div class=\"termy\">\n\n```console\n$ python main.py delete --help\n\n<b> </b><font color=\"#F4BF75\"><b>Usage: </b></font><b>main.py delete [OPTIONS] USERNAME                    </b>\n<b>                                                                     </b>\n <font color=\"#F92672\">(deprecated) </font>\n Delete a user.\n This is deprecated and will stop being supported soon.\n\n<font color=\"#A5A5A1\">╭─ Arguments ───────────────────────────────────────────────────────╮</font>\n<font color=\"#A5A5A1\">│ </font><font color=\"#F92672\">*</font>    username      <font color=\"#F4BF75\"><b>TEXT</b></font>  [default: None] <font color=\"#A6194C\">[required]</font>               │\n<font color=\"#A5A5A1\">╰───────────────────────────────────────────────────────────────────╯</font>\n<font color=\"#A5A5A1\">╭─ Options ─────────────────────────────────────────────────────────╮</font>\n<font color=\"#A5A5A1\">│ </font><font color=\"#A1EFE4\"><b>--help</b></font>          Show this message and exit.                       │\n<font color=\"#A5A5A1\">╰───────────────────────────────────────────────────────────────────╯</font>\n```\n\n</div>\n\n## Suggest Commands\n\nAs of version 0.20.0, Typer added support for suggesting mistyped command names. This feature is **enabled by default**, but you can disable it with the parameter `suggest_commands=False`:\n\n{* docs_src/commands/index/tutorial005_py310.py hl[3] *}\n\nIf a user mistypes a command, they'll see a helpful suggestion:\n\n<div class=\"termy\">\n\n```console\n$ python main.py crate\n\n<font color=\"#C4A000\">Usage: </font>main.py [OPTIONS] COMMAND [ARGS]...\n<font color=\"#AAAAAA\">Try </font><font color=\"#22436D\">&apos;main.py </font><font color=\"#4C6A8A\"><b>--help</b></font><font color=\"#22436D\">&apos;</font><font color=\"#AAAAAA\"> for help.</font>\n<font color=\"#CC0000\">╭─ Error ───────────────────────────────────────────────────────────╮</font>\n<font color=\"#CC0000\">│</font> No such command &apos;crate&apos;. Did you mean &apos;create&apos;?                   <font color=\"#CC0000\">│</font>\n<font color=\"#CC0000\">╰───────────────────────────────────────────────────────────────────╯</font>\n```\n\n</div>\n\nIf there are multiple close matches, Typer will suggest them all. This feature uses Python's built-in `difflib.get_close_matches()` to find similar command names, making your CLI more user-friendly by helping users recover from typos.\n\n## Rich Markdown and Markup\n\nTyper installs **Rich** to allow for more formatting in the docstrings and the `help` parameter for *CLI arguments* and *CLI options*. You will see more about it below. 👇\n\n/// info\n\nYou can disable rich text formatting by setting `rich_markup_mode` to `None` for your specific app.\nAlternatively, you can disable it globally using an environmental variable `TYPER_USE_RICH` set to `False` or `0`.\n\n///\n\n### Rich Markup\n\nIf you set `rich_markup_mode=\"rich\"` when creating the `typer.Typer()` app (which is the default), you will be able to use <a href=\"https://rich.readthedocs.io/en/stable/markup.html\" class=\"external-link\" target=\"_blank\">Rich Console Markup</a> in the docstring, and even in the help for the *CLI arguments* and options:\n\n{* docs_src/commands/help/tutorial004_an_py310.py hl[5,11,15:17,22,25,28] *}\n\nWith that, you can use <a href=\"https://rich.readthedocs.io/en/stable/markup.html\" class=\"external-link\" target=\"_blank\">Rich Console Markup</a> to format the text in the docstring for the command `create`, make the word \"`create`\" bold and green, and even use an <a href=\"https://rich.readthedocs.io/en/stable/markup.html#emoji\" class=\"external-link\" target=\"_blank\">emoji</a>.\n\nYou can also use markup in the help for the `username` CLI Argument.\n\nAnd the same as before, the help text overwritten for the command `delete` can also use Rich Markup, the same in the CLI Argument and CLI Option.\n\nIf you run the program and check the help, you will see that **Typer** uses **Rich** internally to format the help.\n\nCheck the help for the `create` command:\n\n<div class=\"termy\">\n\n```console\n$ python main.py create --help\n\n<b> </b><font color=\"#F4BF75\"><b>Usage: </b></font><b>main.py create [OPTIONS] USERNAME                     </b>\n<b>                                                                     </b>\n <font color=\"#A6E22E\"><b>Create</b></font> a new <i>shiny</i> user. ✨\n This requires a <font color=\"#A5A5A1\"><u style=\"text-decoration-style:single\">username</u></font><font color=\"#A5A5A1\">.                                           </font>\n\n<font color=\"#A5A5A1\">╭─ Arguments ───────────────────────────────────────────────────────╮</font>\n<font color=\"#A5A5A1\">│ </font><font color=\"#F92672\">*</font>    username      <font color=\"#F4BF75\"><b>TEXT</b></font>  The username to be <font color=\"#A6E22E\">created</font>               │\n<font color=\"#A5A5A1\">│                          [default: None]                          │</font>\n<font color=\"#A5A5A1\">│                          </font><font color=\"#A6194C\">[required]                </font>               │\n<font color=\"#A5A5A1\">╰───────────────────────────────────────────────────────────────────╯</font>\n<font color=\"#A5A5A1\">╭─ Options ─────────────────────────────────────────────────────────╮</font>\n<font color=\"#A5A5A1\">│ </font><font color=\"#A1EFE4\"><b>--help</b></font>          Show this message and exit.                       │\n<font color=\"#A5A5A1\">╰───────────────────────────────────────────────────────────────────╯</font>\n```\n\n</div>\n\nAnd check the help for the `delete` command:\n\n<div class=\"termy\">\n\n```console\n$ python main.py delete --help\n\n<b> </b><font color=\"#F4BF75\"><b>Usage: </b></font><b>main.py delete [OPTIONS] USERNAME                     </b>\n<b>                                                                     </b>\n <font color=\"#F92672\"><b>Delete</b></font> a user with <i>USERNAME</i>.\n\n<font color=\"#A5A5A1\">╭─ Arguments ───────────────────────────────────────────────────────╮</font>\n<font color=\"#A5A5A1\">│ </font><font color=\"#F92672\">*</font>    username      <font color=\"#F4BF75\"><b>TEXT</b></font>  The username to be <font color=\"#F92672\">deleted</font>               │\n<font color=\"#A5A5A1\">│                          [default: None]                          │</font>\n<font color=\"#A5A5A1\">│                          </font><font color=\"#A6194C\">[required]                </font>               │\n<font color=\"#A5A5A1\">╰───────────────────────────────────────────────────────────────────╯</font>\n<font color=\"#A5A5A1\">╭─ Options ─────────────────────────────────────────────────────────╮</font>\n<font color=\"#A5A5A1\">│ </font><font color=\"#A1EFE4\"><b>--force</b></font>    <font color=\"#AE81FF\"><b>--no-force</b></font>      Force the <font color=\"#F92672\"><b>deletion</b></font> 💥                  │\n<font color=\"#A5A5A1\">│                            [default: no-force]                    │</font>\n<font color=\"#A5A5A1\">│ </font><font color=\"#A1EFE4\"><b>--help</b></font>                     Show this message and exit.            │\n<font color=\"#A5A5A1\">╰───────────────────────────────────────────────────────────────────╯</font>\n```\n\n</div>\n\n### Rich Markdown\n\nIf you set `rich_markup_mode=\"markdown\"` when creating the `typer.Typer()` app, you will be able to use Markdown in the docstring:\n\n{* docs_src/commands/help/tutorial005_an_py310.py hl[5,10,13:21,26,28:29] *}\n\nWith that, you can use Markdown to format the text in the docstring for the command `create`, make the word \"`create`\" bold, show a list of items, and even use an <a href=\"https://rich.readthedocs.io/en/stable/markup.html#emoji\" class=\"external-link\" target=\"_blank\">emoji</a>.\n\nAnd the same as before, the help text overwritten for the command `delete` can also use Markdown.\n\nCheck the help for the `create` command:\n\n<div class=\"termy\">\n\n```console\n$ python main.py create --help\n\n<b> </b><font color=\"#F4BF75\"><b>Usage: </b></font><b>main.py create [OPTIONS] USERNAME                     </b>\n<b>                                                                     </b>\n <b>Create</b> a new <i>shiny</i> user. ✨\n\n <font color=\"#F4BF75\"><b> • </b></font><font color=\"#A5A5A1\">Create a username                                                </font>\n <font color=\"#F4BF75\"><b> • </b></font><font color=\"#A5A5A1\">Show that the username is created                                </font>\n\n <font color=\"#F4BF75\">───────────────────────────────────────────────────────────────────</font>\n Learn more at the <font color=\"#44919F\">Typer docs website</font>\n\n<font color=\"#A5A5A1\">╭─ Arguments ───────────────────────────────────────────────────────╮</font>\n<font color=\"#A5A5A1\">│ </font><font color=\"#F92672\">*</font>    username      <font color=\"#F4BF75\"><b>TEXT</b></font>  The username to be <b>created</b>               │\n<font color=\"#A5A5A1\">│                          [default: None]                          │</font>\n<font color=\"#A5A5A1\">│                          </font><font color=\"#A6194C\">[required]                              </font> │\n<font color=\"#A5A5A1\">╰───────────────────────────────────────────────────────────────────╯</font>\n<font color=\"#A5A5A1\">╭─ Options ─────────────────────────────────────────────────────────╮</font>\n<font color=\"#A5A5A1\">│ </font><font color=\"#A1EFE4\"><b>--help</b></font>          Show this message and exit.                       │\n<font color=\"#A5A5A1\">╰───────────────────────────────────────────────────────────────────╯</font>\n```\n\n</div>\n\nAnd the same for the `delete` command:\n\n<div class=\"termy\">\n\n```console\n$ python main.py delete --help\n\n<b> </b><font color=\"#F4BF75\"><b>Usage: </b></font><b>main.py delete [OPTIONS] USERNAME                     </b>\n<b>                                                                     </b>\n <b>Delete</b> a user with <i>USERNAME</i>.\n\n<font color=\"#A5A5A1\">╭─ Arguments ───────────────────────────────────────────────────────╮</font>\n<font color=\"#A5A5A1\">│ </font><font color=\"#F92672\">*</font>    username      <font color=\"#F4BF75\"><b>TEXT</b></font>  The username to be <b>deleted</b>               │\n<font color=\"#A5A5A1\">│                          [default: None]                          │</font>\n<font color=\"#A5A5A1\">│                          </font><font color=\"#A6194C\">[required]                              </font> │\n<font color=\"#A5A5A1\">╰───────────────────────────────────────────────────────────────────╯</font>\n<font color=\"#A5A5A1\">╭─ Options ─────────────────────────────────────────────────────────╮</font>\n<font color=\"#A5A5A1\">│ </font><font color=\"#A1EFE4\"><b>--force</b></font>    <font color=\"#AE81FF\"><b>--no-force</b></font>      Force the <b>deletion</b> 💥                  │\n<font color=\"#A5A5A1\">│                            [default: no-force]                    │</font>\n<font color=\"#A5A5A1\">│ </font><font color=\"#A1EFE4\"><b>--help</b></font>                     Show this message and exit.            │\n<font color=\"#A5A5A1\">╰───────────────────────────────────────────────────────────────────╯</font>\n```\n\n</div>\n\n/// info\n\nNotice that in Markdown you cannot define colors. For colors you might prefer to use Rich markup.\n\n///\n\n## Help Panels\n\nIf you have many commands or CLI parameters, you might want to show their documentation in different panels when using the `--help` option.\n\nIf you installed <a href=\"https://rich.readthedocs.io/\" class=\"external-link\" target=\"_blank\">Rich</a> as described in [Printing and Colors](../printing.md){.internal-link target=_blank}, you can configure the panel to use for each command or CLI parameter.\n\n### Help Panels for Commands\n\nTo set the panel for a command you can pass the argument `rich_help_panel` with the name of the panel you want to use:\n\n{* docs_src/commands/help/tutorial006_py310.py hl[22,30,38,46] *}\n\nCommands without a panel will be shown in the default panel `Commands`, and the rest will be shown in the next panels:\n\n<div class=\"termy\">\n\n```console\n$ python main.py --help\n\n<b> </b><font color=\"#F4BF75\"><b>Usage: </b></font><b>main.py [OPTIONS] COMMAND [ARGS]...                   </b>\n<b>                                                                     </b>\n<font color=\"#A5A5A1\">╭─ Options ─────────────────────────────────────────────────────────╮</font>\n<font color=\"#A5A5A1\">│ </font><font color=\"#A1EFE4\"><b>--install-completion</b></font>          Install completion for the current  │\n<font color=\"#A5A5A1\">│                               shell.                              │</font>\n<font color=\"#A5A5A1\">│ </font><font color=\"#A1EFE4\"><b>--show-completion</b></font>             Show completion for the current     │\n<font color=\"#A5A5A1\">│                               shell, to copy it or customize the  │</font>\n<font color=\"#A5A5A1\">│                               installation.                       │</font>\n<font color=\"#A5A5A1\">│ </font><font color=\"#A1EFE4\"><b>--help</b></font>                        Show this message and exit.         │\n<font color=\"#A5A5A1\">╰───────────────────────────────────────────────────────────────────╯</font>\n<font color=\"#A5A5A1\">╭─ Commands ────────────────────────────────────────────────────────╮</font>\n<font color=\"#A5A5A1\">│ </font><font color=\"#A1EFE4\"><b>create          </b></font> <font color=\"#A6E22E\">Create</font> a new user. ✨                            │\n<font color=\"#A5A5A1\">│ </font><font color=\"#A1EFE4\"><b>delete          </b></font> <font color=\"#F92672\">Delete</font> a user. ❌                                │\n<font color=\"#A5A5A1\">╰───────────────────────────────────────────────────────────────────╯</font>\n<font color=\"#A5A5A1\">╭─ Utils and Configs ───────────────────────────────────────────────╮</font>\n<font color=\"#A5A5A1\">│ </font><font color=\"#A1EFE4\"><b>config  </b></font> <font color=\"#66D9EF\">Configure</font> the system. ⚙                                  │\n<font color=\"#A5A5A1\">│ </font><font color=\"#A1EFE4\"><b>sync    </b></font> <font color=\"#66D9EF\">Synchronize</font> the system or something fancy like that. ♻   │\n<font color=\"#A5A5A1\">╰───────────────────────────────────────────────────────────────────╯</font>\n<font color=\"#A5A5A1\">╭─ Help and Others ─────────────────────────────────────────────────╮</font>\n<font color=\"#A5A5A1\">│ </font><font color=\"#A1EFE4\"><b>help         </b></font> Get <font color=\"#F4BF75\">help</font> with the system. ❓                        │\n<font color=\"#A5A5A1\">│ </font><font color=\"#A1EFE4\"><b>report       </b></font> <font color=\"#F4BF75\">Report</font> an issue. ❗                                 │\n<font color=\"#A5A5A1\">╰───────────────────────────────────────────────────────────────────╯</font>\n```\n\n</div>\n\n### Help Panels for CLI Parameters\n\nThe same way, you can configure the panels for *CLI arguments* and *CLI options* with `rich_help_panel`.\n\nAnd of course, in the same application you can also set the `rich_help_panel` for commands.\n\n{* docs_src/commands/help/tutorial007_an_py310.py hl[14,20,26,36] *}\n\nThen if you run the application you will see all the *CLI parameters* in their respective panels.\n\n* First the ***CLI arguments*** that don't have a panel name set in a **default** one named \"`Arguments`\".\n* Next the ***CLI arguments*** with a **custom panel**. In this example named \"`Secondary Arguments`\".\n* After that, the ***CLI options*** that don't have a panel in a **default** one named \"`Options`\".\n* And finally, the ***CLI options*** with a **custom panel** set. In this example named \"`Additional Data`\".\n\nYou can check the `--help` option for the command `create`:\n\n<div class=\"termy\">\n\n```console\n$ python main.py create --help\n\n<b> </b><font color=\"#F4BF75\"><b>Usage: </b></font><b>main.py create [OPTIONS] USERNAME [LASTNAME]          </b>\n<b>                                                                     </b>\n <font color=\"#A6E22E\">Create</font> a new user. ✨\n\n<font color=\"#A5A5A1\">╭─ Arguments ───────────────────────────────────────────────────────╮</font>\n<font color=\"#A5A5A1\">│ </font><font color=\"#F92672\">*</font>    username      <font color=\"#F4BF75\"><b>TEXT</b></font>  The username to create [default: None]   │\n<font color=\"#A5A5A1\">│                          </font><font color=\"#A6194C\">[required]            </font>                   │\n<font color=\"#A5A5A1\">╰───────────────────────────────────────────────────────────────────╯</font>\n<font color=\"#A5A5A1\">╭─ Secondary Arguments ─────────────────────────────────────────────╮</font>\n<font color=\"#A5A5A1\">│   lastname      </font><font color=\"#A37F4E\"><b>[LASTNAME]</b></font>  The last name of the new user         │\n<font color=\"#A5A5A1\">╰───────────────────────────────────────────────────────────────────╯</font>\n<font color=\"#A5A5A1\">╭─ Options ─────────────────────────────────────────────────────────╮</font>\n<font color=\"#A5A5A1\">│ </font><font color=\"#A1EFE4\"><b>--force</b></font>    <font color=\"#AE81FF\"><b>--no-force</b></font>      Force the creation of the user         │\n<font color=\"#A5A5A1\">│                            [default: no-force]                    │</font>\n<font color=\"#A5A5A1\">│ </font><font color=\"#A1EFE4\"><b>--help</b></font>                     Show this message and exit.            │\n<font color=\"#A5A5A1\">╰───────────────────────────────────────────────────────────────────╯</font>\n<font color=\"#A5A5A1\">╭─ Additional Data ─────────────────────────────────────────────────╮</font>\n<font color=\"#A5A5A1\">│ </font><font color=\"#A1EFE4\"><b>--age</b></font>                   <font color=\"#F4BF75\"><b>INTEGER</b></font>  The age of the new user          │\n<font color=\"#A5A5A1\">│                                  [default: None]                  │</font>\n<font color=\"#A5A5A1\">│ </font><font color=\"#A1EFE4\"><b>--favorite-color</b></font>        <font color=\"#F4BF75\"><b>TEXT   </b></font>  The favorite color of the new    │\n<font color=\"#A5A5A1\">│                                  user                             │</font>\n<font color=\"#A5A5A1\">│                                  [default: None]                  │</font>\n<font color=\"#A5A5A1\">╰───────────────────────────────────────────────────────────────────╯</font>\n```\n\n</div>\n\nAnd of course, the `rich_help_panel` can be used in the same way for commands in the same application.\n\nAnd those panels will be shown when you use the main `--help` option.\n\n<div class=\"termy\">\n\n```console\n$ python main.py --help\n\n<b> </b><font color=\"#F4BF75\"><b>Usage: </b></font><b>main.py [OPTIONS] COMMAND [ARGS]...                   </b>\n<b>                                                                     </b>\n<font color=\"#A5A5A1\">╭─ Options ─────────────────────────────────────────────────────────╮</font>\n<font color=\"#A5A5A1\">│ </font><font color=\"#A1EFE4\"><b>--install-completion</b></font>          Install completion for the current  │\n<font color=\"#A5A5A1\">│                               shell.                              │</font>\n<font color=\"#A5A5A1\">│ </font><font color=\"#A1EFE4\"><b>--show-completion</b></font>             Show completion for the current     │\n<font color=\"#A5A5A1\">│                               shell, to copy it or customize the  │</font>\n<font color=\"#A5A5A1\">│                               installation.                       │</font>\n<font color=\"#A5A5A1\">│ </font><font color=\"#A1EFE4\"><b>--help</b></font>                        Show this message and exit.         │\n<font color=\"#A5A5A1\">╰───────────────────────────────────────────────────────────────────╯</font>\n<font color=\"#A5A5A1\">╭─ Commands ────────────────────────────────────────────────────────╮</font>\n<font color=\"#A5A5A1\">│ </font><font color=\"#A1EFE4\"><b>create          </b></font> <font color=\"#A6E22E\">Create</font> a new user. ✨                            │\n<font color=\"#A5A5A1\">╰───────────────────────────────────────────────────────────────────╯</font>\n<font color=\"#A5A5A1\">╭─ Utils and Configs ───────────────────────────────────────────────╮</font>\n<font color=\"#A5A5A1\">│ </font><font color=\"#A1EFE4\"><b>config         </b></font> <font color=\"#66D9EF\">Configure</font> the system. ⚙                           │\n<font color=\"#A5A5A1\">╰───────────────────────────────────────────────────────────────────╯</font>\n```\n\n</div>\n\nYou can see the custom panel for the commands for \"`Utils and Configs`\".\n\n## Epilog\n\nIf you need, you can also add an epilog section to the help of your commands:\n\n{* docs_src/commands/help/tutorial008_py310.py hl[6] *}\n\nAnd when you check the `--help` option it will look like:\n\n<div class=\"termy\">\n\n```console\n$ python main.py --help\n\n<b> </b><font color=\"#F4BF75\"><b>Usage: </b></font><b>main.py [OPTIONS] USERNAME                            </b>\n<b>                                                                     </b>\n <font color=\"#A6E22E\">Create</font> a new user. ✨\n\n<font color=\"#A5A5A1\">╭─ Arguments ───────────────────────────────────────────────────────╮</font>\n<font color=\"#A5A5A1\">│ </font><font color=\"#F92672\">*</font>    username      <font color=\"#F4BF75\"><b>TEXT</b></font>  [default: None] <font color=\"#A6194C\">[required]</font>               │\n<font color=\"#A5A5A1\">╰───────────────────────────────────────────────────────────────────╯</font>\n<font color=\"#A5A5A1\">╭─ Options ─────────────────────────────────────────────────────────╮</font>\n<font color=\"#A5A5A1\">│ </font><font color=\"#A1EFE4\"><b>--install-completion</b></font>          Install completion for the current  │\n<font color=\"#A5A5A1\">│                               shell.                              │</font>\n<font color=\"#A5A5A1\">│ </font><font color=\"#A1EFE4\"><b>--show-completion</b></font>             Show completion for the current     │\n<font color=\"#A5A5A1\">│                               shell, to copy it or customize the  │</font>\n<font color=\"#A5A5A1\">│                               installation.                       │</font>\n<font color=\"#A5A5A1\">│ </font><font color=\"#A1EFE4\"><b>--help</b></font>                        Show this message and exit.         │\n<font color=\"#A5A5A1\">╰───────────────────────────────────────────────────────────────────╯</font>\n\n Made with ❤ in <font color=\"#66D9EF\">Venus</font>\n```\n\n</div>\n"
  },
  {
    "path": "docs/tutorial/commands/index.md",
    "content": "# Commands\n\nWe have seen how to create a CLI program with possibly several *CLI options* and *CLI arguments*.\n\nBut **Typer** allows you to create CLI programs with several commands (also known as subcommands).\n\nFor example, the program `git` has several commands.\n\nOne command of `git` is `git push`. And `git push` in turn takes its own *CLI arguments* and *CLI options*.\n\nFor example:\n\n<div class=\"termy\">\n\n```console\n// The push command with no parameters\n$ git push\n\n---> 100%\n\n// The push command with one CLI option --set-upstream and 2 CLI arguments\n$ git push --set-upstream origin master\n\n---> 100%\n```\n\n</div>\n\nAnother command of `git` is `git pull`, it also has some *CLI parameters*.\n\nIt's like if the same big program `git` had several small programs inside.\n\n/// tip\n\nA command looks the same as a *CLI argument*, it's just some name without a preceding `--`. But commands have a predefined name, and are used to group different sets of functionalities into the same CLI application.\n\n///\n\n## Command or subcommand\n\nIt's common to call a CLI program a \"command\".\n\nBut when one of these programs have subcommands, those subcommands are also frequently called just \"commands\".\n\nHave that in mind so you don't get confused.\n\nHere I'll use **CLI application** or **program** to refer to the program you are building in Python with Typer, and **command** to refer to one of these \"subcommands\" of your program.\n\n## A CLI application with multiple commands\n\n**Typer** allows creating CLI applications with multiple commands/subcommands.\n\nNow that you know how to create an explicit `typer.Typer()` application and add one command, let's see how to add multiple commands.\n\nLet's say that we have a CLI application to manage users.\n\nWe'll have a command to `create` users and another command to `delete` them.\n\nTo begin, let's say it can only create and delete one single predefined user:\n\n{* docs_src/commands/index/tutorial002_py310.py hl[6,11] *}\n\nNow we have a CLI application with 2 commands, `create` and `delete`:\n\n<div class=\"termy\">\n\n```console\n// Check the help\n$ python main.py --help\n\nUsage: main.py [OPTIONS] COMMAND [ARGS]...\n\nOptions:\n  --install-completion  Install completion for the current shell.\n  --show-completion     Show completion for the current shell, to copy it or customize the installation.\n  --help                Show this message and exit.\n\nCommands:\n  create\n  delete\n\n// Test them\n$ python main.py create\n\nCreating user: Hiro Hamada\n\n$ python main.py delete\n\nDeleting user: Hiro Hamada\n\n// Now we have 2 commands! 🎉\n```\n\n</div>\n\nNotice that the help text now shows the 2 commands: `create` and `delete`.\n\n/// tip\n\nBy default, the names of the commands are generated from the function name.\n\n///\n\n## Show the help message if no command is given\n\nBy default, we need to specify `--help` to get the command's help page.\n\nHowever, by setting `no_args_is_help=True` when defining the `typer.Typer()` application, the help function will be shown whenever no argument is given:\n\n{* docs_src/commands/index/tutorial003_py310.py hl[3] *}\n\nNow we can run this:\n\n<div class=\"termy\">\n\n```console\n// Check the help without having to type --help\n$ python main.py\n\nUsage: main.py [OPTIONS] COMMAND [ARGS]...\n\nOptions:\n  --install-completion  Install completion for the current shell.\n  --show-completion     Show completion for the current shell, to copy it or customize the installation.\n  --help                Show this message and exit.\n\nCommands:\n  create\n  delete\n```\n\n</div>\n\n\n## Sorting of the commands\n\nNote that by design, **Typer** shows the commands in the order they've been declared.\n\nSo, if we take our original example, with `create` and `delete` commands, and reverse the order in the Python file:\n\n{* docs_src/commands/index/tutorial004_py310.py hl[7,12] *}\n\nThen we will see the `delete` command first in the help output:\n\n<div class=\"termy\">\n\n```console\n// Check the help\n$ python main.py --help\n\nUsage: main.py [OPTIONS] COMMAND [ARGS]...\n\nOptions:\n  --install-completion  Install completion for the current shell.\n  --show-completion     Show completion for the current shell, to copy it or customize the installation.\n  --help                Show this message and exit.\n\nCommands:\n  delete\n  create\n```\n\n</div>\n\n## Decorator Technical Details\n\nWhen you use `@app.command()` the function under the decorator is registered in the **Typer** application and is then used later by the application.\n\nBut Typer doesn't modify that function itself, the function is left as is.\n\nThat means that if your function is simple enough that you could create it without using `typer.Option()` or `typer.Argument()`, you could use the same function for a **Typer** application and a **FastAPI** application putting both decorators on top, or similar tricks.\n"
  },
  {
    "path": "docs/tutorial/commands/name.md",
    "content": "# Custom Command Name\n\nBy default, the command names are generated from the function name.\n\nSo, if your function is something like:\n\n```Python\ndef create(username: str):\n    ...\n```\n\nThen the command name will be `create`.\n\nBut if you already had a function called `create()` somewhere in your code, you would have to name your CLI function differently.\n\nAnd what if you wanted the command to still be named `create`?\n\nFor this, you can set the name of the command in the first parameter for the `@app.command()` decorator:\n\n{* docs_src/commands/name/tutorial001_py310.py hl[6,11] *}\n\nNow, even though the functions are named `cli_create_user()` and `cli_delete_user()`, the commands will still be named `create` and `delete`:\n\n<div class=\"termy\">\n\n```console\n$ python main.py --help\n\nUsage: main.py [OPTIONS] COMMAND [ARGS]...\n\nOptions:\n  --install-completion  Install completion for the current shell.\n  --show-completion     Show completion for the current shell, to copy it or customize the installation.\n  --help                Show this message and exit.\n\nCommands:\n  create\n  delete\n\n// Test it\n$ python main.py create Camila\n\nCreating user: Camila\n```\n\n</div>\n\nNote that any underscores in the function name will be replaced with dashes.\n\nSo if your function is something like:\n\n```Python\ndef create_user(username: str):\n    ...\n```\nThen the command name will be `create-user`.\n"
  },
  {
    "path": "docs/tutorial/commands/one-or-multiple.md",
    "content": "# One or Multiple Commands\n\nYou might have noticed that if you create a single command, as in the following example:\n\n{* docs_src/typer_app/tutorial001_py310.py hl[3,6,12] *}\n\n**Typer** is smart enough to create a CLI application with that single function as the main CLI application, not as a command/subcommand:\n\n<div class=\"termy\">\n\n```console\n// Without a CLI argument\n$ python main.py\n\nUsage: main.py [OPTIONS] NAME\nTry \"main.py --help\" for help.\n\nError: Missing argument 'NAME'.\n\n// With the NAME CLI argument\n$ python main.py Camila\n\nHello Camila\n\n// Asking for help\n$ python main.py\n\nUsage: main.py [OPTIONS] NAME\n\nOptions:\n  --install-completion  Install completion for the current shell.\n  --show-completion     Show completion for the current shell, to copy it or customize the installation.\n  --help                Show this message and exit.\n```\n\n</div>\n\n/// tip\n\nNotice that it doesn't show a command `main`, even though the function name is `main`.\n\n///\n\nBut if you add multiple commands, **Typer** will create one *CLI command* for each one of them:\n\n{* docs_src/commands/index/tutorial002_py310.py hl[6,11] *}\n\nHere we have 2 commands `create` and `delete`:\n\n<div class=\"termy\">\n\n```console\n// Check the help\n$ python main.py --help\n\nUsage: main.py [OPTIONS] COMMAND [ARGS]...\n\nOptions:\n  --install-completion  Install completion for the current shell.\n  --show-completion     Show completion for the current shell, to copy it or customize the installation.\n  --help                Show this message and exit.\n\nCommands:\n  create\n  delete\n\n// Test the commands\n$ python main.py create\n\nCreating user: Hiro Hamada\n\n$ python main.py delete\n\nDeleting user: Hiro Hamada\n```\n\n</div>\n\n## One command and one callback\n\nIf you want to create a CLI app with one single command but you still want it to be a command/subcommand you can just add a callback:\n\n{* docs_src/commands/one_or_multiple/tutorial001_py310.py hl[11,12,13] *}\n\nAnd now your CLI program will have a single command.\n\nCheck it:\n\n<div class=\"termy\">\n\n```console\n// Check the help\n$ python main.py --help\n\n// Notice the single command create\nUsage: main.py [OPTIONS] COMMAND [ARGS]...\n\nOptions:\n  --install-completion  Install completion for the current shell.\n  --show-completion     Show completion for the current shell, to copy it or customize the installation.\n  --help                Show this message and exit.\n\nCommands:\n  create\n\n// Try it\n$ python main.py create\n\nCreating user: Hiro Hamada\n```\n\n</div>\n\n## Using the callback to document\n\nNow that you are using a callback just to have a single command, you might as well use it to add documentation for your app:\n\n{* docs_src/commands/one_or_multiple/tutorial002_py310.py hl[11,12,13,14,15,16,17] *}\n\nAnd now the docstring from the callback will be used as the help text:\n\n<div class=\"termy\">\n\n```console\n$ python main.py --help\n\n// Notice the help text from the docstring\nUsage: main.py [OPTIONS] COMMAND [ARGS]...\n\n  Creates a single user Hiro Hamada.\n\n  In the next version it will create 5 more users.\n\nOptions:\n  --install-completion  Install completion for the current shell.\n  --show-completion     Show completion for the current shell, to copy it or customize the installation.\n  --help                Show this message and exit.\n\nCommands:\n  create\n\n// And it still works the same, the callback does nothing\n$ python main.py create\n\nCreating user: Hiro Hamada\n```\n\n</div>\n"
  },
  {
    "path": "docs/tutorial/commands/options.md",
    "content": "# Command CLI Options\n\nCommands can also have their own *CLI options*.\n\nIn fact, each command can have different *CLI arguments* and *CLI options*:\n\n{* docs_src/commands/options/tutorial001_an_py310.py hl[9,15:18,28:30,39] *}\n\nHere we have multiple commands, with different *CLI parameters*:\n\n* `create`:\n    * `username`: a *CLI argument*.\n* `delete`:\n    * `username`: a *CLI argument*.\n    * `--force`: a *CLI option*, if not provided, it's prompted.\n* `delete-all`:\n    * `--force`: a *CLI option*, if not provided, it's prompted.\n* `init`:\n    * Doesn't take any *CLI parameters*.\n\n<div class=\"termy\">\n\n```console\n// Check the help\npython main.py --help\n\nUsage: main.py [OPTIONS] COMMAND [ARGS]...\n\nOptions:\n  --install-completion  Install completion for the current shell.\n  --show-completion     Show completion for the current shell, to copy it or customize the installation.\n  --help                Show this message and exit.\n\nCommands:\n  create\n  delete\n  delete-all\n  init\n```\n\n</div>\n\n/// tip\n\nCheck the command `delete-all`, by default command names are generated from the function name, replacing `_` with `-`.\n\n///\n\nTest it:\n\n<div class=\"termy\">\n\n```console\n// Check the command create\n$ python main.py create Camila\n\nCreating user: Camila\n\n// Now test the command delete\n$ python main.py delete Camila\n\n# Are you sure you want to delete the user? [y/n]: $ y\n\nDeleting user: Camila\n\n$ python main.py delete Wade\n\n# Are you sure you want to delete the user? [y/n]: $ n\n\nOperation cancelled\n\n// And finally, the command delete-all\n// Notice it doesn't have CLI arguments, only a CLI option\n\n$ python main.py delete-all\n\n# Are you sure you want to delete ALL users? [y/n]: $ y\n\nDeleting all users\n\n$ python main.py delete-all\n\n# Are you sure you want to delete ALL users? [y/n]: $ n\n\nOperation cancelled\n\n// And if you pass the --force CLI option, it doesn't need to confirm\n\n$ python main.py delete-all --force\n\nDeleting all users\n\n// And init that doesn't take any CLI parameter\n$ python main.py init\n\nInitializing user database\n```\n\n</div>\n"
  },
  {
    "path": "docs/tutorial/exceptions.md",
    "content": "# Exceptions and Errors\n\nWhen your code has errors and you run it, it will show the error and an exception.\n\nTyper does some tricks to help you detect those errors quickly.\n\n## Example Broken App\n\nLet's take this example broken app:\n\n{* docs_src/exceptions/tutorial001_py310.py hl[8] *}\n\nThis code is broken because you can't sum a string and a number (`name + 3`).\n\n## Exceptions with Rich\n\n**Typer** will automatically use Rich to automatically show you nicely printed errors.\n\nIt will **omit** all the parts of the traceback (the chain of things that called your function) that come from the internal parts in Typer.\n\nSo, the error you see will be **much clearer** and simpler, to help you detect the problem in your code quickly:\n\n<div class=\"termy\">\n\n```console\n$ python main.py\n\n<font color=\"#F92672\">╭──────────────── </font><font color=\"#F92672\"><b>Traceback (most recent call last)</b></font><font color=\"#F92672\"> ────────────────╮</font>\n<font color=\"#F92672\">│</font> <font color=\"#A37F4E\">/home/user/code/superapp/</font><font color=\"#F4BF75\"><b>main.py</b></font>:<font color=\"#66D9EF\">8</font> in <font color=\"#A6E22E\">main</font>                        <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font>                                                                   <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font>    5                                                              <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font>    6 <font color=\"#FF00FF\">@app</font>.command()                                               <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font>    7 <font color=\"#66D9EF\">def</font> <font color=\"#A6E22E\">main</font>(name: <font color=\"#A1EFE4\">str</font> = <font color=\"#F4BF75\">&quot;morty&quot;</font>):                               <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font> <font color=\"#F92672\">❱ </font> 8 │   <font color=\"#A1EFE4\">print</font>(name + <font color=\"#66D9EF\">3</font>)                                          <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font>    9                                                              <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font>    10                                                             <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font>    11 <font color=\"#66D9EF\">if</font> <font color=\"#F92672\">__name__</font> == <font color=\"#F4BF75\">&quot;__main__&quot;</font>:                                  <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">╰───────────────────────────────────────────────────────────────────╯</font>\n<font color=\"#F92672\"><b>TypeError: </b></font>can only concatenate str <b>(</b>not <font color=\"#A6E22E\">&quot;int&quot;</font><b>)</b> to str\n```\n\n</div>\n\n## Exceptions without Rich\n\nYou can disable Rich globally using the environmental variable `TYPER_USE_RICH`.\n\nIn this case, Typer will still do some tricks to show you the information **as clearly as possible**:\n\n<div class=\"termy\">\n\n```console\n$ python main.py\n\nTraceback (most recent call last):\n\n  File \"main.py\", line 12, in <module>\n    app()\n\n  File \"main.py\", line 8, in main\n    print(name + 3)\n\nTypeError: can only concatenate str (not \"int\") to str\n```\n\n</div>\n\n## Show Local Variables for Detailed Debugging\n\nWhen using Rich, you can get more verbose output by printing the values of the <abbr title=\"a variable that lives only inside a function, its value is only visible inside of it\">local variables</abbr> as part of the error message.\n\nBy default, this setting is disabled (since Typer 0.23.0) to avoid showing **delicate information**, for example a **password**, a **key** or a **token**.\n\nIn these cases, it could be problematic if the automatic errors show the value in those local variables.\n\nThis would be relevant in particular if your CLI application is being run on some CI (continuous integration) system that is recording the logs.\n\nHowever, if you do want to enable the setting, you can set the parameter `pretty_exceptions_show_locals=True` when creating the `typer.Typer()` application:\n\n{* docs_src/exceptions/tutorial002_py310.py hl[3] *}\n\nNow, when using Rich, you will see the error with the local variables:\n\n<div class=\"termy\">\n\n```console\n$ python main.py\n\n<font color=\"#F92672\">╭──────────────── </font><font color=\"#F92672\"><b>Traceback (most recent call last)</b></font><font color=\"#F92672\"> ────────────────╮</font>\n<font color=\"#F92672\">│</font> <font color=\"#A37F4E\">/home/user/code/superapp/</font><font color=\"#F4BF75\"><b>main.py</b></font>:<font color=\"#66D9EF\">5</font> in <font color=\"#A6E22E\">main</font>                        <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font>                                                                   <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font>    2                                                              <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font>    3                                                              <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font>    4 <font color=\"#66D9EF\">def</font> <font color=\"#A6E22E\">main</font>(name: <font color=\"#A1EFE4\">str</font> = <font color=\"#F4BF75\">&quot;morty&quot;</font>):                               <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font> <font color=\"#F92672\">❱ </font> 5 │   <font color=\"#A1EFE4\">print</font>(name + <font color=\"#66D9EF\">3</font>)                                          <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font>    6                                                              <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font>    7                                                              <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font>    8 <font color=\"#66D9EF\">if</font> <font color=\"#F92672\">__name__</font> == <font color=\"#F4BF75\">&quot;__main__&quot;</font>:                                   <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font>                                                                   <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font> <font color=\"#F4BF75\">╭──── locals ────╮</font>                                                <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font> <font color=\"#F4BF75\">│</font> name = <font color=\"#F4BF75\">&apos;morty&apos;</font> <font color=\"#F4BF75\">│</font>                                                <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font> <font color=\"#F4BF75\">╰────────────────╯</font>                                                <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">╰───────────────────────────────────────────────────────────────────╯</font>\n<font color=\"#F92672\"><b>TypeError: </b></font>can only concatenate str <b>(</b>not <font color=\"#A6E22E\">&quot;int&quot;</font><b>)</b> to str\n```\n\n</div>\n\nBeing able to see the values of local variables is very **helpful** to diagnose, **debug**, and fix problems.\n\nBut you should only enable it if you're not dealing with delicate information.\n\n## Disable Short Output\n\nIf you want to show the full exception, including the internal parts in Typer, you can use the parameter `pretty_exceptions_short=False`:\n\n{* docs_src/exceptions/tutorial003_py310.py hl[3] *}\n\nNow when you run it, you will see the whole output:\n\n<div class=\"termy\">\n\n```console\n$ python main.py\n\n<font color=\"#F92672\">╭──────────────── </font><font color=\"#F92672\"><b>Traceback (most recent call last)</b></font><font color=\"#F92672\"> ────────────────╮</font>\n<font color=\"#F92672\">│</font> <font color=\"#A37F4E\">/home/user/code/superapp/</font><font color=\"#F4BF75\"><b>main.py</b></font>:<font color=\"#66D9EF\">12</font> in <font color=\"#A6E22E\">&lt;module&gt;</font>                   <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font>                                                                   <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font>    9                                                              <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font>   10                                                              <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font>   11 <font color=\"#66D9EF\">if</font> <font color=\"#F92672\">__name__</font> == <font color=\"#F4BF75\">&quot;__main__&quot;</font>:                                   <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font> <font color=\"#F92672\">❱ </font>12 │   app()                                                    <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font>   13                                                              <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font>                                                                   <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font> <font color=\"#F4BF75\">╭─────────────────────────── locals ────────────────────────────╮</font> <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font> <font color=\"#F4BF75\">│</font> <font color=\"#A6194C\">__annotations__</font> = <b>{}</b>                                          <font color=\"#F4BF75\">│</font> <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font> <font color=\"#F4BF75\">│</font>    <font color=\"#A6194C\">__builtins__</font> = <b>&lt;</b><font color=\"#AE81FF\"><b>module</b></font> <font color=\"#F4BF75\">&apos;builtins&apos;</font> <b>(</b>built-in<b>)&gt;</b>              <font color=\"#F4BF75\">│</font> <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font> <font color=\"#F4BF75\">│</font>      <font color=\"#A6194C\">__cached__</font> = <font color=\"#66D9EF\">None</font>                                        <font color=\"#F4BF75\">│</font> <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font> <font color=\"#F4BF75\">│</font>         <font color=\"#A6194C\">__doc__</font> = <font color=\"#66D9EF\">None</font>                                        <font color=\"#F4BF75\">│</font> <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font> <font color=\"#F4BF75\">│</font>        <font color=\"#A6194C\">__file__</font> = <font color=\"#F4BF75\">&apos;main.py&apos;</font>                                   <font color=\"#F4BF75\">│</font> <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font> <font color=\"#F4BF75\">│</font>      <font color=\"#A6194C\">__loader__</font> = <b>&lt;</b><font color=\"#AE81FF\"><b>_frozen_importlib_external.SourceFileLoad…</b></font> <font color=\"#F4BF75\">│</font> <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font> <font color=\"#F4BF75\">│</font>                   object at <font color=\"#66D9EF\">0x7f047db1c050</font><b>&gt;</b>                   <font color=\"#F4BF75\">│</font> <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font> <font color=\"#F4BF75\">│</font>        <font color=\"#A6194C\">__name__</font> = <font color=\"#F4BF75\">&apos;__main__&apos;</font>                                  <font color=\"#F4BF75\">│</font> <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font> <font color=\"#F4BF75\">│</font>     <font color=\"#A6194C\">__package__</font> = <font color=\"#66D9EF\">None</font>                                        <font color=\"#F4BF75\">│</font> <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font> <font color=\"#F4BF75\">│</font>        <font color=\"#A6194C\">__spec__</font> = <font color=\"#66D9EF\">None</font>                                        <font color=\"#F4BF75\">│</font> <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font> <font color=\"#F4BF75\">│</font>             app = <b>&lt;</b><font color=\"#AE81FF\"><b>typer.main.Typer</b></font> object at <font color=\"#66D9EF\">0x7f047db51d90</font><b>&gt;</b> <font color=\"#F4BF75\">│</font> <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font> <font color=\"#F4BF75\">│</font>            main = <b>&lt;</b><font color=\"#AE81FF\"><b>function</b></font> main at <font color=\"#66D9EF\">0x7f047db56830</font><b>&gt;</b>           <font color=\"#F4BF75\">│</font> <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font> <font color=\"#F4BF75\">│</font>           typer = <b>&lt;</b><font color=\"#AE81FF\"><b>module</b></font> <font color=\"#F4BF75\">&apos;typer&apos;</font> from                        <font color=\"#F4BF75\">│</font> <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font> <font color=\"#F4BF75\">│</font>                   <font color=\"#F4BF75\">&apos;/home/user/code/superapp/env/lib/python3.…</font> <font color=\"#F4BF75\">│</font> <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font> <font color=\"#F4BF75\">╰───────────────────────────────────────────────────────────────╯</font> <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font>                                                                   <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font> <font color=\"#A37F4E\">/home/user/code/superapp/env/lib/python3.7/site-packages/typer/</font><font color=\"#F4BF75\"><b>ma</b></font> <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font> <font color=\"#F4BF75\"><b>in.py</b></font>:<font color=\"#66D9EF\">328</font> in <font color=\"#A6E22E\">__call__</font>                                             <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font>                                                                   <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font> <font color=\"#A37F4E\">/home/user/code/superapp/env/lib/python3.7/site-packages/typer/</font><font color=\"#F4BF75\"><b>ma</b></font> <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font> <font color=\"#F4BF75\"><b>in.py</b></font>:<font color=\"#66D9EF\">311</font> in <font color=\"#A6E22E\">__call__</font>                                             <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font>                                                                   <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font> <font color=\"#A37F4E\">/home/user/code/superapp/env/lib/python3.7/site-packages/click/</font><font color=\"#F4BF75\"><b>co</b></font> <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font> <font color=\"#F4BF75\"><b>re.py</b></font>:<font color=\"#66D9EF\">1130</font> in <font color=\"#A6E22E\">__call__</font>                                            <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font>                                                                   <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font> <font color=\"#A37F4E\">/home/user/code/superapp/env/lib/python3.7/site-packages/typer/</font><font color=\"#F4BF75\"><b>co</b></font> <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font> <font color=\"#F4BF75\"><b>re.py</b></font>:<font color=\"#66D9EF\">723</font> in <font color=\"#A6E22E\">main</font>                                                 <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font>                                                                   <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font> <font color=\"#A37F4E\">/home/user/code/superapp/env/lib/python3.7/site-packages/typer/</font><font color=\"#F4BF75\"><b>co</b></font> <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font> <font color=\"#F4BF75\"><b>re.py</b></font>:<font color=\"#66D9EF\">216</font> in <font color=\"#A6E22E\">_main</font>                                                <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font>                                                                   <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font> <font color=\"#A37F4E\">/home/user/code/superapp/env/lib/python3.7/site-packages/click/</font><font color=\"#F4BF75\"><b>co</b></font> <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font> <font color=\"#F4BF75\"><b>re.py</b></font>:<font color=\"#66D9EF\">1404</font> in <font color=\"#A6E22E\">invoke</font>                                              <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font>                                                                   <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font> <font color=\"#A37F4E\">/home/user/code/superapp/env/lib/python3.7/site-packages/click/</font><font color=\"#F4BF75\"><b>co</b></font> <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font> <font color=\"#F4BF75\"><b>re.py</b></font>:<font color=\"#66D9EF\">760</font> in <font color=\"#A6E22E\">invoke</font>                                               <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font>                                                                   <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font> <font color=\"#A37F4E\">/home/user/code/superapp/env/lib/python3.7/site-packages/typer/</font><font color=\"#F4BF75\"><b>ma</b></font> <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font> <font color=\"#F4BF75\"><b>in.py</b></font>:<font color=\"#66D9EF\">683</font> in <font color=\"#A6E22E\">wrapper</font>                                              <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font>                                                                   <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font> <font color=\"#A37F4E\">/home/user/code/superapp/</font><font color=\"#F4BF75\"><b>main.py</b></font>:<font color=\"#66D9EF\">8</font> in <font color=\"#A6E22E\">main</font>                        <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font>                                                                   <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font>    5                                                              <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font>    6 <font color=\"#AE81FF\"><b>@app</b></font>.command()                                               <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font>    7 <font color=\"#66D9EF\">def</font> <font color=\"#A6E22E\">main</font>(name: <font color=\"#A1EFE4\">str</font> = <font color=\"#F4BF75\">&quot;morty&quot;</font>):                               <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font> <font color=\"#F92672\">❱ </font> 8 │   <font color=\"#A1EFE4\">print</font>(name + <font color=\"#66D9EF\">3</font>)                                          <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font>    9                                                              <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font>   10                                                              <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font>   11 <font color=\"#66D9EF\">if</font> <font color=\"#F92672\">__name__</font> == <font color=\"#F4BF75\">&quot;__main__&quot;</font>:                                   <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font>                                                                   <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font> <font color=\"#F4BF75\">╭──── locals ────╮</font>                                                <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font> <font color=\"#F4BF75\">│</font> name = <font color=\"#F4BF75\">&apos;morty&apos;</font> <font color=\"#F4BF75\">│</font>                                                <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">│</font> <font color=\"#F4BF75\">╰────────────────╯</font>                                                <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">╰───────────────────────────────────────────────────────────────────╯</font>\n<font color=\"#F92672\"><b>TypeError: </b></font>can only concatenate str <b>(</b>not <font color=\"#A6E22E\">&quot;int&quot;</font><b>)</b> to str\n```\n\n</div>\n\n## Disable Pretty Exceptions\n\nYou can also entirely disable pretty exceptions with the parameter `pretty_exceptions_enable=False`:\n\n{* docs_src/exceptions/tutorial004_py310.py hl[3] *}\n\nAnd now you will see the full standard exception as with any other Python program:\n\n<div class=\"termy\">\n\n```console\n$ python main.py\n\nTraceback (most recent call last):\n  File \"main.py\", line 12, in <module>\n    app()\n  File \"/home/user/code/superapp/env/lib/python3.7/site-packages/typer/main.py\", line 328, in __call__\n    raise e\n  File \"/home/user/code/superapp/env/lib/python3.7/site-packages/typer/main.py\", line 311, in __call__\n    return get_command(self)(*args, **kwargs)\n  File \"/home/user/code/superapp/env/lib/python3.7/site-packages/click/core.py\", line 1130, in __call__\n    return self.main(*args, **kwargs)\n  File \"/home/user/code/superapp/env/lib/python3.7/site-packages/typer/core.py\", line 723, in main\n    **extra,\n  File \"/home/user/code/superapp/env/lib/python3.7/site-packages/typer/core.py\", line 216, in _main\n    rv = self.invoke(ctx)\n  File \"/home/user/code/superapp/env/lib/python3.7/site-packages/click/core.py\", line 1404, in invoke\n    return ctx.invoke(self.callback, **ctx.params)\n  File \"/home/user/code/superapp/env/lib/python3.7/site-packages/click/core.py\", line 760, in invoke\n    return __callback(*args, **kwargs)\n  File \"/home/user/code/superapp/env/lib/python3.7/site-packages/typer/main.py\", line 683, in wrapper\n    return callback(**use_params)  # type: ignore\n  File \"main.py\", line 8, in main\n    print(name + 3)\nTypeError: can only concatenate str (not \"int\") to str\n```\n\n</div>\n\nYou could also achieve the same with the environment variable `TYPER_STANDARD_TRACEBACK=1` (or by setting the deprecated variable `_TYPER_STANDARD_TRACEBACK=1`).\n\nThis will work for any other Typer program too, in case you need to debug a problem in a Typer program made by someone else:\n\n<div class=\"termy\">\n\n```console\nexport TYPER_STANDARD_TRACEBACK=1\n$ python main.py\n\n\nTraceback (most recent call last):\n  File \"main.py\", line 12, in <module>\n    app()\n  File \"/home/user/code/superapp/env/lib/python3.7/site-packages/typer/main.py\", line 328, in __call__\n    raise e\n  File \"/home/user/code/superapp/env/lib/python3.7/site-packages/typer/main.py\", line 311, in __call__\n    return get_command(self)(*args, **kwargs)\n  File \"/home/user/code/superapp/env/lib/python3.7/site-packages/click/core.py\", line 1130, in __call__\n    return self.main(*args, **kwargs)\n  File \"/home/user/code/superapp/env/lib/python3.7/site-packages/typer/core.py\", line 723, in main\n    **extra,\n  File \"/home/user/code/superapp/env/lib/python3.7/site-packages/typer/core.py\", line 216, in _main\n    rv = self.invoke(ctx)\n  File \"/home/user/code/superapp/env/lib/python3.7/site-packages/click/core.py\", line 1404, in invoke\n    return ctx.invoke(self.callback, **ctx.params)\n  File \"/home/user/code/superapp/env/lib/python3.7/site-packages/click/core.py\", line 760, in invoke\n    return __callback(*args, **kwargs)\n  File \"/home/user/code/superapp/env/lib/python3.7/site-packages/typer/main.py\", line 683, in wrapper\n    return callback(**use_params)  # type: ignore\n  File \"main.py\", line 8, in main\n    print(name + 3)\nTypeError: can only concatenate str (not \"int\") to str\n```\n\n</div>\n"
  },
  {
    "path": "docs/tutorial/first-steps.md",
    "content": "# First Steps\n\n## The simplest example\n\nThe simplest **Typer** file could look like this:\n\n{* docs_src/first_steps/tutorial001_py310.py *}\n\nCopy that to a file `main.py`.\n\nTest it:\n\n<div class=\"termy\">\n\n```console\n$ python main.py\n\nHello World\n\n// It just prints \"Hello World\".\n\n// Now check the --help\n$ python main.py --help\n\n<b> </b><font color=\"#F4BF75\"><b>Usage: </b></font><b>main.py [OPTIONS]                            </b>\n<b>                                                     </b>\n<font color=\"#A5A5A1\">╭─ Options ─────────────────────────────────────────╮</font>\n<font color=\"#A5A5A1\">│ </font><font color=\"#A1EFE4\"><b>--help</b></font>                        Show this message   │\n<font color=\"#A5A5A1\">│                               and exit.           │</font>\n<font color=\"#A5A5A1\">╰───────────────────────────────────────────────────╯</font>\n```\n\n</div>\n\n...but this program is still not very useful. Let's extend it.\n\n## What is a **CLI argument**\n\nHere we will use the word **CLI argument** to refer to **CLI parameters** passed in some specific order to the CLI application. By default, they are *required*.\n\nIf you go to your terminal and type:\n\n<div class=\"termy\">\n\n```bash\n$ ls ./myproject\n\nfirst-steps.md  intro.md\n```\n\n</div>\n\n`ls` will show the contents of the directory `./myproject`.\n\n* `ls` is the *program* (or \"command\", \"CLI app\").\n* `./myproject` is a *CLI argument*, in this case it refers to the path of a directory.\n\nThey are a bit different from **CLI options** that you will see later below.\n\n## Add a CLI argument\n\nUpdate the previous example with an argument `name`:\n\n{* docs_src/first_steps/tutorial002_py310.py hl[4,5] *}\n\n<div class=\"termy\">\n\n```console\n\n$ python main.py\n\n// If you run it without the argument, it shows a nice error\n<font color=\"#F4BF75\">Usage: </font>main.py [OPTIONS] NAME\n<font color=\"#A5A5A1\">Try </font><font color=\"#44919F\">&apos;main.py </font><font color=\"#44919F\"><b>--help</b></font><font color=\"#44919F\">&apos;</font><font color=\"#A5A5A1\"> for help.</font>\n<font color=\"#F92672\">╭─ Error ───────────────────────────────────────────╮</font>\n<font color=\"#F92672\">│</font> Missing argument &apos;NAME&apos;.                          <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">╰───────────────────────────────────────────────────╯</font>\n\n// Now pass that NAME CLI argument\n$ python main.py Camila\n\nHello Camila\n\n// Here \"Camila\" is the CLI argument\n\n// To pass a name with spaces for the same CLI argument, use quotes\n$ python main.py \"Camila Gutiérrez\"\n\nHello Camila Gutiérrez\n```\n\n</div>\n\n/// tip\n\nIf you need to pass a single value that contains spaces to a *CLI argument*, use quotes (`\"`) around it.\n\n///\n\n## Two CLI arguments\n\nNow let's say we want to have the name and last name separated.\n\nSo, extend that to have 2 arguments, `name` and `lastname`:\n\n{* docs_src/first_steps/tutorial003_py310.py hl[4,5] *}\n\n<div class=\"termy\">\n\n```console\n// Check the main --help\n$ python main.py --help\n\n<font color=\"#F4BF75\">Usage: </font>main.py [OPTIONS] NAME\n<font color=\"#A5A5A1\">Try </font><font color=\"#44919F\">&apos;main.py </font><font color=\"#44919F\"><b>--help</b></font><font color=\"#44919F\">&apos;</font><font color=\"#A5A5A1\"> for help.</font>\n<font color=\"#F92672\">╭─ Error ───────────────────────────────────────────╮</font>\n<font color=\"#F92672\">│</font> Missing argument &apos;NAME&apos;.                          <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">╰───────────────────────────────────────────────────╯</font>\n\n<font color=\"#A1EFE4\"><b>typer</b></font> on <font color=\"#AE81FF\"><b> richify</b></font> <font color=\"#F92672\"><b>[»!?] </b></font>via <font color=\"#F4BF75\"><b>🐍 v3.7.5 (env3.7)</b></font>\n<font color=\"#F92672\"><b>❯</b></font> <font color=\"#A6E22E\">python</font> <u style=\"text-decoration-style:single\">main.py</u>\n<font color=\"#F4BF75\">Usage: </font>main.py [OPTIONS] NAME LASTNAME\n<font color=\"#A5A5A1\">Try </font><font color=\"#44919F\">&apos;main.py </font><font color=\"#44919F\"><b>--help</b></font><font color=\"#44919F\">&apos;</font><font color=\"#A5A5A1\"> for help.</font>\n<font color=\"#F92672\">╭─ Error ───────────────────────────────────────────╮</font>\n<font color=\"#F92672\">│</font> Missing argument &apos;NAME&apos;.                          <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">╰───────────────────────────────────────────────────╯</font>\n\n// There are now 2 CLI arguments, name and lastname\n\n// Now pass a single name argument\n$ python main.py Camila\n\n<font color=\"#F4BF75\">Usage: </font>main.py [OPTIONS] NAME LASTNAME\n<font color=\"#A5A5A1\">Try </font><font color=\"#44919F\">&apos;main.py </font><font color=\"#44919F\"><b>--help</b></font><font color=\"#44919F\">&apos;</font><font color=\"#A5A5A1\"> for help.</font>\n<font color=\"#F92672\">╭─ Error ───────────────────────────────────────────╮</font>\n<font color=\"#F92672\">│</font> Missing argument &apos;LASTNAME&apos;.                      <font color=\"#F92672\">│</font>\n<font color=\"#F92672\">╰───────────────────────────────────────────────────╯</font>\n\n// These 2 arguments are required, so, pass both:\n$ python main.py Camila Gutiérrez\n\nHello Camila Gutiérrez\n```\n\n</div>\n\n/// tip\n\nNotice that the order is important. The last name has to go after the first name.\n\nIf you called it with:\n\n```\n$ python main.py Gutiérrez Camila\n```\n\nyour app wouldn't have a way to know which is the `name` and which the `lastname`. It expects the first *CLI argument* to be the `name` and the second *CLI argument* to be the `lastname`.\n\n///\n\n## What is a **CLI option**\n\nHere we will use the word **CLI option** to refer to *CLI parameters* passed to the CLI application with a specific name. For example, if you go to your terminal and type:\n\n<div class=\"termy\">\n\n```console\n$ ls ./myproject --size\n\n12 first-steps.md   4 intro.md\n```\n\n</div>\n\n`ls` will show the contents of the directory `./myproject` with their `size`.\n\n* `ls` is the *program* (or \"command\", \"CLI app\").\n* `./myproject` is a *CLI argument*.\n* `--size` is an optional *CLI option*.\n\nThe program knows it has to show the size because it sees `--size`, not because of the order.\n\nA *CLI option* like `--size` doesn't depend on the order like a *CLI argument*.\n\nSo, if you put the `--size` *before* the *CLI argument*, it still works (in fact, that's the most common way of doing it):\n\n<div class=\"termy\">\n\n```console\n$ ls --size ./myproject\n\n12 first-steps.md   4 intro.md\n```\n\n</div>\n\nThe main visual difference between a *CLI option* and a *CLI argument* is that the *CLI option* has `--` prepended to the name, like in \"`--size`\".\n\nA *CLI option* doesn't depend on the order because it has a predefined name (here it's `--size`). This is because the CLI app is looking specifically for a literal `--size` parameter (also known as \"flag\" or \"switch\"), with that specific \"name\" (here the specific name is \"`--size`\"). The CLI app will check if you typed it or not, it will be actively looking for `--size` even if you didn't type it (to check if it's there or not).\n\nIn contrast, the CLI app is not actively looking for the *CLI argument* with a text \"`./myproject`\", it has no way to know if you would type `./myproject` or `./my-super-awesome-project` or anything else. It's just waiting to get whatever you give it. The only way to know that you refer to a specific *CLI argument* is because of the order. The same way that it knows that the first *CLI argument* was the `name` and the second was the `lastname`, but if you mixed the order, it wouldn't be able to handle it.\n\nInstead, with a *CLI option*, the order doesn't matter.\n\nAlso, by default, a *CLI option* is *optional* (not *required*).\n\nSo, by default:\n\n* A *CLI argument* is **required**\n* A *CLI option* is **optional**\n\nBut the *required* and *optional* defaults can be changed.\n\nSo, the main and **most important** difference is that:\n\n* *CLI options* **start with `--`** and don't depend on the order\n* *CLI arguments* depend on the **sequence order**\n\n/// tip\n\nIn this example above the *CLI option* `--size` is just a \"flag\" or \"switch\" that will contain a boolean value, `True` or `False`, depending on if it was added to the command or not.\n\nThis one doesn't receive any values. But *CLI options* can also receive values like *CLI arguments*. You'll see how later.\n\n///\n\n## Add one *CLI option*\n\nNow add a `--formal` *CLI option*:\n\n{* docs_src/first_steps/tutorial004_py310.py hl[4,5] *}\n\nHere `formal` is a `bool` that is `False` by default.\n\n<div class=\"termy\">\n\n```console\n// Get the help\n$ python main.py --help\n\n<b> </b><font color=\"#F4BF75\"><b>Usage: </b></font><b>main.py [OPTIONS] NAME LASTNAME                            </b>\n<b>                                                                   </b>\n<font color=\"#A5A5A1\">╭─ Arguments ─────────────────────────────────────────────────────╮</font>\n<font color=\"#A5A5A1\">│ </font><font color=\"#F92672\">*</font>    name          <font color=\"#F4BF75\"><b>TEXT</b></font>  [default: None] <font color=\"#A6194C\">[required]</font>             │\n<font color=\"#A5A5A1\">│ </font><font color=\"#F92672\">*</font>    lastname      <font color=\"#F4BF75\"><b>TEXT</b></font>  [default: None] <font color=\"#A6194C\">[required]</font>             │\n<font color=\"#A5A5A1\">╰─────────────────────────────────────────────────────────────────╯</font>\n<font color=\"#A5A5A1\">╭─ Options ───────────────────────────────────────────────────────╮</font>\n<font color=\"#A5A5A1\">│ </font><font color=\"#A1EFE4\"><b>--formal</b></font>                <font color=\"#AE81FF\"><b>--no-formal</b></font>      [default: no-formal]   │\n<font color=\"#A5A5A1\">│ </font><font color=\"#A1EFE4\"><b>--help</b></font>                                   Show this message and  │\n<font color=\"#A5A5A1\">│                                          exit.                  │</font>\n<font color=\"#A5A5A1\">╰─────────────────────────────────────────────────────────────────╯</font>\n```\n\n</div>\n\n/// tip\n\nNotice that it automatically creates a `--formal` and a `--no-formal` because it detected that `formal` is a `bool`.\n\n///\n\nNow call it normally:\n\n<div class=\"termy\">\n\n```console\n$ python main.py Camila Gutiérrez\n\nHello Camila Gutiérrez\n\n// But if you pass --formal\n$ python main.py Camila Gutiérrez --formal\n\nGood day Ms. Camila Gutiérrez.\n\n// And as --formal is a CLI option you can put it anywhere in this command\n$ python main.py Camila --formal Gutiérrez\n\nGood day Ms. Camila Gutiérrez.\n\n$ python main.py --formal Camila Gutiérrez\n\nGood day Ms. Camila Gutiérrez.\n```\n\n</div>\n\n## A *CLI option* with a value\n\nTo convert the `lastname` from a *CLI argument* to a *CLI option*, give it a default value of `\"\"`:\n\n{* docs_src/first_steps/tutorial005_py310.py hl[4] *}\n\nAs `lastname` now has a default value of `\"\"` (an empty string) it is no longer required in the function, and **Typer** will now by default make it an optional *CLI option*.\n\n<div class=\"termy\">\n\n```console\n$ python main.py --help\n\n<b> </b><font color=\"#F4BF75\"><b>Usage: </b></font><b>main.py [OPTIONS] NAME                                       </b>\n<b>                                                                     </b>\n<font color=\"#A5A5A1\">╭─ Arguments ───────────────────────────────────────────────────────╮</font>\n<font color=\"#A5A5A1\">│ </font><font color=\"#F92672\">*</font>    name      <font color=\"#F4BF75\"><b>TEXT</b></font>  [default: None] <font color=\"#A6194C\">[required]</font>                   │\n<font color=\"#A5A5A1\">╰───────────────────────────────────────────────────────────────────╯</font>\n<font color=\"#A5A5A1\">╭─ Options ─────────────────────────────────────────────────────────╮</font>\n<font color=\"#A5A5A1\">│ </font><font color=\"#A1EFE4\"><b>--lastname</b></font>                             <font color=\"#F4BF75\"><b>TEXT</b></font>                       │\n<font color=\"#A5A5A1\">│ </font><font color=\"#A1EFE4\"><b>--formal</b></font>                <font color=\"#AE81FF\"><b>--no-formal</b></font>    <font color=\"#F4BF75\"><b>    </b></font>  [default: no-formal] │\n<font color=\"#A5A5A1\">│ </font><font color=\"#A1EFE4\"><b>--help</b></font>                                 <font color=\"#F4BF75\"><b>    </b></font>  Show this message    │\n<font color=\"#A5A5A1\">│                                              and exit.            │</font>\n<font color=\"#A5A5A1\">╰───────────────────────────────────────────────────────────────────╯</font>\n```\n\n</div>\n\n/// tip\n\nNotice the `--lastname`, and notice that it takes a textual value.\n\nA *CLI option* with a value like `--lastname` (contrary to a *CLI option* without a value, a `bool` flag, like `--formal` or `--size`) takes as its value whatever is at the *right side* of the *CLI option*.\n\n///\n\n<div class=\"termy\">\n\n```console\n// Call it without a --lastname\n$ python main.py Camila\n\nHello Camila\n\n// Pass the --lastname\n$ python main.py Camila --lastname Gutiérrez\n\nHello Camila Gutiérrez\n```\n\n</div>\n\n/// tip\n\nNotice that \"`Gutiérrez`\" is at the right side of `--lastname`. A *CLI option* with a value takes as its value whatever is at the *right side*.\n\n///\n\nAnd as `--lastname` is now a *CLI option* that doesn't depend on the order, you can pass it before the name:\n\n<div class=\"termy\">\n\n```console\n$ python main.py --lastname Gutiérrez Camila\n\n// and it will still work normally\nHello Camila Gutiérrez\n```\n\n</div>\n\n## Document your CLI app\n\nIf you add a <abbr title=\"a multi-line string as the first expression inside a function (not assigned to any variable) used for documentation\">docstring</abbr> to your function it will be used in the help text:\n\n{* docs_src/first_steps/tutorial006_py310.py hl[5,6,7,8,9] *}\n\nNow see it with the `--help` option:\n\n<div class=\"termy\">\n\n```console\n$ python main.py --help\n\n<b> </b><font color=\"#F4BF75\"><b>Usage: </b></font><b>main.py [OPTIONS] NAME                                       </b>\n<b>                                                                     </b>\n Say hi to NAME, optionally with a <font color=\"#A1EFE4\"><b>--lastname</b></font>.\n If <font color=\"#6B9F98\"><b>--formal</b></font><font color=\"#A5A5A1\"> is used, say hi very formally.                          </font>\n\n<font color=\"#A5A5A1\">╭─ Arguments ───────────────────────────────────────────────────────╮</font>\n<font color=\"#A5A5A1\">│ </font><font color=\"#F92672\">*</font>    name      <font color=\"#F4BF75\"><b>TEXT</b></font>  [default: None] <font color=\"#A6194C\">[required]</font>                   │\n<font color=\"#A5A5A1\">╰───────────────────────────────────────────────────────────────────╯</font>\n<font color=\"#A5A5A1\">╭─ Options ─────────────────────────────────────────────────────────╮</font>\n<font color=\"#A5A5A1\">│ </font><font color=\"#A1EFE4\"><b>--lastname</b></font>                             <font color=\"#F4BF75\"><b>TEXT</b></font>                       │\n<font color=\"#A5A5A1\">│ </font><font color=\"#A1EFE4\"><b>--formal</b></font>                <font color=\"#AE81FF\"><b>--no-formal</b></font>    <font color=\"#F4BF75\"><b>    </b></font>  [default: no-formal] │\n<font color=\"#A5A5A1\">│ </font><font color=\"#A1EFE4\"><b>--help</b></font>                                 <font color=\"#F4BF75\"><b>    </b></font>  Show this message    │\n<font color=\"#A5A5A1\">│                                              and exit.            │</font>\n<font color=\"#A5A5A1\">╰───────────────────────────────────────────────────────────────────╯</font>\n```\n\n</div>\n\n/// tip\n\nThere is another place to document the specific *CLI options* and *CLI arguments* that will show up next to them in the help text as with `--install-completion` or `--help`, you will learn that later in the tutorial.\n\n///\n\n## Arguments, options, parameters, optional, required\n\nBe aware that these terms refer to multiple things depending on the context, and sadly, those \"contexts\" mix frequently, so it's easy to get confused.\n\n### In Python\n\nIn Python, the names of the variables in a function, like `name` and `lastname`:\n\n```Python\ndef main(name: str, lastname: str = \"\"):\n    pass\n```\n\nare called \"Python function parameters\" or \"Python function arguments\".\n\n/// note | Technical Details\n\nThere's actually a very small distinction in Python between \"parameter\" and \"argument\".\n\nIt's quite technical... and somewhat pedantic.\n\n*Parameter* refers to the variable name in a function *declaration*. Like:\n\n```\ndef bring_person(name: str, lastname: str = \"\"):\n    pass\n```\n\n*Argument* refers to the value passed when *calling* a function. Like:\n\n```\nperson = bring_person(\"Camila\", lastname=\"Gutiérrez\")\n```\n\n...but you will probably see them used interchangeably in most of the places (including here).\n\n///\n\n#### Python default values\n\nIn Python, in a function, a parameter with a *default value* like `lastname` in:\n\n```Python\ndef main(name: str, lastname: str = \"\"):\n    pass\n```\n\nis considered an \"optional parameter\" (or \"optional argument\").\n\nThe default value can be anything, like `\"\"` or `None`.\n\nAnd a parameter like `name`, that doesn't have a default value, is considered *required*.\n\n### In CLIs\n\nWhen talking about command line interface applications, the words **\"argument\"** and **\"parameter\"** are commonly used to refer to that data passed to a CLI app, those parameters.\n\nBut those words **don't imply** anything about the data being required, needing to be passed in a certain order, nor having a flag like `--lastname`.\n\nThe parameters that come with a name like `--lastname` (and optionally a value) are commonly optional, not required. So, when talking about CLIs it's common to call them **optional arguments** or **optional parameters**. Sometimes these *optional parameters* that start with `--` are also called a **flag** or a **switch**.\n\nIn reality, the parameters that require an order can be made *optional* too. And the ones that come with a flag (like `--lastname`) can be *required* too.\n\n### In **Typer**\n\nTo try and make it a bit easier, we'll normally use the words \"parameter\" or \"argument\" to refer to \"Python functions parameters\" or \"Python functions arguments\".\n\nWe'll use ***CLI argument*** to refer to those *CLI parameters* that depend on the specific order. That are **required** by default.\n\nAnd we'll use ***CLI option*** to refer to those *CLI parameters* that depend on a name that starts with `--` (like `--lastname`). That are **optional** by default.\n\nWe will use ***CLI parameter*** to refer to both, *CLI arguments* and *CLI options*.\n\n## The `typer` Command\n\nWhen you install `typer`, by default it adds a `typer` command to your shell.\n\nThis `typer` command allows you to run your scripts with ✨ auto completion ✨ in your terminal.\n\nAs an alternative to running with Python:\n\n<div class=\"termy\">\n\n```console\n$ python main.py\n\nHello World\n```\n\n</div>\n\nYou can run with the `typer` command:\n\n<div class=\"termy\">\n\n```console\n$ typer main.py run\n\nHello World\n```\n\n</div>\n\n...and it will give you auto completion in your terminal when you hit <kbd>TAB</kbd> for all your code.\n\nSo you can use it to have auto completion for your own scripts as you continue with the tutorial.\n\n/// tip\n\nYour CLI application built with **Typer** won't need the `typer` command to have auto completion once you create a Python package.\n\nBut for short scripts and for learning, before creating a Python package, it might be useful.\n\n///\n"
  },
  {
    "path": "docs/tutorial/index.md",
    "content": "# Learn\n\nLearn how to use **Typer** in this step-by-step **Tutorial** - **User Guide**.\n\nIt covers everything you need to know from the **simplest scripts** to **complex CLI applications**.\n\nYou could consider this a **book**, a **course**, the **official** and recommended way to learn **Typer**. 😎\n\n## Python Types\n\nIf you need a refresher about how to use Python type hints, check the first part of <a href=\"https://fastapi.tiangolo.com/python-types/\" class=\"external-link\" target=\"_blank\">FastAPI's Python types intro</a>.\n\nYou can also check the <a href=\"https://mypy.readthedocs.io/en/latest/cheat_sheet_py3.html\" class=\"external-link\" target=\"_blank\">mypy cheat sheet</a>.\n\nIn short (very short), you can declare a function with parameters like:\n\n```Python\nfrom typing import Optional\n\ndef type_example(name: str, formal: bool = False, intro: Optional[str] = None):\n    pass\n```\n\nAnd your editor (and **Typer**) will know that:\n\n* `name` is of type `str` and is a required parameter.\n* `formal` is a `bool` and is by default `False`.\n* `intro` is an optional `str`, by default is `None`.\n\nThese type hints are what give you autocomplete in your editor and several other features.\n\n**Typer** is based on these type hints.\n\n## About this Tutorial\n\nThis tutorial shows you how to use **Typer** with all its features, step by step.\n\nEach section gradually builds on the previous ones, but it's structured to separate topics, so that you can go directly to any specific one to solve your specific CLI needs.\n\nIt is also built to work as a future reference so you can come back and see exactly what you need.\n\n## Run the Code\n\nAll the code blocks can be copied and used directly (they are tested Python files).\n\nTo run any of the examples, copy the code to a file `main.py`, and run it:\n\n<div class=\"termy\">\n\n```console\n$ python main.py\n\n✨ The magic happens here ✨\n```\n\n</div>\n\nIt is **HIGHLY encouraged** that you write or copy the code, edit it and run it locally.\n\nUsing it in your editor is what really shows you the benefits of **Typer**, seeing how little code you have to write, all the **inline errors**, **autocompletion**, etc.\n\nAnd running the examples is what will really help you **understand** what is going on.\n\nYou can learn a lot more by **running some examples** and **playing around** with them than by reading all the docs here.\n"
  },
  {
    "path": "docs/tutorial/install.md",
    "content": "# Install **Typer**\n\nThe first step is to install **Typer**.\n\nFirst, make sure you create your [virtual environment](../virtual-environments.md){.internal-link target=_blank}, activate it, and then install it, for example with:\n\n<div class=\"termy\">\n\n```console\n$ pip install typer\n---> 100%\nSuccessfully installed typer click shellingham rich\n```\n\n</div>\n\nBy default, `typer` comes with `rich` and `shellingham`.\n"
  },
  {
    "path": "docs/tutorial/launch.md",
    "content": "# Launching Applications\n\nYou can launch applications from your CLI program with `typer.launch()`.\n\nIt will launch the appropriate application depending on the URL or file type you pass it:\n\n{* docs_src/launch/tutorial001_py310.py hl[9] *}\n\nCheck it:\n\n<div class=\"termy\">\n\n```console\n$ python main.py\n\nOpening Typer docs\n\n// Opens browser with Typer's docs\n```\n\n</div>\n\n## Locating a file\n\nYou can also make the operating system open the file browser indicating where a file is located with `locate=True`:\n\n{* docs_src/launch/tutorial002_py310.py hl[20] *}\n\n/// tip\n\nThe rest of the code in this example is just making sure the app directory exists and creating the config file.\n\nBut the most important part is the `typer.launch(config_file_str, locate=True)` with the argument `locate=True`.\n\n///\n\nCheck it:\n\n<div class=\"termy\">\n\n```console\n$ python main.py\n\nOpening config directory\n\n// Opens a file browser indicating where the config file is located\n```\n\n</div>\n"
  },
  {
    "path": "docs/tutorial/multiple-values/arguments-with-multiple-values.md",
    "content": "# CLI Arguments with Multiple Values\n\n*CLI arguments* can also receive multiple values.\n\nYou can define the type of a *CLI argument* using `list`.\n\n{* docs_src/multiple_values/arguments_with_multiple_values/tutorial001_py310.py hl[9] *}\n\nAnd then you can pass it as many *CLI arguments* of that type as you want:\n\n<div class=\"termy\">\n\n```console\n$ python main.py ./index.md ./first-steps.md woohoo!\n\nThis file exists: index.md\nwoohoo!\nThis file exists: first-steps.md\nwoohoo!\n```\n\n</div>\n\n/// tip\n\nWe also declared a final *CLI argument* `celebration`, and it's correctly used even if we pass an arbitrary number of `files` first.\n\n///\n\n/// info\n\nA `list` can only be used in the last command (if there are subcommands), as this will take anything to the right and assume it's part of the expected *CLI arguments*.\n\n///\n\n## *CLI arguments* with tuples\n\nIf you want a specific number of values and types, you can use a tuple, and it can even have default values:\n\n{* docs_src/multiple_values/arguments_with_multiple_values/tutorial002_an_py310.py hl[10:12] *}\n\nCheck it:\n\n<div class=\"termy\">\n\n```console\n// Check the help\n$ python main.py --help\n\nUsage: main.py [OPTIONS] [NAMES]...\n\nArguments:\n  [NAMES]...  Select 3 characters to play with  [default: Harry, Hermione, Ron]\n\nOptions:\n  --help                Show this message and exit.\n\n// Use it with its defaults\n$ python main.py\n\nHello Harry\nHello Hermione\nHello Ron\n\n// If you pass an invalid number of arguments you will get an error\n$ python main.py Draco Hagrid\n\nError: Argument 'names' takes 3 values\n\n// And if you pass the exact number of values it will work correctly\n$ python main.py Draco Hagrid Dobby\n\nHello Draco\nHello Hagrid\nHello Dobby\n```\n\n</div>\n"
  },
  {
    "path": "docs/tutorial/multiple-values/index.md",
    "content": "# Multiple Values\n\nThere are several ways to declare multiple values for *CLI options* and *CLI arguments*.\n\nWe'll see them in the next short sections.\n"
  },
  {
    "path": "docs/tutorial/multiple-values/multiple-options.md",
    "content": "# Multiple CLI Options\n\nYou can declare a *CLI option* that can be used multiple times, and then get all the values.\n\nFor example, let's say you want to accept several users in a single execution.\n\nFor this, use the standard Python `list` to declare it as a list of `str`:\n\n{* docs_src/multiple_values/multiple_options/tutorial001_an_py310.py hl[9] *}\n\nYou will receive the values as you declared them, as a `list` of `str`.\n\nCheck it:\n\n<div class=\"termy\">\n\n```console\n// The default value is 'None'\n$ python main.py\n\nNo provided users (raw input = None)\nAborted!\n\n// Now pass a user\n$ python main.py --user Camila\n\nProcessing user: Camila\n\n// And now try with several users\n$ python main.py --user Camila --user Rick --user Morty\n\nProcessing user: Camila\nProcessing user: Rick\nProcessing user: Morty\n```\n\n</div>\n\n## Multiple `float`\n\nThe same way, you can use other types and they will be converted by **Typer** to their declared type:\n\n{* docs_src/multiple_values/multiple_options/tutorial002_an_py310.py hl[9] *}\n\nCheck it:\n\n<div class=\"termy\">\n\n```console\n$ python main.py\n\nThe sum is 0\n\n// Try with some numbers\n$ python main.py --number 2\n\nThe sum is 2.0\n\n// Try with some numbers\n$ python main.py --number 2 --number 3 --number 4.5\n\nThe sum is 9.5\n```\n\n</div>\n"
  },
  {
    "path": "docs/tutorial/multiple-values/options-with-multiple-values.md",
    "content": "# CLI Options with Multiple Values\n\nYou can also declare a *CLI option* that takes several values of different types.\n\nYou can set the number of values and types to anything you want, but it has to be a fixed number of values.\n\nFor this, use the standard Python `tuple`:\n\n{* docs_src/multiple_values/options_with_multiple_values/tutorial001_an_py310.py hl[9] *}\n\nEach of the internal types defines the type of each value in the tuple.\n\nSo:\n\n```Python\nuser: tuple[str, int, bool]\n```\n\nmeans that the parameter `user` is a tuple of 3 values.\n\n* The first value is a `str`.\n* The second value is an `int`.\n* The third value is a `bool`.\n\nLater we do:\n\n```Python\nusername, coins, is_wizard = user\n```\n\nIf you hadn't seen that, it means that `user` is a tuple with 3 values, and we are assigning each of the values to a new variable:\n\n* The first value in the tuple `user` (a `str`) goes to the variable `username`.\n* The second value in the tuple `user` (an `int`) goes to the variable `coins`.\n* The third value in the tuple `user` (a `bool`) goes to the variable `is_wizard`.\n\nSo, this:\n\n```Python\nusername, coins, is_wizard = user\n```\n\nis equivalent to this:\n\n```Python\nusername = user[0]\ncoins = user[1]\nis_wizard = user[2]\n```\n\n## Check it\n\nNow let's see how this works in the terminal:\n\n<div class=\"termy\">\n\n```console\n// check the help\n$ python main.py --help\n\n// Notice the &lt;TEXT INTEGER BOOLEAN&gt;\nUsage: main.py [OPTIONS]\n\nOptions:\n  --user &lt;TEXT INTEGER BOOLEAN&gt;...\n  --help                          Show this message and exit.\n\n// Now try it\n$ python main.py --user Camila 50 yes\n\nThe username Camila has 50 coins\nAnd this user is a wizard!\n\n// With other values\n$ python main.py --user Morty 3 no\n\nThe username Morty has 3 coins\n\n// Try with invalid values (not enough)\n$ python main.py --user Camila 50\n\nError: Option '--user' requires 3 arguments\n```\n\n</div>\n"
  },
  {
    "path": "docs/tutorial/one-file-per-command.md",
    "content": "# One File Per Command\n\nWhen your CLI application grows, you can split it into multiple files and modules. This pattern helps maintain a clean and organized code structure. ✨\n\nThis tutorial will show you how to use `add_typer` to create sub commands and organize your commands in multiple files.\n\nWe will create a simple CLI with the following commands:\n\n- `version`\n- `users add NAME`\n- `users delete NAME`\n\n## CLI structure\n\nHere is the structure we'll be working with:\n\n```text\nmycli/\n├── __init__.py\n├── main.py\n├── users/\n│   ├── __init__.py\n│   ├── add.py\n│   └── delete.py\n└── version.py\n```\n\n`mycli` will be our <abbr title=\"a directory with an __init__.py file, it can be imported\">package</abbr>, and it will contain the following modules:\n\n- `main.py`: The main <abbr title=\"a Python file that can be imported\">module</abbr> that will import the `version` and `users` modules.\n- `version.py`: A <abbr title=\"a Python file that can be imported\">module</abbr> that will contain the `version` command.\n- `users/`: A <abbr title=\"another directory with an __init__.py file, it can also be imported\">package</abbr> (inside of our `mycli` package) that will contain the `add` and `delete` commands.\n\n## Implementation\n\nLet's start implementing our CLI! 🚀\n\nWe'll create the `version` module, the `main` module, and the `users` package.\n\n### Version Module (`version.py`)\n\nLet's start by creating the `version` module. This module will contain the `version` command.\n\n{* docs_src/one_file_per_command/app_py310/version.py *}\n\nIn this file we are creating a new Typer app instance for the `version` command.\n\nThis is not required in single-file applications, but in the case of multi-file applications it will allow us to include this command in the main application using `app.add_typer()`.\n\nLet's see that next!\n\n### Main Module (`main.py`)\n\nThe main module will be the entry point of the application. It will import the version module and the users module.\n\n/// tip\n\nWe'll see how to implement the users module in the next section.\n\n///\n\n{* docs_src/one_file_per_command/app_py310/main.py hl[8,9] *}\n\nIn this module, we import the `version` and `users` modules and add them to the main app using `app.add_typer()`.\n\nFor the `users` module, we specify the name as `\"users\"` to group the commands under the `users` sub-command.\n\nNotice that we didn't add a name for the `version_app` Typer app. Because of this, Typer will add the commands (just one in this case) declared in the `version_app` directly at the top level. So, there will be a top-level `version` sub-command.\n\nBut for `users`, we add a name `\"users\"`, this way those commands will be under the sub-command `users` instead of at the top level. So, there will be a `users add` and `users delete` sub-sub-commands. 😅\n\n/// tip\n\nIf you want a command to group the included commands in a sub-app, add a name.\n\nIf you want to include the commands from a sub-app directly at the top level, don't add a name, or set it to `None`. 🤓\n\n///\n\nLet's now create the `users` module with the `add` and `delete` commands.\n\n### Users Add Command (`users/add.py`)\n\n{* docs_src/one_file_per_command/app_py310/users/add.py *}\n\nLike the `version` module, we create a new Typer app instance for the `users/add` command. This allows us to include the `add` command in the users app.\n\n### Users Delete Command (`users/delete.py`)\n\n{* docs_src/one_file_per_command/app_py310/users/delete.py *}\n\nAnd once again, we create a new Typer app instance for the `users/delete` command. This allows us to include the `delete` command in the users app.\n\n### Users' app (`users/__init__.py`)\n\nFinally, we need to create an `__init__.py` file in the `users` directory to define the `users` app.\n\n{* docs_src/one_file_per_command/app_py310/users/__init__.py *}\n\nSimilarly to the `version` module, we create a new `Typer` app instance for the `users` module. This allows us to include the `add` and `delete` commands in the users app.\n\n## Running the Application\n\nNow we are ready to run the application! 😎\n\nTo run the application, you can execute it as a Python module:\n\n<div class=\"termy\">\n\n```console\n$ python -m mycli.main version\n\nMy CLI Version 1.0\n\n$ python -m mycli.main users add Camila\n\nAdding user: Camila\n```\n\n</div>\n\nAnd if you built a package and installed your app, you can then use the `mycli` command:\n\n<div class=\"termy\">\n\n```console\n$ mycli version\n\nMy CLI Version 1.0\n\n$ mycli users add Camila\n\nAdding user: Camila\n```\n\n</div>\n\n## Callbacks\n\nHave in mind that if you include a sub-app with `app.add_typer()` **without a name**, the commands will be added to the top level, so **only the top level callback** (if there's any) will be used, the one declared in the main app.\n\nIf you **want to use a callback** for a sub-app, you need to include the sub-app **with a name**, which creates a sub-command grouping the commands in that sub-app. 🤓\n\nIn the example above, if the `users` sub-app had a callback, it would be used. But if the `version` sub-app had a callback, it would not be used, because the `version` sub-app was included without a name.\n"
  },
  {
    "path": "docs/tutorial/options/callback-and-context.md",
    "content": "# CLI Option Callback and Context\n\nIn some occasions you might want to have some custom logic for a specific *CLI parameter* (for a *CLI option*  or *CLI argument*) that is executed with the value received from the terminal.\n\nIn those cases you can use a *CLI parameter* callback function.\n\n## Validate *CLI parameters*\n\nFor example, you could do some validation before the rest of the code is executed.\n\n{* docs_src/options/callback/tutorial001_an_py310.py hl[8:11,15] *}\n\nHere you pass a function to `typer.Option()` or `typer.Argument()` with the keyword argument `callback`.\n\nThe function receives the value from the command line. It can do anything with it, and then return the value.\n\nIn this case, if the `--name` is not `Camila` we raise a `typer.BadParameter()` exception.\n\nThe `BadParameter` exception is special, it shows the error with the parameter that generated it.\n\nCheck it:\n\n<div class=\"termy\">\n\n```console\n$ python main.py --name Camila\n\nHello Camila\n\n$ python main.py --name Rick\n\nUsage: main.py [OPTIONS]\n\n// We get the error from the callback\nError: Invalid value for '--name': Only Camila is allowed\n```\n\n</div>\n\n## Handling completion\n\nThere's something to be aware of with callbacks and completion that requires some small special handling.\n\nBut first let's just use completion in your shell (Bash, Zsh, Fish, or PowerShell).\n\nAfter installing completion (for your own Python package), when you use your CLI program and start adding a *CLI option* with `--` and then hit <kbd>TAB</kbd>, your shell will show you the available *CLI options* (the same for *CLI arguments*, etc).\n\nTo check it quickly with the previous script use the `typer` command:\n\n<div class=\"termy\">\n\n```console\n// Hit the TAB key in your keyboard below where you see the: [TAB]\n$ typer ./main.py [TAB][TAB]\n\n// Depending on your terminal/shell you will get some completion like this ✨\nrun    -- Run the provided Typer app.\nutils  -- Extra utility commands for Typer apps.\n\n// Then try with \"run\" and --help\n$ typer ./main.py run --help\n\n// You get a help text with your CLI options as you normally would\nUsage: typer run [OPTIONS]\n\n  Run the provided Typer app.\n\nOptions:\n  --name TEXT  [required]\n  --help       Show this message and exit.\n\n// Then try completion with your program\n$ typer ./main.py run --[TAB][TAB]\n\n// You get completion for CLI options\n--help  -- Show this message and exit.\n--name\n\n// And you can run it as if it was with Python directly\n$ typer ./main.py run --name Camila\n\nHello Camila\n```\n\n</div>\n\n### How shell completion works\n\nThe way it works internally is that the shell/terminal will call your CLI program with some special environment variables (that hold the current *CLI parameters*, etc) and your CLI program will print some special values that the shell will use to present completion. All this is handled for you by **Typer** behind the scenes.\n\nBut the main **important point** is that it is all based on values printed by your program that the shell reads.\n\n### Breaking completion in a callback\n\nLet's say that when the callback is running, we want to show a message saying that it's validating the name:\n\n{* docs_src/options/callback/tutorial002_an_py310.py hl[9] *}\n\nAnd because the callback will be called when the shell calls your program asking for completion, that message `\"Validating name\"` will be printed and it will break completion.\n\nIt will look something like:\n\n<div class=\"termy\">\n\n```console\n// Run it normally\n$ typer ./main.py run --name Camila\n\n// See the extra message \"Validating name\"\nValidating name\nHello Camila\n\n$ typer ./main.py run --[TAB][TAB]\n\n// Some weird broken error message ⛔️\n(eval):1: command not found: Validating\nrutyper ./main.pyed Typer app.\n```\n\n</div>\n\n### Fix completion - using the `Context`\n\nEvery Typer application has a special object called a \"Context\" that is normally hidden.\n\nBut you can access the context by declaring a function parameter of type `typer.Context`.\n\nThe \"context\" has some additional data about the current execution of your program:\n\n{* docs_src/options/callback/tutorial003_an_py310.py hl[8:10] *}\n\nThe `ctx.resilient_parsing` will be `True` when handling completion, so you can just return without printing anything else.\n\nBut it will be `False` when calling the program normally. So you can continue the execution of your previous code.\n\nThat's all is needed to fix completion. 🚀\n\nCheck it:\n\n<div class=\"termy\">\n\n```console\n$ typer ./main.py run --[TAB][TAB]\n\n// Now it works correctly 🎉\n--help  -- Show this message and exit.\n--name\n\n// And you can call it normally\n$ typer ./main.py run --name Camila\n\nValidating name\nHello Camila\n```\n\n</div>\n\n## Using the `CallbackParam` object\n\nThe same way you can access the `typer.Context` by declaring a function parameter with its value, you can declare another function parameter with type `typer.CallbackParam` to get the specific `Parameter` object.\n\n{* docs_src/options/callback/tutorial004_an_py310.py hl[8,11] *}\n\nIt's probably not very common, but you could do it if you need it.\n\nFor example if you had a callback that could be used by several *CLI parameters*, that way the callback could know which parameter is each time.\n\nCheck it:\n\n<div class=\"termy\">\n\n```console\n$ python main.py --name Camila\n\nValidating param: name\nHello Camila\n```\n\n</div>\n\n## Technical Details\n\nBecause you get the relevant data in the callback function based on standard Python type annotations, you get type checks and autocompletion in your editor for free.\n\nAnd **Typer** will make sure you get the function parameters you want.\n\nYou don't have to worry about their names, their order, etc.\n\nAs it's based on standard Python types, it \"**just works**\". ✨\n\n### Callback with type annotations\n\nYou can get the `typer.Context` and the `typer.CallbackParam` simply by declaring a function parameter of each type.\n\nThe order doesn't matter, the name of the function parameters doesn't matter.\n\nYou could also get only the `typer.CallbackParam` and not the `typer.Context`, or vice versa, it will still work.\n\n### `value` function parameter\n\nThe `value` function parameter in the callback can also have any name (e.g. `lastname`) and any type, but it should have the same type annotation as in the main function, because that's what it will receive.\n\nIt's also possible to not declare its type. It will still work.\n\nAnd it's possible to not declare the `value` parameter at all, and, for example, only get the `typer.Context`. That will also work.\n"
  },
  {
    "path": "docs/tutorial/options/help.md",
    "content": "# CLI Options with Help\n\nYou already saw how to add a help text for *CLI arguments* with the `help` parameter.\n\nLet's now do the same for *CLI options*:\n\n{* docs_src/options/help/tutorial001_an_py310.py hl[11:12] *}\n\nThe same way as with `typer.Argument()`, we can put `typer.Option()` inside of `Annotated`.\n\nWe can then pass the `help` keyword parameter:\n\n```Python\nlastname: Annotated[str, typer.Option(help=\"this option does this and that\")] = \"\"\n```\n\n...to create the help for that *CLI option*.\n\nThe same way as with `typer.Argument()`, **Typer** also supports the old style using the function parameter default value:\n\n```Python\nlastname: str = typer.Option(default=\"\", help=\"this option does this and that\")\n```\n\nCopy that example from above to a file `main.py`.\n\nTest it:\n\n<div class=\"termy\">\n\n```console\n$ python main.py --help\n\nUsage: main.py [OPTIONS] NAME\n\n  Say hi to NAME, optionally with a --lastname.\n\n  If --formal is used, say hi very formally.\n\nArguments:\n  NAME  [required]\n\nOptions:\n  --lastname TEXT         Last name of person to greet. [default: ]\n  --formal / --no-formal  Say hi formally.  [default: False]\n  --help                  Show this message and exit.\n\n// Now you have a help text for the --lastname and --formal CLI options 🎉\n```\n\n</div>\n\n## *CLI Options* help panels\n\nThe same as with *CLI arguments*, you can put the help for some *CLI options* in different panels to be shown with the `--help` option.\n\nUsing Rich, you can set the `rich_help_panel` parameter to the name of the panel you want for each *CLI option*:\n\n{* docs_src/options/help/tutorial002_an_py310.py hl[15,21] *}\n\nNow, when you check the `--help` option, you will see a default panel named \"`Options`\" for the *CLI options* that don't have a custom `rich_help_panel`.\n\nAnd below you will see other panels for the *CLI options* that have a custom panel set in the `rich_help_panel` parameter:\n\n<div class=\"termy\">\n\n```console\n$ python main.py --help\n\n<b> </b><font color=\"#F4BF75\"><b>Usage: </b></font><b>main.py [OPTIONS] NAME                                </b>\n<b>                                                                     </b>\n Say hi to NAME, optionally with a <font color=\"#A1EFE4\"><b>--lastname</b></font>.\n If <font color=\"#6B9F98\"><b>--formal</b></font><font color=\"#A5A5A1\"> is used, say hi very formally.                          </font>\n\n<font color=\"#A5A5A1\">╭─ Arguments ───────────────────────────────────────────────────────╮</font>\n<font color=\"#A5A5A1\">│ </font><font color=\"#F92672\">*</font>    name      <font color=\"#F4BF75\"><b>TEXT</b></font>  [default: None] <font color=\"#A6194C\">[required]</font>                   │\n<font color=\"#A5A5A1\">╰───────────────────────────────────────────────────────────────────╯</font>\n<font color=\"#A5A5A1\">╭─ Options ─────────────────────────────────────────────────────────╮</font>\n<font color=\"#A5A5A1\">│ </font><font color=\"#A1EFE4\"><b>--lastname</b></font>                  <font color=\"#F4BF75\"><b>TEXT</b></font>  Last name of person to greet.   │\n<font color=\"#A5A5A1\">│ </font><font color=\"#A1EFE4\"><b>--help</b></font>                      <font color=\"#F4BF75\"><b>    </b></font>  Show this message and exit.     │\n<font color=\"#A5A5A1\">╰───────────────────────────────────────────────────────────────────╯</font>\n<font color=\"#A5A5A1\">╭─ Customization and Utils ─────────────────────────────────────────╮</font>\n<font color=\"#A5A5A1\">│ </font><font color=\"#A1EFE4\"><b>--formal</b></font>    <font color=\"#AE81FF\"><b>--no-formal</b></font>      Say hi formally.                     │\n<font color=\"#A5A5A1\">│                              [default: no-formal]                 │</font>\n<font color=\"#A5A5A1\">│ </font><font color=\"#A1EFE4\"><b>--debug</b></font>     <font color=\"#AE81FF\"><b>--no-debug</b></font>       Enable debugging.                    │\n<font color=\"#A5A5A1\">│                              [default: no-debug]                  │</font>\n<font color=\"#A5A5A1\">╰───────────────────────────────────────────────────────────────────╯</font>\n```\n\n</div>\n\nHere we have a custom *CLI options* panel named \"`Customization and Utils`\".\n\n## Help with style using Rich\n\nIn a future section you will see how to use custom markup in the `help` for *CLI options* when reading about [Commands - Command Help](../commands/help.md#rich-markdown-and-markup){.internal-link target=_blank}.\n\nIf you are in a hurry you can jump there, but otherwise, it would be better to continue reading here and following the tutorial in order.\n\n\n## Hide default from help\n\nYou can tell Typer to not show the default value in the help text with `show_default=False`:\n\n{* docs_src/options/help/tutorial003_an_py310.py hl[9] *}\n\nAnd it will no longer show the default value in the help text:\n\n<div class=\"termy\">\n\n```console\n$ python main.py\n\nHello Wade Wilson\n\n// Show the help\n$ python main.py --help\n\nUsage: main.py [OPTIONS]\n\nOptions:\n  --fullname TEXT\n  --help                Show this message and exit.\n\n// Notice there's no [default: Wade Wilson] 🔥\n```\n\n</div>\n\n## Custom default string\n\nYou can use the same `show_default` to pass a custom string (instead of a `bool`) to customize the default value to be shown in the help text:\n\n{* docs_src/options/help/tutorial004_an_py310.py hl[11] *}\n\nAnd it will be used in the help text:\n\n<div class=\"termy\">\n\n```console\n$ python main.py\n\nHello Wade Wilson\n\n// Show the help\n$ python main.py --help\n\nUsage: main.py [OPTIONS]\n\nOptions:\n  --fullname TEXT       [default: (Deadpoolio the amazing's name)]\n  --help                Show this message and exit.\n\n// Notice how it shows \"(Deadpoolio the amazing's name)\" instead of the actual default of \"Wade Wilson\"\n```\n\n</div>\n"
  },
  {
    "path": "docs/tutorial/options/index.md",
    "content": "# CLI Options\n\nIn the next short sections we will see how to modify *CLI options* using `typer.Option()`.\n\n`typer.Option()` works very similarly to `typer.Argument()`, but has some extra features that we'll see next.\n"
  },
  {
    "path": "docs/tutorial/options/name.md",
    "content": "# CLI Option Name\n\nBy default **Typer** will create a *CLI option* name from the function parameter.\n\nSo, if you have a function with:\n\n```Python\ndef main(user_name: Optional[str] = None):\n    pass\n```\n\nor\n\n```Python\ndef main(user_name: Annotated[Optional[str], typer.Option()] = None):\n    pass\n```\n\n**Typer** will create a *CLI option*:\n\n```\n--user-name\n```\n\nBut you can customize it if you want to.\n\nLet's say the function parameter name is `user_name` as above, but you want the *CLI option* to be just `--name`.\n\nYou can pass the *CLI option* name that you want to have in the following positional argument passed to `typer.Option()`:\n\n{* docs_src/options/name/tutorial001_an_py310.py hl[9] *}\n\n/// info\n\n\"<a href=\"https://docs.python.org/3.8/glossary.html#term-argument\" class=\"external-link\" target=\"_blank\">Positional</a>\" means that it's not a function argument with a keyword name.\n\nFor example `show_default=True` is a keyword argument. \"`show_default`\" is the keyword.\n\nBut in `\"--name\"` there's no `option_name=\"--name\"` or something similar, it's just the string value `\"--name\"` that goes in `typer.Option()`.\n\nThat's a \"positional argument\" in a function.\n\n///\n\nCheck it:\n\n<div class=\"termy\">\n\n```console\n$ python main.py --help\n\n// Notice the --name instead of --user-name\nUsage: main.py [OPTIONS]\n\nOptions:\n  --name TEXT           [required]\n  --help                Show this message and exit.\n\n// Try it\n$ python main.py --name Camila\n\nHello Camila\n```\n\n</div>\n\n## *CLI option* short names\n\nA short name is a *CLI option* name with a single dash (`-`) instead of 2 (`--`) and a single letter, like `-n` instead of `--name`.\n\nFor example, the `ls` program has a *CLI option* named `--size`, and the same *CLI option* also has a short name `-s`:\n\n<div class=\"termy\">\n\n```console\n// With the long name --size\n$ ls ./myproject --size\n\n12 first-steps.md   4 intro.md\n\n// With the short name -s\n$ ls ./myproject -s\n\n12 first-steps.md   4 intro.md\n\n// Both CLI option names do the same\n```\n\n</div>\n\n### *CLI option* short names together\n\nShort names have another feature, when they have a single letter, as in `-s`, you can put several of these *CLI options* together, with a single dash.\n\nFor example, the `ls` program has these 2 *CLI options* (among others):\n\n* `--size`: show the sizes of the listed files.\n* `--human`: show a human-readable format, like `1MB` instead of just `1024`.\n\nAnd these 2 *CLI options* have short versions too:\n\n* `--size`: short version `-s`.\n* `--human`: short version `-h`.\n\nSo, you can put them together with `-sh` or `-hs`:\n\n<div class=\"termy\">\n\n```console\n// Call ls with long CLI options\n$ ls --size --human\n\n12K first-steps.md   4.0K intro.md\n\n// Now with short versions\n$ ls -s -h\n\n12K first-steps.md   4.0K intro.md\n\n// And with short versions together\n$ ls -sh\n\n12K first-steps.md   4.0K intro.md\n\n// Order in short versions doesn't matter\n$ ls -hs\n\n12K first-steps.md   4.0K intro.md\n\n// They all work the same 🎉\n```\n\n</div>\n\n### *CLI option* short names with values\n\nWhen you use *CLI options* with short names, you can put them together if they are just boolean flags, like `--size` or `--human`.\n\nBut if you have a *CLI option* `--file` with a short name `-f` that takes a value, if you put it with other short names for *CLI options*, you have to put it as the last letter, so that it can receive the value that comes right after.\n\nFor example, let's say you are decompressing/extracting a file `myproject.tar.gz` with the program `tar`.\n\nYou can pass these *CLI option* short names to `tar`:\n\n* `-x`: means \"e`X`tract\", to decompress and extract the contents.\n* `-v`: means \"`V`erbose\", to print on the screen what it is doing, so you can know that it's decompressing each file and can entertain yourself while you wait.\n* `-f`: means \"`F`ile\", this one requires a value, the compressed file to extract (in our example, this is `myproject.tar.gz`).\n    * So if you use all the short names together, this `-f` has to come last, to receive the value that comes next to it.\n\nFor example:\n\n<div class=\"termy\">\n\n```console\n$ tar -xvf myproject.tar.gz\n\nmyproject/\nmyproject/first-steps.md\nmyproject/intro.md\n\n// But if you put the -f before\n$ tar -fxv myproject.tar.gz\n\n// You get an ugly error\ntar: You must specify one of the blah, blah, error, error\n```\n\n</div>\n\n### Defining *CLI option* short names\n\nIn **Typer** you can also define *CLI option* short names the same way you can customize the long names.\n\nYou can pass *positional* arguments to `typer.Option()` to define the *CLI option* name(s).\n\n/// tip\n\nRemember the *positional* function arguments are those that don't have a keyword.\n\nAll the other function arguments/parameters you pass to `typer.Option()` like `prompt=True` and `help=\"This option blah, blah\"` require the keyword.\n\n///\n\nYou can overwrite the *CLI option* name to use as in the previous example, but you can also declare extra alternatives, including short names.\n\nFor example, extending the previous example, let's add a *CLI option* short name `-n`:\n\n{* docs_src/options/name/tutorial002_an_py310.py hl[9] *}\n\nHere we are overwriting the *CLI option* name that by default would be `--user-name`, and we are defining it to be `--name`. And we are also declaring a *CLI option* short name of `-n`.\n\nCheck it:\n\n<div class=\"termy\">\n\n```console\n// Check the help\n$ python main.py --help\n\n// Notice the two CLI option names -n and --name\nUsage: main.py [OPTIONS]\n\nOptions:\n  -n, --name TEXT       [required]\n  --help                Show this message and exit.\n\n// Try the short version\n$ python main.py -n Camila\n\nHello Camila\n```\n\n</div>\n\n### *CLI option* only short name\n\nIf you only declare a short name like `-n` then that will be the only *CLI option* name. And neither `--name` nor `--user-name` will be available.\n\n{* docs_src/options/name/tutorial003_an_py310.py hl[9] *}\n\nCheck it:\n\n<div class=\"termy\">\n\n```console\n$ python main.py --help\n\n// Notice there's no --name nor --user-name, only -n\nUsage: main.py [OPTIONS]\n\nOptions:\n  -n TEXT               [required]\n  --help                Show this message and exit.\n\n// Try it\n$ python main.py -n Camila\n\nHello Camila\n```\n\n</div>\n\n### *CLI option* short name and default\n\nContinuing with the example above, as **Typer** allows you to declare a *CLI option* as having only a short name, if you want to have the default long name plus a short name, you have to declare both explicitly:\n\n{* docs_src/options/name/tutorial004_an_py310.py hl[9] *}\n\nCheck it:\n\n<div class=\"termy\">\n\n```console\n$ python main.py --help\n\n// Notice that we have the long version --user-name back\n// and we also have the short version -n\nUsage: main.py [OPTIONS]\n\nOptions:\n  -n, --user-name TEXT  [required]\n  --help                Show this message and exit.\n\n// Try it\n$ python main.py --user-name Camila\n\nHello Camila\n\n// And try the short version\n$ python main.py -n Camila\n```\n\n</div>\n\n### *CLI option* short names together\n\nYou can create multiple short names and use them together.\n\nYou don't have to do anything special for it to work (apart from declaring those short versions):\n\n{* docs_src/options/name/tutorial005_an_py310.py hl[10:11] *}\n\n/// tip\n\nNotice that, again, we are declaring the long and short version of the *CLI option* names.\n\n///\n\nCheck it:\n\n<div class=\"termy\">\n\n```console\n$ python main.py --help\n\n// We now have short versions -n and -f\n// And also long versions --name and --formal\nUsage: main.py [OPTIONS]\n\nOptions:\n  -n, --name TEXT       [required]\n  -f, --formal\n  --help                Show this message and exit.\n\n// Try the short versions\n$ python main.py -n Camila -f\n\nGood day Ms. Camila.\n\n// And try the 2 short versions together\n// See how -n has to go last, to be able to get the value\n$ python main.py -fn Camila\n\nGood day Ms. Camila.\n```\n\n</div>\n"
  },
  {
    "path": "docs/tutorial/options/password.md",
    "content": "# Password CLI Option and Confirmation Prompt\n\nApart from having a prompt, you can make a *CLI option* have a `confirmation_prompt=True`:\n\n{* docs_src/options/password/tutorial001_an_py310.py hl[11] *}\n\nAnd the CLI program will ask for confirmation:\n\n<div class=\"termy\">\n\n```console\n$ python main.py Camila\n\n// It prompts for the email\n# Email: $ camila@example.com\n# Repeat for confirmation: $ camila@example.com\n\nHello Camila, your email is camila@example.com\n```\n\n</div>\n\n## A Password prompt\n\nWhen receiving a password, it is very common (in most shells) to not show anything on the screen while typing the password.\n\nThe program will still receive the password, but nothing will be shown on screen, not even `****`.\n\nYou can achieve the same using `hide_input=True`.\n\nAnd if you combine it with `confirmation_prompt=True` you can easily receive a password with double confirmation:\n\n{* docs_src/options/password/tutorial002_an_py310.py hl[12] *}\n\nCheck it:\n\n<div class=\"termy\">\n\n```console\n$ python main.py Camila\n\n// It prompts for the password, but doesn't show anything when you type\n# Password: $\n# Repeat for confirmation: $\n\n// Let's imagine the password typed was \"typerrocks\"\nHello Camila. Doing something very secure with password.\n...just kidding, here it is, very insecure: typerrocks\n```\n\n</div>\n"
  },
  {
    "path": "docs/tutorial/options/prompt.md",
    "content": "# CLI Option Prompt\n\nIt's also possible to, instead of just showing an error, ask for the missing value with `prompt=True`:\n\n{* docs_src/options/prompt/tutorial001_an_py310.py hl[9] *}\n\nAnd then your program will ask the user for it in the terminal:\n\n<div class=\"termy\">\n\n```console\n// Call it with the NAME CLI argument\n$ python main.py Camila\n\n// It asks for the missing CLI option --lastname\n# Lastname: $ Gutiérrez\n\nHello Camila Gutiérrez\n```\n\n</div>\n\n## Customize the prompt\n\nYou can also set a custom prompt, passing the string that you want to use instead of just `True`:\n\n{* docs_src/options/prompt/tutorial002_an_py310.py hl[11] *}\n\nAnd then your program will ask for it using with your custom prompt:\n\n<div class=\"termy\">\n\n```console\n// Call it with the NAME CLI argument\n$ python main.py Camila\n\n// It uses the custom prompt\n# Please tell me your last name: $ Gutiérrez\n\nHello Camila Gutiérrez\n```\n\n</div>\n\n## Confirmation prompt\n\nIn some cases you could want to prompt for something and then ask the user to confirm it by typing it twice.\n\nYou can do it passing the parameter `confirmation_prompt=True`.\n\nLet's say it's a CLI app to delete a project:\n\n{* docs_src/options/prompt/tutorial003_an_py310.py hl[10] *}\n\nAnd it will prompt the user for a value and then for the confirmation:\n\n<div class=\"termy\">\n\n```console\n$ python main.py\n\n// Your app will first prompt for the project name, and then for the confirmation\n# Project name: $ Old Project\n# Repeat for confirmation: $ Old Project\n\nDeleting project Old Project\n\n// If the user doesn't type the same, receives an error and a new prompt\n$ python main.py\n\n# Project name: $ Old Project\n# Repeat for confirmation: $ New Spice\n\nError: The two entered values do not match\n\n# Project name: $ Old Project\n# Repeat for confirmation: $ Old Project\n\nDeleting project Old Project\n\n// Now it works 🎉\n```\n\n</div>\n"
  },
  {
    "path": "docs/tutorial/options/required.md",
    "content": "# Required CLI Options\n\nWe said before that *by default*:\n\n* *CLI options* are **optional**\n* *CLI arguments* are **required**\n\nWell, that's how they work *by default*, and that's the convention in many CLI programs and systems.\n\nBut if you really want, you can change that.\n\nTo make a *CLI option* required, you can put `typer.Option()` inside of `Annotated` and leave the parameter without a default value.\n\nLet's make `--lastname` a required *CLI option*:\n\n{* docs_src/options/required/tutorial001_an_py310.py hl[9] *}\n\nThe same way as with `typer.Argument()`, the old style of using the function parameter default value is also supported, in that case you would just not pass anything to the `default` parameter.\n\n{* docs_src/options/required/tutorial001_py310.py hl[7] *}\n\nOr you can explicitly pass `...` to `typer.Option(default=...)`:\n\n{* docs_src/options/required/tutorial002_py310.py hl[7] *}\n\n/// info\n\nIf you hadn't seen that `...` before: it is a special single value, it is <a href=\"https://docs.python.org/3/library/constants.html#Ellipsis\" class=\"external-link\" target=\"_blank\">part of Python and is called \"Ellipsis\"</a>.\n\n///\n\nThat will tell **Typer** that it's still a *CLI option*, but it doesn't have a default value, and it's required.\n\n/// tip\n\nAgain, prefer to use the `Annotated` version if possible. That way your code will mean the same in standard Python and in **Typer**.\n\n///\n\nAnd test it:\n\n<div class=\"termy\">\n\n```console\n// Pass the NAME CLI argument\n$ python main.py Camila\n\n// We didn't pass the now required --lastname CLI option\nUsage: main.py [OPTIONS] NAME\nTry \"main.py --help\" for help.\n\nError: Missing option '--lastname'.\n\n// Now update it to pass the required --lastname CLI option\n$ python main.py Camila --lastname Gutiérrez\n\nHello Camila Gutiérrez\n\n// And if you check the help\n$ python main.py --help\n\nUsage: main.py [OPTIONS] NAME\n\nOptions:\n  --lastname TEXT       [required]\n  --help                Show this message and exit.\n\n// It now tells you that --lastname is required 🎉\n```\n\n</div>\n"
  },
  {
    "path": "docs/tutorial/options/version.md",
    "content": "# Version CLI Option, `is_eager`\n\nYou could use a callback to implement a `--version` *CLI option*.\n\nIt would show the version of your CLI program and then it would terminate it. Even before any other *CLI parameter* is processed.\n\n## First version of `--version`\n\nLet's see a first version of how it could look like:\n\n{* docs_src/options/version/tutorial001_an_py310.py hl[10:13,19:21] *}\n\n/// tip\n\nNotice that we don't have to get the `typer.Context` and check for `ctx.resilient_parsing` for completion to work, because we only print and modify the program when `--version` is passed, otherwise, nothing is printed or changed from the callback.\n\n///\n\nIf the `--version` *CLI option* is passed, we get a value `True` in the callback.\n\nThen we can print the version and raise `typer.Exit()` to make sure the program is terminated before anything else is executed.\n\nWe also declare the explicit *CLI option* name `--version`, because we don't want an automatic `--no-version`, it would look awkward.\n\nCheck it:\n\n<div class=\"termy\">\n\n```console\n$ python main.py --help\n\n// We get a --version, and don't get an awkward --no-version 🎉\nUsage: main.py [OPTIONS]\n\nOptions:\n  --version\n  --name TEXT\n  --help                Show this message and exit.\n\n\n// We can call it normally\n$ python main.py --name Camila\n\nHello Camila\n\n// And we can get the version\n$ python main.py --version\n\nAwesome CLI Version: 0.1.0\n\n// Because we exit in the callback, we don't get a \"Hello World\" message after the version 🚀\n```\n\n</div>\n\n## Previous parameters and `is_eager`\n\nBut now let's say that the `--name` *CLI option* that we declared before `--version` is required, and it has a callback that could exit the program:\n\n{* docs_src/options/version/tutorial002_an_py310.py hl[16:19,25:27] *}\n\nThen our CLI program could not work as expected in some cases as it is *right now*, because if we use `--version` after `--name` then the callback for `--name` will be processed before and we can get its error:\n\n<div class=\"termy\">\n\n```console\n$ python main.py --name Rick --version\n\nOnly Camila is allowed\nAborted!\n```\n\n</div>\n\n/// tip\n\nWe don't have to check for `ctx.resilient_parsing` in the `name_callback()` for completion to work, because we are not using `typer.echo()`, instead we are raising a `typer.BadParameter`.\n\n///\n\n/// note | Technical Details\n\n`typer.BadParameter` prints the error to \"standard error\", not to \"standard output\", and because the completion system only reads from \"standard output\", it won't break completion.\n\n///\n\n/// info\n\nIf you need a refresher about what is \"standard output\" and \"standard error\" check the section in [Printing and Colors: \"Standard Output\" and \"Standard Error\"](../printing.md#standard-output-and-standard-error){.internal-link target=_blank}.\n\n///\n\n### Fix with `is_eager`\n\nFor those cases, we can mark a *CLI parameter* (a *CLI option* or *CLI argument*) with `is_eager=True`.\n\nThat will tell **Typer** that it should process this *CLI parameter* before the others:\n\n{* docs_src/options/version/tutorial003_an_py310.py hl[25:28] *}\n\nCheck it:\n\n<div class=\"termy\">\n\n```console\n$ python main.py --name Rick --version\n\n// Now we only get the version, and the name is not used\nAwesome CLI Version: 0.1.0\n```\n\n</div>\n"
  },
  {
    "path": "docs/tutorial/options-autocompletion.md",
    "content": "# CLI Option autocompletion\n\nAs you have seen, apps built with **Typer** have completion in your shell that works when you create a Python package or using the `typer` command.\n\nIt normally completes *CLI options*, *CLI arguments*, and subcommands (that you will learn about later).\n\nBut you can also provide auto completion for the **values** of *CLI options* and *CLI arguments*. We will learn about that here.\n\n## Review completion\n\nBefore checking how to provide custom completions, let's check again how it works.\n\nAfter installing completion for your own Python package (or using the `typer` command), when you use your CLI program and start adding a *CLI option* with `--` and then hit <kbd>TAB</kbd>, your shell will show you the available *CLI options* (the same for *CLI arguments*, etc).\n\nTo check it quickly without creating a new Python package, use the `typer` command.\n\nThen let's create a small example program:\n\n{* docs_src/options_autocompletion/tutorial001_an_py310.py *}\n\nAnd let's try it with the `typer` command to get completion:\n\n<div class=\"termy\">\n\n```console\n// Hit the TAB key in your keyboard below where you see the: [TAB]\n$ typer ./main.py [TAB][TAB]\n\n// Depending on your terminal/shell you will get some completion like this ✨\nrun    -- Run the provided Typer app.\nutils  -- Extra utility commands for Typer apps.\n\n// Then try with \"run\" and --\n$ typer ./main.py run --[TAB][TAB]\n\n// You will get completion for --name, depending on your terminal it will look something like this\n--name  -- The name to say hi to.\n\n// And you can run it as if it was with Python directly\n$ typer ./main.py run --name Camila\n\nHello Camila\n```\n\n</div>\n\n## Custom completion for values\n\nRight now we get completion for the *CLI option* names, but not for the values.\n\nWe can provide completion for the values creating an `autocompletion` function, similar to the `callback` functions from [CLI Option Callback and Context](./options/callback-and-context.md){.internal-link target=_blank}:\n\n{* docs_src/options_autocompletion/tutorial002_an_py310.py hl[6:7,16] *}\n\nWe return a `list` of strings from the `complete_name()` function.\n\nAnd then we get those values when using completion:\n\n<div class=\"termy\">\n\n```console\n$ typer ./main.py run --name [TAB][TAB]\n\n// We get the values returned from the function 🎉\nCamila     Carlos     Sebastian\n```\n\n</div>\n\nWe got the basics working. Now let's improve it.\n\n## Check the incomplete value\n\nRight now, we always return those values, even if users start typing `Sebast` and then hit <kbd>TAB</kbd>, they will also get the completion for `Camila` and `Carlos` (depending on the shell), while we should only get completion for `Sebastian`.\n\nBut we can fix that so that it always works correctly.\n\nModify the `complete_name()` function to receive a parameter of type `str`, it will contain the incomplete value.\n\nThen we can check and return only the values that start with the incomplete value from the command line:\n\n{* docs_src/options_autocompletion/tutorial003_an_py310.py hl[8:13] *}\n\nNow let's try it:\n\n<div class=\"termy\">\n\n```console\n$ typer ./main.py run --name Ca[TAB][TAB]\n\n// We get the values returned from the function that start with Ca 🎉\nCamila     Carlos\n```\n\n</div>\n\nNow we are only returning the valid values, that start with `Ca`, we are no longer returning `Sebastian` as a completion option.\n\n/// tip\n\nYou have to declare the incomplete value of type `str` and that's what you will receive in the function.\n\nNo matter if the actual value will be an `int`, or something else, when doing completion, you will only get a `str` as the incomplete value.\n\nAnd the same way, you can only return `str`, not `int`, etc.\n\n///\n\n## Add help to completions\n\nRight now we are returning a `list` of `str`.\n\nBut some shells (Zsh, Fish, PowerShell) are capable of showing extra help text for completion.\n\nWe can provide that extra help text so that those shells can show it.\n\nIn the `complete_name()` function, instead of providing one `str` per completion element, we provide a `tuple` with 2 items. The first item is the actual completion string, and the second item is the help text.\n\nSo, in the end, we return a `list` of `tuples` of `str`:\n\n{* docs_src/options_autocompletion/tutorial004_an_py310.py hl[5:9,12:18] *}\n\n/// tip\n\nIf you want to have help text for each item, make sure each item in the list is a `tuple`. Not a `list`.\n\nIn the end, the return will be a `list` (or other iterable) of `tuples` of 2 `str`.\n\n///\n\n/// info\n\nThe help text will be visible in Zsh, Fish, and PowerShell.\n\nBash doesn't support showing the help text, but completion will still work the same.\n\n///\n\nIf you have a shell like Zsh, it would look like:\n\n<div class=\"termy\">\n\n```console\n$ typer ./main.py run --name [TAB][TAB]\n\n// We get the completion items with their help text 🎉\nCamila     -- The reader of books.\nCarlos     -- The writer of scripts.\nSebastian  -- The type hints guy.\n```\n\n</div>\n\n## Simplify with `yield`\n\nInstead of creating and returning a list with values (`str` or `tuple`), we can use `yield` with each value that we want in the completion.\n\nThat way our function will be a <a href=\"https://docs.python.org/3.8/glossary.html#index-19\" class=\"external-link\" target=\"_blank\">generator</a> that **Typer** can iterate:\n\n{* docs_src/options_autocompletion/tutorial005_an_py310.py hl[12:15] *}\n\nThat simplifies our code a bit and works the same.\n\n/// tip\n\nIf the `yield` part seems complex for you, don't worry, you can just use the version with the `list` above.\n\nIn the end, that's just to save us a couple of lines of code.\n\n///\n\n/// info\n\nThe function can use `yield`, so it doesn't have to return strictly a `list`, it just has to be <a href=\"https://docs.python.org/3.8/glossary.html#term-iterable\" class=\"external-link\" target=\"_blank\">iterable</a>.\n\nBut each of the elements for completion has to be a `str` or a `tuple` (when containing a help text).\n\n///\n\n## Access other *CLI parameters* with the Context\n\nLet's say that now we want to modify the program to be able to \"say hi\" to multiple people at the same time.\n\nSo, we will allow multiple `--name` *CLI options*.\n\n/// tip\n\nYou will learn more about *CLI parameters* with multiple values later in the tutorial.\n\nSo, for now, take this as a sneak peek 😉.\n\n///\n\nFor this we use a `list` of `str`:\n\n{* docs_src/options_autocompletion/tutorial006_an_py310.py hl[8:13] *}\n\nAnd then we can use it like:\n\n<div class=\"termy\">\n\n```console\n$ typer ./main.py run --name Camila --name Sebastian\n\nHello Camila\nHello Sebastian\n```\n\n</div>\n\n### Getting completion for multiple values\n\nAnd the same way as before, we want to provide **completion** for those names. But we don't want to provide the **same names** for completion if they were already given in previous parameters.\n\nFor that, we will access and use the \"Context\". Every Typer application has a special object called a \"Context\" that is normally hidden.\n\nBut you can access the context by declaring a function parameter of type `typer.Context`.\n\nAnd from that context you can get the current values for each parameter.\n\n{* docs_src/options_autocompletion/tutorial007_an_py310.py hl[12:13,15] *}\n\nWe are getting the `names` already provided with `--name` in the command line before this completion was triggered.\n\nIf there's no `--name` in the command line, it will be `None`, so we use `or []` to make sure we have a `list` (even if empty) to check its contents later.\n\nThen, when we have a completion candidate, we check if each `name` was already provided with `--name` by checking if it's in that list of `names` with `name not in names`.\n\nAnd then we `yield` each item that has not been used yet.\n\nCheck it:\n\n<div class=\"termy\">\n\n```console\n$ typer ./main.py run --name [TAB][TAB]\n\n// The first time we trigger completion, we get all the names\nCamila     -- The reader of books.\nCarlos     -- The writer of scripts.\nSebastian  -- The type hints guy.\n\n// Add a name and trigger completion again\n$ typer ./main.py run --name Sebastian --name Ca[TAB][TAB]\n\n// Now we get completion only for the names we haven't used 🎉\nCamila  -- The reader of books.\nCarlos  -- The writer of scripts.\n\n// And if we add another of the available names:\n$ typer ./main.py run --name Sebastian --name Camila --name [TAB][TAB]\n\n// We get completion for the only available one\nCarlos  -- The writer of scripts.\n```\n\n</div>\n\n/// tip\n\nIt's quite possible that if there's only one option left, your shell will complete it right away instead of showing the option with the help text, to save you more typing.\n\n///\n\n## Getting the raw *CLI parameters*\n\nYou can also get the raw *CLI parameters*, just a `list` of `str` with everything passed in the command line before the incomplete value.\n\nFor example, something like `[\"typer\", \"main.py\", \"run\", \"--name\"]`.\n\n/// tip\n\nThis would be for advanced scenarios, in most use cases you would be better off using the context.\n\nBut it's still possible if you need it.\n\n///\n\nAs a simple example, let's show it on the screen before completion.\n\nBecause completion is based on the output printed by your program (handled internally by **Typer**), during completion we can't just print something else as we normally do.\n\n### Printing to \"standard error\"\n\n/// tip\n\nIf you need a refresher about what is \"standard output\" and \"standard error\" check the section in [Printing and Colors: \"Standard Output\" and \"Standard Error\"](./printing.md#standard-output-and-standard-error){.internal-link target=_blank}.\n\n///\n\nThe completion system only reads from \"standard output\", so, printing to \"standard error\" won't break completion. 🚀\n\nYou can print to \"standard error\" with a **Rich** `Console(stderr=True)`.\n\nUsing `stderr=True` tells **Rich** that the output should be shown in \"standard error\".\n\n{* docs_src/options_autocompletion/tutorial008_an_py310.py hl[12,15:16] *}\n\n/// info\n\nIf you have disabled Rich, you can also use `print(lastname, file=sys.stderr)` or `typer.echo(\"some text\", err=True)` instead.\n\n///\n\nWe get all the *CLI parameters* as a raw `list` of `str` by declaring a parameter with type `list[str]`, here it's named `args`.\n\n/// tip\n\nHere we name the list of all the raw *CLI parameters* `args` because that's the usual convention.\n\nBut it doesn't contain only *CLI arguments*, it has everything, including *CLI options* and values, as a raw `list` of `str`.\n\n///\n\nAnd then we just print it to \"standard error\".\n\n<div class=\"termy\">\n\n```console\n$ typer ./main.py run --name [TAB][TAB]\n\n// First we see the raw CLI parameters\n['./main.py', 'run', '--name']\n\n// And then we see the actual completion\nCamila     -- The reader of books.\nCarlos     -- The writer of scripts.\nSebastian  -- The type hints guy.\n```\n\n</div>\n\n/// tip\n\nThis is a very simple (and quite useless) example, just so you know how it works and that you can use it.\n\nBut it's probably useful only in very advanced use cases.\n\n///\n\n## Getting the Context and the raw *CLI parameters*\n\nOf course, you can declare everything if you need it, the context, the raw *CLI parameters*, and the incomplete `str`:\n\n{* docs_src/options_autocompletion/tutorial009_an_py310.py hl[15] *}\n\nCheck it:\n\n<div class=\"termy\">\n\n```console\n$ typer ./main.py run --name [TAB][TAB]\n\n// First we see the raw CLI parameters\n['./main.py', 'run', '--name']\n\n// And then we see the actual completion\nCamila     -- The reader of books.\nCarlos     -- The writer of scripts.\nSebastian  -- The type hints guy.\n\n$ typer ./main.py run --name Sebastian --name Ca[TAB][TAB]\n\n// Again, we see the raw CLI parameters\n['./main.py', 'run', '--name', 'Sebastian', '--name']\n\n// And then we see the rest of the valid completion items\nCamila     -- The reader of books.\nCarlos     -- The writer of scripts.\n```\n\n</div>\n\n## Types, types everywhere\n\n**Typer** uses the type declarations to detect what it has to provide to your `autocompletion` function.\n\nYou can declare function parameters of these types:\n\n* `str`: for the incomplete value.\n* `typer.Context`: for the current context.\n* `list[str]`: for the raw *CLI parameters*.\n\nIt doesn't matter how you name them, in which order, or which ones of the 3 options you declare. It will all \"**just work**\" ✨\n"
  },
  {
    "path": "docs/tutorial/package.md",
    "content": "# Building a Package\n\nWhen you create a CLI program with **Typer** you probably want to create your own Python package.\n\nThat's what allows your users to install it and have it as an independent program that they can use in their terminal.\n\nAnd that's also required for shell auto completion to work (unless you use your program through the `typer` command).\n\nNowadays, there are several ways and tools to create Python packages (what you install with `pip install something` or `uv add something`).\n\nYou might even have your favorite already.\n\nHere's a very opinionated, short guide, showing one of the alternative ways of creating a Python package with a **Typer** app, from scratch.\n\n/// tip\n\nIf you already have a favorite way of creating Python packages, feel free to skip this.\n\n///\n\n## Prerequisites\n\nFor this guide we'll use <a href=\"https://docs.astral.sh/uv/\" class=\"external-link\" target=\"_blank\">uv</a>.\n\nuv's docs are great, so go ahead, check them and install it.\n\n## Create a project\n\nLet's say we want to create a CLI application called `portal-gun`.\n\nTo make sure your package doesn't collide with the package created by someone else, we'll name it with a prefix of your name.\n\nSo, if your name is Rick, we'll call it `rick-portal-gun`.\n\nCreate a project with uv:\n\n<div class=\"termy\">\n\n```console\n$ uv init --package rick-portal-gun\n\nInitialized project `rick-portal-gun` at `/home/rick-portal-gun`\n\n// Enter the new project directory\ncd ./rick-portal-gun\n```\n\n</div>\n\n## Dependencies and environment\n\nAdd `typer` to your dependencies:\n\n<div class=\"termy\">\n\n```console\n$ uv add typer\n\n// It creates a virtual environment for your project\nUsing CPython 3.14.0 interpreter at: /location/of/python/\nCreating virtual environment at: .venv\n\nResolved 10 packages in 21ms\n      Built rick-portal-gun @ file:/home/rick-portal-gun\nPrepared 1 package in 19ms\nInstalled 10 packages in 34ms\n + click==8.3.1\n + colorama==0.4.6\n + markdown-it-py==4.0.0\n + mdurl==0.1.2\n + pygments==2.19.2\n + rich==14.2.0\n + rick-portal-gun==0.1.0 (from file:/home/rick-portal-gun)\n + shellingham==1.5.4\n + typer==0.21.0\n + typing-extensions==4.15.0\n\n\n// Activate that new virtual environment\n$ source .venv/bin/activate\n\n// Open an editor using this new environment, for example VS Code\n$ code ./\n```\n\n</div>\n\nYou can see that you have a generated project structure that looks like:\n\n```\n.\n├── pyproject.toml\n├── README.md\n├── src\n│   └── rick_portal_gun\n│     └── __init__.py\n└── uv.lock\n```\n\n## Create your app\n\nNow let's create an extremely simple **Typer** app.\n\nCreate a file `src/rick_portal_gun/main.py` with:\n\n```Python\nimport typer\n\n\napp = typer.Typer()\n\n\n@app.callback()\ndef callback():\n    \"\"\"\n    Awesome Portal Gun\n    \"\"\"\n\n\n@app.command()\ndef shoot():\n    \"\"\"\n    Shoot the portal gun\n    \"\"\"\n    typer.echo(\"Shooting portal gun\")\n\n\n@app.command()\ndef load():\n    \"\"\"\n    Load the portal gun\n    \"\"\"\n    typer.echo(\"Loading portal gun\")\n```\n\n/// tip\n\nAs we are creating an installable Python package, there's no need to add a section with `if __name__ == \"__main__\":`.\n\n///\n\n## Modify the README\n\nLet's change the README to have something like:\n\n```Markdown\n# Portal Gun\n\nThe awesome Portal Gun\n```\n\n## Add a \"script\"\n\nWe are creating a Python package that can be installed with `uv add` or `pip install`.\n\nBut we want it to provide a CLI program that can be executed in the shell.\n\nTo do that, we add a configuration to the `pyproject.toml` in the section `[project.scripts]`:\n\n```TOML hl_lines=\"12 13\"\n[project]\nname = \"rick-portal-gun\"\nversion = \"0.1.0\"\ndescription = \"Add your description here\"\nreadme = \"README.md\"\nauthors = [\"Rick Sanchez <rick@example.com>\"]\nrequires-python = \">=3.14\"\ndependencies = [\n    \"typer>=0.21.0\",\n]\n\n[project.scripts]\nrick-portal-gun = \"rick_portal_gun.main:app\"\n\n[build-system]\nrequires = [\"uv_build>=0.8.14,<0.9.0\"]\nbuild-backend = \"uv_build\"\n```\n\nHere's what that line means:\n\n`rick-portal-gun`: will be the name of the CLI program. That's how we will call it in the terminal once it is installed. Like:\n\n<div class=\"termy\">\n\n```console\n$ rick-portal-gun\n\n// Something happens here ✨\n```\n\n</div>\n\n`rick_portal_gun.main`, in the part `\"rick_portal_gun.main:app\"`, with underscores, refers to the Python module to import. That's what someone would use in a section like:\n\n```Python\nfrom rick_portal_gun.main import # something goes here\n```\n\nThe `app` in `\"rick_portal_gun.main:app\"` is the thing to import from the module, and to call as a function, like:\n\n```Python\nfrom rick_portal_gun.main import app\napp()\n```\n\nThat config section tells uv that when this package is installed, we want it to create a command line program called `rick-portal-gun`.\n\nAnd that the object to call (like a function) is the one in the variable `app` inside of the module `rick_portal_gun.main`.\n\n## Install your package\n\nThat's what we need to create a package.\n\nYou can now install it:\n\n<div class=\"termy\">\n\n```console\n$ uv sync\n\nResolved 10 packages in 1ms\n      Built rick-portal-gun @ file:/home/rick-portal-gun\nPrepared 1 package in 18ms\nUninstalled 1 package in 1ms\nInstalled 1 package in 13ms\n ~ rick-portal-gun==0.1.0 (from file:/home/rick-portal-gun)\n\n```\n\n</div>\n\n## Try your CLI program\n\nYour package is installed in the environment created by uv, but you can already use it.\n\n<div class=\"termy\">\n\n```console\n// You can use the which program to check which rick-portal-gun program is available (if any)\n$ which rick-portal-gun\n\n// You get the one from your environment\n/home/rick-portal-gun/.venv/bin/rick-portal-gun\n\n// Try it\n$ rick-portal-gun --help\n\n// You get all the standard help\nUsage: rick-portal-gun [OPTIONS] COMMAND [ARGS]...\n\n  Awesome Portal Gun\n\nOptions:\n  --install-completion  Install completion for the current shell.\n  --show-completion     Show completion for the current shell, to copy it or customize the installation.\n\n  --help                Show this message and exit.\n\nCommands:\n  shoot  Shoot the portal gun\n  load   Load the portal gun\n```\n\n</div>\n\n## Create a wheel package\n\nPython packages have a standard format called a \"wheel\". It's a file that ends in `.whl`.\n\nYou can create a wheel with uv:\n\n<div class=\"termy\">\n\n```console\n$ uv build\n\nBuilding source distribution (uv build backend)...\nBuilding wheel from source distribution (uv build backend)...\nSuccessfully built dist/rick_portal_gun-0.1.0.tar.gz\nSuccessfully built dist/rick_portal_gun-0.1.0-py3-none-any.whl\n```\n\n</div>\n\nAfter that, if you check in your project directory, you should now have a couple of extra files at `./dist/`:\n\n``` hl_lines=\"3 4\"\n.\n├── dist\n│   ├── rick_portal_gun-0.1.0-py3-none-any.whl\n│   └── rick-portal-gun-0.1.0.tar.gz\n├── pyproject.toml\n├── README.md\n├── ...\n```\n\nThe `.whl` is the wheel file. You can send that wheel file to anyone and they can use it to install your program (we'll see how to upload it to PyPI in a bit).\n\n## Test your wheel package\n\nNow you can open another terminal and install that package from the file for your own user with:\n\n<div class=\"termy\">\n\n```console\n$ pip install --user /home/rick/rick-portal-gun/dist/rick_portal_gun-0.1.0-py3-none-any.whl\n\n---> 100%\n```\n\n</div>\n\n/// warning\n\nThe `--user` is important, that ensures you install it in your user's directory and not in the global system.\n\nIf you installed it in the global system (e.g. with `sudo`) you could install a version of a library (e.g. a sub-dependency) that is incompatible with your system.\n\n///\n\n/// tip\n\nBonus points if you use <a href=\"https://docs.astral.sh/uv/\" class=\"external-link\" target=\"_blank\">uvx</a> to install it while keeping an isolated environment for your Python CLI programs 🚀\n\n///\n\nNow you have your CLI program installed. And you can use it freely:\n\n<div class=\"termy\">\n\n```console\n$ rick-portal-gun shoot\n\n// It works 🎉\nShooting portal gun\n```\n\n</div>\n\nHaving it installed globally (and not in a single environment), you can now install completion globally for it:\n\n<div class=\"termy\">\n\n```console\n$ rick-portal-gun --install-completion\n\nzsh completion installed in /home/rick/.zshrc.\nCompletion will take effect once you restart the terminal.\n```\n\n</div>\n\n/// tip\n\nIf you want to remove completion you can just delete the added line in that file.\n\n///\n\nAnd after you restart the terminal you will get completion for your new CLI program:\n\n<div class=\"termy\">\n\n```console\n$ rick-portal-gun [TAB][TAB]\n\n// You get completion for your CLI program ✨\nload   -- Load the portal gun\nshoot  -- Shoot the portal gun\n```\n\n</div>\n\n## Support `python -m` (optional)\n\nYou may have seen that you can call many Python modules as scripts with `python -m some-module`.\n\nFor example, one way to call `pip` is:\n\n<div class=\"termy\">\n\n```console\n$ pip install fastapi\n```\n\n</div>\n\nBut you can also call Python with the `-m` *CLI Option* and pass a module for it to execute as if it was a script, like:\n\n<div class=\"termy\">\n\n```console\n$ python -m pip install fastapi\n```\n\n</div>\n\nHere we pass `pip` as the value for `-m`, so, Python will execute the module `pip` as if it was a script. And then it will pass the rest of the *CLI Parameters* (`install fastapi`) to it.\n\nThese two are more or less equivalent, the `install fastapi` will be passed to `pip`.\n\n/// tip\n\nIn the case of `pip`, in many occasions it's actually recommended that you run it with `python -m`, because if you create a virtual environment with its own `python`, that will ensure that you use the `pip` from *that* environment.\n\n///\n\n### Add a `__main__.py`\n\nYou can support that same style of calling the package/module for your own package, simply by adding a file `__main__.py`.\n\nPython will look for that file and execute it.\n\nThe file would live right beside `__init__.py` and `main.py`:\n\n``` hl_lines=\"7\"\n.\n├── pyproject.toml\n├── README.md\n├── src\n│   └── rick_portal_gun\n│     ├── __init__.py\n│     ├── __main__.py\n│     └── main.py\n└── uv.lock\n```\n\nNo other file has to import it, you don't have to reference it in your `pyproject.toml` or anything else, it just works by default, as it is standard Python behavior.\n\nThen in that file you can execute your **Typer** program:\n\n```Python\nfrom .main import app\napp()\n```\n\nNow, after installing your package, if you call it with `python -m` it will work:\n\n<div class=\"termy\">\n\n```console\n$ python -m rick_portal_gun --help\n\nUsage: python -m rick_portal_gun [OPTIONS] COMMAND [ARGS]...\n\n  Awesome Portal Gun\n\nOptions:\n  --install-completion  Install completion for the current shell.\n  --show-completion     Show completion for the current shell, to copy it or customize the installation.\n\n  --help                Show this message and exit.\n\nCommands:\n  shoot  Shoot the portal gun\n  load   Load the portal gun\n```\n\n</div>\n\n/// tip\n\nNotice that you have to pass the importable version of the package name, so `rick_portal_gun` instead of `rick-portal-gun`.\n\n///\n\nThat works! 🚀\n\n### Autocompletion and `python -m`\n\nHave in mind that TAB completion (shell auto-completion) won't work when using `python -m`.\n\nAuto-completion depends on the name of the program called, it's tied to each specific program name.\n\nSo, to have shell completion for `rick-portal-gun` you would have to call it directly:\n\n<div class=\"termy\">\n\n```console\n$ rick-portal-gun [TAB][TAB]\n```\n\n</div>\n\nBut you can still support `python -m` for the cases where it's useful.\n\n## Publish to PyPI (optional)\n\nYou can publish that new package to <a href=\"https://pypi.org/\" class=\"external-link\" target=\"_blank\">PyPI</a> to make it public, so others can install it easily.\n\nSo, go ahead and create an account there (it's free).\n\n### PyPI API token\n\nTo do it, you first need to configure a PyPI auth token.\n\nLogin to <a href=\"https://pypi.org/\" class=\"external-link\" target=\"_blank\">PyPI</a>.\n\nAnd then go to <a href=\"https://pypi.org/manage/account/token/\" class=\"external-link\" target=\"_blank\">https://pypi.org/manage/account/token/</a> to create a new token.\n\nLet's say your new API token is:\n\n```\npypi-wubalubadubdub-deadbeef1234\n```\n\nNow configure uv to use this token by setting an environment variable:\n\n<div class=\"termy\">\n\n```console\n$ export UV_PUBLISH_TOKEN=pypi-wubalubadubdub-deadbeef1234\n// It won't show any output, but it's already configured\n```\n\n</div>\n\n### Publish to PyPI\n\nNow you can publish your package.\n\n<div class=\"termy\">\n\n```console\n$ uv publish\n\nPublishing 2 files https://upload.pypi.org/legacy/\nUploading rick_portal_gun-0.1.0-py3-none-any.whl (2.3KiB)\nUploading rick_portal_gun-0.1.0.tar.gz (841.0B)\n```\n\n</div>\n\nNow you can go to PyPI and check your projects at <a href=\"https://pypi.org/manage/projects/\" class=\"external-link\" target=\"_blank\">https://pypi.org/manage/projects/</a>.\n\nYou should now see your new \"rick-portal-gun\" package.\n\n### Install from PyPI\n\nNow to see that we can install it from PyPI, open another terminal, and uninstall the currently installed package.\n\n<div class=\"termy\">\n\n```console\n$ pip uninstall rick-portal-gun\n\nFound existing installation: rick-portal-gun 0.1.0\nUninstalling rick-portal-gun-0.1.0:\n  Would remove:\n    /home/rick/.local/bin/rick-portal-gun\n    /home/rick/.local/lib/python3.10/site-packages/rick_portal_gun-0.1.0.dist-info/*\n    /home/rick/.local/lib/python3.10/site-packages/rick_portal_gun/*\n# Proceed (Y/n)? $ Y\n    Successfully uninstalled rick-portal-gun-0.1.0\n```\n\n</div>\n\nAnd now install it again, but this time using just the name, so that `pip` pulls it from PyPI:\n\n<div class=\"termy\">\n\n```console\n$ pip install --user rick-portal-gun\n\n// Notice that it says \"Downloading\" 🚀\nCollecting rick-portal-gun\n  Downloading rick_portal_gun-0.1.0-py3-none-any.whl.metadata (435 bytes)\nRequirement already satisfied: typer<0.13.0,>=0.12.3 in ./.local/lib/python3.10/site-packages (from rick-portal-gun==0.1.0) (0.12.3)\nRequirement already satisfied: typing-extensions>=3.7.4.3 in ./.local/lib/python3.10/site-packages (from typer<0.13.0,>=0.12.3->rick-portal-gun==0.1.0) (4.11.0)\nRequirement already satisfied: click>=8.0.0 in ./.local/lib/python3.10/site-packages (from typer<0.13.0,>=0.12.3->rick-portal-gun==0.1.0) (8.1.7)\nRequirement already satisfied: shellingham>=1.3.0 in ./.local/lib/python3.10/site-packages (from typer<0.13.0,>=0.12.3->rick-portal-gun==0.1.0) (1.5.4)\nRequirement already satisfied: rich>=10.11.0 in ./.local/lib/python3.10/site-packages (from typer<0.13.0,>=0.12.3->rick-portal-gun==0.1.0) (13.7.1)\nRequirement already satisfied: pygments<3.0.0,>=2.13.0 in ./.local/lib/python3.10/site-packages (from rich>=10.11.0->typer<0.13.0,>=0.12.3->rick-portal-gun==0.1.0) (2.17.2)\nRequirement already satisfied: markdown-it-py>=2.2.0 in ./.local/lib/python3.10/site-packages (from rich>=10.11.0->typer<0.13.0,>=0.12.3->rick-portal-gun==0.1.0) (3.0.0)\nRequirement already satisfied: mdurl~=0.1 in ./.local/lib/python3.10/site-packages (from markdown-it-py>=2.2.0->rich>=10.11.0->typer<0.13.0,>=0.12.3->rick-portal-gun==0.1.0) (0.1.2)\nDownloading rick_portal_gun-0.1.0-py3-none-any.whl (1.8 kB)\nInstalling collected packages: rick-portal-gun\nSuccessfully installed rick-portal-gun-0.1.0\n```\n\n</div>\n\nAnd now test the newly installed package from PyPI:\n\n<div class=\"termy\">\n\n```console\n$ rick-portal-gun load\n\n// It works! 🎉\nLoading portal gun\n```\n\n</div>\n\n## Generate docs\n\nYou can use the `typer` command to generate docs for your package that you can put in your `README.md`:\n\n<div class=\"termy\">\n\n```console\n$ typer rick_portal_gun.main utils docs --output README.md --name rick-portal-gun\n\nDocs saved to: README.md\n```\n\n</div>\n\nYou just have to pass it the module to import (`rick_portal_gun.main`) and it will detect the `typer.Typer` app automatically.\n\nBy specifying the `--name` of the program it will be able to use it while generating the docs.\n\n### Publish a new version with the docs\n\nNow you can publish a new version with the updated docs.\n\nFor that you need to first increase the version in `pyproject.toml`:\n\n```TOML hl_lines=\"3\"\n[project]\nname = \"rick-portal-gun\"\nversion = \"0.2.0\"\ndescription = \"Add your description here\"\nreadme = \"README.md\"\nauthors = [\"Rick Sanchez <rick@example.com>\"]\nrequires-python = \">=3.14\"\ndependencies = [\n    \"typer>=0.21.0\",\n]\n\n[project.scripts]\nrick-portal-gun = \"rick_portal_gun.main:app\"\n\n[build-system]\nrequires = [\"uv_build>=0.8.14,<0.9.0\"]\nbuild-backend = \"uv_build\"\n```\n\nAnd then build and publish again:\n\n<div class=\"termy\">\n\n```console\n$ uv build\n$ uv publish\n\nPublishing 2 files https://upload.pypi.org/legacy/\nUploading rick_portal_gun-0.2.0-py3-none-any.whl (2.3KiB)\nUploading rick_portal_gun-0.2.0.tar.gz (840.0B)\n```\n\n</div>\n\nAnd now you can go to PyPI, to the project page, and reload it, and it will now have your new generated docs.\n\n## What's next\n\nThis is a very simple guide. You could add many more steps.\n\nFor example, you should use <a href=\"https://git-scm.com/\" class=\"external-link\" target=\"_blank\">Git</a>, the version control system, to save your code.\n\nYou could use <a href=\"https://docs.astral.sh/uv/\" class=\"external-link\" target=\"_blank\">uv</a> to manage your installed CLI Python programs in isolated environments.\n\nMaybe use automatic formatting with <a href=\"https://docs.astral.sh/ruff/\" class=\"external-link\" target=\"_blank\">Ruff</a>.\n\nYou'll probably want to publish your code as open source to <a href=\"https://github.com/\" class=\"external-link\" target=\"_blank\">GitHub</a>.\n\nAnd then you could integrate a <abbr title=\"Continuous Integration\">CI</abbr> tool to run your tests and deploy your package automatically.\n\nAnd there's a long etc. But now you have the basics and you can continue on your own. 🚀\n"
  },
  {
    "path": "docs/tutorial/parameter-types/bool.md",
    "content": "# Boolean CLI Options\n\nWe have seen some examples of *CLI options* with `bool`, and how **Typer** creates `--something` and `--no-something` automatically.\n\nBut we can customize those names.\n\n## Only `--force`\n\nLet's say that we want a `--force` *CLI option* only, we want to discard `--no-force`.\n\nWe can do that by specifying the exact name we want:\n\n{* docs_src/parameter_types/bool/tutorial001_an_py310.py hl[9] *}\n\nNow there's only a `--force` *CLI option*:\n\n<div class=\"termy\">\n\n```console\n// Check the help\n$ python main.py --help\n\n// Notice there's only --force, we no longer have --no-force\nUsage: main.py [OPTIONS]\n\nOptions:\n  --force               [default: False]\n  --help                Show this message and exit.\n\n// Try it:\n$ python main.py\n\nNot forcing\n\n// Now add --force\n$ python main.py --force\n\nForcing operation\n\n// And --no-force no longer exists ⛔️\n$ python main.py --no-force\n\nUsage: main.py [OPTIONS]\nTry \"main.py --help\" for help.\n\nError: No such option: --no-force\n```\n\n</div>\n\n## Alternative names\n\nNow let's imagine we have a *CLI option* `--accept`.\n\nAnd we want to allow setting `--accept` or the contrary, but `--no-accept` looks ugly.\n\nWe might want to instead have `--accept` and `--reject`.\n\nWe can do that by passing a single `str` with the 2 names for the `bool` *CLI option* separated by `/`:\n\n{* docs_src/parameter_types/bool/tutorial002_an_py310.py hl[9] *}\n\nCheck it:\n\n<div class=\"termy\">\n\n```console\n// Check the help\n$ python main.py --help\n\n// Notice the --accept / --reject\nUsage: main.py [OPTIONS]\n\nOptions:\n  --accept / --reject\n  --help                Show this message and exit.\n\n// Try it\n$ python main.py\n\nI don't know what you want yet\n\n// Now pass --accept\n$ python main.py --accept\n\nAccepting!\n\n// And --reject\n$ python main.py --reject\n\nRejecting!\n```\n\n</div>\n\n## Short names\n\nThe same way, you can declare short versions of the names for these *CLI options*.\n\nFor example, let's say we want `-f` for `--force` and `-F` for `--no-force`:\n\n{* docs_src/parameter_types/bool/tutorial003_an_py310.py hl[9] *}\n\nCheck it:\n\n<div class=\"termy\">\n\n```console\n// Check the help\n$ python main.py --help\n\n// Notice the -f, --force / -F, --no-force\nUsage: main.py [OPTIONS]\n\nOptions:\n  -f, --force / -F, --no-force  [default: False]\n  --help                        Show this message and exit.\n\n// Try with the short name -f\n$ python main.py -f\n\nForcing operation\n\n// Try with the short name -F\n$ python main.py -F\n\nNot forcing\n```\n\n</div>\n\n## Only names for `False`\n\nIf you want to (although it might not be a good idea), you can declare only *CLI option* names to set the `False` value.\n\nTo do that, use a space and a single `/` and pass the negative name after:\n\n{* docs_src/parameter_types/bool/tutorial004_an_py310.py hl[9] *}\n\n/// tip\n\nHave in mind that it's a string with a preceding space and then a `/`.\n\nSo, it's `\" /-S\"` not `\"/-S\"`.\n\n///\n\nCheck it:\n\n<div class=\"termy\">\n\n```console\n// Check the help\n$ python main.py --help\n\n// Notice the / -d, --demo\nUsage: main.py [OPTIONS]\n\nOptions:\n   / -d, --demo         [default: True]\n  --help                Show this message and exit.\n\n// Try it\n$ python main.py\n\nRunning in production\n\n// Now pass --demo\n$ python main.py --demo\n\nRunning demo\n\n// And the short version\n$ python main.py -d\n\nRunning demo\n```\n\n</div>\n"
  },
  {
    "path": "docs/tutorial/parameter-types/custom-types.md",
    "content": "# Custom Types\n\nYou can easily use your own custom types in your **Typer** applications.\n\nThe way to do it is by providing a way to <abbr title=\"convert from some plain format, like the input text in the CLI, into Python objects\">parse</abbr> input into your own types.\n\n## Type Parser\n\n`typer.Argument` and `typer.Option` can create custom parameter types with a `parser` <abbr title=\"something that can be called like a function\">callable</abbr>.\n\n{* docs_src/parameter_types/custom_types/tutorial001_an_py310.py hl[14:15,23:24] *}\n\nThe function (or callable) that you pass to the parameter `parser` will receive the input value as a string and should return the parsed value with your own custom type.\n"
  },
  {
    "path": "docs/tutorial/parameter-types/datetime.md",
    "content": "# DateTime\n\nYou can specify a *CLI parameter* as a Python <a href=\"https://docs.python.org/3/library/datetime.html\" class=\"external-link\" target=\"_blank\">`datetime`</a>.\n\nYour function will receive a standard Python `datetime` object, and again, your editor will give you completion, etc.\n\n{* docs_src/parameter_types/datetime/tutorial001_py310.py hl[1,9,10,11] *}\n\nTyper will accept any string from the following formats:\n\n* `%Y-%m-%d`\n* `%Y-%m-%dT%H:%M:%S`\n* `%Y-%m-%d %H:%M:%S`\n\nCheck it:\n\n<div class=\"termy\">\n\n```console\n$ python main.py --help\n\nUsage: main.py [OPTIONS] BIRTH:[%Y-%m-%d|%Y-%m-%dT%H:%M:%S|%Y-%m-%d %H:%M:%S]\n\nArguments:\n  BIRTH:[%Y-%m-%d|%Y-%m-%dT%H:%M:%S|%Y-%m-%d %H:%M:%S][required]\n\nOptions:\n  --help                Show this message and exit.\n\n// Pass a datetime\n$ python main.py 1956-01-31T10:00:00\n\nInteresting day to be born: 1956-01-31 10:00:00\nBirth hour: 10\n\n// An invalid date\n$ python main.py july-19-1989\n\nUsage: main.py [OPTIONS] [%Y-%m-%d|%Y-%m-%dT%H:%M:%S|%Y-%m-%d%H:%M:%S]\n\nError: Invalid value for 'BIRTH:[%Y-%m-%d|%Y-%m-%dT%H:%M:%S|%Y-%m-%d %H:%M:%S]': 'july-19-1989' does not match the formats '%Y-%m-%d', '%Y-%m-%dT%H:%M:%S', '%Y-%m-%d %H:%M:%S'.\n```\n\n</div>\n\n## Custom date format\n\nYou can also customize the formats received for the `datetime` with the `formats` parameter.\n\n`formats` receives a list of strings with the date formats that would be passed to <a href=\"https://docs.python.org/3/library/datetime.html#datetime.datetime.strptime\" class=\"external-link\" target=\"_blank\">datetime.strptime()</a>.\n\nFor example, let's imagine that you want to accept an ISO formatted datetime, but for some strange reason, you also want to accept a format with:\n\n* first the month\n* then the day\n* then the year\n* separated with \"`/`\"\n\n...It's a crazy example, but let's say you also needed that strange format:\n\n{* docs_src/parameter_types/datetime/tutorial002_an_py310.py hl[14] *}\n\n/// tip\n\nNotice the last string in `formats`: `\"%m/%d/%Y\"`.\n\n///\n\nCheck it:\n\n<div class=\"termy\">\n\n```console\n// ISO dates work\n$ python main.py 1969-10-29\n\nLaunch will be at: 1969-10-29 00:00:00\n\n// But the strange custom format also works\n$ python main.py 10/29/1969\n\nLaunch will be at: 1969-10-29 00:00:00\n```\n\n</div>\n"
  },
  {
    "path": "docs/tutorial/parameter-types/enum.md",
    "content": "# Enum - Choices\n\nTo define a *CLI parameter* that can take a value from a predefined set of values you can use a standard Python <a href=\"https://docs.python.org/3/library/enum.html\" class=\"external-link\" target=\"_blank\">`enum.Enum`</a>:\n\n{* docs_src/parameter_types/enum/tutorial001_py310.py hl[1,6:9,16:17] *}\n\n/// tip\n\nNotice that the function parameter `network` will be an `Enum`, not a `str`.\n\nTo get the `str` value in your function's code use `network.value`.\n\n///\n\nCheck it:\n\n<div class=\"termy\">\n\n```console\n$ python main.py --help\n\n// Notice the predefined values [simple|conv|lstm]\nUsage: main.py [OPTIONS]\n\nOptions:\n  --network [simple|conv|lstm]  [default: simple]\n  --help                        Show this message and exit.\n\n// Try it\n$ python main.py --network conv\n\nTraining neural network of type: conv\n\n// Invalid value\n$ python main.py --network capsule\n\nUsage: main.py [OPTIONS]\nTry \"main.py --help\" for help.\n\nError: Invalid value for '--network': 'capsule' is not one of 'simple', 'conv', 'lstm'.\n\n// Note that enums are case sensitive by default\n$ python main.py --network CONV\n\nUsage: main.py [OPTIONS]\nTry \"main.py --help\" for help.\n\nError: Invalid value for '--network': 'CONV' is not one of 'simple', 'conv', 'lstm'.\n```\n\n</div>\n\n### Case insensitive Enum choices\n\nYou can make an `Enum` (choice) *CLI parameter* be case-insensitive with the `case_sensitive` parameter:\n\n{* docs_src/parameter_types/enum/tutorial002_an_py310.py hl[19] *}\n\nAnd then the values of the `Enum` will be checked no matter if lower case, upper case, or a mix:\n\n<div class=\"termy\">\n\n```console\n// Notice the upper case CONV\n$ python main.py --network CONV\n\nTraining neural network of type: conv\n\n// A mix also works\n$ python main.py --network LsTm\n\nTraining neural network of type: lstm\n```\n\n</div>\n\n### List of Enum values\n\nA *CLI parameter* can also take a list of `Enum` values:\n\n{* docs_src/parameter_types/enum/tutorial003_an_py310.py hl[17] *}\n\nThis works just like any other parameter value taking a list of things:\n\n<div class=\"termy\">\n\n```console\n$ python main.py --help\n\n// Notice the default values being shown\nUsage: main.py [OPTIONS]\n\nOptions:\n  --groceries [Eggs|Bacon|Cheese]  [default: Eggs, Cheese]\n  --help                           Show this message and exit.\n\n// Try it with the default values\n$ python main.py\n\nBuying groceries: Eggs, Cheese\n\n// Try it with a single value\n$ python main.py --groceries \"Eggs\"\n\nBuying groceries: Eggs\n\n// Try it with multiple values\n$ python main.py --groceries \"Eggs\" --groceries \"Bacon\"\n\nBuying groceries: Eggs, Bacon\n```\n\n</div>\n\n### Literal choices\n\nYou can also use `Literal` to represent a set of possible predefined choices, without having to use an `Enum`:\n\n{* docs_src/parameter_types/enum/tutorial004_an_py310.py hl[10] *}\n\n<div class=\"termy\">\n\n```console\n$ python main.py --help\n\n// Notice the predefined values [simple|conv|lstm]\nUsage: main.py [OPTIONS]\n\nOptions:\n  --network [simple|conv|lstm]  [default: simple]\n  --help                        Show this message and exit.\n\n// Try it\n$ python main.py --network conv\n\nTraining neural network of type: conv\n\n// Invalid value\n$ python main.py --network capsule\n\nUsage: main.py [OPTIONS]\nTry \"main.py --help\" for help.\n\nError: Invalid value for '--network': 'capsule' is not one of 'simple', 'conv', 'lstm'.\n```\n\n</div>\n"
  },
  {
    "path": "docs/tutorial/parameter-types/file.md",
    "content": "# File\n\nApart from `Path` *CLI parameters* you can also declare some types of \"files\".\n\n/// tip\n\nIn most of the cases you are probably fine just using `Path`.\n\nYou can read and write data with `Path` the same way.\n\n///\n\nThe difference is that these types will give you a Python <a href=\"https://docs.python.org/3/glossary.html#term-file-object\" class=\"external-link\" target=\"_blank\">file-like object</a> instead of a Python <a href=\"https://docs.python.org/3/library/pathlib.html#basic-use\" class=\"external-link\" target=\"_blank\">Path</a>.\n\nA \"file-like object\" is the same type of object returned by `open()` as in:\n\n```Python\nwith open('file.txt') as f:\n    # Here f is the file-like object\n    read_data = f.read()\n    print(read_data)\n```\n\nBut in some special use cases you might want to use these special types. For example if you are migrating an existing application.\n\n## `FileText` reading\n\n`typer.FileText` gives you a file-like object for reading text, you will get `str` data from it.\n\nThis means that even if your file has text written in a non-english language, e.g. a `text.txt` file with:\n\n```\nla cigüeña trae al niño\n```\n\nYou will have a `str` with the text inside, e.g.:\n\n```Python\ncontent = \"la cigüeña trae al niño\"\n```\n\ninstead of having `bytes`, e.g.:\n\n```Python\ncontent = b\"la cig\\xc3\\xbce\\xc3\\xb1a trae al ni\\xc3\\xb1o\"\n```\n\nYou will get all the correct editor support, attributes, methods, etc for the file-like object:`\n\n{* docs_src/parameter_types/file/tutorial001_an_py310.py hl[9] *}\n\nCheck it:\n\n<div class=\"termy\">\n\n```console\n// Create a quick text config\n$ echo \"some settings\" > config.txt\n\n// Add another line to the config to test it\n$ echo \"some more settings\" >> config.txt\n\n// Now run your program\n$ python main.py --config config.txt\n\nConfig line: some settings\n\nConfig line: some more settings\n```\n\n</div>\n\n## `FileTextWrite`\n\nFor writing text, you can use `typer.FileTextWrite`:\n\n{* docs_src/parameter_types/file/tutorial002_an_py310.py hl[9:10] *}\n\nThis would be for writing human text, like:\n\n```\nsome settings\nla cigüeña trae al niño\n```\n\n...not to write binary `bytes`.\n\nCheck it:\n\n<div class=\"termy\">\n\n```console\n$ python main.py --config text.txt\n\nConfig written\n\n// Check the contents of the file\n$ cat text.txt\n\nSome config written by the app\n```\n\n</div>\n\n/// info | Technical Details\n\n`typer.FileTextWrite` is a just a convenience class.\n\nIt's the same as using `typer.FileText` and setting `mode=\"w\"`. You will learn about `mode` later below.\n\n///\n\n## `FileBinaryRead`\n\nTo read binary data you can use `typer.FileBinaryRead`.\n\nYou will receive `bytes` from it.\n\nIt's useful for reading binary files like images:\n\n{* docs_src/parameter_types/file/tutorial003_an_py310.py hl[9] *}\n\nCheck it:\n\n<div class=\"termy\">\n\n```console\n$ python main.py --file lena.jpg\n\nProcessed bytes total: 512\nProcessed bytes total: 1024\nProcessed bytes total: 1536\nProcessed bytes total: 2048\n```\n\n</div>\n\n## `FileBinaryWrite`\n\nTo write binary data you can use `typer.FileBinaryWrite`.\n\nYou would write `bytes` to it.\n\nIt's useful for writing binary files like images.\n\nHave in mind that you have to pass `bytes` to its `.write()` method, not `str`.\n\nIf you have a `str`, you have to encode it first to get `bytes`.\n\n{* docs_src/parameter_types/file/tutorial004_an_py310.py hl[9] *}\n\n<div class=\"termy\">\n\n```console\n$ python main.py --file binary.dat\n\nBinary file written\n\n// Check the binary file was created\n$ ls ./binary.dat\n\n./binary.dat\n```\n\n</div>\n\n## File *CLI parameter* configurations\n\nYou can use several configuration parameters for these types (classes) in `typer.Option()` and `typer.Argument()`:\n\n* `mode`: controls the \"<a href=\"https://docs.python.org/3/library/functions.html#open\" class=\"external-link\" target=\"_blank\">mode</a>\" to open the file with.\n    * It's automatically set for you by using the classes above.\n    * Read more about it below.\n* `encoding`: to force a specific encoding, e.g. `\"utf-8\"`.\n* `lazy`: delay <abbr title=\"input and output, reading and writing files\">I/O</abbr> operations. Automatic by default.\n    * By default, when writing files, Typer will generate a file-like object that is not yet the actual file. Once you start writing, it will go, open the file and start writing to it, but not before. This is mainly useful to avoid creating the file until you start writing to it. It's normally safe to leave this automatic. But you can overwrite it setting `lazy=False`. By default, it's `lazy=True` for writing and `lazy=False` for reading.\n* `atomic`: if true, all writes will actually go to a temporal file and then moved to the final destination after completing. This is useful with files modified frequently by several users/programs.\n\n## Advanced `mode`\n\nBy default, **Typer** will configure the <a href=\"https://docs.python.org/3/library/functions.html#open\" class=\"external-link\" target=\"_blank\">`mode`</a> for you:\n\n* `typer.FileText`: `mode=\"r\"`, to read text.\n* `typer.FileTextWrite`: `mode=\"w\"`, to write text.\n* `typer.FileBinaryRead`: `mode=\"rb\"`, to read binary data.\n* `typer.FileBinaryWrite`: `mode=\"wb\"`, to write binary data.\n\n### Note about `FileTextWrite`\n\n`typer.FileTextWrite` is actually just a convenience class. It's the same as using `typer.FileText` with `mode=\"w\"`.\n\nBut it's probably shorter and more intuitive as you can get it with autocompletion in your editor by just starting to type `typer.File`... just like the other classes.\n\n### Customize `mode`\n\nYou can override the `mode` from the defaults above.\n\nFor example, you could use `mode=\"a\"` to write \"appending\" to the same file:\n\n{* docs_src/parameter_types/file/tutorial005_an_py310.py hl[9] *}\n\n/// tip\n\nAs you are manually setting `mode=\"a\"`, you can use `typer.FileText` or `typer.FileTextWrite`, both will work.\n\n///\n\nCheck it:\n\n<div class=\"termy\">\n\n```console\n$ python main.py --config config.txt\n\nConfig line written\n\n// Run your program a couple more times to see how it appends instead of overwriting\n$ python main.py --config config.txt\n\nConfig line written\n\n$ python main.py --config config.txt\n\nConfig line written\n\n// Check the contents of the file, it should have each of the 3 lines appended\n$ cat config.txt\n\nThis is a single line\nThis is a single line\nThis is a single line\n```\n\n</div>\n"
  },
  {
    "path": "docs/tutorial/parameter-types/index.md",
    "content": "# CLI Parameter Types\n\nYou can use several data types for the *CLI options* and *CLI arguments*, and you can add data validation requirements too.\n\n## Data conversion\n\nWhen you declare a *CLI parameter* with some type **Typer** will convert the data received in the command line to that data type.\n\nFor example:\n\n{* docs_src/parameter_types/index/tutorial001_py310.py hl[7] *}\n\nIn this example, the value received for the *CLI argument* `NAME` will be treated as `str`.\n\nThe value for the *CLI option* `--age` will be converted to an `int` and `--height-meters` will be converted to a `float`.\n\nAnd as `female` is a `bool` *CLI option*, **Typer** will convert it to a \"flag\" `--female` and the counterpart `--no-female`.\n\nAnd here's how it looks like:\n\n<div class=\"termy\">\n\n```console\n$ python main.py --help\n\n// Notice how --age is an INTEGER and --height-meters is a FLOAT\nUsage: main.py [OPTIONS] NAME\n\nArguments:\n  NAME  [required]\n\nOptions:\n  --age INTEGER           [default: 20]\n  --height-meters FLOAT   [default: 1.89]\n  --female / --no-female  [default: True]\n  --help                  Show this message and exit.\n\n// Call it with CLI parameters\n$ python main.py Camila --age 15 --height-meters 1.70 --female\n\n// All the data has the correct Python type\nNAME is Camila, of type: class 'str'\n--age is 15, of type: class 'int'\n--height-meters is 1.7, of type: class 'float'\n--female is True, of type: class 'bool'\n\n// And if you pass an incorrect type\n$ python main.py Camila --age 15.3\n\nUsage: main.py [OPTIONS] NAME\nTry \"main.py --help\" for help.\n\nError: Invalid value for '--age': '15.3' is not a valid integer\n\n// Because 15.3 is not an INTEGER (it's a float)\n```\n\n</div>\n\n## Watch next\n\nSee more about specific types and validations in the next sections...\n"
  },
  {
    "path": "docs/tutorial/parameter-types/number.md",
    "content": "# Number\n\nYou can define numeric validations with `max` and `min` values for `int` and `float` *CLI parameters*:\n\n{* docs_src/parameter_types/number/tutorial001_an_py310.py hl[10:12] *}\n\n*CLI arguments* and *CLI options* can both use these validations.\n\nYou can specify `min`, `max` or both.\n\nCheck it:\n\n<div class=\"termy\">\n\n```console\n$ python main.py --help\n\n// Notice the extra RANGE in the help text for --age and --score\nUsage: main.py [OPTIONS] ID\n\nArguments:\n  ID  [required]\n\nOptions:\n  --age INTEGER RANGE   [default: 20]\n  --score FLOAT RANGE   [default: 0]\n  --help                Show this message and exit.\n\n// Pass all the CLI parameters\n$ python main.py 5 --age 20 --score 90\n\nID is 5\n--age is 20\n--score is 90.0\n\n// Pass an invalid ID\n$ python main.py 1002\n\nUsage: main.py [OPTIONS] ID\nTry \"main.py --help\" for help.\n\nError: Invalid value for 'ID': 1002 is not in the range 0<=x<=1000.\n\n// Pass an invalid age\n$ python main.py 5 --age 15\n\nUsage: main.py [OPTIONS] ID\nTry \"main.py --help\" for help.\n\nError: Invalid value for '--age': 15 is not in the range x>=18.\n\n// Pass an invalid score\n$ python main.py 5 --age 20 --score 100.5\n\nUsage: main.py [OPTIONS] ID\nTry \"main.py --help\" for help.\n\nError: Invalid value for '--score': 100.5 is not in the range x<=100.\n\n// But as we didn't specify a minimum score, this is accepted\n$ python main.py 5 --age 20 --score -5\n\nID is 5\n--age is 20\n--score is -5.0\n```\n\n</div>\n\n## Clamping numbers\n\nYou might want to, instead of showing an error, use the closest minimum or maximum valid values.\n\nYou can do it with the `clamp` parameter:\n\n{* docs_src/parameter_types/number/tutorial002_an_py310.py hl[10:12] *}\n\nAnd then, when you pass data that is out of the valid range, it will be \"clamped\", the closest valid value will be used:\n\n<div class=\"termy\">\n\n```console\n// ID doesn't have clamp, so it shows an error\n$ python main.py 1002\n\nUsage: main.py [OPTIONS] ID\nTry \"main.py --help\" for help.\n\nError: Invalid value for 'ID': 1002 is not in the range 0<=x<=1000.\n\n// But --rank and --score use clamp\n$ python main.py 5 --rank 11 --score -5\n\nID is 5\n--rank is 10\n--score is 0\n```\n\n</div>\n\n## Counter *CLI options*\n\nYou can make a *CLI option* work as a counter with the `count` parameter:\n\n{* docs_src/parameter_types/number/tutorial003_an_py310.py hl[9] *}\n\nIt means that the *CLI option* will be like a boolean flag, e.g. `--verbose`.\n\nAnd the value you receive in the function will be the amount of times that `--verbose` was added:\n\n<div class=\"termy\">\n\n```console\n// Check it\n$ python main.py\n\nVerbose level is 0\n\n// Now use one --verbose\n$ python main.py --verbose\n\nVerbose level is 1\n\n// Now 3 --verbose\n$ python main.py --verbose --verbose --verbose\n\nVerbose level is 3\n\n// And with the short name\n$ python main.py -v\n\nVerbose level is 1\n\n// And with the short name 3 times\n$ python main.py -v -v -v\n\nVerbose level is 3\n\n// As short names can be put together, this also works\n$ python main.py -vvv\n\nVerbose level is 3\n```\n\n</div>\n"
  },
  {
    "path": "docs/tutorial/parameter-types/path.md",
    "content": "# Path\n\nYou can declare a *CLI parameter* to be a standard Python <a href=\"https://docs.python.org/3/library/pathlib.html#basic-use\" class=\"external-link\" target=\"_blank\">`pathlib.Path`</a>.\n\nThis is what you would do for directory paths, file paths, etc:\n\n{* docs_src/parameter_types/path/tutorial001_an_py310.py hl[1,10] *}\n\nAnd again, as you receive a standard Python `Path` object the same as the type annotation, your editor will give you autocompletion for all its attributes and methods.\n\nCheck it:\n\n<div class=\"termy\">\n\n```console\n// No config\n$ python main.py\n\nNo config file\nAborted!\n\n// Pass a config that doesn't exist\n$ python main.py --config config.txt\n\nThe config doesn't exist\n\n// Now create a quick config\n$ echo \"some settings\" > config.txt\n\n// And try again\n$ python main.py --config config.txt\n\nConfig file contents: some settings\n\n// And with a directory\n$ python main.py --config ./\n\nConfig is a directory, will use all its config files\n```\n\n</div>\n\n## Path validations\n\nYou can perform several validations for `Path` *CLI parameters*:\n\n* `exists`: if set to true, the file or directory needs to exist for this value to be valid. If this is not required and a file does indeed not exist, then all further checks are silently skipped.\n* `file_okay`: controls if a file is a possible value.\n* `dir_okay`: controls if a directory is a possible value.\n* `writable`: if true, a writable check is performed.\n* `readable`: if true, a readable check is performed.\n* `resolve_path`: if this is true, then the path is fully resolved before the value is passed onwards. This means that it’s absolute and <abbr title=\"symbolic links, also known as shortcuts. Links in a file system that point to other location. For example, some applications when installed create symlinks in the desktop to launch them.\">symlinks</abbr> are resolved.\n\n/// note | Technical Details\n\nIt will not expand a tilde-prefix (something with `~`, like `~/Documents/`), as this is supposed to be done by the shell only.\n\n///\n\nFor example:\n\n{* docs_src/parameter_types/path/tutorial002_an_py310.py hl[14:19] *}\n\nCheck it:\n\n<div class=\"termy\">\n\n```console\n$ python main.py --config config.txt\n\nUsage: main.py [OPTIONS]\nTry \"main.py --help\" for help.\n\nError: Invalid value for '--config': File 'config.txt' does not exist.\n\n// Now create a quick config\n$ echo \"some settings\" > config.txt\n\n// And try again\n$ python main.py --config config.txt\n\nConfig file contents: some settings\n\n// And with a directory\n$ python main.py --config ./\n\nUsage: main.py [OPTIONS]\nTry \"main.py --help\" for help.\n\nError: Invalid value for '--config': File './' is a directory.\n```\n\n</div>\n\n### Advanced `Path` configurations\n\n/// warning | Advanced Details\n\nYou probably won't need these configurations at first, you may want to skip it.\n\nThey are used for more advanced use cases.\n\n///\n\n* `allow_dash`: If this is set to True, a single dash to indicate standard streams is permitted.\n* `path_type`: optionally a string type that should be used to represent the path. The default is `None` which means the return value will be either bytes or unicode depending on what makes most sense given the input data.\n"
  },
  {
    "path": "docs/tutorial/parameter-types/uuid.md",
    "content": "# UUID\n\n/// info\n\nA UUID is a <a href=\"https://en.wikipedia.org/wiki/Universally_unique_identifier\" class=\"external-link\" target=\"_blank\">\"Universally Unique Identifier\"</a>.\n\nIt's a standard format for identifiers, like passport numbers, but for anything, not just people in countries.\n\nThey look like this:\n\n```\nd48edaa6-871a-4082-a196-4daab372d4a1\n```\n\nThe way they are generated makes them sufficiently long and random that you could assume that every UUID generated is unique. Even if it was generated by a different application, database, or system.\n\nSo, if your system uses UUIDs to identify your data, you could mix it with the data from some other system that also uses UUIDs with some confidence that their IDs (UUIDs) won't clash with yours.\n\nThis wouldn't be true if you just used `int`s as identifiers, as most databases do.\n\n///\n\nYou can declare a *CLI parameter* as a UUID:\n\n{* docs_src/parameter_types/uuid/tutorial001_py310.py hl[1,9:11] *}\n\nYour Python code will receive a standard Python <a href=\"https://docs.python.org/3.8/library/uuid.html\" class=\"external-link\" target=\"_blank\">`UUID`</a> object with all its attributes and methods, and as you are annotating your function parameter with that type, you will have type checks, autocompletion in your editor, etc.\n\nCheck it:\n\n<div class=\"termy\">\n\n```console\n// Pass a valid UUID v4\n$ python main.py d48edaa6-871a-4082-a196-4daab372d4a1\n\nUSER_ID is d48edaa6-871a-4082-a196-4daab372d4a1\nUUID version is: 4\n\n// An invalid value\n$ python main.py 7479706572-72756c6573\n\nUsage: main.py [OPTIONS] USER_ID\nTry \"main.py --help\" for help.\n\nError: Invalid value for 'USER_ID': 7479706572-72756c6573 is not a valid UUID.\n```\n\n</div>\n"
  },
  {
    "path": "docs/tutorial/printing.md",
    "content": "# Printing and Colors\n\nYou can use the normal `print()` to show information on the screen:\n\n{* docs_src/typer_app/tutorial001_py310.py hl[8] *}\n\nIt will show the output normally:\n\n<div class=\"termy\">\n\n```console\n$ python main.py World\n\nHello World\n```\n\n</div>\n\n## Use Rich\n\nYou can also display beautiful and more complex information using <a href=\"https://rich.readthedocs.io/\" class=\"external-link\" target=\"_blank\">Rich</a>. It comes by default when you install `typer`.\n\n### Use Rich `print`\n\nFor the simplest cases, you can just import `print` from `rich` and use it instead of the standard `print`:\n\n{* docs_src/printing/tutorial001_py310.py hl[2,18] *}\n\nJust with that, **Rich** will be able to print your data with nice colors and structure:\n\n<div class=\"termy\">\n\n```console\n$ python main.py\n\nHere's the data\n<b>{</b>\n    <font color=\"#A6E22E\">&apos;name&apos;</font>: <font color=\"#A6E22E\">&apos;Rick&apos;</font>,\n    <font color=\"#A6E22E\">&apos;age&apos;</font>: <font color=\"#A1EFE4\"><b>42</b></font>,\n    <font color=\"#A6E22E\">&apos;items&apos;</font>: <b>[</b>\n        <b>{</b><font color=\"#A6E22E\">&apos;name&apos;</font>: <font color=\"#A6E22E\">&apos;Portal Gun&apos;</font><b>}</b>,\n        <b>{</b><font color=\"#A6E22E\">&apos;name&apos;</font>: <font color=\"#A6E22E\">&apos;Plumbus&apos;</font><b>}</b>\n    <b>]</b>,\n    <font color=\"#A6E22E\">&apos;active&apos;</font>: <font color=\"#A6E22E\"><i>True</i></font>,\n    <font color=\"#A6E22E\">&apos;affiliation&apos;</font>: <font color=\"#AE81FF\"><i>None</i></font>\n<b>}</b>\n```\n\n</div>\n\n### Rich Markup\n\nRich also supports a <a href=\"https://rich.readthedocs.io/en/stable/markup.html\" class=\"external-link\" target=\"_blank\">custom markup syntax</a> to set colors and styles, for example:\n\n{* docs_src/printing/tutorial002_py310.py hl[9] *}\n\n<div class=\"termy\">\n\n```console\n$ python main.py\n\n<font color=\"#F92672\"><b>Alert!</b></font> <font color=\"#A6E22E\">Portal gun</font> shooting! 💥\n```\n\n</div>\n\nIn this example you can see how to use font styles, colors, and even emojis.\n\nTo learn more check out the <a href=\"https://rich.readthedocs.io/en/stable/markup.html\" class=\"external-link\" target=\"_blank\">Rich docs</a>.\n\n### Rich Tables\n\nThe way Rich works internally is that it uses a `Console` object to display the information.\n\nWhen you call Rich's `print`, it automatically creates this object and uses it.\n\nBut for advanced use cases, you could create a `Console` yourself.\n\n{* docs_src/printing/tutorial003_py310.py hl[2:3,5,12:15] *}\n\nIn this example, we create a `Console`, and a `Table`. And then we can add some rows to the table, and print it.\n\nIf you run it, you will see a nicely formatted table:\n\n<div class=\"termy\">\n\n```console\n$ python main.py\n\n┏━━━━━━━┳━━━━━━━━━━━━┓\n┃<b> Name  </b>┃<b> Item       </b>┃\n┡━━━━━━━╇━━━━━━━━━━━━┩\n│ Rick  │ Portal Gun │\n│ Morty │ Plumbus    │\n└───────┴────────────┘\n```\n\n</div>\n\nRich has many other features, as an example, you can check the docs for:\n\n* <a href=\"https://rich.readthedocs.io/en/stable/prompt.html\" class=\"external-link\" target=\"_blank\">Prompt</a>\n* <a href=\"https://rich.readthedocs.io/en/stable/markdown.html\" class=\"external-link\" target=\"_blank\">Markdown</a>\n* <a href=\"https://rich.readthedocs.io/en/stable/panel.html\" class=\"external-link\" target=\"_blank\">Panel</a>\n* ...and more.\n\n### Typer and Rich\n\nIf you are wondering what tool should be used for what, **Typer** is useful for structuring the command line application, with options, arguments, subcommands, data validation, etc.\n\nIn general, **Typer** tends to be the entry point to your program, taking the first input from the user.\n\n**Rich** is useful for the parts that need to *display* information. Showing beautiful content on the screen.\n\nThe best results for your command line application are achieved combining both **Typer** and **Rich**.\n\n## \"Standard Output\" and \"Standard Error\"\n\nThe way printing works underneath is that the **operating system** (Linux, Windows, macOS) treats what we print as if our CLI program was **writing text** to a \"**virtual file**\" called \"**standard output**\".\n\nWhen our code \"prints\" things it is actually \"writing\" to this \"virtual file\" of \"standard output\".\n\nThis might seem strange, but that's how the CLI program and the operating system interact with each other.\n\nAnd then the operating system **shows on the screen** whatever our CLI program \"**wrote**\" to that \"**virtual file**\" called \"**standard output**\".\n\n### Standard Error\n\nAnd there's another \"**virtual file**\" called \"**standard error**\" that is normally only used for errors.\n\nBut we can also \"print\" to \"standard error\". And both are shown on the terminal to the users.\n\n/// info\n\nIf you use PowerShell it's quite possible that what you print to \"standard error\" won't be shown in the terminal.\n\nIn PowerShell, to see \"standard error\" you would have to check the variable `$Error`.\n\nBut it will work normally in Bash, Zsh, and Fish.\n\n///\n\n### Printing to \"standard error\"\n\nYou can print to \"standard error\" creating a Rich `Console` with `stderr=True`.\n\n/// tip\n\n`stderr` is short for \"standard error\".\n\n///\n\nUsing `stderr=True` tells **Rich** that the output should be shown in \"standard error\".\n\n{* docs_src/printing/tutorial004_py310.py hl[4,11] *}\n\nWhen you try it in the terminal, it will probably just look the same:\n\n<div class=\"termy\">\n\n```console\n$ python main.py\n\nHere is something written to standard error\n```\n\n</div>\n\n## \"Standard Input\"\n\nAs a final detail, when you type text in your keyboard to your terminal, the operating system also considers it another \"**virtual file**\" that you are writing text to.\n\nThis virtual file is called \"**standard input**\".\n\n### What is this for\n\nRight now this probably seems quite useless 🤷‍♂.\n\nBut understanding that will come handy in the future, for example for autocompletion and testing.\n\n## Typer Echo\n\n/// warning\n\nIn most of the cases, for displaying advanced information, it is recommended to use <a href=\"https://rich.readthedocs.io/\" class=\"external-link\" target=\"_blank\">Rich</a>.\n\nYou can probably skip the rest of this section. 🎉😎\n\n///\n\n**Typer** also has a small utility `typer.echo()` to print information on the screen. But normally you shouldn't need it.\n\nFor the simplest cases, you can use the standard Python `print()`.\n\nAnd for the cases where you want to display data more beautifully, or more advanced content, you should use **Rich** instead.\n\n### Why `typer.echo`\n\n`typer.echo()` (which is actually just `click.echo()`) applies some checks to try and convert binary data to strings, and other similar things.\n\nBut in most of the cases you wouldn't need it, as in modern Python strings (`str`) already support and use Unicode, and you would rarely deal with pure `bytes` that you want to print on the screen.\n\nIf you have some `bytes` objects, you would probably want to decode them intentionally and directly before trying to print them.\n\nAnd if you want to print data with colors and other features, you are much better off with the more advanced tools in **Rich**.\n\n### Color\n\n/// note | Technical Details\n\nThe way color works in terminals is by using some codes (ANSI escape sequences) as part of the text.\n\nSo, a colored text is still just a `str`.\n\n///\n\n/// tip\n\nAgain, you are much better off using <a href=\"https://rich.readthedocs.io/\" class=\"external-link\" target=\"_blank\">Rich</a> for this. 😎\n\n///\n\nYou can create colored strings to output to the terminal with `typer.style()`, that gives you `str`s that you can then pass to `typer.echo()`:\n\n{* docs_src/printing/tutorial005_py310.py hl[10,12] *}\n\n/// tip\n\nThe parameters `fg` and `bg` receive strings with the color names for the \"<strong>f</strong>ore<strong>g</strong>round\" and \"<strong>b</strong>ack<strong>g</strong>round\" colors. You could simply pass `fg=\"green\"` and `bg=\"red\"`.\n\nBut **Typer** provides them all as variables like `typer.colors.GREEN` just so you can use autocompletion while selecting them.\n\n///\n\nCheck it:\n\n<div class=\"use-termynal\" data-termynal>\n<span data-ty=\"input\">python main.py</span>\n<span data-ty>everything is <span style=\"color: green; font-weight: bold;\">good</span></span>\n<span data-ty=\"input\">python main.py --no-good</span>\n<span data-ty>everything is <span style=\"color: white; background-color: red;\">bad</span></span>\n</div>\n\nYou can pass these function arguments to `typer.style()`:\n\n* `fg`: the foreground color.\n* `bg`: the background color.\n* `bold`: enable or disable bold mode.\n* `dim`: enable or disable dim mode. This is badly supported.\n* `underline`: enable or disable underline.\n* `blink`: enable or disable blinking.\n* `reverse`: enable or disable inverse rendering (foreground becomes background and the other way round).\n* `reset`: by default a reset-all code is added at the end of the string which means that styles do not carry over.  This can be disabled to compose styles.\n\n### `typer.secho()` - style and print\n\n/// tip\n\nIn case you didn't see above, you are much better off using <a href=\"https://rich.readthedocs.io/\" class=\"external-link\" target=\"_blank\">Rich</a> for this. 😎\n\n///\n\nThere's a shorter form to style and print at the same time with `typer.secho()` it's like `typer.echo()` but also adds style like `typer.style()`:\n\n{* docs_src/printing/tutorial006_py310.py hl[8] *}\n\nCheck it:\n\n<div class=\"use-termynal\" data-termynal>\n<span data-ty=\"input\">python main.py Camila</span>\n<span style=\"color: magenta;\" data-ty>Welcome here Camila</span>\n</div>\n"
  },
  {
    "path": "docs/tutorial/progressbar.md",
    "content": "# Progress Bar\n\nIf you are executing an operation that can take some time, you can inform it to the user. 🤓\n\n## Progress Bar\n\nYou can use <a href=\"https://rich.readthedocs.io/en/stable/progress.html\" class=\"external-link\" target=\"_blank\">Rich's Progress Display</a> to show a progress bar, for example:\n\n{* docs_src/progressbar/tutorial001_py310.py hl[4,12] *}\n\nYou put the thing that you want to iterate over inside of Rich's `track()`, and then iterate over that.\n\nCheck it:\n\n<div class=\"termy\">\n\n```console\n$ python main.py\n\n---> 100%\n\nProcessed 100 things.\n```\n\n</div>\n\n...actually, it will look a lot prettier. ✨ But I can't show you the animation here in the docs. 😅\n\nThe colors and information will look something like this:\n\n<div class=\"termy\">\n\n```console\n$ python main.py\n\nProcessing... <font color=\"#F92672\">━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╸</font><font color=\"#3A3A3A\">━━━━━━━━━━</font> <font color=\"#AE81FF\"> 74%</font> <font color=\"#A1EFE4\">0:00:01</font>\n```\n\n</div>\n\n## Spinner\n\nWhen you don't know how long the operation will take, you can use a spinner instead.\n\nRich allows you to display many things in complex and advanced ways.\n\nFor example, this will show two spinners:\n\n{* docs_src/progressbar/tutorial002_py310.py hl[4,11:18] *}\n\nI can't show you the beautiful animation here in the docs. 😅\n\nBut at some point in time it will look like this (imagine it's spinning). 🤓\n\n<div class=\"termy\">\n\n```console\n$ python main.py\n\n<font color=\"#A6E22E\">⠹</font> Processing...\n<font color=\"#A6E22E\">⠹</font> Preparing...\n```\n\n</div>\n\nYou can learn more about it in the <a href=\"https://rich.readthedocs.io/en/stable/progress.html\" class=\"external-link\" target=\"_blank\">Rich docs for Progress Display</a>.\n\n## Typer `progressbar`\n\nIf you can, you should use **Rich** as explained above, it has more features, it's more advanced, and can display information more beautifully. ✨\n\n/// tip\n\nIf you can use Rich, use the information above, the Rich docs, and skip the rest of this page. 😎\n\n///\n\nBut if you can't use Rich and have it disabled, Typer comes with a simple utility to show progress bars.\n\n### Use `typer.progressbar`\n\n/// tip\n\nRemember, you are much better off using <a href=\"https://rich.readthedocs.io/\" class=\"external-link\" target=\"_blank\">Rich</a> for this. 😎\n\n///\n\nYou can use `typer.progressbar()` with a `with` statement, as in:\n\n```Python\nwith typer.progressbar(something) as progress:\n    pass\n```\n\nAnd you pass as function argument to `typer.progressbar()` the thing that you would normally iterate over.\n\n{* docs_src/progressbar/tutorial003_py310.py hl[11] *}\n\nSo, if you have a list of users, this could be:\n\n```Python\nusers = [\"Camila\", \"Rick\", \"Morty\"]\n\nwith typer.progressbar(users) as progress:\n    pass\n```\n\nAnd the `with` statement using `typer.progressbar()` gives you an object that you can iterate over, just like if it was the same thing that you would iterate over normally.\n\nBut by iterating over this object **Typer** will know to update the progress bar:\n\n```Python\nusers = [\"Camila\", \"Rick\", \"Morty\"]\n\nwith typer.progressbar(users) as progress:\n    for user in progress:\n        typer.echo(user)\n```\n\n/// tip\n\nNotice that there are 2 levels of code blocks. One for the `with` statement and one for the `for` statement.\n\n///\n\n/// info\n\nThis is mostly useful for operations that take some time.\n\nIn the example above we are faking it with `time.sleep()`.\n\n///\n\nCheck it:\n\n<div class=\"termy\">\n\n```console\n$ python main.py\n\n---> 100%\n\nProcessed 100 things.\n```\n\n</div>\n\n### Setting a Progress Bar `length`\n\n/// tip\n\nRemember, you are much better off using <a href=\"https://rich.readthedocs.io/\" class=\"external-link\" target=\"_blank\">Rich</a> for this. 😎\n\n///\n\nThe progress bar is generated from the length of the iterable (e.g. the list of users).\n\nBut if the length is not available (for example, with something that fetches a new user from a web API each time) you can pass an explicit `length` to `typer.progressbar()`.\n\n{* docs_src/progressbar/tutorial004_py310.py hl[18] *}\n\nCheck it:\n\n<div class=\"termy\">\n\n```console\n$ python main.py\n\n---> 100%\n\nProcessed 100 user IDs.\n```\n\n</div>\n\n#### About the function with `yield`\n\nIf you hadn't seen something like that `yield` above, that's a \"<a href=\"https://docs.python.org/3/glossary.html#term-generator\" class=\"external-link\" target=\"_blank\">generator</a>\".\n\nYou can iterate over that function with a `for` and at each iteration it will give you the value at `yield`.\n\n`yield` is like a `return` that gives values multiple times and let's you use the function in a `for` loop.\n\nFor example:\n\n```Python\ndef iterate_user_ids():\n    # Let's imagine this is a web API, not a range()\n    for i in range(100):\n        yield i\n\nfor i in iterate_user_ids():\n    print(i)\n```\n\nwould print each of the \"user IDs\" (here it's just the numbers from `0` to `99`).\n\n### Add a `label`\n\n/// tip\n\nRemember, you are much better off using <a href=\"https://rich.readthedocs.io/\" class=\"external-link\" target=\"_blank\">Rich</a> for this. 😎\n\n///\n\nYou can also set a `label`:\n\n{* docs_src/progressbar/tutorial005_py310.py hl[11] *}\n\nCheck it:\n\n<div class=\"use-termynal\">\n<span data-ty=\"input\">python main.py</span>\n<span data-ty=\"progress\" data-ty-prompt=\"Processing\"></span>\n<span data-ty>Processed 100 things.</span>\n</div>\n\n## Iterate manually\n\nIf you need to manually iterate over something and update the progress bar irregularly, you can do it by not passing an iterable but just a `length` to `typer.progressbar()`.\n\nAnd then calling the `.update()` method in the object from the `with` statement:\n\n{* docs_src/progressbar/tutorial006_py310.py hl[11,17] *}\n\nCheck it:\n\n<div class=\"use-termynal\">\n<span data-ty=\"input\">python main.py</span>\n<span data-ty=\"progress\" data-ty-prompt=\"Batches\"></span>\n<span data-ty>Processed 1000 things in batches.</span>\n</div>\n"
  },
  {
    "path": "docs/tutorial/prompt.md",
    "content": "# Ask with Prompt\n\nWhen you need to ask the user for info interactively you should normally use [*CLI Option*s with Prompt](options/prompt.md){.internal-link target=_blank}, because they allow using the CLI program in a non-interactive way (for example, a Bash script could use it).\n\nBut if you absolutely need to ask for interactive information without using a *CLI option*, you can use `typer.prompt()`:\n\n{* docs_src/prompt/tutorial001_py310.py hl[8] *}\n\nCheck it:\n\n<div class=\"termy\">\n\n```console\n$ python main.py\n\n# What's your name?:$ Camila\n\nHello Camila\n```\n\n</div>\n\n## Confirm\n\nThere's also an alternative to ask for confirmation. Again, if possible, you should use a [*CLI Option* with a confirmation prompt](options/prompt.md){.internal-link target=_blank}:\n\n{* docs_src/prompt/tutorial002_py310.py hl[8] *}\n\nCheck it:\n\n<div class=\"termy\">\n\n```console\n$ python main.py\n\n# Are you sure you want to delete it? [y/N]:$ y\n\nDeleting it!\n\n// This time cancel it\n$ python main.py\n\n# Are you sure you want to delete it? [y/N]:$ n\n\nNot deleting\nAborted!\n```\n\n</div>\n\n## Confirm or abort\n\nAs it's very common to abort if the user doesn't confirm, there's an integrated parameter `abort` that does it automatically:\n\n{* docs_src/prompt/tutorial003_py310.py hl[8] *}\n\n<div class=\"termy\">\n\n```console\n$ python main.py\n\n# Are you sure you want to delete it? [y/N]:$ y\n\nDeleting it!\n\n// This time cancel it\n$ python main.py\n\n# Are you sure you want to delete it? [y/N]:$ n\n\nAborted!\n```\n\n</div>\n\n## Prompt with Rich\n\nYou can use Rich to prompt the user for input:\n\n{* docs_src/prompt/tutorial004_py310.py hl[2,9] *}\n\nAnd when you run it, it will look like:\n\n<div class=\"termy\">\n\n```console\n$ python main.py\n\n# Enter your name 😎:$ Morty\n\nHello Morty\n```\n\n</div>\n"
  },
  {
    "path": "docs/tutorial/subcommands/add-typer.md",
    "content": "# Add Typer\n\nWe'll start with the core idea.\n\nTo add a `typer.Typer()` app inside of another.\n\n## Manage items\n\nLet's imagine that you are creating a *CLI program* to manage items in some distant land.\n\nIt could be in an `items.py` file with this:\n\n{* docs_src/subcommands/tutorial001_py310/items.py *}\n\nAnd you would use it like:\n\n<div class=\"termy\">\n\n```console\n$ python items.py create Wand\n\nCreating item: Wand\n```\n\n</div>\n\n## Manage users\n\nBut then you realize that you also have to manage users from your *CLI app*.\n\nIt could be a file `users.py` with something like:\n\n{* docs_src/subcommands/tutorial001_py310/users.py *}\n\nAnd you would use it like:\n\n<div class=\"termy\">\n\n```console\n$ python users.py create Camila\n\nCreating user: Camila\n```\n\n</div>\n\n## Put them together\n\nBoth parts are similar. In fact, `items.py` and `users.py` both have commands `create` and `delete`.\n\nBut we need them to be part of the same *CLI program*.\n\nIn this case, as with `git remote`, we can put them together as subcommands in another `typer.Typer()` *CLI program*.\n\nNow create a `main.py` with:\n\n{* docs_src/subcommands/tutorial001_py310/main.py hl[3,4,7,8] *}\n\nHere's what we do in `main.py`:\n\n* Import the other Python modules (the files `users.py` and `items.py`).\n* Create the main `typer.Typer()` application.\n* Use `app.add_typer()` to include the `app` from `items.py` and `users.py`, each of those 2 was also created with `typer.Typer()`.\n* Define a `name` with the command that will be used for each of these \"sub-Typers\" to group their own commands.\n\nAnd now your *CLI program* has 2 commands:\n\n* `users`: with all of the commands (subcommands) in the `app` from `users.py`.\n* `items` with all the commands (subcommands) in the `app` from `items.py`.\n\nCheck it:\n\n<div class=\"termy\">\n\n```console\n// Check the help\n$ python main.py --help\n\nUsage: main.py [OPTIONS] COMMAND [ARGS]...\n\nOptions:\n  --install-completion  Install completion for the current shell.\n  --show-completion     Show completion for the current shell, to copy it or customize the installation.\n  --help                Show this message and exit.\n\nCommands:\n  items\n  users\n```\n\n</div>\n\nNow you have a *CLI program* with commands `items` and `users`, and they in turn have their own commands (subcommands).\n\nLet's check the `items` command:\n\n<div class=\"termy\">\n\n```console\n// Check the help for items\n$ python main.py items --help\n\n// It shows its own commands (subcommands): create, delete, sell\nUsage: main.py items [OPTIONS] COMMAND [ARGS]...\n\nOptions:\n  --help  Show this message and exit.\n\nCommands:\n  create\n  delete\n  sell\n\n// Try it\n$ python main.py items create Wand\n\nCreating item: Wand\n\n$ python main.py items sell Vase\n\nSelling item: Vase\n```\n\n</div>\n\n/// tip\n\nNotice that we are still calling `$ python main.py` but now we are using the command `items`.\n\n///\n\nAnd now check the command `users`, with all its subcommands:\n\n<div class=\"termy\">\n\n```console\n$ python main.py users --help\n\nUsage: main.py users [OPTIONS] COMMAND [ARGS]...\n\nOptions:\n  --help  Show this message and exit.\n\nCommands:\n  create\n  delete\n\n// Try it\n$ python main.py users create Camila\n\nCreating user: Camila\n```\n\n</div>\n\n## Recap\n\nThat's the core idea.\n\nYou can just create `typer.Typer()` apps and add them inside one another.\n\nAnd you can do that with any levels of commands that you want.\n\nDo you need sub-sub-sub-subcommands? Go ahead, create all the `typer.Typer()`s you need and put them together with `app.add_typer()`.\n\nIn the next sections we'll update this with more features, but you already have the core idea.\n\nThis way, **Typer** applications are composable, each `typer.Typer()` can be a *CLI app* by itself, but it can also be added as a command group to another Typer app.\n"
  },
  {
    "path": "docs/tutorial/subcommands/callback-override.md",
    "content": "# Sub-Typer Callback Override\n\nWhen creating a **Typer** app you can define a callback function, it always executes and defines the *CLI arguments* and *CLI options* that go before a command.\n\nWhen adding a Typer app inside of another, the sub-Typer can also have its own callback.\n\nIt can handle any *CLI parameters* that go before its own commands and execute any extra code:\n\n{* docs_src/subcommands/callback_override/tutorial001_py310.py hl[9,10,11] *}\n\nIn this case it doesn't define any *CLI parameters*, it just writes a message.\n\nCheck it:\n\n<div class=\"termy\">\n\n```console\n$ python main.py users create Camila\n\n// Notice the first message is not created by the command function but by the callback\nRunning a users command\nCreating user: Camila\n```\n\n</div>\n\n## Add a callback on creation\n\nIt's also possible to add a callback when creating the `typer.Typer()` app that will be added to another Typer app:\n\n{* docs_src/subcommands/callback_override/tutorial002_py310.py hl[6,7,10] *}\n\nThis achieves exactly the same as above, it's just another place to add the callback.\n\nCheck it:\n\n<div class=\"termy\">\n\n```console\n$ python main.py users create Camila\n\nRunning a users command\nCreating user: Camila\n```\n\n</div>\n\n## Overriding the callback on creation\n\nIf a callback was added when creating the `typer.Typer()` app, it's possible to override it with a new one using `@app.callback()`.\n\nThis is the same information you saw on the section about [Commands - Typer Callback](../commands/callback.md){.internal-link target=_blank}, and it applies the same for sub-Typer apps:\n\n{* docs_src/subcommands/callback_override/tutorial003_py310.py hl[6,7,10,14,15,16] *}\n\nHere we had defined a callback when creating the `typer.Typer()` sub-app, but then we override it with a new callback with the function `user_callback()`.\n\nAs `@app.callback()` takes precedence over `typer.Typer(callback=some_function)`, now our CLI app will use this new callback.\n\nCheck it:\n\n<div class=\"termy\">\n\n```console\n$ python main.py users create Camila\n\n// Notice the message from the new callback\nCallback override, running users command\nCreating user: Camila\n```\n\n</div>\n\n## Overriding the callback when adding a sub-Typer\n\nLastly, you can override the callback defined anywhere else when adding a sub-Typer with `app.add_typer()` using the `callback` parameter.\n\nThis has the highest priority:\n\n{* docs_src/subcommands/callback_override/tutorial004_py310.py hl[13,14,17] *}\n\nNotice that the precedence goes to `app.add_typer()` and is not affected by the order of execution. There's another callback defined below, but the one from `app.add_typer()` wins.\n\nNow when you use the CLI program it will use the new callback function `callback_for_add_typer()`.\n\nCheck it:\n\n<div class=\"termy\">\n\n```console\n$ python users create Camila\n\n// Notice the message from the callback added in add_typer()\nI have the high land! Running users command\nCreating user: Camila\n```\n\n</div>\n"
  },
  {
    "path": "docs/tutorial/subcommands/index.md",
    "content": "# SubCommands - Command Groups\n\nYou read before how to create a program with [Commands](../commands/index.md){.internal-link target=_blank}.\n\nNow we'll see how to create a *CLI program* with commands that have their own subcommands. Also known as command groups.\n\nFor example, the *CLI program* `git` has a command `remote`.\n\nBut `git remote`, in turn, has its own subcommands, like `add`:\n\n<div class=\"termy\">\n\n```console\n// git remote alone shows the current remote repositories\n$ git remote\n\norigin\n\n// Use -v to make it verbose and show more info\n$ git remote -v\n\norigin  git@github.com:yourusername/typer.git (fetch)\norigin  git@github.com:yourusername/typer.git (push)\n\n// git remote add takes 2 CLI arguments, a name and URL\n$ git remote add upstream https://github.com/fastapi/typer.git\n\n// Doesn't output anything, but now you have another remote repository called upstream\n\n// Now check again\n$ git remote -v\n\norigin  git@github.com:yourusername/typer.git (fetch)\norigin  git@github.com:yourusername/typer.git (push)\nupstream        https://github.com/fastapi/typer.git (fetch)\nupstream        https://github.com/fastapi/typer.git (push)\n```\n\n</div>\n\nIn the next sections we'll see how to create subcommands like these.\n"
  },
  {
    "path": "docs/tutorial/subcommands/name-and-help.md",
    "content": "# SubCommand Name and Help\n\nWhen adding a Typer app to another we have seen how to set the `name` to use for the command.\n\nFor example to set the command to `users`:\n\n```Python\napp.add_typer(users.app, name=\"users\")\n```\n\n## Add a help text\n\nWe can also set the `help` text while adding a Typer:\n\n{* docs_src/subcommands/name_help/tutorial001_py310.py hl[6] *}\n\nAnd then we get that help text for that command in the *CLI program*:\n\n<div class=\"termy\">\n\n```console\n// Check the main help\n$ python main.py --help\n\nUsage: main.py [OPTIONS] COMMAND [ARGS]...\n\nOptions:\n  --install-completion  Install completion for the current shell.\n  --show-completion     Show completion for the current shell, to copy it or customize the installation.\n  --help                Show this message and exit.\n\nCommands:\n  users  Manage users in the app.\n\n// Check the help for the users command\n$ python main.py users --help\n\nUsage: main.py users [OPTIONS] COMMAND [ARGS]...\n\n  Manage users in the app.\n\nOptions:\n  --help  Show this message and exit.\n\nCommands:\n  create\n```\n\n</div>\n\nWe can set the `help` in several places, each one taking precedence over the other, overriding the previous value.\n\nLet's see those locations.\n\n/// tip\n\nThere are other attributes that can be set in that same way in the same places we'll see next.\n\nBut those are documented later in another section.\n\n///\n\n## Inferring help text from callback\n\n### Inferring a command's help text\n\nWhen you create a command with `@app.command()`, by default, it generates the name from the function name.\n\nAnd by default, the help text is extracted from the function's docstring.\n\nFor example:\n\n```Python\n@app.command()\ndef create(item: str):\n    \"\"\"\n    Create an item.\n    \"\"\"\n    typer.echo(f\"Creating item: {item}\")\n```\n\n...will create a command `create` with a help text of `Create an item`.\n\n### Inferring the help text from `@app.callback()`\n\nThe same way, if you define a callback in a `typer.Typer()`, the help text is extracted from the callback function's docstring.\n\nHere's an example:\n\n{* docs_src/subcommands/name_help/tutorial002_py310.py hl[9,10,11,12,13] *}\n\nThe help text for that command will be the callback function's docstring: `Manage users in the app.`.\n\nCheck it:\n\n<div class=\"termy\">\n\n```console\n// Check the main help\n$ python main.py --help\n\n// Notice the help text \"Manage users in the app.\"\nUsage: main.py [OPTIONS] COMMAND [ARGS]...\n\nOptions:\n  --install-completion  Install completion for the current shell.\n  --show-completion     Show completion for the current shell, to copy it or customize the installation.\n  --help                Show this message and exit.\n\nCommands:\n  users  Manage users in the app.\n\n// Check the help for the users command\n$ python main.py users --help\n\n// Notice the main description: \"Manage users in the app.\"\nUsage: main.py users [OPTIONS] COMMAND [ARGS]...\n\n  Manage users in the app.\n\nOptions:\n  --help  Show this message and exit.\n\nCommands:\n  create\n```\n\n</div>\n\n/// note\n\nBefore Typer 0.14.0, in addition to the help text, the command name was also inferred from the callback function name, this is no longer the case.\n\n///\n\n### Help from callback parameter in `typer.Typer()`\n\nIf you pass a `callback` parameter while creating a `typer.Typer(callback=some_function)` it will be used to infer the help text.\n\nThis has the lowest priority, we'll see later what has a higher priority and can override it.\n\nCheck the code:\n\n{* docs_src/subcommands/name_help/tutorial003_py310.py hl[6,7,8,9,12] *}\n\nThis achieves exactly the same as the previous example.\n\nCheck it:\n\n<div class=\"termy\">\n\n```console\n// Check the main help\n$ python main.py --help\n\n// Notice the help text \"Manage users in the app.\"\nUsage: main.py [OPTIONS] COMMAND [ARGS]...\n\nOptions:\n  --install-completion  Install completion for the current shell.\n  --show-completion     Show completion for the current shell, to copy it or customize the installation.\n  --help                Show this message and exit.\n\nCommands:\n  users  Manage users in the app.\n\n// Check the help for the users command\n$ python main.py users --help\n\n// Notice the main description: \"Manage users in the app.\"\nUsage: main.py users [OPTIONS] COMMAND [ARGS]...\n\n  Manage users in the app.\n\nOptions:\n  --help  Show this message and exit.\n\nCommands:\n  create\n```\n\n</div>\n\n### Override a callback set in `typer.Typer()` with `@app.callback()`\n\nThe same as with normal **Typer** apps, if you pass a `callback` to `typer.Typer(callback=some_function)` and then override it with `@app.callback()`, the help text will be inferred from the new callback:\n\n{* docs_src/subcommands/name_help/tutorial004_py310.py hl[16,17,18,19,20] *}\n\nNow the help text will be `Manage users in the app.` instead of `Old callback help.`.\n\nCheck it:\n\n<div class=\"termy\">\n\n```console\n// Check the main help\n$ python main.py --help\n\n// Notice the help text \"Manage users in the app.\"\nUsage: main.py [OPTIONS] COMMAND [ARGS]...\n\nOptions:\n  --install-completion  Install completion for the current shell.\n  --show-completion     Show completion for the current shell, to copy it or customize the installation.\n  --help                Show this message and exit.\n\nCommands:\n  users  Manage users in the app.\n\n// Check the help for the users command\n$ python main.py users --help\n\n// Notice the main description: \"Manage users in the app.\"\nUsage: main.py users [OPTIONS] COMMAND [ARGS]...\n\n  Manage users in the app.\n\nOptions:\n  --help  Show this message and exit.\n\nCommands:\n  create\n```\n\n</div>\n\n### Help from callback in `app.add_typer()`\n\nIf you override the callback in `app.add_typer()` when including a sub-app, the help will be inferred from this callback function.\n\nThis takes precedence over inferring the help from a callback set in `@sub_app.callback()` and `typer.Typer(callback=sub_app_callback)`.\n\nCheck the code:\n\n{* docs_src/subcommands/name_help/tutorial005_py310.py hl[15,16,17,18,21] *}\n\nThe help text will be `I have the highland! Create some users.` instead of the previous ones.\n\nCheck it:\n\n<div class=\"termy\">\n\n```console\n// Check the main help\n$ python main.py --help\n\n// Check the command new-users and its help text\nUsage: main.py [OPTIONS] COMMAND [ARGS]...\n\nOptions:\n  --install-completion  Install completion for the current shell.\n  --show-completion     Show completion for the current shell, to copy it or customize the installation.\n  --help                Show this message and exit.\n\nCommands:\n  new-users  I have the highland! Create some users.\n\n// Now check the help for the new-users command\n$ python main.py new-users --help\n\n// Notice the help text\nUsage: main.py new-users [OPTIONS] COMMAND [ARGS]...\n\n  I have the highland! Create some users.\n\nOptions:\n  --help  Show this message and exit.\n\nCommands:\n  create\n```\n\n</div>\n\n### Enough inferring\n\nSo, when inferring help text, the precedence order from lowest priority to highest is:\n\n* `sub_app = typer.Typer(callback=some_function)`\n* `@sub_app.callback()`\n* `app.add_typer(sub_app, callback=new_function)`\n\nThat's for inferring the help text from functions.\n\nBut if you set the help text explicitly, that has a higher priority than these.\n\n## Set the name and help\n\nLet's now see the places where you can set the command name and help text, from lowest priority to highest.\n\n/// tip\n\nSetting the help text explicitly always has a higher precedence than inferring from a callback function.\n\n///\n\n### Name and help in `typer.Typer()`\n\nYou could have all the callbacks and overrides we defined before, but the help text was inferred from the function docstring.\n\nIf you set it explicitly, that takes precedence over inferring.\n\nYou can set it when creating a new `typer.Typer()`:\n\n{* docs_src/subcommands/name_help/tutorial006_py310.py hl[12] *}\n\n/// info\n\nThe rest of the callbacks and overrides are there only to show you that they don't affect the name and help text when you set it explicitly.\n\n///\n\nWe set an explicit help `Explicit help.`.\n\nSo that will take precedence now.\n\nCheck it:\n\n<div class=\"termy\">\n\n```console\n// Check the main help\n$ python main.py --help\n\n// Notice the command name is exp-users and the help text is \"Explicit help.\"\nUsage: main.py [OPTIONS] COMMAND [ARGS]...\n\nOptions:\n  --install-completion  Install completion for the current shell.\n  --show-completion     Show completion for the current shell, to copy it or customize the installation.\n  --help                Show this message and exit.\n\nCommands:\n  exp-users  Explicit help.\n\n// Check the help for the exp-users command\n$ python main.py exp-users --help\n\n// Notice the main help text\nUsage: main.py exp-users [OPTIONS] COMMAND [ARGS]...\n\n  Explicit help.\n\nOptions:\n  --help  Show this message and exit.\n\nCommands:\n  create\n```\n\n</div>\n\n### Help text in `@app.callback()`\n\nMany parameters that you use when creating a `typer.Typer()` app can be overridden in the parameters of `@app.callback()`.\n\nContinuing with the previous example, we now override the `help` in `@user_app.callback()`:\n\n{* docs_src/subcommands/name_help/tutorial007_py310.py hl[24] *}\n\nAnd now the help text will be `Help from callback for users.`.\n\nCheck it:\n\n<div class=\"termy\">\n\n```console\n// Check the help\n$ python main.py --help\n\n// The help text is now \"Help from callback for users.\".\nUsage: main.py [OPTIONS] COMMAND [ARGS]...\n\nOptions:\n  --install-completion  Install completion for the current shell.\n  --show-completion     Show completion for the current shell, to copy it or customize the installation.\n  --help                Show this message and exit.\n\nCommands:\n  users  Help from callback for users.\n\n// Check the users command help\n$ python main.py users --help\n\n// Notice the main help text\nUsage: main.py users [OPTIONS] COMMAND [ARGS]...\n\n  Help from callback for users.\n\nOptions:\n  --help  Show this message and exit.\n\nCommands:\n  create\n```\n\n</div>\n\n### Name and help in `app.add_typer()`\n\nAnd finally, with the highest priority, you can override all that by explicitly setting the `name` and `help` in `app.add_typer()`, just like we did on the first example above:\n\n{* docs_src/subcommands/name_help/tutorial008_py310.py hl[21] *}\n\nAnd now, with the highest priorities of them all, the command name will now be `cake-sith-users` and the help text will be `Unlimited powder! Eh, users.`.\n\nCheck it:\n\n<div class=\"termy\">\n\n```console\n// Check the help\n$ python main.py --help\n\n// Notice the command name cake-sith-users and the new help text \"Unlimited powder! Eh, users.\"\nUsage: main.py [OPTIONS] COMMAND [ARGS]...\n\nOptions:\n  --install-completion  Install completion for the current shell.\n  --show-completion     Show completion for the current shell, to copy it or customize the installation.\n  --help                Show this message and exit.\n\nCommands:\n  cake-sith-users  Unlimited powder! Eh, users.\n\n// And check the help for the command cake-sith-users\n$ python main.py cake-sith-users --help\n\n// Notice the main help text\nUsage: main.py cake-sith-users [OPTIONS] COMMAND [ARGS]...\n\n  Unlimited powder! Eh, users.\n\nOptions:\n  --help  Show this message and exit.\n\nCommands:\n  create\n```\n\n</div>\n\n## Recap\n\nThe precedence to generate a command's **help**, from lowest priority to highest, is:\n\n* Implicitly inferred from `sub_app = typer.Typer(callback=some_function)`\n* Implicitly inferred from the callback function under `@sub_app.callback()`\n* Implicitly inferred from `app.add_typer(sub_app, callback=some_function)`\n* Explicitly set on `sub_app = typer.Typer(help=\"Some help.\")`\n* Explicitly set on `app.add_typer(sub_app, help=\"Some help.\")`\n\nAnd the priority to set the command's **name**, from lowest priority to highest, is:\n\n* Explicitly set on `sub_app = typer.Typer(name=\"some-name\")`\n* Explicitly set on `app.add_typer(sub_app, name=\"some-name\")`\n\nSo, `app.add_typer(sub_app, name=\"some-name\", help=\"Some help.\")` always wins.\n"
  },
  {
    "path": "docs/tutorial/subcommands/nested-subcommands.md",
    "content": "# Nested SubCommands\n\nWe'll now see how these same ideas can be extended for deeply nested commands.\n\nLet's imagine that the same *CLI program* from the previous examples now needs to handle `lands`.\n\nBut a land could be a `reign` or `town`.\n\nAnd each of those could have their own commands, like `create` and `delete`.\n\n## A CLI app for reigns\n\nLet's start with a file `reigns.py`:\n\n{* docs_src/subcommands/tutorial003_py310/reigns.py *}\n\nThis is already a simple *CLI program* to manage reigns:\n\n<div class=\"termy\">\n\n```console\n// Check the help\n$ python reigns.py --help\n\nUsage: reigns.py [OPTIONS] COMMAND [ARGS]...\n\nOptions:\n  --install-completion  Install completion for the current shell.\n  --show-completion     Show completion for the current shell, to copy it or customize the installation.\n  --help                Show this message and exit.\n\nCommands:\n  conquer\n  destroy\n\n// Try it\n$ python reigns.py conquer Cintra\n\nConquering reign: Cintra\n\n$ python reigns.py destroy Mordor\n\nDestroying reign: Mordor\n```\n\n</div>\n\n## A CLI app for towns\n\nAnd now the equivalent for managing towns in `towns.py`:\n\n{* docs_src/subcommands/tutorial003_py310/towns.py *}\n\nWith it, you can manage towns:\n\n<div class=\"termy\">\n\n```console\n// Check the help\n$ python towns.py --help\n\nUsage: towns.py [OPTIONS] COMMAND [ARGS]...\n\nOptions:\n  --install-completion  Install completion for the current shell.\n  --show-completion     Show completion for the current shell, to copy it or customize the installation.\n  --help                Show this message and exit.\n\nCommands:\n  burn\n  found\n\n// Try it\n$ python towns.py found \"New Asgard\"\n\nFounding town: New Asgard\n\n$ python towns.py burn Vizima\n\nBurning town: Vizima\n```\n\n</div>\n\n## Manage the land in a CLI app\n\nNow let's put the `reigns` and `towns` together in the same *CLI program* in `lands.py`:\n\n{* docs_src/subcommands/tutorial003_py310/lands.py *}\n\nAnd now we have a single *CLI program* with a command (or command group) `reigns` that has its own commands. And another command `towns` with its own subcommands.\n\nCheck it:\n\n<div class=\"termy\">\n\n```console\n// Check the help\n$ python lands.py --help\n\nUsage: lands.py [OPTIONS] COMMAND [ARGS]...\n\nOptions:\n  --install-completion  Install completion for the current shell.\n  --show-completion     Show completion for the current shell, to copy it or customize the installation.\n  --help                Show this message and exit.\n\nCommands:\n  reigns\n  towns\n\n// We still have the help for reigns\n$ python lands.py reigns --help\n\nUsage: lands.py reigns [OPTIONS] COMMAND [ARGS]...\n\nOptions:\n  --help  Show this message and exit.\n\nCommands:\n  conquer\n  destroy\n\n// And the help for towns\n$ python lands.py towns --help\n\nUsage: lands.py towns [OPTIONS] COMMAND [ARGS]...\n\nOptions:\n  --help  Show this message and exit.\n\nCommands:\n  burn\n  found\n```\n\n</div>\n\nNow try it, manage the lands through the CLI:\n\n<div class=\"termy\">\n\n```console\n// Try the reigns command\n$ python lands.py reigns conquer Gondor\n\nConquering reign: Gondor\n\n$ python lands.py reigns destroy Nilfgaard\n\nDestroying reign: Nilfgaard\n\n// Try the towns command\n$ python lands.py towns found Springfield\n\nFounding town: Springfield\n\n$ python lands.py towns burn Atlantis\n\nBurning town: Atlantis\n```\n\n</div>\n\n## Deeply nested subcommands\n\nNow let's say that all these commands in the `lands.py` *CLI program* should be part of the previous *CLI program* we built in the first example.\n\nWe want our *CLI program* to have these commands/command groups:\n\n* `users`:\n    * `create`\n    * `delete`\n* `items`:\n    * `create`\n    * `delete`\n    * `sell`\n* `lands`:\n    * `reigns`:\n        * `conquer`\n        * `destroy`\n    * `towns`:\n        * `found`\n        * `burn`\n\nThis already is a quite deeply nested \"tree\" of commands/command groups.\n\nBut to achieve that, we just have to add the `lands` **Typer** app to the same `main.py` file we already had:\n\n{* docs_src/subcommands/tutorial003_py310/main.py hl[4,10] *}\n\nAnd now we have everything in a single *CLI program*:\n\n<div class=\"termy\">\n\n```console\n// Check the main help\n$ python main.py --help\n\nUsage: main.py [OPTIONS] COMMAND [ARGS]...\n\nOptions:\n  --install-completion  Install completion for the current shell.\n  --show-completion     Show completion for the current shell, to copy it or customize the installation.\n  --help                Show this message and exit.\n\nCommands:\n  items\n  lands\n  users\n\n// Try some users commands\n$ python main.py users create Camila\n\nCreating user: Camila\n\n// Now try some items commands\n$ python main.py items create Sword\n\nCreating item: Sword\n\n// And now some lands commands for reigns\n$ python main.py lands reigns conquer Gondor\n\nConquering reign: Gondor\n\n// And for towns\n$ python main.py lands towns found Cartagena\n\nFounding town: Cartagena\n```\n\n</div>\n\n## Review the files\n\nHere are all the files if you want to review/copy them:\n\n`reigns.py`:\n\n{* docs_src/subcommands/tutorial003_py310/reigns.py *}\n\n`towns.py`:\n\n{* docs_src/subcommands/tutorial003_py310/towns.py *}\n\n`lands.py`:\n\n{* docs_src/subcommands/tutorial003_py310/lands.py *}\n\n`users.py`:\n\n{* docs_src/subcommands/tutorial003_py310/users.py *}\n\n`items.py`:\n\n{* docs_src/subcommands/tutorial003_py310/items.py *}\n\n`main.py`:\n\n{* docs_src/subcommands/tutorial003_py310/main.py *}\n\n/// tip\n\nAll these files have an `if __name__ == \"__main__\"` block just to demonstrate how each of them can also be an independent *CLI app*.\n\nBut for your final application, only `main.py` would need it.\n\n///\n\n## Recap\n\nThat's it, you can just add **Typer** applications one inside another as much as you want and create complex *CLI programs* while writing simple code.\n\nYou can probably achieve a simpler *CLI program* design that's easier to use than the example here. But if your requirements are complex, **Typer** helps you build your *CLI app* easily.\n\n/// tip\n\nAuto completion helps a lot, specially with complex programs.\n\nCheck the docs about adding auto completion to your *CLI apps*.\n\n///\n"
  },
  {
    "path": "docs/tutorial/subcommands/single-file.md",
    "content": "# SubCommands in a Single File\n\nIn some cases, it's possible that your application code needs to live on a single file.\n\nYou can still use the same ideas:\n\n{* docs_src/subcommands/tutorial002_py310/main.py *}\n\nThere are several things to notice here...\n\n## Apps at the top\n\nFirst, you can create `typer.Typer()` objects and add them to another one at the top.\n\nIt doesn't have to be done after creating the subcommands:\n\n{* docs_src/subcommands/tutorial002_py310/main.py hl[4,5,6,7] *}\n\nYou can add the commands (subcommands) to each `typer.Typer()` app later and it will still work.\n\n## Function names\n\nAs you now have subcommands like `create` for `users` and for `items`, you can no longer call the functions with just the name, like `def create()`, because they would overwrite each other.\n\nSo we use longer names:\n\n{* docs_src/subcommands/tutorial002_py310/main.py hl[11,16,21,26,31] *}\n\n## Command name\n\nWe are naming the functions with longer names so that they don't overwrite each other.\n\nBut we still want the subcommands to be `create`, `delete`, etc.\n\nTo call them like:\n\n<div class=\"termy\">\n\n```console\n// We want this ✔️\n$ python main.py items create\n```\n\n</div>\n\ninstead of:\n\n<div class=\"termy\">\n\n```console\n// We don't want this ⛔️\n$ python main.py items items-create\n```\n\n</div>\n\nSo we pass the name we want to use for each subcommand as the function argument to the decorator:\n\n{* docs_src/subcommands/tutorial002_py310/main.py hl[10,15,20,25,30] *}\n\n## Check it\n\nIt still works the same:\n\n\n<div class=\"termy\">\n\n```console\n// Check the help\n$ python main.py --help\n\nUsage: main.py [OPTIONS] COMMAND [ARGS]...\n\nOptions:\n  --install-completion  Install completion for the current shell.\n  --show-completion     Show completion for the current shell, to copy it or\n                        customize the installation.\n  --help                Show this message and exit.\n\nCommands:\n  items\n  users\n```\n\n</div>\n\nCheck the `items` command:\n\n\n<div class=\"termy\">\n\n```console\n// Check the help for items\n$ python main.py items --help\n\n// It shows its own commands (subcommands): create, delete, sell\nUsage: main.py items [OPTIONS] COMMAND [ARGS]...\n\nOptions:\n  --help  Show this message and exit.\n\nCommands:\n  create\n  delete\n  sell\n\n// Try it\n$ python main.py items create Wand\n\nCreating item: Wand\n\n$ python main.py items sell Vase\n\nSelling item: Vase\n```\n\n</div>\n\nAnd the same for the `users` command:\n\n\n<div class=\"termy\">\n\n```console\n$ python main.py users --help\n\nUsage: main.py users [OPTIONS] COMMAND [ARGS]...\n\nOptions:\n  --help  Show this message and exit.\n\nCommands:\n  create\n  delete\n\n// Try it\n$ python main.py users create Camila\n\nCreating user: Camila\n```\n\n</div>\n"
  },
  {
    "path": "docs/tutorial/terminating.md",
    "content": "# Terminating\n\nThere are some cases where you might want to terminate a command at some point, and stop all subsequent execution.\n\nIt could be that your code determined that the program completed successfully, or it could be an operation aborted.\n\n## `Exit` a CLI program\n\nYou can normally just let the code of your CLI program finish its execution, but in some scenarios, you might want to terminate at some point in the middle of it. And prevent any subsequent code to run.\n\nThis doesn't have to mean that there's an error, just that nothing else needs to be executed.\n\nIn that case, you can raise a `typer.Exit()` exception:\n\n{* docs_src/terminating/tutorial001_py310.py hl[9] *}\n\nThere are several things to see in this example.\n\n* The CLI program is the function `main()`, not the others. This is the one that takes a *CLI argument*.\n* The function `maybe_create_user()` can terminate the program by raising `typer.Exit()`.\n* If the program is terminated by `maybe_create_user()` then `send_new_user_notification()` will never execute inside of `main()`.\n\nCheck it:\n\n<div class=\"termy\">\n\n```console\n$ python main.py Camila\n\nUser created: Camila\nNotification sent for new user: Camila\n\n// Try with an existing user\n$ python main.py rick\n\nThe user already exists\n\n// Notice that the notification code was never run, the second message is not printed\n```\n\n</div>\n\n/// tip\n\nEven though you are raising an exception, it doesn't necessarily mean there's an error.\n\nThis is done with an exception because it works as an \"error\" and stops all execution.\n\nBut then **Typer** catches it and just terminates the program normally.\n\n///\n\n## Exit with an error\n\n`typer.Exit()` takes an optional `code` parameter. By default, `code` is `0`, meaning there was no error.\n\nYou can pass a `code` with a number other than `0` to tell the terminal that there was an error in the execution of the program:\n\n{* docs_src/terminating/tutorial002_py310.py hl[10] *}\n\nCheck it:\n\n<div class=\"termy\">\n\n```console\n$ python main.py Camila\n\nNew user created: Camila\n\n// Print the result code of the last program executed\n$ echo $?\n\n0\n\n// Now make it exit with an error\n$ python main.py root\n\nThe root user is reserved\n\n// Print the result code of the last program executed\n$ echo $?\n\n1\n\n// 1 means there was an error, 0 means no errors.\n```\n\n</div>\n\n/// tip\n\nThe error code might be used by other programs (for example a Bash script) that execute your CLI program.\n\n///\n\n## Abort\n\nThere's a special exception that you can use to \"abort\" a program.\n\nIt works more or less the same as `typer.Exit()` but will print `\"Aborted!\"` to the screen and can be useful in certain cases later to make it explicit that the execution was aborted:\n\n{* docs_src/terminating/tutorial003_py310.py hl[10] *}\n\nCheck it:\n\n<div class=\"termy\">\n\n```console\n$ python main.py Camila\n\nNew user created: Camila\n\n// Now make it exit with an error\n$ python main.py root\n\nThe root user is reserved\nAborted!\n```\n\n</div>\n"
  },
  {
    "path": "docs/tutorial/testing.md",
    "content": "# Testing\n\nTesting **Typer** applications is very easy with <a href=\"https://docs.pytest.org/en/latest/\" class=\"external-link\" target=\"_blank\">pytest</a>.\n\nLet's say you have an application `app/main.py` with:\n\n{* docs_src/testing/app01_py310/main.py *}\n\nSo, you would use it like:\n\n<div class=\"termy\">\n\n```console\n$ python main.py Camila --city Berlin\n\nHello Camila\nLet's have a coffee in Berlin\n```\n\n</div>\n\nAnd the directory also has an empty `app/__init__.py` file.\n\nSo, the `app` is a \"Python package\".\n\n## Test the app\n\n### Import and create a `CliRunner`\n\nCreate another file/module `app/test_main.py`.\n\nImport `CliRunner` and create a `runner` object.\n\nThis runner is what will \"invoke\" or \"call\" your command line application.\n\n{* docs_src/testing/app01_py310/test_main.py hl[1,5] *}\n\n/// tip\n\nIt's important that the name of the file starts with `test_`, that way pytest will be able to detect it and use it automatically.\n\n///\n\n### Call the app\n\nThen create a function `test_app()`.\n\nAnd inside of the function, use the `runner` to `invoke` the application.\n\nThe first parameter to `runner.invoke()` is a `Typer` app.\n\nThe second parameter is a `list` of `str`, with all the text you would pass in the command line, right as you would pass it:\n\n{* docs_src/testing/app01_py310/test_main.py hl[8,9] *}\n\n/// tip\n\nThe name of the function has to start with `test_`, that way pytest can detect it and use it automatically.\n\n///\n\n### Check the result\n\nThen, inside of the test function, add `assert` statements to ensure that everything in the result of the call is as it should be.\n\n{* docs_src/testing/app01_py310/test_main.py hl[10,11,12] *}\n\nHere we are checking that the exit code is 0, as it is for programs that exit without errors.\n\nThen we check that the text printed to \"standard output\" contains the text that our CLI program prints.\n\n/// tip\n\nYou could also check the output sent to \"standard error\" (`stderr`) or \"standard output\" (`stdout`) independently by accessing `result.stdout` and `result.stderr` in your tests.\n\n///\n\n/// info\n\nIf you need a refresher about what is \"standard output\" and \"standard error\" check the section in [Printing and Colors: \"Standard Output\" and \"Standard Error\"](printing.md#standard-output-and-standard-error){.internal-link target=_blank}.\n\n///\n\n### Call `pytest`\n\nThen you can call `pytest` in your directory and it will run your tests:\n\n<div class=\"termy\">\n\n```console\n$ pytest\n\n================ test session starts ================\nplatform linux -- Python 3.10, pytest-5.3.5, py-1.8.1, pluggy-0.13.1\nrootdir: /home/user/code/superawesome-cli/app\nplugins: forked-1.1.3, xdist-1.31.0, cov-2.8.1\ncollected 1 item\n\n---> 100%\n\ntest_main.py <span style=\"color: green; white-space: pre;\">.                                 [100%]</span>\n\n<span style=\"color: green;\">================= 1 passed in 0.03s =================</span>\n```\n\n</div>\n\n## Testing input\n\nIf you have a CLI with prompts, like:\n\n{* docs_src/testing/app02_an_py310/main.py hl[9] *}\n\nThat you would use like:\n\n<div class=\"termy\">\n\n```console\n$ python main.py Camila\n\n# Email: $ camila@example.com\n\nHello Camila, your email is: camila@example.com\n```\n\n</div>\n\nYou can test the input typed in the terminal using `input=\"camila@example.com\\n\"`.\n\nThis is because what you type in the terminal goes to \"**standard input**\" and is handled by the operating system as if it was a \"virtual file\".\n\n/// info\n\nIf you need a refresher about what is \"standard output\", \"standard error\", and \"standard input\" check the section in [Printing and Colors: \"Standard Output\" and \"Standard Error\"](printing.md#standard-output-and-standard-error){.internal-link target=_blank}.\n\n///\n\nWhen you hit the <kbd>ENTER</kbd> key after typing the email, that is just a \"new line character\". And in Python that is represented with `\"\\n\"`.\n\nSo, if you use `input=\"camila@example.com\\n\"` it means: \"type `camila@example.com` in the terminal, then hit the <kbd>ENTER</kbd> key\":\n\n{* docs_src/testing/app02_py310/test_main.py hl[9] *}\n\n## Test a function\n\nIf you have a script and you never created an explicit `typer.Typer` app, like:\n\n{* docs_src/testing/app03_py310/main.py hl[9] *}\n\n...you can still test it, by creating an app during testing:\n\n{* docs_src/testing/app03_py310/test_main.py hl[6,7,13] *}\n\nOf course, if you are testing that script, it's probably easier/cleaner to just create the explicit `typer.Typer` app in `main.py` instead of creating it just during the test.\n\nBut if you want to keep it that way, e.g. because it's a simple example in documentation, then you can use that trick.\n\n### About the `app.command` decorator\n\nNotice the `app.command()(main)`.\n\nIf it's not obvious what it's doing, continue reading...\n\nYou would normally write something like:\n\n```Python\n@app.command()\ndef main(name: str = \"World\"):\n    # Some code here\n```\n\nBut `@app.command()` is just a decorator.\n\nThat's equivalent to:\n\n```Python\ndef main(name: str = \"World\"):\n    # Some code here\n\ndecorator = app.command()\n\nnew_main = decorator(main)\nmain = new_main\n```\n\n`app.command()` returns a function (`decorator`) that takes another function as it's only parameter (`main`).\n\nAnd by using the `@something` you normally tell Python to replace the thing below (the function `main`) with the return of the `decorator` function (`new_main`).\n\nNow, in the specific case of **Typer**, the decorator doesn't change the original function. It registers it internally and returns it unmodified.\n\nSo, `new_main` is actually the same original `main`.\n\nSo, in the case of **Typer**, as it doesn't really modify the decorated function, that would be equivalent to:\n\n```Python\ndef main(name: str = \"World\"):\n    # Some code here\n\ndecorator = app.command()\n\ndecorator(main)\n```\n\nBut then we don't need to create the variable `decorator` to use it below, we can just use it directly:\n\n```Python\ndef main(name: str = \"World\"):\n    # Some code here\n\napp.command()(main)\n```\n\n...that's it. It's still probably simpler to just create the explicit `typer.Typer` in the `main.py` file 😅.\n"
  },
  {
    "path": "docs/tutorial/typer-app.md",
    "content": "# Typer App\n\n## Explicit application\n\nSo far, you have seen how to create a single function and then pass that function to `typer.run()`.\n\nFor example:\n\n{* docs_src/first_steps/tutorial002_py310.py hl[9] *}\n\nBut that is actually a shortcut. Under the hood, **Typer** converts that to a CLI application with `typer.Typer()` and executes it. All that inside of `typer.run()`.\n\nThere's also a more explicit way to achieve the same:\n\n{* docs_src/typer_app/tutorial001_py310.py hl[3,6,12] *}\n\nWhen you use `typer.run()`, **Typer** is doing more or less the same as above, it will:\n\n* Create a new `typer.Typer()` \"application\".\n* Create a new \"`command`\" with your function.\n* Call the same \"application\" as if it was a function with \"`app()`\".\n\n/// info | `@decorator` Info\n\nThat `@something` syntax in Python is called a \"decorator\".\n\nYou put it on top of a function. Like a pretty decorative hat (I guess that's where the term came from).\n\nA \"decorator\" takes the function below and does something with it.\n\nIn our case, this decorator tells **Typer** that the function below is a \"`command`\".\nYou will learn more about commands later in the section [commands](./commands/index.md){.internal-link target=_blank}.\n\n///\n\nBoth ways, with `typer.run()` and creating the explicit application, achieve almost the same.\n\n/// tip\n\nIf your use case is solved with just `typer.run()`, that's fine, you don't have to create the explicit `app` and use `@app.command()`, etc.\n\nYou might want to do that later when your app needs extra features, but if it doesn't need them yet, that's fine.\n\n///\n\nIf you run the second example, with the explicit `app`, it works exactly the same:\n\n<div class=\"termy\">\n\n```console\n// Without a CLI argument\n$ python main.py\n\nUsage: main.py [OPTIONS] NAME\nTry \"main.py --help\" for help.\n\nError: Missing argument 'NAME'.\n\n// With the NAME CLI argument\n$ python main.py Camila\n\nHello Camila\n\n// Asking for help\n$ python main.py  --help\n\nUsage: main.py [OPTIONS] NAME\n\nOptions:\n  --install-completion  Install completion for the current shell.\n  --show-completion     Show completion for the current shell, to copy it or customize the installation.\n  --help                Show this message and exit.\n```\n\n</div>\n\n## CLI application completion\n\nThere's a little detail that is worth noting here.\n\nNow the help shows two new *CLI options*:\n\n* `--install-completion`\n* `--show-completion`\n\nTo get shell/tab completion, it's necessary to build a package that you and your users can install and **call directly**.\n\nSo instead of running a Python script like:\n\n<div class=\"termy\">\n\n```console\n$ python main.py\n\n✨ Some magic here ✨\n```\n\n</div>\n\n...It would be called like:\n\n<div class=\"termy\">\n\n```console\n$ magic-app\n\n✨ Some magic here ✨\n```\n\n</div>\n\nHaving a standalone program like that allows setting up shell/tab completion.\n\nThe first step to be able to create an installable package like that is to use an explicit `typer.Typer()` app.\n\nLater you can learn all the process to create a standalone CLI application and [Build a Package](./package.md){.internal-link target=_blank}.\n\nBut for now, it's just good to know that you are on that path. 😎\n"
  },
  {
    "path": "docs/tutorial/typer-command.md",
    "content": "# `typer` command\n\nThe `typer` command provides ✨ completion ✨ in the Terminal for your own small scripts. Even if they don't use Typer internally. Of course, it works better if you use **Typer** in your script.\n\nIt's probably most useful if you have a small custom Python script using **Typer** (maybe as part of some project), for some small tasks, and it's not complex/important enough to create a whole installable Python package for it (something to be installed with `pip`).\n\nIn that case, you can run your program with the `typer` command in your Terminal, and it will provide completion for your script.\n\nThe `typer` command also has functionality to generate Markdown documentation for your own **Typer** programs 📝.\n\n## Install\n\nWhen you install **Typer** with:\n\n```bash\npip install typer\n```\n\n...it includes the `typer` command.\n\nIf you don't want to use the `typer` command, you can call the Typer library as a module with:\n\n```bash\npython -m typer\n```\n\n## Install completion\n\nYou can then install completion for the `typer` command with:\n\n<div class=\"termy\">\n\n```console\n$ typer --install-completion\n\nbash completion installed in /home/user/.bashrc.\nCompletion will take effect once you restart the terminal.\n```\n\n</div>\n\n### Sample script\n\nLet's say you have a script that uses **Typer** in `my_custom_script.py`:\n\n```Python\nfrom typing import Optional\n\nimport typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef hello(name: Optional[str] = None):\n    if name:\n        typer.echo(f\"Hello {name}\")\n    else:\n        typer.echo(\"Hello World!\")\n\n\n@app.command()\ndef bye(name: Optional[str] = None):\n    if name:\n        typer.echo(f\"Bye {name}\")\n    else:\n        typer.echo(\"Goodbye!\")\n\n\nif __name__ == \"__main__\":\n    app()\n```\n\nFor it to work, you would also install **Typer**:\n\n<div class=\"termy\">\n\n```console\n$ python -m pip install typer\n---> 100%\nSuccessfully installed typer\n```\n\n</div>\n\n### Run with Python\n\nThen you could run your script with normal Python:\n\n<div class=\"termy\">\n\n```console\n$ python my_custom_script.py hello\n\nHello World!\n\n$ python my_custom_script.py hello --name Camila\n\nHello Camila!\n\n$ python my_custom_script.py bye --name Camila\n\nBye Camila\n```\n\n</div>\n\nThere's nothing wrong with using Python directly to run it. And, in fact, if some other code or program uses your script, that would probably be the best way to do it.\n\n⛔️ But in your terminal, you won't get completion when hitting <kbd>TAB</kbd> for any of the subcommands or options, like `hello`, `bye`, and `--name`.\n\n### Run with the `typer` command.\n\nYou can also run the same script with the `typer` command:\n\n<div class=\"termy\">\n\n```console\n$ typer my_custom_script.py run hello\n\nHello World!\n\n$ typer my_custom_script.py run hello --name Camila\n\nHello Camila!\n\n$ typer my_custom_script.py run bye --name Camila\n\nBye Camila\n```\n\n</div>\n\n* Instead of using `python` directly you use the `typer` command.\n* After the name of the file, add the subcommand `run`.\n\n✔️ If you installed completion for the `typer` command as described above, when you hit <kbd>TAB</kbd> you will have ✨ completion for everything ✨, including all the subcommands and options of your script, like `hello`, `bye`, and `--name` 🚀.\n\n## If main\n\nBecause the `typer` command won't use the block with:\n\n```Python\nif __name__ == \"__main__\":\n    app()\n```\n\n...you can also remove it if you are calling that script only with the `typer` command.\n\n## Run other files\n\nThe `typer` command can run any script with **Typer**, but the script doesn't even have to use **Typer** at all.\n\nYou could even run a file with a function that could be used with `typer.run()`, even if the script doesn't use `typer.run()` or anything else.\n\nFor example, a file `main.py` like this will still work:\n\n```Python\ndef main(name: str = \"World\"):\n    \"\"\"\n    Say hi to someone, by default to the World.\n    \"\"\"\n    print(f\"Hello {name}\")\n```\n\nThen you can call it with:\n\n<div class=\"termy\">\n\n```console\n$ typer main.py run --help\nUsage: typer run [OPTIONS]\n\n  Say hi to someone, by default to the World.\n\nOptions:\n  --name TEXT\n  --help       Show this message and exit.\n\n$ typer main.py run --name Camila\n\nHello Camila\n```\n\n</div>\n\nAnd it will also have completion for things like the `--name` *CLI Option*.\n\n## Run a package or module\n\nInstead of a file path you can pass a module (possibly in a package) to import.\n\nFor example:\n\n<div class=\"termy\">\n\n```console\n$ typer my_package.main run --help\nUsage: typer run [OPTIONS]\n\nOptions:\n  --name TEXT\n  --help       Show this message and exit.\n\n$ typer my_package.main run --name Camila\n\nHello Camila\n```\n\n</div>\n\n## Options\n\nYou can specify one of the following **CLI options**:\n\n* `--app`: the name of the variable with a `Typer()` object to run as the main app.\n* `--func`: the name of the variable with a function that would be used with `typer.run()`.\n\n### Defaults\n\nWhen your run a script with the `typer` command it will use the app from the following priority:\n\n* An app object from the `--app` *CLI Option*.\n* A function to convert to a **Typer** app from `--func` *CLI Option* (like when using `typer.run()`).\n* A **Typer** app in a variable with a name of `app`, `cli`, or `main`.\n* The first **Typer** app available in the file, with any name.\n* A function in a variable with a name of `main`, `cli`, or `app`.\n* The first function in the file, with any name.\n\n## Generate docs\n\nYou can also use the `typer` command to generate Markdown documentation for your **Typer** application.\n\n### Sample script with docs\n\nFor example, you could have a script like:\n\n{* docs_src/commands/help/tutorial001_an_py310.py *}\n\n### Generate docs with the `typer` command\n\nThen you could generate docs for it with the `typer` command.\n\nYou can use the subcommand `utils`.\n\nAnd then the subcommand `docs`.\n\n<div class=\"termy\">\n\n```console\n$ typer some_script.py utils docs\n```\n\n</div>\n\n/// tip\n\nIf you don't want to use the `typer` command, you can still generate docs with:\n\n```console\n$ python -m typer some_script.py utils docs\n```\n\n///\n\n**Options**:\n\n* `--name TEXT`: The name of the CLI program to use in docs.\n* `--output FILE`: An output file to write docs to, like README.md.\n* `--title TEXT`: A title to use in the docs, by default the name of the command.\n\nFor example:\n\n<div class=\"termy\">\n\n```console\n$ typer my_package.main utils docs --name awesome-cli --output README.md\n\nDocs saved to: README.md\n```\n\n</div>\n\n### Sample docs output\n\nFor example, for the previous script, the generated docs would look like:\n\n---\n\n## `awesome-cli`\n\nAwesome CLI user manager.\n\n**Usage**:\n\n```console\n$ awesome-cli [OPTIONS] COMMAND [ARGS]...\n```\n\n**Options**:\n\n* `--install-completion`: Install completion for the current shell.\n* `--show-completion`: Show completion for the current shell, to copy it or customize the installation.\n* `--help`: Show this message and exit.\n\n**Commands**:\n\n* `create`: Create a new user with USERNAME.\n* `delete`: Delete a user with USERNAME.\n* `delete-all`: Delete ALL users in the database.\n* `init`: Initialize the users database.\n\n## `awesome-cli create`\n\nCreate a new user with USERNAME.\n\n**Usage**:\n\n```console\n$ awesome-cli create [OPTIONS] USERNAME\n```\n\n**Options**:\n\n* `--help`: Show this message and exit.\n\n## `awesome-cli delete`\n\nDelete a user with USERNAME.\n\nIf --force is not used, will ask for confirmation.\n\n**Usage**:\n\n```console\n$ awesome-cli delete [OPTIONS] USERNAME\n```\n\n**Options**:\n\n* `--force / --no-force`: Force deletion without confirmation.  [required]\n* `--help`: Show this message and exit.\n\n## `awesome-cli delete-all`\n\nDelete ALL users in the database.\n\nIf --force is not used, will ask for confirmation.\n\n**Usage**:\n\n```console\n$ awesome-cli delete-all [OPTIONS]\n```\n\n**Options**:\n\n* `--force / --no-force`: Force deletion without confirmation.  [required]\n* `--help`: Show this message and exit.\n\n## `awesome-cli init`\n\nInitialize the users database.\n\n**Usage**:\n\n```console\n$ awesome-cli init [OPTIONS]\n```\n\n**Options**:\n\n* `--help`: Show this message and exit.\n"
  },
  {
    "path": "docs/virtual-environments.md",
    "content": "# Virtual Environments\n\nWhen you work in Python projects you probably should use a **virtual environment** (or a similar mechanism) to isolate the packages you install for each project.\n\n/// info\n\nIf you already know about virtual environments, how to create them and use them, you might want to skip this section. 🤓\n\n///\n\n/// tip\n\nA **virtual environment** is different than an **environment variable**.\n\nAn **environment variable** is a variable in the system that can be used by programs.\n\nA **virtual environment** is a directory with some files in it.\n\n///\n\n/// info\n\nThis page will teach you how to use **virtual environments** and how they work.\n\nIf you are ready to adopt a **tool that manages everything** for you (including installing Python), try <a href=\"https://github.com/astral-sh/uv\" class=\"external-link\" target=\"_blank\">uv</a>.\n\n///\n\n## Create a Project\n\nFirst, create a directory for your project.\n\nWhat I normally do is that I create a directory named `code` inside my home/user directory.\n\nAnd inside of that I create one directory per project.\n\n<div class=\"termy\">\n\n```console\n// Go to the home directory\n$ cd\n// Create a directory for all your code projects\n$ mkdir code\n// Enter into that code directory\n$ cd code\n// Create a directory for this project\n$ mkdir awesome-project\n// Enter into that project directory\n$ cd awesome-project\n```\n\n</div>\n\n## Create a Virtual Environment\n\nWhen you start working on a Python project **for the first time**, create a virtual environment **<abbr title=\"there are other options, this is a simple guideline\">inside your project</abbr>**.\n\n/// tip\n\nYou only need to do this **once per project**, not every time you work.\n\n///\n\n//// tab | `venv`\n\nTo create a virtual environment, you can use the `venv` module that comes with Python.\n\n<div class=\"termy\">\n\n```console\n$ python -m venv .venv\n```\n\n</div>\n\n/// details | What that command means\n\n* `python`: use the program called `python`\n* `-m`: call a module as a script, we'll tell it which module next\n* `venv`: use the module called `venv` that normally comes installed with Python\n* `.venv`: create the virtual environment in the new directory `.venv`\n\n///\n\n////\n\n//// tab | `uv`\n\nIf you have <a href=\"https://github.com/astral-sh/uv\" class=\"external-link\" target=\"_blank\">`uv`</a> installed, you can use it to create a virtual environment.\n\n<div class=\"termy\">\n\n```console\n$ uv venv\n```\n\n</div>\n\n/// tip\n\nBy default, `uv` will create a virtual environment in a directory called `.venv`.\n\nBut you could customize it passing an additional argument with the directory name.\n\n///\n\n////\n\nThat command creates a new virtual environment in a directory called `.venv`.\n\n/// details | `.venv` or other name\n\nYou could create the virtual environment in a different directory, but there's a convention of calling it `.venv`.\n\n///\n\n## Activate the Virtual Environment\n\nActivate the new virtual environment so that any Python command you run or package you install uses it.\n\n/// tip\n\nDo this **every time** you start a **new terminal session** to work on the project.\n\n///\n\n//// tab | Linux, macOS\n\n<div class=\"termy\">\n\n```console\n$ source .venv/bin/activate\n```\n\n</div>\n\n////\n\n//// tab | Windows PowerShell\n\n<div class=\"termy\">\n\n```console\n$ .venv\\Scripts\\Activate.ps1\n```\n\n</div>\n\n////\n\n//// tab | Windows Bash\n\nOr if you use Bash for Windows (e.g. <a href=\"https://gitforwindows.org/\" class=\"external-link\" target=\"_blank\">Git Bash</a>):\n\n<div class=\"termy\">\n\n```console\n$ source .venv/Scripts/activate\n```\n\n</div>\n\n////\n\n/// tip\n\nEvery time you install a **new package** in that environment, **activate** the environment again.\n\nThis makes sure that if you use a **terminal (<abbr title=\"command line interface\">CLI</abbr>) program** installed by that package, you use the one from your virtual environment and not any other that could be installed globally, probably with a different version than what you need.\n\n///\n\n## Check the Virtual Environment is Active\n\nCheck that the virtual environment is active (the previous command worked).\n\n/// tip\n\nThis is **optional**, but it's a good way to **check** that everything is working as expected and you are using the virtual environment you intended.\n\n///\n\n//// tab | Linux, macOS, Windows Bash\n\n<div class=\"termy\">\n\n```console\n$ which python\n\n/home/user/code/awesome-project/.venv/bin/python\n```\n\n</div>\n\nIf it shows the `python` binary at `.venv/bin/python`, inside of your project (in this case `awesome-project`), then it worked. 🎉\n\n////\n\n//// tab | Windows PowerShell\n\n<div class=\"termy\">\n\n```console\n$ Get-Command python\n\nC:\\Users\\user\\code\\awesome-project\\.venv\\Scripts\\python\n```\n\n</div>\n\nIf it shows the `python` binary at `.venv\\Scripts\\python`, inside of your project (in this case `awesome-project`), then it worked. 🎉\n\n////\n\n## Upgrade `pip`\n\n/// tip\n\nIf you use <a href=\"https://github.com/astral-sh/uv\" class=\"external-link\" target=\"_blank\">`uv`</a> you would use it to install things instead of `pip`, so you don't need to upgrade `pip`. 😎\n\n///\n\nIf you are using `pip` to install packages (it comes by default with Python), you should **upgrade** it to the latest version.\n\nMany exotic errors while installing a package are solved by just upgrading `pip` first.\n\n/// tip\n\nYou would normally do this **once**, right after you create the virtual environment.\n\n///\n\nMake sure the virtual environment is active (with the command above) and then run:\n\n<div class=\"termy\">\n\n```console\n$ python -m pip install --upgrade pip\n\n---> 100%\n```\n\n</div>\n\n## Add `.gitignore`\n\nIf you are using **Git** (you should), add a `.gitignore` file to exclude everything in your `.venv` from Git.\n\n/// tip\n\nIf you used <a href=\"https://github.com/astral-sh/uv\" class=\"external-link\" target=\"_blank\">`uv`</a> to create the virtual environment, it already did this for you, you can skip this step. 😎\n\n///\n\n/// tip\n\nDo this **once**, right after you create the virtual environment.\n\n///\n\n<div class=\"termy\">\n\n```console\n$ echo \"*\" > .venv/.gitignore\n```\n\n</div>\n\n/// details | What that command means\n\n* `echo \"*\"`: will \"print\" the text `*` in the terminal (the next part changes that a bit)\n* `>`: anything printed to the terminal by the command to the left of `>` should not be printed but instead written to the file that goes to the right of `>`\n* `.gitignore`: the name of the file where the text should be written\n\nAnd `*` for Git means \"everything\". So, it will ignore everything in the `.venv` directory.\n\nThat command will create a file `.gitignore` with the content:\n\n```gitignore\n*\n```\n\n///\n\n## Install Packages\n\nAfter activating the environment, you can install packages in it.\n\n/// tip\n\nDo this **once** when installing or upgrading the packages your project needs.\n\nIf you need to upgrade a version or add a new package you would **do this again**.\n\n///\n\n### Install Packages Directly\n\nIf you're in a hurry and don't want to use a file to declare your project's package requirements, you can install them directly.\n\n/// tip\n\nIt's a (very) good idea to put the packages and versions your program needs in a file (for example `requirements.txt` or `pyproject.toml`).\n\n///\n\n//// tab | `pip`\n\n<div class=\"termy\">\n\n```console\n$ pip install typer\n\n---> 100%\n```\n\n</div>\n\n////\n\n//// tab | `uv`\n\nIf you have <a href=\"https://github.com/astral-sh/uv\" class=\"external-link\" target=\"_blank\">`uv`</a>:\n\n<div class=\"termy\">\n\n```console\n$ uv pip install typer\n---> 100%\n```\n\n</div>\n\n////\n\n### Install from `requirements.txt`\n\nIf you have a `requirements.txt`, you can now use it to install its packages.\n\n//// tab | `pip`\n\n<div class=\"termy\">\n\n```console\n$ pip install -r requirements.txt\n---> 100%\n```\n\n</div>\n\n////\n\n//// tab | `uv`\n\nIf you have <a href=\"https://github.com/astral-sh/uv\" class=\"external-link\" target=\"_blank\">`uv`</a>:\n\n<div class=\"termy\">\n\n```console\n$ uv pip install -r requirements.txt\n---> 100%\n```\n\n</div>\n\n////\n\n/// details | `requirements.txt`\n\nA `requirements.txt` with some packages could look like:\n\n```requirements.txt\ntyper==0.13.0\nrich==13.7.1\n```\n\n///\n\n## Run Your Program\n\nAfter you activated the virtual environment, you can run your program, and it will use the Python inside of your virtual environment with the packages you installed there.\n\n<div class=\"termy\">\n\n```console\n$ python main.py\n\nHello World\n```\n\n</div>\n\n## Configure Your Editor\n\nYou would probably use an editor, make sure you configure it to use the same virtual environment you created (it will probably autodetect it) so that you can get autocompletion and inline errors.\n\nFor example:\n\n* <a href=\"https://code.visualstudio.com/docs/python/environments#_select-and-activate-an-environment\" class=\"external-link\" target=\"_blank\">VS Code</a>\n* <a href=\"https://www.jetbrains.com/help/pycharm/creating-virtual-environment.html\" class=\"external-link\" target=\"_blank\">PyCharm</a>\n\n/// tip\n\nYou normally have to do this only **once**, when you create the virtual environment.\n\n///\n\n## Deactivate the Virtual Environment\n\nOnce you are done working on your project you can **deactivate** the virtual environment.\n\n<div class=\"termy\">\n\n```console\n$ deactivate\n```\n\n</div>\n\nThis way, when you run `python` it won't try to run it from that virtual environment with the packages installed there.\n\n## Ready to Work\n\nNow you're ready to start working on your project.\n\n\n\n/// tip\n\nDo you want to understand what's all that above?\n\nContinue reading. 👇🤓\n\n///\n\n## Why Virtual Environments\n\nTo work with Typer you need to install <a href=\"https://www.python.org/\" class=\"external-link\" target=\"_blank\">Python</a>.\n\nAfter that, you would need to **install** Typer and any other **packages** you want to use.\n\nTo install packages you would normally use the `pip` command that comes with Python (or similar alternatives).\n\nNevertheless, if you just use `pip` directly, the packages would be installed in your **global Python environment** (the global installation of Python).\n\n### The Problem\n\nSo, what's the problem with installing packages in the global Python environment?\n\nAt some point, you will probably end up writing many different programs that depend on **different packages**. And some of these projects you work on will depend on **different versions** of the same package. 😱\n\nFor example, you could create a project called `philosophers-stone`, this program depends on another package called **`harry`, using the version `1`**. So, you need to install `harry`.\n\n```mermaid\nflowchart LR\n    stone(philosophers-stone) -->|requires| harry-1[harry v1]\n```\n\nThen, at some point later, you create another project called `prisoner-of-azkaban`, and this project also depends on `harry`, but this project needs **`harry` version `3`**.\n\n```mermaid\nflowchart LR\n    azkaban(prisoner-of-azkaban) --> |requires| harry-3[harry v3]\n```\n\nBut now the problem is, if you install the packages globally (in the global environment) instead of in a local **virtual environment**, you will have to choose which version of `harry` to install.\n\nIf you want to run `philosophers-stone` you will need to first install `harry` version `1`, for example with:\n\n<div class=\"termy\">\n\n```console\n$ pip install \"harry==1\"\n```\n\n</div>\n\nAnd then you would end up with `harry` version `1` installed in your global Python environment.\n\n```mermaid\nflowchart LR\n    subgraph global[global env]\n        harry-1[harry v1]\n    end\n    subgraph stone-project[philosophers-stone project]\n        stone(philosophers-stone) -->|requires| harry-1\n    end\n```\n\nBut then if you want to run `prisoner-of-azkaban`, you will need to uninstall `harry` version `1` and install `harry` version `3` (or just installing version `3` would automatically uninstall version `1`).\n\n<div class=\"termy\">\n\n```console\n$ pip install \"harry==3\"\n```\n\n</div>\n\nAnd then you would end up with `harry` version `3` installed in your global Python environment.\n\nAnd if you try to run `philosophers-stone` again, there's a chance it would **not work** because it needs `harry` version `1`.\n\n```mermaid\nflowchart LR\n    subgraph global[global env]\n        harry-1[<strike>harry v1</strike>]\n        style harry-1 fill:#ccc,stroke-dasharray: 5 5\n        harry-3[harry v3]\n    end\n    subgraph stone-project[philosophers-stone project]\n        stone(philosophers-stone) -.-x|⛔️| harry-1\n    end\n    subgraph azkaban-project[prisoner-of-azkaban project]\n        azkaban(prisoner-of-azkaban) --> |requires| harry-3\n    end\n```\n\n/// tip\n\nIt's very common in Python packages to try the best to **avoid breaking changes** in **new versions**, but it's better to be safe, and install newer versions intentionally and when you can run the tests to check everything is working correctly.\n\n///\n\nNow, imagine that with **many** other **packages** that all your **projects depend on**. That's very difficult to manage. And you would probably end up running some projects with some **incompatible versions** of the packages, and not knowing why something isn't working.\n\nAlso, depending on your operating system (e.g. Linux, Windows, macOS), it could have come with Python already installed. And in that case it probably had some packages pre-installed with some specific versions **needed by your system**. If you install packages in the global Python environment, you could end up **breaking** some of the programs that came with your operating system.\n\n## Where are Packages Installed\n\nWhen you install Python, it creates some directories with some files in your computer.\n\nSome of these directories are the ones in charge of having all the packages you install.\n\nWhen you run:\n\n<div class=\"termy\">\n\n```console\n// Don't run this now, it's just an example 🤓\n$ pip install typer\n---> 100%\n```\n\n</div>\n\nThat will download a compressed file with the Typer code, normally from <a href=\"https://pypi.org/project/typer/\" class=\"external-link\" target=\"_blank\">PyPI</a>.\n\nIt will also **download** files for other packages that Typer depends on.\n\nThen it will **extract** all those files and put them in a directory in your computer.\n\nBy default, it will put those files downloaded and extracted in the directory that comes with your Python installation, that's the **global environment**.\n\n## What are Virtual Environments\n\nThe solution to the problems of having all the packages in the global environment is to use a **virtual environment for each project** you work on.\n\nA virtual environment is a **directory**, very similar to the global one, where you can install the packages for a project.\n\nThis way, each project will have its own virtual environment (`.venv` directory) with its own packages.\n\n```mermaid\nflowchart TB\n    subgraph stone-project[philosophers-stone project]\n        stone(philosophers-stone) --->|requires| harry-1\n        subgraph venv1[.venv]\n            harry-1[harry v1]\n        end\n    end\n    subgraph azkaban-project[prisoner-of-azkaban project]\n        azkaban(prisoner-of-azkaban) --->|requires| harry-3\n        subgraph venv2[.venv]\n            harry-3[harry v3]\n        end\n    end\n    stone-project ~~~ azkaban-project\n```\n\n## What Does Activating a Virtual Environment Mean\n\nWhen you activate a virtual environment, for example with:\n\n//// tab | Linux, macOS\n\n<div class=\"termy\">\n\n```console\n$ source .venv/bin/activate\n```\n\n</div>\n\n////\n\n//// tab | Windows PowerShell\n\n<div class=\"termy\">\n\n```console\n$ .venv\\Scripts\\Activate.ps1\n```\n\n</div>\n\n////\n\n//// tab | Windows Bash\n\nOr if you use Bash for Windows (e.g. <a href=\"https://gitforwindows.org/\" class=\"external-link\" target=\"_blank\">Git Bash</a>):\n\n<div class=\"termy\">\n\n```console\n$ source .venv/Scripts/activate\n```\n\n</div>\n\n////\n\nThat command will create or modify some [environment variables](environment-variables.md){.internal-link target=_blank} that will be available for the next commands.\n\nOne of those variables is the `PATH` variable.\n\n/// tip\n\nYou can learn more about the `PATH` environment variable in the [Environment Variables](environment-variables.md#path-environment-variable){.internal-link target=_blank} section.\n\n///\n\nActivating a virtual environment adds its path `.venv/bin` (on Linux and macOS) or `.venv\\Scripts` (on Windows) to the `PATH` environment variable.\n\nLet's say that before activating the environment, the `PATH` variable looked like this:\n\n//// tab | Linux, macOS\n\n```plaintext\n/usr/bin:/bin:/usr/sbin:/sbin\n```\n\nThat means that the system would look for programs in:\n\n* `/usr/bin`\n* `/bin`\n* `/usr/sbin`\n* `/sbin`\n\n////\n\n//// tab | Windows\n\n```plaintext\nC:\\Windows\\System32\n```\n\nThat means that the system would look for programs in:\n\n* `C:\\Windows\\System32`\n\n////\n\nAfter activating the virtual environment, the `PATH` variable would look something like this:\n\n//// tab | Linux, macOS\n\n```plaintext\n/home/user/code/awesome-project/.venv/bin:/usr/bin:/bin:/usr/sbin:/sbin\n```\n\nThat means that the system will now start looking first look for programs in:\n\n```plaintext\n/home/user/code/awesome-project/.venv/bin\n```\n\nbefore looking in the other directories.\n\nSo, when you type `python` in the terminal, the system will find the Python program in\n\n```plaintext\n/home/user/code/awesome-project/.venv/bin/python\n```\n\nand use that one.\n\n////\n\n//// tab | Windows\n\n```plaintext\nC:\\Users\\user\\code\\awesome-project\\.venv\\Scripts;C:\\Windows\\System32\n```\n\nThat means that the system will now start looking first look for programs in:\n\n```plaintext\nC:\\Users\\user\\code\\awesome-project\\.venv\\Scripts\n```\n\nbefore looking in the other directories.\n\nSo, when you type `python` in the terminal, the system will find the Python program in\n\n```plaintext\nC:\\Users\\user\\code\\awesome-project\\.venv\\Scripts\\python\n```\n\nand use that one.\n\n////\n\nAn important detail is that it will put the virtual environment path at the **beginning** of the `PATH` variable. The system will find it **before** finding any other Python available. This way, when you run `python`, it will use the Python **from the virtual environment** instead of any other `python` (for example, a `python` from a global environment).\n\nActivating a virtual environment also changes a couple of other things, but this is one of the most important things it does.\n\n## Checking a Virtual Environment\n\nWhen you check if a virtual environment is active, for example with:\n\n//// tab | Linux, macOS, Windows Bash\n\n<div class=\"termy\">\n\n```console\n$ which python\n\n/home/user/code/awesome-project/.venv/bin/python\n```\n\n</div>\n\n////\n\n//// tab | Windows PowerShell\n\n<div class=\"termy\">\n\n```console\n$ Get-Command python\n\nC:\\Users\\user\\code\\awesome-project\\.venv\\Scripts\\python\n```\n\n</div>\n\n////\n\nThat means that the `python` program that will be used is the one **in the virtual environment**.\n\nyou use `which` in Linux and macOS and `Get-Command` in Windows PowerShell.\n\nThe way that command works is that it will go and check in the `PATH` environment variable, going through **each path in order**, looking for the program called `python`. Once it finds it, it will **show you the path** to that program.\n\nThe most important part is that when you call `python`, that is the exact \"`python`\" that will be executed.\n\nSo, you can confirm if you are in the correct virtual environment.\n\n/// tip\n\nIt's easy to activate one virtual environment, get one Python, and then **go to another project**.\n\nAnd the second project **wouldn't work** because you are using the **incorrect Python**, from a virtual environment for another project.\n\nIt's useful being able to check what `python` is being used. 🤓\n\n///\n\n## Why Deactivate a Virtual Environment\n\nFor example, you could be working on a project `philosophers-stone`, **activate that virtual environment**, install packages and work with that environment.\n\nAnd then you want to work on **another project** `prisoner-of-azkaban`.\n\nYou go to that project:\n\n<div class=\"termy\">\n\n```console\n$ cd ~/code/prisoner-of-azkaban\n```\n\n</div>\n\nIf you don't deactivate the virtual environment for `philosophers-stone`, when you run `python` in the terminal, it will try to use the Python from `philosophers-stone`.\n\n<div class=\"termy\">\n\n```console\n$ cd ~/code/prisoner-of-azkaban\n\n$ python main.py\n\n// Error importing sirius, it's not installed 😱\nTraceback (most recent call last):\n    File \"main.py\", line 1, in <module>\n        import sirius\n```\n\n</div>\n\nBut if you deactivate the virtual environment and activate the new one for `prisoner-of-askaban` then when you run `python` it will use the Python from the virtual environment in `prisoner-of-azkaban`.\n\n<div class=\"termy\">\n\n```console\n$ cd ~/code/prisoner-of-azkaban\n\n// You don't need to be in the old directory to deactivate, you can do it wherever you are, even after going to the other project 😎\n$ deactivate\n\n// Activate the virtual environment in prisoner-of-azkaban/.venv 🚀\n$ source .venv/bin/activate\n\n// Now when you run python, it will find the package sirius installed in this virtual environment ✨\n$ python main.py\n\nI solemnly swear 🐺\n```\n\n</div>\n\n## Alternatives\n\nThis is a simple guide to get you started and teach you how everything works **underneath**.\n\nThere are many **alternatives** to managing virtual environments, package dependencies (requirements), projects.\n\nOnce you are ready and want to use a tool to **manage the entire project**, packages dependencies, virtual environments, etc. I would suggest you try <a href=\"https://github.com/astral-sh/uv\" class=\"external-link\" target=\"_blank\">uv</a>.\n\n`uv` can do a lot of things, it can:\n\n* **Install Python** for you, including different versions\n* Manage the **virtual environment** for your projects\n* Install **packages**\n* Manage package **dependencies and versions** for your project\n* Make sure you have an **exact** set of packages and versions to install, including their dependencies, so that you can be sure that you can run your project in production exactly the same as in your computer while developing, this is called **locking**\n* And many other things\n\n## Conclusion\n\nIf you read and understood all this, now **you know much more** about virtual environments than many developers out there. 🤓\n\nKnowing these details will most probably be useful in a future time when you are debugging something that seems complex, but you will know **how it all works underneath**. 😎\n"
  },
  {
    "path": "docs_src/app_dir/__init__.py",
    "content": ""
  },
  {
    "path": "docs_src/app_dir/tutorial001_py310.py",
    "content": "from pathlib import Path\n\nimport typer\n\nAPP_NAME = \"my-super-cli-app\"\n\napp = typer.Typer()\n\n\n@app.command()\ndef main():\n    app_dir = typer.get_app_dir(APP_NAME)\n    config_path: Path = Path(app_dir) / \"config.json\"\n    if not config_path.is_file():\n        print(\"Config file doesn't exist yet\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/arguments/__init__.py",
    "content": ""
  },
  {
    "path": "docs_src/arguments/default/__init__.py",
    "content": ""
  },
  {
    "path": "docs_src/arguments/default/tutorial001_an_py310.py",
    "content": "from typing import Annotated\n\nimport typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(name: Annotated[str, typer.Argument()] = \"Wade Wilson\"):\n    print(f\"Hello {name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/arguments/default/tutorial001_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(name: str = typer.Argument(\"Wade Wilson\")):\n    print(f\"Hello {name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/arguments/default/tutorial002_an_py310.py",
    "content": "import random\nfrom typing import Annotated\n\nimport typer\n\napp = typer.Typer()\n\n\ndef get_name():\n    return random.choice([\"Deadpool\", \"Rick\", \"Morty\", \"Hiro\"])\n\n\n@app.command()\ndef main(name: Annotated[str, typer.Argument(default_factory=get_name)]):\n    print(f\"Hello {name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/arguments/default/tutorial002_py310.py",
    "content": "import random\n\nimport typer\n\napp = typer.Typer()\n\n\ndef get_name():\n    return random.choice([\"Deadpool\", \"Rick\", \"Morty\", \"Hiro\"])\n\n\n@app.command()\ndef main(name: str = typer.Argument(default_factory=get_name)):\n    print(f\"Hello {name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/arguments/envvar/__init__.py",
    "content": ""
  },
  {
    "path": "docs_src/arguments/envvar/tutorial001_an_py310.py",
    "content": "from typing import Annotated\n\nimport typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(name: Annotated[str, typer.Argument(envvar=\"AWESOME_NAME\")] = \"World\"):\n    print(f\"Hello Mr. {name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/arguments/envvar/tutorial001_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(name: str = typer.Argument(\"World\", envvar=\"AWESOME_NAME\")):\n    print(f\"Hello Mr. {name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/arguments/envvar/tutorial002_an_py310.py",
    "content": "from typing import Annotated\n\nimport typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(\n    name: Annotated[str, typer.Argument(envvar=[\"AWESOME_NAME\", \"GOD_NAME\"])] = \"World\",\n):\n    print(f\"Hello Mr. {name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/arguments/envvar/tutorial002_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(name: str = typer.Argument(\"World\", envvar=[\"AWESOME_NAME\", \"GOD_NAME\"])):\n    print(f\"Hello Mr. {name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/arguments/envvar/tutorial003_an_py310.py",
    "content": "from typing import Annotated\n\nimport typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(\n    name: Annotated[\n        str, typer.Argument(envvar=\"AWESOME_NAME\", show_envvar=False)\n    ] = \"World\",\n):\n    print(f\"Hello Mr. {name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/arguments/envvar/tutorial003_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(name: str = typer.Argument(\"World\", envvar=\"AWESOME_NAME\", show_envvar=False)):\n    print(f\"Hello Mr. {name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/arguments/help/__init__.py",
    "content": ""
  },
  {
    "path": "docs_src/arguments/help/tutorial001_an_py310.py",
    "content": "from typing import Annotated\n\nimport typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(name: Annotated[str, typer.Argument(help=\"The name of the user to greet\")]):\n    print(f\"Hello {name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/arguments/help/tutorial001_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(name: str = typer.Argument(..., help=\"The name of the user to greet\")):\n    print(f\"Hello {name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/arguments/help/tutorial002_an_py310.py",
    "content": "from typing import Annotated\n\nimport typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(name: Annotated[str, typer.Argument(help=\"The name of the user to greet\")]):\n    \"\"\"\n    Say hi to NAME very gently, like Dirk.\n    \"\"\"\n    print(f\"Hello {name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/arguments/help/tutorial002_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(name: str = typer.Argument(..., help=\"The name of the user to greet\")):\n    \"\"\"\n    Say hi to NAME very gently, like Dirk.\n    \"\"\"\n    print(f\"Hello {name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/arguments/help/tutorial003_an_py310.py",
    "content": "from typing import Annotated\n\nimport typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(name: Annotated[str, typer.Argument(help=\"Who to greet\")] = \"World\"):\n    \"\"\"\n    Say hi to NAME very gently, like Dirk.\n    \"\"\"\n    print(f\"Hello {name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/arguments/help/tutorial003_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(name: str = typer.Argument(\"World\", help=\"Who to greet\")):\n    \"\"\"\n    Say hi to NAME very gently, like Dirk.\n    \"\"\"\n    print(f\"Hello {name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/arguments/help/tutorial004_an_py310.py",
    "content": "from typing import Annotated\n\nimport typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(\n    name: Annotated[\n        str, typer.Argument(help=\"Who to greet\", show_default=False)\n    ] = \"World\",\n):\n    \"\"\"\n    Say hi to NAME very gently, like Dirk.\n    \"\"\"\n    print(f\"Hello {name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/arguments/help/tutorial004_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(name: str = typer.Argument(\"World\", help=\"Who to greet\", show_default=False)):\n    \"\"\"\n    Say hi to NAME very gently, like Dirk.\n    \"\"\"\n    print(f\"Hello {name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/arguments/help/tutorial005_an_py310.py",
    "content": "from typing import Annotated\n\nimport typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(\n    name: Annotated[\n        str,\n        typer.Argument(\n            help=\"Who to greet\", show_default=\"Deadpoolio the amazing's name\"\n        ),\n    ] = \"Wade Wilson\",\n):\n    print(f\"Hello {name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/arguments/help/tutorial005_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(\n    name: str = typer.Argument(\n        \"Wade Wilson\", help=\"Who to greet\", show_default=\"Deadpoolio the amazing's name\"\n    ),\n):\n    print(f\"Hello {name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/arguments/help/tutorial006_an_py310.py",
    "content": "from typing import Annotated\n\nimport typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(name: Annotated[str, typer.Argument(metavar=\"✨username✨\")] = \"World\"):\n    print(f\"Hello {name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/arguments/help/tutorial006_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(name: str = typer.Argument(\"World\", metavar=\"✨username✨\")):\n    print(f\"Hello {name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/arguments/help/tutorial007_an_py310.py",
    "content": "from typing import Annotated\n\nimport typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(\n    name: Annotated[str, typer.Argument(help=\"Who to greet\")],\n    lastname: Annotated[\n        str, typer.Argument(help=\"The last name\", rich_help_panel=\"Secondary Arguments\")\n    ] = \"\",\n    age: Annotated[\n        str,\n        typer.Argument(help=\"The user's age\", rich_help_panel=\"Secondary Arguments\"),\n    ] = \"\",\n):\n    \"\"\"\n    Say hi to NAME very gently, like Dirk.\n    \"\"\"\n    print(f\"Hello {name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/arguments/help/tutorial007_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(\n    name: str = typer.Argument(..., help=\"Who to greet\"),\n    lastname: str = typer.Argument(\n        \"\", help=\"The last name\", rich_help_panel=\"Secondary Arguments\"\n    ),\n    age: str = typer.Argument(\n        \"\", help=\"The user's age\", rich_help_panel=\"Secondary Arguments\"\n    ),\n):\n    \"\"\"\n    Say hi to NAME very gently, like Dirk.\n    \"\"\"\n    print(f\"Hello {name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/arguments/help/tutorial008_an_py310.py",
    "content": "from typing import Annotated\n\nimport typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(name: Annotated[str, typer.Argument(hidden=True)] = \"World\"):\n    \"\"\"\n    Say hi to NAME very gently, like Dirk.\n    \"\"\"\n    print(f\"Hello {name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/arguments/help/tutorial008_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(name: str = typer.Argument(\"World\", hidden=True)):\n    \"\"\"\n    Say hi to NAME very gently, like Dirk.\n    \"\"\"\n    print(f\"Hello {name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/arguments/optional/__init__.py",
    "content": ""
  },
  {
    "path": "docs_src/arguments/optional/tutorial000_an_py310.py",
    "content": "from typing import Annotated\n\nimport typer\n\n\ndef main(name: Annotated[str, typer.Argument()]):\n    print(f\"Hello {name}\")\n\n\nif __name__ == \"__main__\":\n    typer.run(main)\n"
  },
  {
    "path": "docs_src/arguments/optional/tutorial000_py310.py",
    "content": "import typer\n\n\ndef main(name: str = typer.Argument()):\n    print(f\"Hello {name}\")\n\n\nif __name__ == \"__main__\":\n    typer.run(main)\n"
  },
  {
    "path": "docs_src/arguments/optional/tutorial001_an_py310.py",
    "content": "from typing import Annotated\n\nimport typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(name: Annotated[str, typer.Argument()]):\n    print(f\"Hello {name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/arguments/optional/tutorial001_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(name: str = typer.Argument()):\n    print(f\"Hello {name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/arguments/optional/tutorial002_an_py310.py",
    "content": "from typing import Annotated\n\nimport typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(name: Annotated[str, typer.Argument()] = \"World\"):\n    print(f\"Hello {name}!\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/arguments/optional/tutorial002_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(name: str = typer.Argument(default=\"World\")):\n    print(f\"Hello {name}!\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/arguments/optional/tutorial003_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(name: str = typer.Argument(default=...)):\n    print(f\"Hello {name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/commands/__init__.py",
    "content": ""
  },
  {
    "path": "docs_src/commands/arguments/__init__.py",
    "content": ""
  },
  {
    "path": "docs_src/commands/arguments/tutorial001_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef create(username: str):\n    print(f\"Creating user: {username}\")\n\n\n@app.command()\ndef delete(username: str):\n    print(f\"Deleting user: {username}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/commands/callback/__init__.py",
    "content": ""
  },
  {
    "path": "docs_src/commands/callback/tutorial001_py310.py",
    "content": "import typer\n\napp = typer.Typer()\nstate = {\"verbose\": False}\n\n\n@app.command()\ndef create(username: str):\n    if state[\"verbose\"]:\n        print(\"About to create a user\")\n    print(f\"Creating user: {username}\")\n    if state[\"verbose\"]:\n        print(\"Just created a user\")\n\n\n@app.command()\ndef delete(username: str):\n    if state[\"verbose\"]:\n        print(\"About to delete a user\")\n    print(f\"Deleting user: {username}\")\n    if state[\"verbose\"]:\n        print(\"Just deleted a user\")\n\n\n@app.callback()\ndef main(verbose: bool = False):\n    \"\"\"\n    Manage users in the awesome CLI app.\n    \"\"\"\n    if verbose:\n        print(\"Will write verbose output\")\n        state[\"verbose\"] = True\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/commands/callback/tutorial002_py310.py",
    "content": "import typer\n\n\ndef callback():\n    print(\"Running a command\")\n\n\napp = typer.Typer(callback=callback)\n\n\n@app.command()\ndef create(name: str):\n    print(f\"Creating user: {name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/commands/callback/tutorial003_py310.py",
    "content": "import typer\n\n\ndef callback():\n    print(\"Running a command\")\n\n\napp = typer.Typer(callback=callback)\n\n\n@app.callback()\ndef new_callback():\n    print(\"Override callback, running a command\")\n\n\n@app.command()\ndef create(name: str):\n    print(f\"Creating user: {name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/commands/callback/tutorial004_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.callback()\ndef callback():\n    \"\"\"\n    Manage users CLI app.\n\n    Use it with the create command.\n\n    A new user with the given NAME will be created.\n    \"\"\"\n\n\n@app.command()\ndef create(name: str):\n    print(f\"Creating user: {name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/commands/context/__init__.py",
    "content": ""
  },
  {
    "path": "docs_src/commands/context/tutorial001_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef create(username: str):\n    print(f\"Creating user: {username}\")\n\n\n@app.command()\ndef delete(username: str):\n    print(f\"Deleting user: {username}\")\n\n\n@app.callback()\ndef main(ctx: typer.Context):\n    \"\"\"\n    Manage users in the awesome CLI app.\n    \"\"\"\n    print(f\"About to execute command: {ctx.invoked_subcommand}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/commands/context/tutorial002_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef create(username: str):\n    print(f\"Creating user: {username}\")\n\n\n@app.command()\ndef delete(username: str):\n    print(f\"Deleting user: {username}\")\n\n\n@app.callback(invoke_without_command=True)\ndef main():\n    \"\"\"\n    Manage users in the awesome CLI app.\n    \"\"\"\n    print(\"Initializing database\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/commands/context/tutorial003_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef create(username: str):\n    print(f\"Creating user: {username}\")\n\n\n@app.command()\ndef delete(username: str):\n    print(f\"Deleting user: {username}\")\n\n\n@app.callback(invoke_without_command=True)\ndef main(ctx: typer.Context):\n    \"\"\"\n    Manage users in the awesome CLI app.\n    \"\"\"\n    if ctx.invoked_subcommand is None:\n        print(\"Initializing database\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/commands/context/tutorial004_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command(\n    context_settings={\"allow_extra_args\": True, \"ignore_unknown_options\": True}\n)\ndef main(ctx: typer.Context):\n    for extra_arg in ctx.args:\n        print(f\"Got extra arg: {extra_arg}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/commands/help/__init__.py",
    "content": ""
  },
  {
    "path": "docs_src/commands/help/tutorial001_an_py310.py",
    "content": "from typing import Annotated\n\nimport typer\n\napp = typer.Typer(help=\"Awesome CLI user manager.\")\n\n\n@app.command()\ndef create(username: str):\n    \"\"\"\n    Create a new user with USERNAME.\n    \"\"\"\n    print(f\"Creating user: {username}\")\n\n\n@app.command()\ndef delete(\n    username: str,\n    force: Annotated[\n        bool,\n        typer.Option(\n            prompt=\"Are you sure you want to delete the user?\",\n            help=\"Force deletion without confirmation.\",\n        ),\n    ],\n):\n    \"\"\"\n    Delete a user with USERNAME.\n\n    If --force is not used, will ask for confirmation.\n    \"\"\"\n    if force:\n        print(f\"Deleting user: {username}\")\n    else:\n        print(\"Operation cancelled\")\n\n\n@app.command()\ndef delete_all(\n    force: Annotated[\n        bool,\n        typer.Option(\n            prompt=\"Are you sure you want to delete ALL users?\",\n            help=\"Force deletion without confirmation.\",\n        ),\n    ],\n):\n    \"\"\"\n    Delete ALL users in the database.\n\n    If --force is not used, will ask for confirmation.\n    \"\"\"\n    if force:\n        print(\"Deleting all users\")\n    else:\n        print(\"Operation cancelled\")\n\n\n@app.command()\ndef init():\n    \"\"\"\n    Initialize the users database.\n    \"\"\"\n    print(\"Initializing user database\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/commands/help/tutorial001_py310.py",
    "content": "import typer\n\napp = typer.Typer(help=\"Awesome CLI user manager.\")\n\n\n@app.command()\ndef create(username: str):\n    \"\"\"\n    Create a new user with USERNAME.\n    \"\"\"\n    print(f\"Creating user: {username}\")\n\n\n@app.command()\ndef delete(\n    username: str,\n    force: bool = typer.Option(\n        ...,\n        prompt=\"Are you sure you want to delete the user?\",\n        help=\"Force deletion without confirmation.\",\n    ),\n):\n    \"\"\"\n    Delete a user with USERNAME.\n\n    If --force is not used, will ask for confirmation.\n    \"\"\"\n    if force:\n        print(f\"Deleting user: {username}\")\n    else:\n        print(\"Operation cancelled\")\n\n\n@app.command()\ndef delete_all(\n    force: bool = typer.Option(\n        ...,\n        prompt=\"Are you sure you want to delete ALL users?\",\n        help=\"Force deletion without confirmation.\",\n    ),\n):\n    \"\"\"\n    Delete ALL users in the database.\n\n    If --force is not used, will ask for confirmation.\n    \"\"\"\n    if force:\n        print(\"Deleting all users\")\n    else:\n        print(\"Operation cancelled\")\n\n\n@app.command()\ndef init():\n    \"\"\"\n    Initialize the users database.\n    \"\"\"\n    print(\"Initializing user database\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/commands/help/tutorial002_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command(help=\"Create a new user with USERNAME.\")\ndef create(username: str):\n    \"\"\"\n    Some internal utility function to create.\n    \"\"\"\n    print(f\"Creating user: {username}\")\n\n\n@app.command(help=\"Delete a user with USERNAME.\")\ndef delete(username: str):\n    \"\"\"\n    Some internal utility function to delete.\n    \"\"\"\n    print(f\"Deleting user: {username}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/commands/help/tutorial003_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef create(username: str):\n    \"\"\"\n    Create a user.\n    \"\"\"\n    print(f\"Creating user: {username}\")\n\n\n@app.command(deprecated=True)\ndef delete(username: str):\n    \"\"\"\n    Delete a user.\n\n    This is deprecated and will stop being supported soon.\n    \"\"\"\n    print(f\"Deleting user: {username}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/commands/help/tutorial004_an_py310.py",
    "content": "from typing import Annotated\n\nimport typer\n\napp = typer.Typer(rich_markup_mode=\"rich\")\n\n\n@app.command()\ndef create(\n    username: Annotated[\n        str, typer.Argument(help=\"The username to be [green]created[/green]\")\n    ],\n):\n    \"\"\"\n    [bold green]Create[/bold green] a new [italic]shiny[/italic] user. :sparkles:\n\n    This requires a [underline]username[/underline].\n    \"\"\"\n    print(f\"Creating user: {username}\")\n\n\n@app.command(help=\"[bold red]Delete[/bold red] a user with [italic]USERNAME[/italic].\")\ndef delete(\n    username: Annotated[\n        str, typer.Argument(help=\"The username to be [red]deleted[/red]\")\n    ],\n    force: Annotated[\n        bool, typer.Option(help=\"Force the [bold red]deletion[/bold red] :boom:\")\n    ] = False,\n):\n    \"\"\"\n    Some internal utility function to delete.\n    \"\"\"\n    print(f\"Deleting user: {username}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/commands/help/tutorial004_py310.py",
    "content": "import typer\n\napp = typer.Typer(rich_markup_mode=\"rich\")\n\n\n@app.command()\ndef create(\n    username: str = typer.Argument(\n        ..., help=\"The username to be [green]created[/green]\"\n    ),\n):\n    \"\"\"\n    [bold green]Create[/bold green] a new [italic]shiny[/italic] user. :sparkles:\n\n    This requires a [underline]username[/underline].\n    \"\"\"\n    print(f\"Creating user: {username}\")\n\n\n@app.command(help=\"[bold red]Delete[/bold red] a user with [italic]USERNAME[/italic].\")\ndef delete(\n    username: str = typer.Argument(..., help=\"The username to be [red]deleted[/red]\"),\n    force: bool = typer.Option(\n        False, help=\"Force the [bold red]deletion[/bold red] :boom:\"\n    ),\n):\n    \"\"\"\n    Some internal utility function to delete.\n    \"\"\"\n    print(f\"Deleting user: {username}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/commands/help/tutorial005_an_py310.py",
    "content": "from typing import Annotated\n\nimport typer\n\napp = typer.Typer(rich_markup_mode=\"markdown\")\n\n\n@app.command()\ndef create(\n    username: Annotated[str, typer.Argument(help=\"The username to be **created**\")],\n):\n    \"\"\"\n    **Create** a new *shiny* user. :sparkles:\n\n    * Create a username\n\n    * Show that the username is created\n\n    ---\n\n    Learn more at the [Typer docs website](https://typer.tiangolo.com)\n    \"\"\"\n    print(f\"Creating user: {username}\")\n\n\n@app.command(help=\"**Delete** a user with *USERNAME*.\")\ndef delete(\n    username: Annotated[str, typer.Argument(help=\"The username to be **deleted**\")],\n    force: Annotated[bool, typer.Option(help=\"Force the **deletion** :boom:\")] = False,\n):\n    \"\"\"\n    Some internal utility function to delete.\n    \"\"\"\n    print(f\"Deleting user: {username}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/commands/help/tutorial005_py310.py",
    "content": "import typer\n\napp = typer.Typer(rich_markup_mode=\"markdown\")\n\n\n@app.command()\ndef create(username: str = typer.Argument(..., help=\"The username to be **created**\")):\n    \"\"\"\n    **Create** a new *shiny* user. :sparkles:\n\n    * Create a username\n\n    * Show that the username is created\n\n    ---\n\n    Learn more at the [Typer docs website](https://typer.tiangolo.com)\n    \"\"\"\n    print(f\"Creating user: {username}\")\n\n\n@app.command(help=\"**Delete** a user with *USERNAME*.\")\ndef delete(\n    username: str = typer.Argument(..., help=\"The username to be **deleted**\"),\n    force: bool = typer.Option(False, help=\"Force the **deletion** :boom:\"),\n):\n    \"\"\"\n    Some internal utility function to delete.\n    \"\"\"\n    print(f\"Deleting user: {username}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/commands/help/tutorial006_py310.py",
    "content": "import typer\n\napp = typer.Typer(rich_markup_mode=\"rich\")\n\n\n@app.command()\ndef create(username: str):\n    \"\"\"\n    [green]Create[/green] a new user. :sparkles:\n    \"\"\"\n    print(f\"Creating user: {username}\")\n\n\n@app.command()\ndef delete(username: str):\n    \"\"\"\n    [red]Delete[/red] a user. :x:\n    \"\"\"\n    print(f\"Deleting user: {username}\")\n\n\n@app.command(rich_help_panel=\"Utils and Configs\")\ndef config(configuration: str):\n    \"\"\"\n    [blue]Configure[/blue] the system. :gear:\n    \"\"\"\n    print(f\"Configuring the system with: {configuration}\")\n\n\n@app.command(rich_help_panel=\"Utils and Configs\")\ndef sync():\n    \"\"\"\n    [blue]Synchronize[/blue] the system or something fancy like that. :recycle:\n    \"\"\"\n    print(\"Syncing the system\")\n\n\n@app.command(rich_help_panel=\"Help and Others\")\ndef help():\n    \"\"\"\n    Get [yellow]help[/yellow] with the system. :question:\n    \"\"\"\n    print(\"Opening help portal...\")\n\n\n@app.command(rich_help_panel=\"Help and Others\")\ndef report():\n    \"\"\"\n    [yellow]Report[/yellow] an issue. :exclamation:\n    \"\"\"\n    print(\"Please open a new issue online, not a direct message\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/commands/help/tutorial007_an_py310.py",
    "content": "from typing import Annotated\n\nimport typer\n\napp = typer.Typer(rich_markup_mode=\"rich\")\n\n\n@app.command()\ndef create(\n    username: Annotated[str, typer.Argument(help=\"The username to create\")],\n    lastname: Annotated[\n        str,\n        typer.Argument(\n            help=\"The last name of the new user\", rich_help_panel=\"Secondary Arguments\"\n        ),\n    ] = \"\",\n    force: Annotated[bool, typer.Option(help=\"Force the creation of the user\")] = False,\n    age: Annotated[\n        int | None,\n        typer.Option(help=\"The age of the new user\", rich_help_panel=\"Additional Data\"),\n    ] = None,\n    favorite_color: Annotated[\n        str | None,\n        typer.Option(\n            help=\"The favorite color of the new user\",\n            rich_help_panel=\"Additional Data\",\n        ),\n    ] = None,\n):\n    \"\"\"\n    [green]Create[/green] a new user. :sparkles:\n    \"\"\"\n    print(f\"Creating user: {username}\")\n\n\n@app.command(rich_help_panel=\"Utils and Configs\")\ndef config(configuration: str):\n    \"\"\"\n    [blue]Configure[/blue] the system. :gear:\n    \"\"\"\n    print(f\"Configuring the system with: {configuration}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/commands/help/tutorial007_py310.py",
    "content": "import typer\n\napp = typer.Typer(rich_markup_mode=\"rich\")\n\n\n@app.command()\ndef create(\n    username: str = typer.Argument(..., help=\"The username to create\"),\n    lastname: str = typer.Argument(\n        \"\", help=\"The last name of the new user\", rich_help_panel=\"Secondary Arguments\"\n    ),\n    force: bool = typer.Option(False, help=\"Force the creation of the user\"),\n    age: int | None = typer.Option(\n        None, help=\"The age of the new user\", rich_help_panel=\"Additional Data\"\n    ),\n    favorite_color: str | None = typer.Option(\n        None,\n        help=\"The favorite color of the new user\",\n        rich_help_panel=\"Additional Data\",\n    ),\n):\n    \"\"\"\n    [green]Create[/green] a new user. :sparkles:\n    \"\"\"\n    print(f\"Creating user: {username}\")\n\n\n@app.command(rich_help_panel=\"Utils and Configs\")\ndef config(configuration: str):\n    \"\"\"\n    [blue]Configure[/blue] the system. :gear:\n    \"\"\"\n    print(f\"Configuring the system with: {configuration}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/commands/help/tutorial008_py310.py",
    "content": "import typer\n\napp = typer.Typer(rich_markup_mode=\"rich\")\n\n\n@app.command(epilog=\"Made with :heart: in [blue]Venus[/blue]\")\ndef create(username: str):\n    \"\"\"\n    [green]Create[/green] a new user. :sparkles:\n    \"\"\"\n    print(f\"Creating user: {username}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/commands/index/__init__.py",
    "content": ""
  },
  {
    "path": "docs_src/commands/index/tutorial002_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef create():\n    print(\"Creating user: Hiro Hamada\")\n\n\n@app.command()\ndef delete():\n    print(\"Deleting user: Hiro Hamada\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/commands/index/tutorial003_py310.py",
    "content": "import typer\n\napp = typer.Typer(no_args_is_help=True)\n\n\n@app.command()\ndef create():\n    print(\"Creating user: Hiro Hamada\")\n\n\n@app.command()\ndef delete():\n    print(\"Deleting user: Hiro Hamada\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/commands/index/tutorial004_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef delete():\n    print(\"Deleting user: Hiro Hamada\")\n\n\n@app.command()\ndef create():\n    print(\"Creating user: Hiro Hamada\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/commands/index/tutorial005_py310.py",
    "content": "import typer\n\napp = typer.Typer(suggest_commands=True)\n\n\n@app.command()\ndef create():\n    typer.echo(\"Creating...\")\n\n\n@app.command()\ndef delete():\n    typer.echo(\"Deleting...\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/commands/name/__init__.py",
    "content": ""
  },
  {
    "path": "docs_src/commands/name/tutorial001_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command(\"create\")\ndef cli_create_user(username: str):\n    print(f\"Creating user: {username}\")\n\n\n@app.command(\"delete\")\ndef cli_delete_user(username: str):\n    print(f\"Deleting user: {username}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/commands/one_or_multiple/__init__.py",
    "content": ""
  },
  {
    "path": "docs_src/commands/one_or_multiple/tutorial001_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef create():\n    print(\"Creating user: Hiro Hamada\")\n\n\n@app.callback()\ndef callback():\n    pass\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/commands/one_or_multiple/tutorial002_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef create():\n    print(\"Creating user: Hiro Hamada\")\n\n\n@app.callback()\ndef callback():\n    \"\"\"\n    Creates a single user Hiro Hamada.\n\n    In the next version it will create 5 more users.\n    \"\"\"\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/commands/options/__init__.py",
    "content": ""
  },
  {
    "path": "docs_src/commands/options/tutorial001_an_py310.py",
    "content": "from typing import Annotated\n\nimport typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef create(username: str):\n    print(f\"Creating user: {username}\")\n\n\n@app.command()\ndef delete(\n    username: str,\n    force: Annotated[\n        bool, typer.Option(prompt=\"Are you sure you want to delete the user?\")\n    ],\n):\n    if force:\n        print(f\"Deleting user: {username}\")\n    else:\n        print(\"Operation cancelled\")\n\n\n@app.command()\ndef delete_all(\n    force: Annotated[\n        bool, typer.Option(prompt=\"Are you sure you want to delete ALL users?\")\n    ],\n):\n    if force:\n        print(\"Deleting all users\")\n    else:\n        print(\"Operation cancelled\")\n\n\n@app.command()\ndef init():\n    print(\"Initializing user database\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/commands/options/tutorial001_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef create(username: str):\n    print(f\"Creating user: {username}\")\n\n\n@app.command()\ndef delete(\n    username: str,\n    force: bool = typer.Option(..., prompt=\"Are you sure you want to delete the user?\"),\n):\n    if force:\n        print(f\"Deleting user: {username}\")\n    else:\n        print(\"Operation cancelled\")\n\n\n@app.command()\ndef delete_all(\n    force: bool = typer.Option(\n        ..., prompt=\"Are you sure you want to delete ALL users?\"\n    ),\n):\n    if force:\n        print(\"Deleting all users\")\n    else:\n        print(\"Operation cancelled\")\n\n\n@app.command()\ndef init():\n    print(\"Initializing user database\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/exceptions/__init__.py",
    "content": ""
  },
  {
    "path": "docs_src/exceptions/tutorial001_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(name: str = \"morty\"):\n    print(name + 3)\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/exceptions/tutorial002_py310.py",
    "content": "import typer\n\napp = typer.Typer(pretty_exceptions_show_locals=True)\n\n\n@app.command()\ndef main(name: str = \"morty\"):\n    print(name + 3)\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/exceptions/tutorial003_py310.py",
    "content": "import typer\n\napp = typer.Typer(pretty_exceptions_short=False)\n\n\n@app.command()\ndef main(name: str = \"morty\"):\n    print(name + 3)\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/exceptions/tutorial004_py310.py",
    "content": "import typer\n\napp = typer.Typer(pretty_exceptions_enable=False)\n\n\n@app.command()\ndef main(name: str = \"morty\"):\n    print(name + 3)\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/first_steps/__init__.py",
    "content": ""
  },
  {
    "path": "docs_src/first_steps/tutorial001_py310.py",
    "content": "import typer\n\n\ndef main():\n    print(\"Hello World\")\n\n\nif __name__ == \"__main__\":\n    typer.run(main)\n"
  },
  {
    "path": "docs_src/first_steps/tutorial002_py310.py",
    "content": "import typer\n\n\ndef main(name: str):\n    print(f\"Hello {name}\")\n\n\nif __name__ == \"__main__\":\n    typer.run(main)\n"
  },
  {
    "path": "docs_src/first_steps/tutorial003_py310.py",
    "content": "import typer\n\n\ndef main(name: str, lastname: str):\n    print(f\"Hello {name} {lastname}\")\n\n\nif __name__ == \"__main__\":\n    typer.run(main)\n"
  },
  {
    "path": "docs_src/first_steps/tutorial004_py310.py",
    "content": "import typer\n\n\ndef main(name: str, lastname: str, formal: bool = False):\n    if formal:\n        print(f\"Good day Ms. {name} {lastname}.\")\n    else:\n        print(f\"Hello {name} {lastname}\")\n\n\nif __name__ == \"__main__\":\n    typer.run(main)\n"
  },
  {
    "path": "docs_src/first_steps/tutorial005_py310.py",
    "content": "import typer\n\n\ndef main(name: str, lastname: str = \"\", formal: bool = False):\n    if formal:\n        print(f\"Good day Ms. {name} {lastname}.\")\n    else:\n        print(f\"Hello {name} {lastname}\")\n\n\nif __name__ == \"__main__\":\n    typer.run(main)\n"
  },
  {
    "path": "docs_src/first_steps/tutorial006_py310.py",
    "content": "import typer\n\n\ndef main(name: str, lastname: str = \"\", formal: bool = False):\n    \"\"\"\n    Say hi to NAME, optionally with a --lastname.\n\n    If --formal is used, say hi very formally.\n    \"\"\"\n    if formal:\n        print(f\"Good day Ms. {name} {lastname}.\")\n    else:\n        print(f\"Hello {name} {lastname}\")\n\n\nif __name__ == \"__main__\":\n    typer.run(main)\n"
  },
  {
    "path": "docs_src/launch/__init__.py",
    "content": ""
  },
  {
    "path": "docs_src/launch/tutorial001_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main():\n    print(\"Opening Typer's docs\")\n    typer.launch(\"https://typer.tiangolo.com\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/launch/tutorial002_py310.py",
    "content": "from pathlib import Path\n\nimport typer\n\nAPP_NAME = \"my-super-cli-app\"\n\napp = typer.Typer()\n\n\n@app.command()\ndef main():\n    app_dir = typer.get_app_dir(APP_NAME)\n    app_dir_path = Path(app_dir)\n    app_dir_path.mkdir(parents=True, exist_ok=True)\n    config_path: Path = Path(app_dir) / \"config.json\"\n    if not config_path.is_file():\n        config_path.write_text('{\"version\": \"1.0.0\"}')\n    config_file_str = str(config_path)\n    print(\"Opening config directory\")\n    typer.launch(config_file_str, locate=True)\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/multiple_values/__init__.py",
    "content": ""
  },
  {
    "path": "docs_src/multiple_values/arguments_with_multiple_values/__init__.py",
    "content": ""
  },
  {
    "path": "docs_src/multiple_values/arguments_with_multiple_values/tutorial001_py310.py",
    "content": "from pathlib import Path\n\nimport typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(files: list[Path], celebration: str):\n    for path in files:\n        if path.is_file():\n            print(f\"This file exists: {path.name}\")\n            print(celebration)\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/multiple_values/arguments_with_multiple_values/tutorial002_an_py310.py",
    "content": "from typing import Annotated\n\nimport typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(\n    names: Annotated[\n        tuple[str, str, str], typer.Argument(help=\"Select 3 characters to play with\")\n    ] = (\"Harry\", \"Hermione\", \"Ron\"),\n):\n    for name in names:\n        print(f\"Hello {name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/multiple_values/arguments_with_multiple_values/tutorial002_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(\n    names: tuple[str, str, str] = typer.Argument(\n        (\"Harry\", \"Hermione\", \"Ron\"), help=\"Select 3 characters to play with\"\n    ),\n):\n    for name in names:\n        print(f\"Hello {name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/multiple_values/multiple_options/__init__.py",
    "content": ""
  },
  {
    "path": "docs_src/multiple_values/multiple_options/tutorial001_an_py310.py",
    "content": "from typing import Annotated\n\nimport typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(user: Annotated[list[str] | None, typer.Option()] = None):\n    if not user:\n        print(f\"No provided users (raw input = {user})\")\n        raise typer.Abort()\n    for u in user:\n        print(f\"Processing user: {u}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/multiple_values/multiple_options/tutorial001_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(user: list[str] | None = typer.Option(None)):\n    if not user:\n        print(f\"No provided users (raw input = {user})\")\n        raise typer.Abort()\n    for u in user:\n        print(f\"Processing user: {u}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/multiple_values/multiple_options/tutorial002_an_py310.py",
    "content": "from typing import Annotated\n\nimport typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(number: Annotated[list[float], typer.Option()] = []):\n    print(f\"The sum is {sum(number)}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/multiple_values/multiple_options/tutorial002_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(number: list[float] = typer.Option([])):\n    print(f\"The sum is {sum(number)}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/multiple_values/options_with_multiple_values/__init__.py",
    "content": ""
  },
  {
    "path": "docs_src/multiple_values/options_with_multiple_values/tutorial001_an_py310.py",
    "content": "from typing import Annotated\n\nimport typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(user: Annotated[tuple[str, int, bool], typer.Option()] = (None, None, None)):\n    username, coins, is_wizard = user\n    if not username:\n        print(\"No user provided\")\n        raise typer.Abort()\n    print(f\"The username {username} has {coins} coins\")\n    if is_wizard:\n        print(\"And this user is a wizard!\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/multiple_values/options_with_multiple_values/tutorial001_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(user: tuple[str, int, bool] = typer.Option((None, None, None))):\n    username, coins, is_wizard = user\n    if not username:\n        print(\"No user provided\")\n        raise typer.Abort()\n    print(f\"The username {username} has {coins} coins\")\n    if is_wizard:\n        print(\"And this user is a wizard!\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/one_file_per_command/__init__.py",
    "content": ""
  },
  {
    "path": "docs_src/one_file_per_command/app_py310/__init__.py",
    "content": ""
  },
  {
    "path": "docs_src/one_file_per_command/app_py310/main.py",
    "content": "import typer\n\nfrom .users import app as users_app\nfrom .version import app as version_app\n\napp = typer.Typer()\n\napp.add_typer(version_app)\napp.add_typer(users_app, name=\"users\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/one_file_per_command/app_py310/users/__init__.py",
    "content": "import typer\n\nfrom .add import app as add_app\nfrom .delete import app as delete_app\n\napp = typer.Typer()\n\napp.add_typer(add_app)\napp.add_typer(delete_app)\n"
  },
  {
    "path": "docs_src/one_file_per_command/app_py310/users/add.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef add(name: str):\n    print(f\"Adding user: {name}\")\n"
  },
  {
    "path": "docs_src/one_file_per_command/app_py310/users/delete.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef delete(name: str):\n    print(f\"Deleting user: {name}\")\n"
  },
  {
    "path": "docs_src/one_file_per_command/app_py310/version.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef version():\n    print(\"My CLI Version 1.0\")\n"
  },
  {
    "path": "docs_src/options/__init__.py",
    "content": ""
  },
  {
    "path": "docs_src/options/callback/__init__.py",
    "content": ""
  },
  {
    "path": "docs_src/options/callback/tutorial001_an_py310.py",
    "content": "from typing import Annotated\n\nimport typer\n\napp = typer.Typer()\n\n\ndef name_callback(value: str):\n    if value != \"Camila\":\n        raise typer.BadParameter(\"Only Camila is allowed\")\n    return value\n\n\n@app.command()\ndef main(name: Annotated[str | None, typer.Option(callback=name_callback)] = None):\n    print(f\"Hello {name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/options/callback/tutorial001_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\ndef name_callback(value: str):\n    if value != \"Camila\":\n        raise typer.BadParameter(\"Only Camila is allowed\")\n    return value\n\n\n@app.command()\ndef main(name: str | None = typer.Option(default=None, callback=name_callback)):\n    print(f\"Hello {name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/options/callback/tutorial002_an_py310.py",
    "content": "from typing import Annotated\n\nimport typer\n\napp = typer.Typer()\n\n\ndef name_callback(value: str):\n    print(\"Validating name\")\n    if value != \"Camila\":\n        raise typer.BadParameter(\"Only Camila is allowed\")\n    return value\n\n\n@app.command()\ndef main(name: Annotated[str | None, typer.Option(callback=name_callback)] = None):\n    print(f\"Hello {name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/options/callback/tutorial002_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\ndef name_callback(value: str):\n    print(\"Validating name\")\n    if value != \"Camila\":\n        raise typer.BadParameter(\"Only Camila is allowed\")\n    return value\n\n\n@app.command()\ndef main(name: str | None = typer.Option(default=None, callback=name_callback)):\n    print(f\"Hello {name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/options/callback/tutorial003_an_py310.py",
    "content": "from typing import Annotated\n\nimport typer\n\napp = typer.Typer()\n\n\ndef name_callback(ctx: typer.Context, value: str):\n    if ctx.resilient_parsing:\n        return\n    print(\"Validating name\")\n    if value != \"Camila\":\n        raise typer.BadParameter(\"Only Camila is allowed\")\n    return value\n\n\n@app.command()\ndef main(name: Annotated[str | None, typer.Option(callback=name_callback)] = None):\n    print(f\"Hello {name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/options/callback/tutorial003_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\ndef name_callback(ctx: typer.Context, value: str):\n    if ctx.resilient_parsing:\n        return\n    print(\"Validating name\")\n    if value != \"Camila\":\n        raise typer.BadParameter(\"Only Camila is allowed\")\n    return value\n\n\n@app.command()\ndef main(name: str | None = typer.Option(default=None, callback=name_callback)):\n    print(f\"Hello {name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/options/callback/tutorial004_an_py310.py",
    "content": "from typing import Annotated\n\nimport typer\n\napp = typer.Typer()\n\n\ndef name_callback(ctx: typer.Context, param: typer.CallbackParam, value: str):\n    if ctx.resilient_parsing:\n        return\n    print(f\"Validating param: {param.name}\")\n    if value != \"Camila\":\n        raise typer.BadParameter(\"Only Camila is allowed\")\n    return value\n\n\n@app.command()\ndef main(name: Annotated[str | None, typer.Option(callback=name_callback)] = None):\n    print(f\"Hello {name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/options/callback/tutorial004_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\ndef name_callback(ctx: typer.Context, param: typer.CallbackParam, value: str):\n    if ctx.resilient_parsing:\n        return\n    print(f\"Validating param: {param.name}\")\n    if value != \"Camila\":\n        raise typer.BadParameter(\"Only Camila is allowed\")\n    return value\n\n\n@app.command()\ndef main(name: str | None = typer.Option(default=None, callback=name_callback)):\n    print(f\"Hello {name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/options/help/__init__.py",
    "content": ""
  },
  {
    "path": "docs_src/options/help/tutorial001_an_py310.py",
    "content": "from typing import Annotated\n\nimport typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(\n    name: str,\n    lastname: Annotated[str, typer.Option(help=\"Last name of person to greet.\")] = \"\",\n    formal: Annotated[bool, typer.Option(help=\"Say hi formally.\")] = False,\n):\n    \"\"\"\n    Say hi to NAME, optionally with a --lastname.\n\n    If --formal is used, say hi very formally.\n    \"\"\"\n    if formal:\n        print(f\"Good day Ms. {name} {lastname}.\")\n    else:\n        print(f\"Hello {name} {lastname}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/options/help/tutorial001_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(\n    name: str,\n    lastname: str = typer.Option(\"\", help=\"Last name of person to greet.\"),\n    formal: bool = typer.Option(False, help=\"Say hi formally.\"),\n):\n    \"\"\"\n    Say hi to NAME, optionally with a --lastname.\n\n    If --formal is used, say hi very formally.\n    \"\"\"\n    if formal:\n        print(f\"Good day Ms. {name} {lastname}.\")\n    else:\n        print(f\"Hello {name} {lastname}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/options/help/tutorial002_an_py310.py",
    "content": "from typing import Annotated\n\nimport typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(\n    name: str,\n    lastname: Annotated[str, typer.Option(help=\"Last name of person to greet.\")] = \"\",\n    formal: Annotated[\n        bool,\n        typer.Option(\n            help=\"Say hi formally.\", rich_help_panel=\"Customization and Utils\"\n        ),\n    ] = False,\n    debug: Annotated[\n        bool,\n        typer.Option(\n            help=\"Enable debugging.\", rich_help_panel=\"Customization and Utils\"\n        ),\n    ] = False,\n):\n    \"\"\"\n    Say hi to NAME, optionally with a --lastname.\n\n    If --formal is used, say hi very formally.\n    \"\"\"\n    if formal:\n        print(f\"Good day Ms. {name} {lastname}.\")\n    else:\n        print(f\"Hello {name} {lastname}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/options/help/tutorial002_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(\n    name: str,\n    lastname: str = typer.Option(\"\", help=\"Last name of person to greet.\"),\n    formal: bool = typer.Option(\n        False, help=\"Say hi formally.\", rich_help_panel=\"Customization and Utils\"\n    ),\n    debug: bool = typer.Option(\n        False, help=\"Enable debugging.\", rich_help_panel=\"Customization and Utils\"\n    ),\n):\n    \"\"\"\n    Say hi to NAME, optionally with a --lastname.\n\n    If --formal is used, say hi very formally.\n    \"\"\"\n    if formal:\n        print(f\"Good day Ms. {name} {lastname}.\")\n    else:\n        print(f\"Hello {name} {lastname}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/options/help/tutorial003_an_py310.py",
    "content": "from typing import Annotated\n\nimport typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(fullname: Annotated[str, typer.Option(show_default=False)] = \"Wade Wilson\"):\n    print(f\"Hello {fullname}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/options/help/tutorial003_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(fullname: str = typer.Option(\"Wade Wilson\", show_default=False)):\n    print(f\"Hello {fullname}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/options/help/tutorial004_an_py310.py",
    "content": "from typing import Annotated\n\nimport typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(\n    fullname: Annotated[\n        str, typer.Option(show_default=\"Deadpoolio the amazing's name\")\n    ] = \"Wade Wilson\",\n):\n    print(f\"Hello {fullname}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/options/help/tutorial004_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(\n    fullname: str = typer.Option(\n        \"Wade Wilson\", show_default=\"Deadpoolio the amazing's name\"\n    ),\n):\n    print(f\"Hello {fullname}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/options/name/__init__.py",
    "content": ""
  },
  {
    "path": "docs_src/options/name/tutorial001_an_py310.py",
    "content": "from typing import Annotated\n\nimport typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(user_name: Annotated[str, typer.Option(\"--name\")]):\n    print(f\"Hello {user_name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/options/name/tutorial001_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(user_name: str = typer.Option(..., \"--name\")):\n    print(f\"Hello {user_name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/options/name/tutorial002_an_py310.py",
    "content": "from typing import Annotated\n\nimport typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(user_name: Annotated[str, typer.Option(\"--name\", \"-n\")]):\n    print(f\"Hello {user_name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/options/name/tutorial002_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(user_name: str = typer.Option(..., \"--name\", \"-n\")):\n    print(f\"Hello {user_name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/options/name/tutorial003_an_py310.py",
    "content": "from typing import Annotated\n\nimport typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(user_name: Annotated[str, typer.Option(\"-n\")]):\n    print(f\"Hello {user_name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/options/name/tutorial003_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(user_name: str = typer.Option(..., \"-n\")):\n    print(f\"Hello {user_name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/options/name/tutorial004_an_py310.py",
    "content": "from typing import Annotated\n\nimport typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(user_name: Annotated[str, typer.Option(\"--user-name\", \"-n\")]):\n    print(f\"Hello {user_name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/options/name/tutorial004_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(user_name: str = typer.Option(..., \"--user-name\", \"-n\")):\n    print(f\"Hello {user_name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/options/name/tutorial005_an_py310.py",
    "content": "from typing import Annotated\n\nimport typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(\n    name: Annotated[str, typer.Option(\"--name\", \"-n\")],\n    formal: Annotated[bool, typer.Option(\"--formal\", \"-f\")] = False,\n):\n    if formal:\n        print(f\"Good day Ms. {name}.\")\n    else:\n        print(f\"Hello {name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/options/name/tutorial005_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(\n    name: str = typer.Option(..., \"--name\", \"-n\"),\n    formal: bool = typer.Option(False, \"--formal\", \"-f\"),\n):\n    if formal:\n        print(f\"Good day Ms. {name}.\")\n    else:\n        print(f\"Hello {name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/options/password/__init__.py",
    "content": ""
  },
  {
    "path": "docs_src/options/password/tutorial001_an_py310.py",
    "content": "from typing import Annotated\n\nimport typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(\n    name: str,\n    email: Annotated[str, typer.Option(prompt=True, confirmation_prompt=True)],\n):\n    print(f\"Hello {name}, your email is {email}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/options/password/tutorial001_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(\n    name: str, email: str = typer.Option(..., prompt=True, confirmation_prompt=True)\n):\n    print(f\"Hello {name}, your email is {email}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/options/password/tutorial002_an_py310.py",
    "content": "from typing import Annotated\n\nimport typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(\n    name: str,\n    password: Annotated[\n        str, typer.Option(prompt=True, confirmation_prompt=True, hide_input=True)\n    ],\n):\n    print(f\"Hello {name}. Doing something very secure with password.\")\n    print(f\"...just kidding, here it is, very insecure: {password}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/options/password/tutorial002_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(\n    name: str,\n    password: str = typer.Option(\n        ..., prompt=True, confirmation_prompt=True, hide_input=True\n    ),\n):\n    print(f\"Hello {name}. Doing something very secure with password.\")\n    print(f\"...just kidding, here it is, very insecure: {password}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/options/prompt/__init__.py",
    "content": ""
  },
  {
    "path": "docs_src/options/prompt/tutorial001_an_py310.py",
    "content": "from typing import Annotated\n\nimport typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(name: str, lastname: Annotated[str, typer.Option(prompt=True)]):\n    print(f\"Hello {name} {lastname}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/options/prompt/tutorial001_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(name: str, lastname: str = typer.Option(..., prompt=True)):\n    print(f\"Hello {name} {lastname}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/options/prompt/tutorial002_an_py310.py",
    "content": "from typing import Annotated\n\nimport typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(\n    name: str,\n    lastname: Annotated[str, typer.Option(prompt=\"Please tell me your last name\")],\n):\n    print(f\"Hello {name} {lastname}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/options/prompt/tutorial002_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(\n    name: str, lastname: str = typer.Option(..., prompt=\"Please tell me your last name\")\n):\n    print(f\"Hello {name} {lastname}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/options/prompt/tutorial003_an_py310.py",
    "content": "from typing import Annotated\n\nimport typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(\n    project_name: Annotated[str, typer.Option(prompt=True, confirmation_prompt=True)],\n):\n    print(f\"Deleting project {project_name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/options/prompt/tutorial003_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(project_name: str = typer.Option(..., prompt=True, confirmation_prompt=True)):\n    print(f\"Deleting project {project_name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/options/required/__init__.py",
    "content": ""
  },
  {
    "path": "docs_src/options/required/tutorial001_an_py310.py",
    "content": "from typing import Annotated\n\nimport typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(name: str, lastname: Annotated[str, typer.Option()]):\n    print(f\"Hello {name} {lastname}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/options/required/tutorial001_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(name: str, lastname: str = typer.Option()):\n    print(f\"Hello {name} {lastname}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/options/required/tutorial002_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(name: str, lastname: str = typer.Option(default=...)):\n    print(f\"Hello {name} {lastname}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/options/version/__init__.py",
    "content": ""
  },
  {
    "path": "docs_src/options/version/tutorial001_an_py310.py",
    "content": "from typing import Annotated\n\nimport typer\n\n__version__ = \"0.1.0\"\n\napp = typer.Typer()\n\n\ndef version_callback(value: bool):\n    if value:\n        print(f\"Awesome CLI Version: {__version__}\")\n        raise typer.Exit()\n\n\n@app.command()\ndef main(\n    name: Annotated[str, typer.Option()] = \"World\",\n    version: Annotated[\n        bool | None, typer.Option(\"--version\", callback=version_callback)\n    ] = None,\n):\n    print(f\"Hello {name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/options/version/tutorial001_py310.py",
    "content": "import typer\n\n__version__ = \"0.1.0\"\n\napp = typer.Typer()\n\n\ndef version_callback(value: bool):\n    if value:\n        print(f\"Awesome CLI Version: {__version__}\")\n        raise typer.Exit()\n\n\n@app.command()\ndef main(\n    name: str = typer.Option(\"World\"),\n    version: bool | None = typer.Option(None, \"--version\", callback=version_callback),\n):\n    print(f\"Hello {name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/options/version/tutorial002_an_py310.py",
    "content": "from typing import Annotated\n\nimport typer\n\n__version__ = \"0.1.0\"\n\napp = typer.Typer()\n\n\ndef version_callback(value: bool):\n    if value:\n        print(f\"Awesome CLI Version: {__version__}\")\n        raise typer.Exit()\n\n\ndef name_callback(name: str):\n    if name != \"Camila\":\n        raise typer.BadParameter(\"Only Camila is allowed\")\n    return name\n\n\n@app.command()\ndef main(\n    name: Annotated[str, typer.Option(callback=name_callback)],\n    version: Annotated[\n        bool | None, typer.Option(\"--version\", callback=version_callback)\n    ] = None,\n):\n    print(f\"Hello {name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/options/version/tutorial002_py310.py",
    "content": "import typer\n\n__version__ = \"0.1.0\"\n\napp = typer.Typer()\n\n\ndef version_callback(value: bool):\n    if value:\n        print(f\"Awesome CLI Version: {__version__}\")\n        raise typer.Exit()\n\n\ndef name_callback(name: str):\n    if name != \"Camila\":\n        raise typer.BadParameter(\"Only Camila is allowed\")\n    return name\n\n\n@app.command()\ndef main(\n    name: str = typer.Option(..., callback=name_callback),\n    version: bool | None = typer.Option(None, \"--version\", callback=version_callback),\n):\n    print(f\"Hello {name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/options/version/tutorial003_an_py310.py",
    "content": "from typing import Annotated\n\nimport typer\n\n__version__ = \"0.1.0\"\n\napp = typer.Typer()\n\n\ndef version_callback(value: bool):\n    if value:\n        print(f\"Awesome CLI Version: {__version__}\")\n        raise typer.Exit()\n\n\ndef name_callback(name: str):\n    if name != \"Camila\":\n        raise typer.BadParameter(\"Only Camila is allowed\")\n    return name\n\n\n@app.command()\ndef main(\n    name: Annotated[str, typer.Option(callback=name_callback)],\n    version: Annotated[\n        bool | None,\n        typer.Option(\"--version\", callback=version_callback, is_eager=True),\n    ] = None,\n):\n    print(f\"Hello {name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/options/version/tutorial003_py310.py",
    "content": "import typer\n\n__version__ = \"0.1.0\"\n\napp = typer.Typer()\n\n\ndef version_callback(value: bool):\n    if value:\n        print(f\"Awesome CLI Version: {__version__}\")\n        raise typer.Exit()\n\n\ndef name_callback(name: str):\n    if name != \"Camila\":\n        raise typer.BadParameter(\"Only Camila is allowed\")\n    return name\n\n\n@app.command()\ndef main(\n    name: str = typer.Option(..., callback=name_callback),\n    version: bool | None = typer.Option(\n        None, \"--version\", callback=version_callback, is_eager=True\n    ),\n):\n    print(f\"Hello {name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/options_autocompletion/__init__.py",
    "content": ""
  },
  {
    "path": "docs_src/options_autocompletion/tutorial001_an_py310.py",
    "content": "from typing import Annotated\n\nimport typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(name: Annotated[str, typer.Option(help=\"The name to say hi to.\")] = \"World\"):\n    print(f\"Hello {name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/options_autocompletion/tutorial001_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(name: str = typer.Option(\"World\", help=\"The name to say hi to.\")):\n    print(f\"Hello {name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/options_autocompletion/tutorial002_an_py310.py",
    "content": "from typing import Annotated\n\nimport typer\n\n\ndef complete_name():\n    return [\"Camila\", \"Carlos\", \"Sebastian\"]\n\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(\n    name: Annotated[\n        str, typer.Option(help=\"The name to say hi to.\", autocompletion=complete_name)\n    ] = \"World\",\n):\n    print(f\"Hello {name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/options_autocompletion/tutorial002_py310.py",
    "content": "import typer\n\n\ndef complete_name():\n    return [\"Camila\", \"Carlos\", \"Sebastian\"]\n\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(\n    name: str = typer.Option(\n        \"World\", help=\"The name to say hi to.\", autocompletion=complete_name\n    ),\n):\n    print(f\"Hello {name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/options_autocompletion/tutorial003_an_py310.py",
    "content": "from typing import Annotated\n\nimport typer\n\nvalid_names = [\"Camila\", \"Carlos\", \"Sebastian\"]\n\n\ndef complete_name(incomplete: str):\n    completion = []\n    for name in valid_names:\n        if name.startswith(incomplete):\n            completion.append(name)\n    return completion\n\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(\n    name: Annotated[\n        str, typer.Option(help=\"The name to say hi to.\", autocompletion=complete_name)\n    ] = \"World\",\n):\n    print(f\"Hello {name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/options_autocompletion/tutorial003_py310.py",
    "content": "import typer\n\nvalid_names = [\"Camila\", \"Carlos\", \"Sebastian\"]\n\n\ndef complete_name(incomplete: str):\n    completion = []\n    for name in valid_names:\n        if name.startswith(incomplete):\n            completion.append(name)\n    return completion\n\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(\n    name: str = typer.Option(\n        \"World\", help=\"The name to say hi to.\", autocompletion=complete_name\n    ),\n):\n    print(f\"Hello {name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/options_autocompletion/tutorial004_an_py310.py",
    "content": "from typing import Annotated\n\nimport typer\n\nvalid_completion_items = [\n    (\"Camila\", \"The reader of books.\"),\n    (\"Carlos\", \"The writer of scripts.\"),\n    (\"Sebastian\", \"The type hints guy.\"),\n]\n\n\ndef complete_name(incomplete: str):\n    completion = []\n    for name, help_text in valid_completion_items:\n        if name.startswith(incomplete):\n            completion_item = (name, help_text)\n            completion.append(completion_item)\n    return completion\n\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(\n    name: Annotated[\n        str, typer.Option(help=\"The name to say hi to.\", autocompletion=complete_name)\n    ] = \"World\",\n):\n    print(f\"Hello {name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/options_autocompletion/tutorial004_py310.py",
    "content": "import typer\n\nvalid_completion_items = [\n    (\"Camila\", \"The reader of books.\"),\n    (\"Carlos\", \"The writer of scripts.\"),\n    (\"Sebastian\", \"The type hints guy.\"),\n]\n\n\ndef complete_name(incomplete: str):\n    completion = []\n    for name, help_text in valid_completion_items:\n        if name.startswith(incomplete):\n            completion_item = (name, help_text)\n            completion.append(completion_item)\n    return completion\n\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(\n    name: str = typer.Option(\n        \"World\", help=\"The name to say hi to.\", autocompletion=complete_name\n    ),\n):\n    print(f\"Hello {name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/options_autocompletion/tutorial005_an_py310.py",
    "content": "from typing import Annotated\n\nimport typer\n\nvalid_completion_items = [\n    (\"Camila\", \"The reader of books.\"),\n    (\"Carlos\", \"The writer of scripts.\"),\n    (\"Sebastian\", \"The type hints guy.\"),\n]\n\n\ndef complete_name(incomplete: str):\n    for name, help_text in valid_completion_items:\n        if name.startswith(incomplete):\n            yield (name, help_text)\n\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(\n    name: Annotated[\n        str, typer.Option(help=\"The name to say hi to.\", autocompletion=complete_name)\n    ] = \"World\",\n):\n    print(f\"Hello {name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/options_autocompletion/tutorial005_py310.py",
    "content": "import typer\n\nvalid_completion_items = [\n    (\"Camila\", \"The reader of books.\"),\n    (\"Carlos\", \"The writer of scripts.\"),\n    (\"Sebastian\", \"The type hints guy.\"),\n]\n\n\ndef complete_name(incomplete: str):\n    for name, help_text in valid_completion_items:\n        if name.startswith(incomplete):\n            yield (name, help_text)\n\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(\n    name: str = typer.Option(\n        \"World\", help=\"The name to say hi to.\", autocompletion=complete_name\n    ),\n):\n    print(f\"Hello {name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/options_autocompletion/tutorial006_an_py310.py",
    "content": "from typing import Annotated\n\nimport typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(\n    name: Annotated[list[str], typer.Option(help=\"The name to say hi to.\")] = [\"World\"],\n):\n    for each_name in name:\n        print(f\"Hello {each_name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/options_autocompletion/tutorial006_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(name: list[str] = typer.Option([\"World\"], help=\"The name to say hi to.\")):\n    for each_name in name:\n        print(f\"Hello {each_name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/options_autocompletion/tutorial007_an_py310.py",
    "content": "from typing import Annotated\n\nimport typer\n\nvalid_completion_items = [\n    (\"Camila\", \"The reader of books.\"),\n    (\"Carlos\", \"The writer of scripts.\"),\n    (\"Sebastian\", \"The type hints guy.\"),\n]\n\n\ndef complete_name(ctx: typer.Context, incomplete: str):\n    names = ctx.params.get(\"name\") or []\n    for name, help_text in valid_completion_items:\n        if name.startswith(incomplete) and name not in names:\n            yield (name, help_text)\n\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(\n    name: Annotated[\n        list[str],\n        typer.Option(help=\"The name to say hi to.\", autocompletion=complete_name),\n    ] = [\"World\"],\n):\n    for n in name:\n        print(f\"Hello {n}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/options_autocompletion/tutorial007_py310.py",
    "content": "import typer\n\nvalid_completion_items = [\n    (\"Camila\", \"The reader of books.\"),\n    (\"Carlos\", \"The writer of scripts.\"),\n    (\"Sebastian\", \"The type hints guy.\"),\n]\n\n\ndef complete_name(ctx: typer.Context, incomplete: str):\n    names = ctx.params.get(\"name\") or []\n    for name, help_text in valid_completion_items:\n        if name.startswith(incomplete) and name not in names:\n            yield (name, help_text)\n\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(\n    name: list[str] = typer.Option(\n        [\"World\"], help=\"The name to say hi to.\", autocompletion=complete_name\n    ),\n):\n    for n in name:\n        print(f\"Hello {n}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/options_autocompletion/tutorial008_an_py310.py",
    "content": "from typing import Annotated\n\nimport typer\nfrom rich.console import Console\n\nvalid_completion_items = [\n    (\"Camila\", \"The reader of books.\"),\n    (\"Carlos\", \"The writer of scripts.\"),\n    (\"Sebastian\", \"The type hints guy.\"),\n]\n\nerr_console = Console(stderr=True)\n\n\ndef complete_name(args: list[str], incomplete: str):\n    err_console.print(f\"{args}\")\n    for name, help_text in valid_completion_items:\n        if name.startswith(incomplete):\n            yield (name, help_text)\n\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(\n    name: Annotated[\n        list[str],\n        typer.Option(help=\"The name to say hi to.\", autocompletion=complete_name),\n    ] = [\"World\"],\n):\n    for n in name:\n        print(f\"Hello {n}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/options_autocompletion/tutorial008_py310.py",
    "content": "import typer\nfrom rich.console import Console\n\nvalid_completion_items = [\n    (\"Camila\", \"The reader of books.\"),\n    (\"Carlos\", \"The writer of scripts.\"),\n    (\"Sebastian\", \"The type hints guy.\"),\n]\n\nerr_console = Console(stderr=True)\n\n\ndef complete_name(args: list[str], incomplete: str):\n    err_console.print(f\"{args}\")\n    for name, help_text in valid_completion_items:\n        if name.startswith(incomplete):\n            yield (name, help_text)\n\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(\n    name: list[str] = typer.Option(\n        [\"World\"], help=\"The name to say hi to.\", autocompletion=complete_name\n    ),\n):\n    for n in name:\n        print(f\"Hello {n}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/options_autocompletion/tutorial009_an_py310.py",
    "content": "from typing import Annotated\n\nimport typer\nfrom rich.console import Console\n\nvalid_completion_items = [\n    (\"Camila\", \"The reader of books.\"),\n    (\"Carlos\", \"The writer of scripts.\"),\n    (\"Sebastian\", \"The type hints guy.\"),\n]\n\nerr_console = Console(stderr=True)\n\n\ndef complete_name(ctx: typer.Context, args: list[str], incomplete: str):\n    err_console.print(f\"{args}\")\n    names = ctx.params.get(\"name\") or []\n    for name, help_text in valid_completion_items:\n        if name.startswith(incomplete) and name not in names:\n            yield (name, help_text)\n\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(\n    name: Annotated[\n        list[str],\n        typer.Option(help=\"The name to say hi to.\", autocompletion=complete_name),\n    ] = [\"World\"],\n):\n    for n in name:\n        print(f\"Hello {n}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/options_autocompletion/tutorial009_py310.py",
    "content": "import typer\nfrom rich.console import Console\n\nvalid_completion_items = [\n    (\"Camila\", \"The reader of books.\"),\n    (\"Carlos\", \"The writer of scripts.\"),\n    (\"Sebastian\", \"The type hints guy.\"),\n]\n\nerr_console = Console(stderr=True)\n\n\ndef complete_name(ctx: typer.Context, args: list[str], incomplete: str):\n    err_console.print(f\"{args}\")\n    names = ctx.params.get(\"name\") or []\n    for name, help_text in valid_completion_items:\n        if name.startswith(incomplete) and name not in names:\n            yield (name, help_text)\n\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(\n    name: list[str] = typer.Option(\n        [\"World\"], help=\"The name to say hi to.\", autocompletion=complete_name\n    ),\n):\n    for n in name:\n        print(f\"Hello {n}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/parameter_types/__init__.py",
    "content": ""
  },
  {
    "path": "docs_src/parameter_types/bool/__init__.py",
    "content": ""
  },
  {
    "path": "docs_src/parameter_types/bool/tutorial001_an_py310.py",
    "content": "from typing import Annotated\n\nimport typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(force: Annotated[bool, typer.Option(\"--force\")] = False):\n    if force:\n        print(\"Forcing operation\")\n    else:\n        print(\"Not forcing\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/parameter_types/bool/tutorial001_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(force: bool = typer.Option(False, \"--force\")):\n    if force:\n        print(\"Forcing operation\")\n    else:\n        print(\"Not forcing\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/parameter_types/bool/tutorial002_an_py310.py",
    "content": "from typing import Annotated\n\nimport typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(accept: Annotated[bool | None, typer.Option(\"--accept/--reject\")] = None):\n    if accept is None:\n        print(\"I don't know what you want yet\")\n    elif accept:\n        print(\"Accepting!\")\n    else:\n        print(\"Rejecting!\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/parameter_types/bool/tutorial002_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(accept: bool | None = typer.Option(None, \"--accept/--reject\")):\n    if accept is None:\n        print(\"I don't know what you want yet\")\n    elif accept:\n        print(\"Accepting!\")\n    else:\n        print(\"Rejecting!\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/parameter_types/bool/tutorial003_an_py310.py",
    "content": "from typing import Annotated\n\nimport typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(force: Annotated[bool, typer.Option(\"--force/--no-force\", \"-f/-F\")] = False):\n    if force:\n        print(\"Forcing operation\")\n    else:\n        print(\"Not forcing\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/parameter_types/bool/tutorial003_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(force: bool = typer.Option(False, \"--force/--no-force\", \"-f/-F\")):\n    if force:\n        print(\"Forcing operation\")\n    else:\n        print(\"Not forcing\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/parameter_types/bool/tutorial004_an_py310.py",
    "content": "from typing import Annotated\n\nimport typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(in_prod: Annotated[bool, typer.Option(\" /--demo\", \" /-d\")] = True):\n    if in_prod:\n        print(\"Running in production\")\n    else:\n        print(\"Running demo\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/parameter_types/bool/tutorial004_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(in_prod: bool = typer.Option(True, \" /--demo\", \" /-d\")):\n    if in_prod:\n        print(\"Running in production\")\n    else:\n        print(\"Running demo\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/parameter_types/custom_types/__init__.py",
    "content": ""
  },
  {
    "path": "docs_src/parameter_types/custom_types/tutorial001_an_py310.py",
    "content": "from typing import Annotated\n\nimport typer\n\n\nclass CustomClass:\n    def __init__(self, value: str):\n        self.value = value\n\n    def __str__(self):\n        return f\"<CustomClass: value={self.value}>\"\n\n\ndef parse_custom_class(value: str):\n    return CustomClass(value * 2)\n\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(\n    custom_arg: Annotated[CustomClass, typer.Argument(parser=parse_custom_class)],\n    custom_opt: Annotated[CustomClass, typer.Option(parser=parse_custom_class)] = \"Foo\",\n):\n    print(f\"custom_arg is {custom_arg}\")\n    print(f\"--custom-opt is {custom_opt}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/parameter_types/custom_types/tutorial001_py310.py",
    "content": "import typer\n\n\nclass CustomClass:\n    def __init__(self, value: str):\n        self.value = value\n\n    def __str__(self):\n        return f\"<CustomClass: value={self.value}>\"\n\n\ndef parse_custom_class(value: str):\n    return CustomClass(value * 2)\n\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(\n    custom_arg: CustomClass = typer.Argument(parser=parse_custom_class),\n    custom_opt: CustomClass = typer.Option(\"Foo\", parser=parse_custom_class),\n):\n    print(f\"custom_arg is {custom_arg}\")\n    print(f\"--custom-opt is {custom_opt}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/parameter_types/datetime/__init__.py",
    "content": ""
  },
  {
    "path": "docs_src/parameter_types/datetime/tutorial001_py310.py",
    "content": "from datetime import datetime\n\nimport typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(birth: datetime):\n    print(f\"Interesting day to be born: {birth}\")\n    print(f\"Birth hour: {birth.hour}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/parameter_types/datetime/tutorial002_an_py310.py",
    "content": "from datetime import datetime\nfrom typing import Annotated\n\nimport typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(\n    launch_date: Annotated[\n        datetime,\n        typer.Argument(\n            formats=[\"%Y-%m-%d\", \"%Y-%m-%dT%H:%M:%S\", \"%Y-%m-%d %H:%M:%S\", \"%m/%d/%Y\"]\n        ),\n    ],\n):\n    print(f\"Launch will be at: {launch_date}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/parameter_types/datetime/tutorial002_py310.py",
    "content": "from datetime import datetime\n\nimport typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(\n    launch_date: datetime = typer.Argument(\n        ..., formats=[\"%Y-%m-%d\", \"%Y-%m-%dT%H:%M:%S\", \"%Y-%m-%d %H:%M:%S\", \"%m/%d/%Y\"]\n    ),\n):\n    print(f\"Launch will be at: {launch_date}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/parameter_types/enum/__init__.py",
    "content": ""
  },
  {
    "path": "docs_src/parameter_types/enum/tutorial001_py310.py",
    "content": "from enum import Enum\n\nimport typer\n\n\nclass NeuralNetwork(str, Enum):\n    simple = \"simple\"\n    conv = \"conv\"\n    lstm = \"lstm\"\n\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(network: NeuralNetwork = NeuralNetwork.simple):\n    print(f\"Training neural network of type: {network.value}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/parameter_types/enum/tutorial002_an_py310.py",
    "content": "from enum import Enum\nfrom typing import Annotated\n\nimport typer\n\n\nclass NeuralNetwork(str, Enum):\n    simple = \"simple\"\n    conv = \"conv\"\n    lstm = \"lstm\"\n\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(\n    network: Annotated[\n        NeuralNetwork, typer.Option(case_sensitive=False)\n    ] = NeuralNetwork.simple,\n):\n    print(f\"Training neural network of type: {network.value}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/parameter_types/enum/tutorial002_py310.py",
    "content": "from enum import Enum\n\nimport typer\n\n\nclass NeuralNetwork(str, Enum):\n    simple = \"simple\"\n    conv = \"conv\"\n    lstm = \"lstm\"\n\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(\n    network: NeuralNetwork = typer.Option(NeuralNetwork.simple, case_sensitive=False),\n):\n    print(f\"Training neural network of type: {network.value}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/parameter_types/enum/tutorial003_an_py310.py",
    "content": "from enum import Enum\nfrom typing import Annotated\n\nimport typer\n\n\nclass Food(str, Enum):\n    food_1 = \"Eggs\"\n    food_2 = \"Bacon\"\n    food_3 = \"Cheese\"\n\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(groceries: Annotated[list[Food], typer.Option()] = [Food.food_1, Food.food_3]):\n    print(f\"Buying groceries: {', '.join([f.value for f in groceries])}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/parameter_types/enum/tutorial003_py310.py",
    "content": "from enum import Enum\n\nimport typer\n\n\nclass Food(str, Enum):\n    food_1 = \"Eggs\"\n    food_2 = \"Bacon\"\n    food_3 = \"Cheese\"\n\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(groceries: list[Food] = typer.Option([Food.food_1, Food.food_3])):\n    print(f\"Buying groceries: {', '.join([f.value for f in groceries])}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/parameter_types/enum/tutorial004_an_py310.py",
    "content": "from typing import Annotated, Literal\n\nimport typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(\n    network: Annotated[Literal[\"simple\", \"conv\", \"lstm\"], typer.Option()] = \"simple\",\n):\n    print(f\"Training neural network of type: {network}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/parameter_types/enum/tutorial004_py310.py",
    "content": "from typing import Literal\n\nimport typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(network: Literal[\"simple\", \"conv\", \"lstm\"] = typer.Option(\"simple\")):\n    print(f\"Training neural network of type: {network}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/parameter_types/file/__init__.py",
    "content": ""
  },
  {
    "path": "docs_src/parameter_types/file/tutorial001_an_py310.py",
    "content": "from typing import Annotated\n\nimport typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(config: Annotated[typer.FileText, typer.Option()]):\n    for line in config:\n        print(f\"Config line: {line}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/parameter_types/file/tutorial001_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(config: typer.FileText = typer.Option(...)):\n    for line in config:\n        print(f\"Config line: {line}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/parameter_types/file/tutorial002_an_py310.py",
    "content": "from typing import Annotated\n\nimport typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(config: Annotated[typer.FileTextWrite, typer.Option()]):\n    config.write(\"Some config written by the app\")\n    print(\"Config written\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/parameter_types/file/tutorial002_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(config: typer.FileTextWrite = typer.Option(...)):\n    config.write(\"Some config written by the app\")\n    print(\"Config written\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/parameter_types/file/tutorial003_an_py310.py",
    "content": "from typing import Annotated\n\nimport typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(file: Annotated[typer.FileBinaryRead, typer.Option()]):\n    processed_total = 0\n    for bytes_chunk in file:\n        # Process the bytes in bytes_chunk\n        processed_total += len(bytes_chunk)\n        print(f\"Processed bytes total: {processed_total}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/parameter_types/file/tutorial003_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(file: typer.FileBinaryRead = typer.Option(...)):\n    processed_total = 0\n    for bytes_chunk in file:\n        # Process the bytes in bytes_chunk\n        processed_total += len(bytes_chunk)\n        print(f\"Processed bytes total: {processed_total}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/parameter_types/file/tutorial004_an_py310.py",
    "content": "from typing import Annotated\n\nimport typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(file: Annotated[typer.FileBinaryWrite, typer.Option()]):\n    first_line_str = \"some settings\\n\"\n    # You cannot write str directly to a binary file, you have to encode it to get bytes\n    first_line_bytes = first_line_str.encode(\"utf-8\")\n    # Then you can write the bytes\n    file.write(first_line_bytes)\n    # This is already bytes, it starts with b\"\n    second_line = b\"la cig\\xc3\\xbce\\xc3\\xb1a trae al ni\\xc3\\xb1o\"\n    file.write(second_line)\n    print(\"Binary file written\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/parameter_types/file/tutorial004_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(file: typer.FileBinaryWrite = typer.Option(...)):\n    first_line_str = \"some settings\\n\"\n    # You cannot write str directly to a binary file, you have to encode it to get bytes\n    first_line_bytes = first_line_str.encode(\"utf-8\")\n    # Then you can write the bytes\n    file.write(first_line_bytes)\n    # This is already bytes, it starts with b\"\n    second_line = b\"la cig\\xc3\\xbce\\xc3\\xb1a trae al ni\\xc3\\xb1o\"\n    file.write(second_line)\n    print(\"Binary file written\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/parameter_types/file/tutorial005_an_py310.py",
    "content": "from typing import Annotated\n\nimport typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(config: Annotated[typer.FileText, typer.Option(mode=\"a\")]):\n    config.write(\"This is a single line\\n\")\n    print(\"Config line written\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/parameter_types/file/tutorial005_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(config: typer.FileText = typer.Option(..., mode=\"a\")):\n    config.write(\"This is a single line\\n\")\n    print(\"Config line written\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/parameter_types/index/__init__.py",
    "content": ""
  },
  {
    "path": "docs_src/parameter_types/index/tutorial001_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(name: str, age: int = 20, height_meters: float = 1.89, female: bool = True):\n    print(f\"NAME is {name}, of type: {type(name)}\")\n    print(f\"--age is {age}, of type: {type(age)}\")\n    print(f\"--height-meters is {height_meters}, of type: {type(height_meters)}\")\n    print(f\"--female is {female}, of type: {type(female)}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/parameter_types/number/__init__.py",
    "content": ""
  },
  {
    "path": "docs_src/parameter_types/number/tutorial001_an_py310.py",
    "content": "from typing import Annotated\n\nimport typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(\n    id: Annotated[int, typer.Argument(min=0, max=1000)],\n    age: Annotated[int, typer.Option(min=18)] = 20,\n    score: Annotated[float, typer.Option(max=100)] = 0,\n):\n    print(f\"ID is {id}\")\n    print(f\"--age is {age}\")\n    print(f\"--score is {score}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/parameter_types/number/tutorial001_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(\n    id: int = typer.Argument(..., min=0, max=1000),\n    age: int = typer.Option(20, min=18),\n    score: float = typer.Option(0, max=100),\n):\n    print(f\"ID is {id}\")\n    print(f\"--age is {age}\")\n    print(f\"--score is {score}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/parameter_types/number/tutorial002_an_py310.py",
    "content": "from typing import Annotated\n\nimport typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(\n    id: Annotated[int, typer.Argument(min=0, max=1000)],\n    rank: Annotated[int, typer.Option(max=10, clamp=True)] = 0,\n    score: Annotated[float, typer.Option(min=0, max=100, clamp=True)] = 0,\n):\n    print(f\"ID is {id}\")\n    print(f\"--rank is {rank}\")\n    print(f\"--score is {score}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/parameter_types/number/tutorial002_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(\n    id: int = typer.Argument(..., min=0, max=1000),\n    rank: int = typer.Option(0, max=10, clamp=True),\n    score: float = typer.Option(0, min=0, max=100, clamp=True),\n):\n    print(f\"ID is {id}\")\n    print(f\"--rank is {rank}\")\n    print(f\"--score is {score}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/parameter_types/number/tutorial003_an_py310.py",
    "content": "from typing import Annotated\n\nimport typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(verbose: Annotated[int, typer.Option(\"--verbose\", \"-v\", count=True)] = 0):\n    print(f\"Verbose level is {verbose}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/parameter_types/number/tutorial003_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(verbose: int = typer.Option(0, \"--verbose\", \"-v\", count=True)):\n    print(f\"Verbose level is {verbose}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/parameter_types/path/__init__.py",
    "content": ""
  },
  {
    "path": "docs_src/parameter_types/path/tutorial001_an_py310.py",
    "content": "from pathlib import Path\nfrom typing import Annotated\n\nimport typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(config: Annotated[Path | None, typer.Option()] = None):\n    if config is None:\n        print(\"No config file\")\n        raise typer.Abort()\n    if config.is_file():\n        text = config.read_text()\n        print(f\"Config file contents: {text}\")\n    elif config.is_dir():\n        print(\"Config is a directory, will use all its config files\")\n    elif not config.exists():\n        print(\"The config doesn't exist\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/parameter_types/path/tutorial001_py310.py",
    "content": "from pathlib import Path\n\nimport typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(config: Path | None = typer.Option(None)):\n    if config is None:\n        print(\"No config file\")\n        raise typer.Abort()\n    if config.is_file():\n        text = config.read_text()\n        print(f\"Config file contents: {text}\")\n    elif config.is_dir():\n        print(\"Config is a directory, will use all its config files\")\n    elif not config.exists():\n        print(\"The config doesn't exist\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/parameter_types/path/tutorial002_an_py310.py",
    "content": "from pathlib import Path\nfrom typing import Annotated\n\nimport typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(\n    config: Annotated[\n        Path,\n        typer.Option(\n            exists=True,\n            file_okay=True,\n            dir_okay=False,\n            writable=False,\n            readable=True,\n            resolve_path=True,\n        ),\n    ],\n):\n    text = config.read_text()\n    print(f\"Config file contents: {text}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/parameter_types/path/tutorial002_py310.py",
    "content": "from pathlib import Path\n\nimport typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(\n    config: Path = typer.Option(\n        ...,\n        exists=True,\n        file_okay=True,\n        dir_okay=False,\n        writable=False,\n        readable=True,\n        resolve_path=True,\n    ),\n):\n    text = config.read_text()\n    print(f\"Config file contents: {text}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/parameter_types/uuid/__init__.py",
    "content": ""
  },
  {
    "path": "docs_src/parameter_types/uuid/tutorial001_py310.py",
    "content": "from uuid import UUID\n\nimport typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(user_id: UUID):\n    print(f\"USER_ID is {user_id}\")\n    print(f\"UUID version is: {user_id.version}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/printing/__init__.py",
    "content": ""
  },
  {
    "path": "docs_src/printing/tutorial001_py310.py",
    "content": "import typer\nfrom rich import print\n\ndata = {\n    \"name\": \"Rick\",\n    \"age\": 42,\n    \"items\": [{\"name\": \"Portal Gun\"}, {\"name\": \"Plumbus\"}],\n    \"active\": True,\n    \"affiliation\": None,\n}\n\napp = typer.Typer()\n\n\n@app.command()\ndef main():\n    print(\"Here's the data\")\n    print(data)\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/printing/tutorial002_py310.py",
    "content": "import typer\nfrom rich import print\n\napp = typer.Typer()\n\n\n@app.command()\ndef main():\n    print(\"[bold red]Alert![/bold red] [green]Portal gun[/green] shooting! :boom:\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/printing/tutorial003_py310.py",
    "content": "import typer\nfrom rich.console import Console\nfrom rich.table import Table\n\nconsole = Console()\n\napp = typer.Typer()\n\n\n@app.command()\ndef main():\n    table = Table(\"Name\", \"Item\")\n    table.add_row(\"Rick\", \"Portal Gun\")\n    table.add_row(\"Morty\", \"Plumbus\")\n    console.print(table)\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/printing/tutorial004_py310.py",
    "content": "import typer\nfrom rich.console import Console\n\nerr_console = Console(stderr=True)\n\napp = typer.Typer()\n\n\n@app.command()\ndef main():\n    err_console.print(\"Here is something written to standard error\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/printing/tutorial005_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(good: bool = True):\n    message_start = \"everything is \"\n    if good:\n        ending = typer.style(\"good\", fg=typer.colors.GREEN, bold=True)\n    else:\n        ending = typer.style(\"bad\", fg=typer.colors.WHITE, bg=typer.colors.RED)\n    message = message_start + ending\n    typer.echo(message)\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/printing/tutorial006_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(name: str):\n    typer.secho(f\"Welcome here {name}\", fg=typer.colors.MAGENTA)\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/progressbar/__init__.py",
    "content": ""
  },
  {
    "path": "docs_src/progressbar/tutorial001_py310.py",
    "content": "import time\n\nimport typer\nfrom rich.progress import track\n\napp = typer.Typer()\n\n\n@app.command()\ndef main():\n    total = 0\n    for value in track(range(100), description=\"Processing...\"):\n        # Fake processing time\n        time.sleep(0.01)\n        total += 1\n    print(f\"Processed {total} things.\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/progressbar/tutorial002_py310.py",
    "content": "import time\n\nimport typer\nfrom rich.progress import Progress, SpinnerColumn, TextColumn\n\napp = typer.Typer()\n\n\n@app.command()\ndef main():\n    with Progress(\n        SpinnerColumn(),\n        TextColumn(\"[progress.description]{task.description}\"),\n        transient=True,\n    ) as progress:\n        progress.add_task(description=\"Processing...\", total=None)\n        progress.add_task(description=\"Preparing...\", total=None)\n        time.sleep(5)\n    print(\"Done!\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/progressbar/tutorial003_py310.py",
    "content": "import time\n\nimport typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main():\n    total = 0\n    with typer.progressbar(range(100)) as progress:\n        for value in progress:\n            # Fake processing time\n            time.sleep(0.01)\n            total += 1\n    print(f\"Processed {total} things.\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/progressbar/tutorial004_py310.py",
    "content": "import time\n\nimport typer\n\n\ndef iterate_user_ids():\n    # Let's imagine this is a web API, not a range()\n    for i in range(100):\n        yield i\n\n\napp = typer.Typer()\n\n\n@app.command()\ndef main():\n    total = 0\n    with typer.progressbar(iterate_user_ids(), length=100) as progress:\n        for value in progress:\n            # Fake processing time\n            time.sleep(0.01)\n            total += 1\n    print(f\"Processed {total} user IDs.\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/progressbar/tutorial005_py310.py",
    "content": "import time\n\nimport typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main():\n    total = 0\n    with typer.progressbar(range(100), label=\"Processing\") as progress:\n        for value in progress:\n            # Fake processing time\n            time.sleep(0.01)\n            total += 1\n    print(f\"Processed {total} things.\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/progressbar/tutorial006_py310.py",
    "content": "import time\n\nimport typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main():\n    total = 1000\n    with typer.progressbar(length=total) as progress:\n        for batch in range(4):\n            # Fake processing time\n            time.sleep(1)\n            # Increment by 250 on each loop iteration\n            # (it will take 4 seconds to reach 1000)\n            progress.update(250)\n    print(f\"Processed {total} things in batches.\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/prompt/__init__.py",
    "content": ""
  },
  {
    "path": "docs_src/prompt/tutorial001_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main():\n    person_name = typer.prompt(\"What's your name?\")\n    print(f\"Hello {person_name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/prompt/tutorial002_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main():\n    delete = typer.confirm(\"Are you sure you want to delete it?\")\n    if not delete:\n        print(\"Not deleting\")\n        raise typer.Abort()\n    print(\"Deleting it!\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/prompt/tutorial003_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main():\n    delete = typer.confirm(\"Are you sure you want to delete it?\", abort=True)\n    print(\"Deleting it!\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/prompt/tutorial004_py310.py",
    "content": "import typer\nfrom rich.prompt import Prompt\n\napp = typer.Typer()\n\n\n@app.command()\ndef main():\n    name = Prompt.ask(\"Enter your name :sunglasses:\")\n    print(f\"Hey there {name}!\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/subcommands/__init__.py",
    "content": ""
  },
  {
    "path": "docs_src/subcommands/callback_override/__init__.py",
    "content": ""
  },
  {
    "path": "docs_src/subcommands/callback_override/tutorial001_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\nusers_app = typer.Typer()\napp.add_typer(users_app, name=\"users\")\n\n\n@users_app.callback()\ndef users_callback():\n    print(\"Running a users command\")\n\n\n@users_app.command()\ndef create(name: str):\n    print(f\"Creating user: {name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/subcommands/callback_override/tutorial002_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\ndef users_callback():\n    print(\"Running a users command\")\n\n\nusers_app = typer.Typer(callback=users_callback)\napp.add_typer(users_app, name=\"users\")\n\n\n@users_app.command()\ndef create(name: str):\n    print(f\"Creating user: {name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/subcommands/callback_override/tutorial003_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\ndef default_callback():\n    print(\"Running a users command\")\n\n\nusers_app = typer.Typer(callback=default_callback)\napp.add_typer(users_app, name=\"users\")\n\n\n@users_app.callback()\ndef user_callback():\n    print(\"Callback override, running users command\")\n\n\n@users_app.command()\ndef create(name: str):\n    print(f\"Creating user: {name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/subcommands/callback_override/tutorial004_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\ndef default_callback():\n    print(\"Running a users command\")\n\n\nusers_app = typer.Typer(callback=default_callback)\n\n\ndef callback_for_add_typer():\n    print(\"I have the high land! Running users command\")\n\n\napp.add_typer(users_app, name=\"users\", callback=callback_for_add_typer)\n\n\n@users_app.callback()\ndef user_callback():\n    print(\"Callback override, running users command\")\n\n\n@users_app.command()\ndef create(name: str):\n    print(f\"Creating user: {name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/subcommands/name_help/__init__.py",
    "content": ""
  },
  {
    "path": "docs_src/subcommands/name_help/tutorial001_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\nusers_app = typer.Typer()\napp.add_typer(users_app, name=\"users\", help=\"Manage users in the app.\")\n\n\n@users_app.command()\ndef create(name: str):\n    print(f\"Creating user: {name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/subcommands/name_help/tutorial002_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\nusers_app = typer.Typer()\napp.add_typer(users_app, name=\"users\")\n\n\n@users_app.callback()\ndef users():\n    \"\"\"\n    Manage users in the app.\n    \"\"\"\n\n\n@users_app.command()\ndef create(name: str):\n    print(f\"Creating user: {name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/subcommands/name_help/tutorial003_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\ndef users():\n    \"\"\"\n    Manage users in the app.\n    \"\"\"\n\n\nusers_app = typer.Typer(callback=users, name=\"users\")\napp.add_typer(users_app)\n\n\n@users_app.command()\ndef create(name: str):\n    print(f\"Creating user: {name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/subcommands/name_help/tutorial004_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\ndef old_callback():\n    \"\"\"\n    Old callback help.\n    \"\"\"\n\n\nusers_app = typer.Typer(callback=old_callback)\napp.add_typer(users_app, name=\"users\")\n\n\n@users_app.callback()\ndef users():\n    \"\"\"\n    Manage users in the app.\n    \"\"\"\n\n\n@users_app.command()\ndef create(name: str):\n    print(f\"Creating user: {name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/subcommands/name_help/tutorial005_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\ndef old_callback():\n    \"\"\"\n    Old callback help.\n    \"\"\"\n\n\nusers_app = typer.Typer(callback=old_callback, name=\"users\")\n\n\ndef new_users():\n    \"\"\"\n    I have the highland! Create some users.\n    \"\"\"\n\n\napp.add_typer(users_app, callback=new_users, name=\"new-users\")\n\n\n@users_app.callback()\ndef users():\n    \"\"\"\n    Manage users in the app.\n    \"\"\"\n\n\n@users_app.command()\ndef create(name: str):\n    print(f\"Creating user: {name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/subcommands/name_help/tutorial006_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\ndef old_callback():\n    \"\"\"\n    Old callback help.\n    \"\"\"\n\n\nusers_app = typer.Typer(callback=old_callback, name=\"exp-users\", help=\"Explicit help.\")\n\n\ndef new_users():\n    \"\"\"\n    I have the highland! Create some users.\n    \"\"\"\n\n\napp.add_typer(users_app, callback=new_users)\n\n\n@users_app.callback()\ndef users():\n    \"\"\"\n    Manage users in the app.\n    \"\"\"\n\n\n@users_app.command()\ndef create(name: str):\n    print(f\"Creating user: {name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/subcommands/name_help/tutorial007_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\ndef old_callback():\n    \"\"\"\n    Old callback help.\n    \"\"\"\n\n\nusers_app = typer.Typer(callback=old_callback, name=\"users\", help=\"Explicit help.\")\n\n\ndef new_users():\n    \"\"\"\n    I have the highland! Create some users.\n    \"\"\"\n\n\napp.add_typer(users_app, callback=new_users)\n\n\n@users_app.callback(help=\"Help from callback for users.\")\ndef users():\n    \"\"\"\n    Manage users in the app.\n    \"\"\"\n\n\n@users_app.command()\ndef create(name: str):\n    print(f\"Creating user: {name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/subcommands/name_help/tutorial008_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\ndef old_callback():\n    \"\"\"\n    Old callback help.\n    \"\"\"\n\n\nusers_app = typer.Typer(callback=old_callback, name=\"exp-users\", help=\"Explicit help.\")\n\n\ndef new_users():\n    \"\"\"\n    I have the highland! Create some users.\n    \"\"\"\n\n\napp.add_typer(\n    users_app,\n    callback=new_users,\n    name=\"cake-sith-users\",\n    help=\"Unlimited powder! Eh, users.\",\n)\n\n\n@users_app.callback(help=\"Help from callback for users.\")\ndef users():\n    \"\"\"\n    Manage users in the app.\n    \"\"\"\n\n\n@users_app.command()\ndef create(name: str):\n    print(f\"Creating user: {name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/subcommands/tutorial001_py310/__init__.py",
    "content": ""
  },
  {
    "path": "docs_src/subcommands/tutorial001_py310/items.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef create(item: str):\n    print(f\"Creating item: {item}\")\n\n\n@app.command()\ndef delete(item: str):\n    print(f\"Deleting item: {item}\")\n\n\n@app.command()\ndef sell(item: str):\n    print(f\"Selling item: {item}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/subcommands/tutorial001_py310/main.py",
    "content": "import typer\n\nimport items\nimport users\n\napp = typer.Typer()\napp.add_typer(users.app, name=\"users\")\napp.add_typer(items.app, name=\"items\")\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/subcommands/tutorial001_py310/users.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef create(user_name: str):\n    print(f\"Creating user: {user_name}\")\n\n\n@app.command()\ndef delete(user_name: str):\n    print(f\"Deleting user: {user_name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/subcommands/tutorial002_py310/__init__.py",
    "content": ""
  },
  {
    "path": "docs_src/subcommands/tutorial002_py310/main.py",
    "content": "import typer\n\napp = typer.Typer()\nitems_app = typer.Typer()\napp.add_typer(items_app, name=\"items\")\nusers_app = typer.Typer()\napp.add_typer(users_app, name=\"users\")\n\n\n@items_app.command(\"create\")\ndef items_create(item: str):\n    print(f\"Creating item: {item}\")\n\n\n@items_app.command(\"delete\")\ndef items_delete(item: str):\n    print(f\"Deleting item: {item}\")\n\n\n@items_app.command(\"sell\")\ndef items_sell(item: str):\n    print(f\"Selling item: {item}\")\n\n\n@users_app.command(\"create\")\ndef users_create(user_name: str):\n    print(f\"Creating user: {user_name}\")\n\n\n@users_app.command(\"delete\")\ndef users_delete(user_name: str):\n    print(f\"Deleting user: {user_name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/subcommands/tutorial003_py310/__init__.py",
    "content": ""
  },
  {
    "path": "docs_src/subcommands/tutorial003_py310/items.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef create(item: str):\n    print(f\"Creating item: {item}\")\n\n\n@app.command()\ndef delete(item: str):\n    print(f\"Deleting item: {item}\")\n\n\n@app.command()\ndef sell(item: str):\n    print(f\"Selling item: {item}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/subcommands/tutorial003_py310/lands.py",
    "content": "import typer\n\nimport reigns\nimport towns\n\napp = typer.Typer()\napp.add_typer(reigns.app, name=\"reigns\")\napp.add_typer(towns.app, name=\"towns\")\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/subcommands/tutorial003_py310/main.py",
    "content": "import typer\n\nimport items\nimport lands\nimport users\n\napp = typer.Typer()\napp.add_typer(users.app, name=\"users\")\napp.add_typer(items.app, name=\"items\")\napp.add_typer(lands.app, name=\"lands\")\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/subcommands/tutorial003_py310/reigns.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef conquer(name: str):\n    print(f\"Conquering reign: {name}\")\n\n\n@app.command()\ndef destroy(name: str):\n    print(f\"Destroying reign: {name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/subcommands/tutorial003_py310/towns.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef found(name: str):\n    print(f\"Founding town: {name}\")\n\n\n@app.command()\ndef burn(name: str):\n    print(f\"Burning town: {name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/subcommands/tutorial003_py310/users.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef create(user_name: str):\n    print(f\"Creating user: {user_name}\")\n\n\n@app.command()\ndef delete(user_name: str):\n    print(f\"Deleting user: {user_name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/terminating/__init__.py",
    "content": ""
  },
  {
    "path": "docs_src/terminating/tutorial001_py310.py",
    "content": "import typer\n\nexisting_usernames = [\"rick\", \"morty\"]\n\n\ndef maybe_create_user(username: str):\n    if username in existing_usernames:\n        print(\"The user already exists\")\n        raise typer.Exit()\n    else:\n        print(f\"User created: {username}\")\n\n\ndef send_new_user_notification(username: str):\n    # Somehow send a notification here for the new user, maybe an email\n    print(f\"Notification sent for new user: {username}\")\n\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(username: str):\n    maybe_create_user(username=username)\n    send_new_user_notification(username=username)\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/terminating/tutorial002_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(username: str):\n    if username == \"root\":\n        print(\"The root user is reserved\")\n        raise typer.Exit(code=1)\n    print(f\"New user created: {username}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/terminating/tutorial003_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(username: str):\n    if username == \"root\":\n        print(\"The root user is reserved\")\n        raise typer.Abort()\n    print(f\"New user created: {username}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/testing/__init__.py",
    "content": ""
  },
  {
    "path": "docs_src/testing/app01_py310/__init__.py",
    "content": ""
  },
  {
    "path": "docs_src/testing/app01_py310/main.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(name: str, city: str | None = None):\n    print(f\"Hello {name}\")\n    if city:\n        print(f\"Let's have a coffee in {city}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/testing/app01_py310/test_main.py",
    "content": "from typer.testing import CliRunner\n\nfrom .main import app\n\nrunner = CliRunner()\n\n\ndef test_app():\n    result = runner.invoke(app, [\"Camila\", \"--city\", \"Berlin\"])\n    assert result.exit_code == 0\n    assert \"Hello Camila\" in result.output\n    assert \"Let's have a coffee in Berlin\" in result.output\n"
  },
  {
    "path": "docs_src/testing/app02_an_py310/__init__.py",
    "content": ""
  },
  {
    "path": "docs_src/testing/app02_an_py310/main.py",
    "content": "from typing import Annotated\n\nimport typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(name: str, email: Annotated[str, typer.Option(prompt=True)]):\n    print(f\"Hello {name}, your email is: {email}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/testing/app02_an_py310/test_main.py",
    "content": "from typer.testing import CliRunner\n\nfrom .main import app\n\nrunner = CliRunner()\n\n\ndef test_app():\n    result = runner.invoke(app, [\"Camila\"], input=\"camila@example.com\\n\")\n    assert result.exit_code == 0\n    assert \"Hello Camila, your email is: camila@example.com\" in result.output\n"
  },
  {
    "path": "docs_src/testing/app02_py310/__init__.py",
    "content": ""
  },
  {
    "path": "docs_src/testing/app02_py310/main.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(name: str, email: str = typer.Option(..., prompt=True)):\n    print(f\"Hello {name}, your email is: {email}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "docs_src/testing/app02_py310/test_main.py",
    "content": "from typer.testing import CliRunner\n\nfrom .main import app\n\nrunner = CliRunner()\n\n\ndef test_app():\n    result = runner.invoke(app, [\"Camila\"], input=\"camila@example.com\\n\")\n    assert result.exit_code == 0\n    assert \"Hello Camila, your email is: camila@example.com\" in result.output\n"
  },
  {
    "path": "docs_src/testing/app03_py310/__init__.py",
    "content": ""
  },
  {
    "path": "docs_src/testing/app03_py310/main.py",
    "content": "import typer\n\n\ndef main(name: str = \"World\"):\n    print(f\"Hello {name}\")\n\n\nif __name__ == \"__main__\":\n    typer.run(main)\n"
  },
  {
    "path": "docs_src/testing/app03_py310/test_main.py",
    "content": "import typer\nfrom typer.testing import CliRunner\n\nfrom .main import main\n\napp = typer.Typer()\napp.command()(main)\n\nrunner = CliRunner()\n\n\ndef test_app():\n    result = runner.invoke(app, [\"--name\", \"Camila\"])\n    assert result.exit_code == 0\n    assert \"Hello Camila\" in result.output\n"
  },
  {
    "path": "docs_src/typer_app/__init__.py",
    "content": ""
  },
  {
    "path": "docs_src/typer_app/tutorial001_py310.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(name: str):\n    print(f\"Hello {name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "mkdocs.env.yml",
    "content": "# Define this here and not in the main mkdocs.yml file because that one could be auto\n# updated and written, and the script would remove the env var\nmarkdown_extensions:\n  pymdownx.highlight:\n    linenums: !ENV [LINENUMS, false]\n"
  },
  {
    "path": "mkdocs.yml",
    "content": "INHERIT: ./mkdocs.env.yml\nsite_name: Typer\nsite_description: Typer, build great CLIs. Easy to code. Based on Python type hints.\nsite_url: https://typer.tiangolo.com/\n\ntheme:\n  name: material\n  custom_dir: docs/overrides\n  palette:\n  - media: \"(prefers-color-scheme)\"\n    toggle:\n      icon: material/lightbulb-auto\n      name: Switch to light mode\n  - media: '(prefers-color-scheme: light)'\n    scheme: default\n    primary: black\n    accent: teal\n    toggle:\n      icon: material/lightbulb\n      name: Switch to dark mode\n  - media: '(prefers-color-scheme: dark)'\n    scheme: slate\n    primary: black\n    accent: teal\n    toggle:\n      icon: material/lightbulb-outline\n      name: Switch to system preference\n  features:\n  - content.code.annotate\n  - content.code.copy\n  # - content.code.select\n  - content.footnote.tooltips\n  - content.tabs.link\n  - content.tooltips\n  - navigation.footer\n  - navigation.indexes\n  - navigation.instant\n  - navigation.instant.prefetch\n  # - navigation.instant.preview\n  - navigation.instant.progress\n  - navigation.path\n  - navigation.tabs\n  - navigation.tabs.sticky\n  - navigation.top\n  - navigation.tracking\n  - search.highlight\n  - search.share\n  - search.suggest\n  - toc.follow\n\n  icon:\n    repo: fontawesome/brands/github-alt\n  logo: img/icon.svg\n  favicon: img/favicon.png\n  language: en\nrepo_name: fastapi/typer\nrepo_url: https://github.com/fastapi/typer\nplugins:\n  # Material for MkDocs\n  search:\n  social:\n  typeset:\n  # Other plugins\n  macros:\n    include_yaml:\n    - members: data/members.yml\n  redirects:\n    redirect_maps:\n      typer-cli.md: tutorial/typer-command.md\n  mkdocstrings:\n    handlers:\n      python:\n        options:\n          extensions:\n            - griffe_typingdoc\n          show_root_heading: true\n          show_if_no_docstring: true\n          inherited_members: true\n          members_order: source\n          separate_signature: true\n          unwrap_annotated: true\n          filters:\n            - '!^_'\n          merge_init_into_class: true\n          docstring_section_style: spacy\n          signature_crossrefs: true\n          show_symbol_type_heading: true\n          show_symbol_type_toc: true\n\nnav:\n  - Typer: index.md\n  - features.md\n  - Tutorial - User Guide:\n      - tutorial/index.md\n      - environment-variables.md\n      - virtual-environments.md\n      - tutorial/install.md\n      - tutorial/first-steps.md\n      - tutorial/typer-app.md\n      - tutorial/printing.md\n      - tutorial/terminating.md\n      - CLI Arguments:\n          - tutorial/arguments/index.md\n          - tutorial/arguments/optional.md\n          - tutorial/arguments/default.md\n          - tutorial/arguments/help.md\n          - tutorial/arguments/envvar.md\n          - tutorial/arguments/other-uses.md\n      - CLI Options:\n          - tutorial/options/index.md\n          - tutorial/options/help.md\n          - tutorial/options/required.md\n          - tutorial/options/prompt.md\n          - tutorial/options/password.md\n          - tutorial/options/name.md\n          - tutorial/options/callback-and-context.md\n          - tutorial/options/version.md\n      - Commands:\n          - tutorial/commands/index.md\n          - tutorial/commands/arguments.md\n          - tutorial/commands/options.md\n          - tutorial/commands/help.md\n          - tutorial/commands/name.md\n          - tutorial/commands/callback.md\n          - tutorial/commands/one-or-multiple.md\n          - tutorial/commands/context.md\n      - tutorial/options-autocompletion.md\n      - CLI Parameter Types:\n          - tutorial/parameter-types/index.md\n          - tutorial/parameter-types/number.md\n          - tutorial/parameter-types/bool.md\n          - tutorial/parameter-types/uuid.md\n          - tutorial/parameter-types/datetime.md\n          - tutorial/parameter-types/enum.md\n          - tutorial/parameter-types/path.md\n          - tutorial/parameter-types/file.md\n          - tutorial/parameter-types/custom-types.md\n      - SubCommands - Command Groups:\n          - tutorial/subcommands/index.md\n          - tutorial/subcommands/add-typer.md\n          - tutorial/subcommands/single-file.md\n          - tutorial/subcommands/nested-subcommands.md\n          - tutorial/subcommands/callback-override.md\n          - tutorial/subcommands/name-and-help.md\n      - Multiple Values:\n          - tutorial/multiple-values/index.md\n          - tutorial/multiple-values/multiple-options.md\n          - tutorial/multiple-values/options-with-multiple-values.md\n          - tutorial/multiple-values/arguments-with-multiple-values.md\n      - tutorial/prompt.md\n      - tutorial/progressbar.md\n      - tutorial/app-dir.md\n      - tutorial/launch.md\n      - tutorial/testing.md\n      - tutorial/package.md\n      - tutorial/exceptions.md\n      - tutorial/one-file-per-command.md\n      - tutorial/typer-command.md\n  - Reference (Code API):\n    - reference/index.md\n    - reference/typer.md\n    - reference/run_launch.md\n    - reference/parameters.md\n    - reference/file_objects.md\n    - reference/context.md\n  - Resources:\n    - resources/index.md\n    - help-typer.md\n    - contributing.md\n    - management-tasks.md\n  - About:\n    - about/index.md\n    - alternatives.md\n    - management.md\n  - release-notes.md\n\nmarkdown_extensions:\n  # Material for MkDocs Extensions\n  material.extensions.preview:\n    targets:\n      include:\n        - \"*\"\n  # Python Markdown\n  abbr:\n  attr_list:\n  footnotes:\n  md_in_html:\n  tables:\n  toc:\n    permalink: true\n\n  # Python Markdown Extensions\n  pymdownx.betterem:\n    smart_enable: all\n  pymdownx.caret:\n  pymdownx.highlight:\n    line_spans: __span\n  pymdownx.inlinehilite:\n  pymdownx.keys:\n  pymdownx.mark:\n  pymdownx.superfences:\n    custom_fences:\n    - name: mermaid\n      class: mermaid\n      format: !!python/name:pymdownx.superfences.fence_code_format\n  pymdownx.tilde:\n\n  # pymdownx blocks\n  pymdownx.blocks.admonition:\n    types:\n    - note\n    - attention\n    - caution\n    - danger\n    - error\n    - tip\n    - hint\n    - warning\n    # Custom types\n    - info\n    - check\n  pymdownx.blocks.details:\n  pymdownx.blocks.tab:\n    alternate_style: True\n\n  # Other extensions\n  mdx_include:\n  markdown_include_variants:\n\nextra:\n  social:\n    - icon: fontawesome/brands/github-alt\n      link: https://github.com/fastapi/typer\n    - icon: fontawesome/brands/twitter\n      link: https://twitter.com/tiangolo\n    - icon: fontawesome/brands/linkedin\n      link: https://www.linkedin.com/in/tiangolo\n    - icon: fontawesome/brands/dev\n      link: https://dev.to/tiangolo\n    - icon: fontawesome/brands/medium\n      link: https://medium.com/@tiangolo\n    - icon: fontawesome/solid/globe\n      link: https://tiangolo.com\n\nextra_css:\n  - css/termynal.css\n  - css/custom.css\n\nextra_javascript:\n  - js/termynal.js\n  - js/custom.js\n\nhooks:\n  - scripts/mkdocs_hooks.py\n"
  },
  {
    "path": "pyproject.toml",
    "content": "[build-system]\nrequires = [\"pdm-backend\"]\nbuild-backend = \"pdm.backend\"\n\n[project]\nname = \"typer\"\nlicense = \"MIT\"\nlicense-files = [\"LICENSE\"]\ndynamic = [\"version\"]\ndescription = \"Typer, build great CLIs. Easy to code. Based on Python type hints.\"\nauthors = [\n    {name = \"Sebastián Ramírez\", email = \"tiangolo@gmail.com\"},\n]\nrequires-python = \">=3.10\"\nclassifiers = [\n    \"Intended Audience :: Information Technology\",\n    \"Intended Audience :: System Administrators\",\n    \"Operating System :: OS Independent\",\n    \"Programming Language :: Python :: 3\",\n    \"Programming Language :: Python\",\n    \"Topic :: Software Development :: Libraries :: Application Frameworks\",\n    \"Topic :: Software Development :: Libraries :: Python Modules\",\n    \"Topic :: Software Development :: Libraries\",\n    \"Topic :: Software Development\",\n    \"Typing :: Typed\",\n    \"Development Status :: 4 - Beta\",\n    \"Intended Audience :: Developers\",\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.13\",\n    \"Programming Language :: Python :: 3.14\",\n]\ndependencies = [\n    \"click >= 8.2.1\",\n    \"shellingham >=1.3.0\",\n    \"rich >=12.3.0\",\n    \"annotated-doc >=0.0.2\",\n]\nreadme = \"README.md\"\n\n[project.urls]\nHomepage = \"https://github.com/fastapi/typer\"\nDocumentation = \"https://typer.tiangolo.com\"\nRepository = \"https://github.com/fastapi/typer\"\nIssues = \"https://github.com/fastapi/typer/issues\"\nChangelog = \"https://typer.tiangolo.com/release-notes/\"\n\n[project.scripts]\ntyper = \"typer.cli:main\"\n\n[dependency-groups]\ndev = [\n    { include-group = \"tests\" },\n    { include-group = \"docs\" },\n    \"prek >=0.3.2\",\n]\ndocs = [\n    \"cairosvg >=2.8.2\",\n    \"griffe-typingdoc >=0.3.0\",\n    \"griffe-warnings-deprecated >=1.1.0\",\n    \"markdown-include-variants >=0.0.8\",\n    \"mdx-include >=1.4.1\",\n    \"mkdocs-macros-plugin >=1.5.0\",\n    \"mkdocs-material >=9.7.1\",\n    \"mkdocs-redirects >=1.2.1\",\n    \"mkdocstrings[python] >=0.30.1\",\n    \"pillow >=11.3.0\",\n    \"pyyaml >=5.3.1\",\n]\ngithub-actions = [\n    \"httpx >=0.27.0\",\n    \"pydantic >=2.5.3\",\n    \"pydantic-settings >=2.1.0\",\n    \"pygithub >=2.3.0\",\n    \"smokeshow >=0.5.0\",\n]\ntests = [\n    \"coverage[toml] >=7.13\",\n    \"mypy >=1.19.1\",\n    \"ty >=0.0.9\",\n    \"pytest >=9.0.0\",\n    \"pytest-cov >=4.0.0\",\n    \"pytest-sugar >=0.9.5\",\n    \"pytest-xdist >=1.32.0\",\n    \"rich >=12.3.0\",\n    \"ruff >=0.15.0\",\n    \"shellingham >=1.3.0\",\n]\n\n[tool.pdm]\nversion = { source = \"file\", path = \"typer/__init__.py\" }\ndistribution = true\n\n[tool.pdm.build]\nsource-includes = [\n    \"tests/\",\n    \"docs_src/\",\n    \"scripts/\",\n]\n\n[tool.pytest]\nminversion = \"9.0\"\naddopts = [\n  \"--strict-config\",\n  \"--strict-markers\",\n]\nstrict_xfail = true\nfilterwarnings = [\n    \"error\",\n    # For pytest-xdist\n    'ignore::DeprecationWarning:xdist',\n]\n\n[tool.coverage.run]\nparallel = true\ndata_file = \"coverage/.coverage\"\nsource = [\n    \"docs_src\",\n    \"tests\",\n    \"typer\"\n]\nomit = [\n    \"typer/_typing.py\",\n]\ncontext = '${CONTEXT}'\nrelative_files = true\n\n[tool.coverage.report]\nexclude_lines = [\n    \"pragma: no cover\",\n    \"@overload\",\n    'if __name__ == \"__main__\":',\n    \"if TYPE_CHECKING:\",\n]\n\n[tool.mypy]\nstrict = true\n\n[[tool.mypy.overrides]]\nmodule = \"docs_src.*\"\ndisallow_incomplete_defs = false\ndisallow_untyped_defs = false\ndisallow_untyped_calls = false\n\n[[tool.mypy.overrides]]\nmodule = \"shellingham\"\nignore_missing_imports = true\n\n[tool.ruff.lint]\nselect = [\n    \"E\",  # pycodestyle errors\n    \"W\",  # pycodestyle warnings\n    \"F\",  # pyflakes\n    \"I\",  # isort\n    \"B\",  # flake8-bugbear\n    \"C4\",  # flake8-comprehensions\n    \"UP\", # pyupgrade\n    \"TID\", # flake8-tidy-imports\n]\nignore = [\n    \"E501\",  # line too long, handled by black\n    \"B008\",  # do not perform function calls in argument defaults\n    \"C901\",  # too complex\n    \"W191\", # indentation contains tabs\n    \"TID252\", # relative imports okay\n]\n\n[tool.ruff.lint.per-file-ignores]\n# \"__init__.py\" = [\"F401\"]\n# rich_utils is allowed to use rich imports\n\"typer/rich_utils.py\" = [\"TID251\"]\n# This file is more readable without yield from\n\"docs_src/progressbar/tutorial004_py310.py\" = [\"UP028\", \"B007\"]\n# Default mutable data structure\n\"docs_src/options_autocompletion/tutorial006_an_py310.py\" = [\"B006\"]\n\"docs_src/multiple_values/multiple_options/tutorial002_an_py310.py\" = [\"B006\"]\n\"docs_src/options_autocompletion/tutorial007_an_py310.py\" = [\"B006\"]\n\"docs_src/options_autocompletion/tutorial008_an_py310.py\" = [\"B006\"]\n\"docs_src/options_autocompletion/tutorial009_an_py310.py\" = [\"B006\"]\n\"docs_src/parameter_types/enum/tutorial003_an_py310.py\" = [\"B006\"]\n# Loop control variable `value` not used within loop body\n\"docs_src/progressbar/tutorial001_py310.py\" = [\"B007\"]\n\"docs_src/progressbar/tutorial003_py310.py\" = [\"B007\"]\n\"docs_src/progressbar/tutorial005_py310.py\" = [\"B007\"]\n\"docs_src/progressbar/tutorial006_py310.py\" = [\"B007\"]\n# Local variable `delete` is assigned to but never used\n\"docs_src/prompt/tutorial003_py310.py\" = [\"F841\"]\n# No need to worry about rich imports in docs\n\"docs_src/*\" = [\"TID\"]\n\n[tool.ruff.lint.isort]\nknown-third-party = [\"typer\", \"click\"]\n# For docs_src/subcommands/tutorial003/\nknown-first-party = [\"reigns\", \"towns\", \"lands\", \"items\", \"users\"]\n\n[tool.ruff.lint.pyupgrade]\n# Preserve types, even if a file imports `from __future__ import annotations`.\nkeep-runtime-typing = true\n\n[tool.ruff.lint.flake8-tidy-imports]\n# Import rich_utils from within functions (lazy), not at the module level (TID253)\nbanned-module-level-imports = [\"typer.rich_utils\"]\n\n[tool.ruff.lint.flake8-tidy-imports.banned-api]\n\"rich\".msg = \"Use 'typer.rich_utils' instead of importing from 'rich' directly.\"\n\"shellingham.detect_shell\".msg = \"\"\"\\\nUse 'typer._completion_shared._get_shell_name' instead of using \\\n'shellingham.detect_shell' directly.\n\"\"\"\n"
  },
  {
    "path": "scripts/deploy_docs_status.py",
    "content": "import logging\nimport re\nfrom typing import Literal\n\nfrom github import Auth, Github\nfrom pydantic import BaseModel, SecretStr\nfrom pydantic_settings import BaseSettings\n\nsite_domain = \"typer.tiangolo.com\"\n\n\nclass Settings(BaseSettings):\n    github_repository: str\n    github_token: SecretStr\n    deploy_url: str | None = None\n    commit_sha: str\n    run_id: int\n    state: Literal[\"pending\", \"success\", \"error\"] = \"pending\"\n\n\nclass LinkData(BaseModel):\n    previous_link: str\n    preview_link: str\n\n\ndef main() -> None:\n    logging.basicConfig(level=logging.INFO)\n    settings = Settings()\n\n    logging.info(f\"Using config: {settings.model_dump_json()}\")\n    g = Github(auth=Auth.Token(settings.github_token.get_secret_value()))\n    repo = g.get_repo(settings.github_repository)\n    use_pr = next(\n        (pr for pr in repo.get_pulls() if pr.head.sha == settings.commit_sha), None\n    )\n    if not use_pr:\n        logging.error(f\"No PR found for hash: {settings.commit_sha}\")\n        return\n    commits = list(use_pr.get_commits())\n    current_commit = [c for c in commits if c.sha == settings.commit_sha][0]\n    run_url = f\"https://github.com/{settings.github_repository}/actions/runs/{settings.run_id}\"\n    if settings.state == \"pending\":\n        current_commit.create_status(\n            state=\"pending\",\n            description=\"Deploying Docs\",\n            context=\"deploy-docs\",\n            target_url=run_url,\n        )\n        logging.info(\"No deploy URL available yet\")\n        return\n    if settings.state == \"error\":\n        current_commit.create_status(\n            state=\"error\",\n            description=\"Error Deploying Docs\",\n            context=\"deploy-docs\",\n            target_url=run_url,\n        )\n        logging.info(\"Error deploying docs\")\n        return\n    assert settings.state == \"success\"\n    if not settings.deploy_url:\n        current_commit.create_status(\n            state=\"success\",\n            description=\"No Docs Changes\",\n            context=\"deploy-docs\",\n            target_url=run_url,\n        )\n        logging.info(\"No docs changes found\")\n        return\n    assert settings.deploy_url\n    current_commit.create_status(\n        state=\"success\",\n        description=\"Docs Deployed\",\n        context=\"deploy-docs\",\n        target_url=run_url,\n    )\n\n    files = list(use_pr.get_files())\n    docs_files = [f for f in files if f.filename.startswith(\"docs/\")]\n\n    deploy_url = settings.deploy_url.rstrip(\"/\")\n    links: list[LinkData] = []\n    for f in docs_files:\n        match = re.match(r\"docs/(.*)\", f.filename)\n        if not match:\n            continue\n        path = match.group(1)\n        if path.endswith(\"index.md\"):\n            use_path = path.replace(\"index.md\", \"\")\n        else:\n            use_path = path.replace(\".md\", \"/\")\n        link = LinkData(\n            previous_link=f\"https://{site_domain}/{use_path}\",\n            preview_link=f\"{deploy_url}/{use_path}\",\n        )\n        links.append(link)\n        links.sort(key=lambda x: x.preview_link)\n\n    header = \"## 📝 Docs preview\"\n    message = header\n    message += f\"\\n\\nLast commit {settings.commit_sha} at: {deploy_url}\"\n\n    if links:\n        message += \"\\n\\n### Modified Pages\\n\\n\"\n        for link in links:\n            message += f\"* {link.preview_link}\"\n            message += f\" - ([before]({link.previous_link}))\"\n            message += \"\\n\"\n\n    print(message)\n    issue = use_pr.as_issue()\n    comments = list(issue.get_comments())\n    for comment in comments:\n        if (\n            comment.body.startswith(header)\n            and comment.user.login == \"github-actions[bot]\"\n        ):\n            comment.edit(message)\n            break\n    else:\n        issue.create_comment(message)\n\n    logging.info(\"Finished\")\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "scripts/docker/Dockerfile",
    "content": "FROM python:latest\n\n# Add Fish\nRUN echo 'deb http://download.opensuse.org/repositories/shells:/fish:/release:/3/Debian_12/ /' | tee /etc/apt/sources.list.d/shells:fish:release:3.list\nRUN curl -fsSL https://download.opensuse.org/repositories/shells:fish:release:3/Debian_12/Release.key | gpg --dearmor | tee /etc/apt/trusted.gpg.d/shells_fish_release_3.gpg > /dev/null\n\n# Install packages including Fish, Zsh, PowerShell\nRUN apt-get update && apt-get install -y \\\n    wget \\\n    apt-transport-https \\\n    software-properties-common \\\n    nano \\\n    vim \\\n    fish \\\n    zsh \\\n    && wget https://github.com/PowerShell/PowerShell/releases/download/v7.4.4/powershell_7.4.4-1.deb_amd64.deb \\\n    && dpkg -i powershell_7.4.4-1.deb_amd64.deb\n\n# Install uv\nCOPY --from=ghcr.io/astral-sh/uv:latest /uv /bin/uv\n\nENV UV_SYSTEM_PYTHON=1\n\nCOPY . /code\n\nWORKDIR /code\n\nRUN uv pip install -r requirements.txt\n"
  },
  {
    "path": "scripts/docker/compose.yaml",
    "content": "services:\n  typer:\n    build:\n      context: ../../\n      dockerfile: scripts/docker/Dockerfile\n    volumes:\n      - ../../:/code\n    command: sleep infinity\n"
  },
  {
    "path": "scripts/docs.py",
    "content": "import logging\nimport os\nimport re\nimport shutil\nimport subprocess\nfrom http.server import HTTPServer, SimpleHTTPRequestHandler\nfrom pathlib import Path\n\nimport typer\nfrom ruff.__main__ import find_ruff_bin\n\nlogging.basicConfig(level=logging.INFO)\n\nmkdocs_name = \"mkdocs.yml\"\ndocs_path = Path(\"docs\")\nen_docs_path = Path(\"\")\n\napp = typer.Typer()\n\n\n@app.callback()\ndef callback() -> None:\n    # For MacOS with Cairo\n    os.environ[\"DYLD_FALLBACK_LIBRARY_PATH\"] = \"/opt/homebrew/lib\"\n\n\ndef generate_readme_content() -> str:\n    en_index = en_docs_path / \"docs\" / \"index.md\"\n    content = en_index.read_text(\"utf-8\")\n    match_pre = re.search(r\"</style>\\n\\n\", content)\n    if not match_pre:\n        raise RuntimeError(\"Couldn't find pre section (<style>) in index.md\")\n    frontmatter_end = match_pre.end()\n    new_content = content[frontmatter_end:]\n    # Remove content between <!-- only-mkdocs --> and <!-- /only-mkdocs -->\n    new_content = re.sub(\n        r\"<!-- only-mkdocs -->.*?<!-- /only-mkdocs -->\",\n        \"\",\n        new_content,\n        flags=re.DOTALL,\n    )\n    return new_content\n\n\n@app.command()\ndef generate_readme() -> None:\n    \"\"\"\n    Generate README.md content from main index.md\n    \"\"\"\n    typer.echo(\"Generating README\")\n    readme_path = Path(\"README.md\")\n    new_content = generate_readme_content()\n    readme_path.write_text(new_content, encoding=\"utf-8\")\n\n\n@app.command()\ndef verify_readme() -> None:\n    \"\"\"\n    Verify README.md content from main index.md\n    \"\"\"\n    typer.echo(\"Verifying README\")\n    readme_path = Path(\"README.md\")\n    generated_content = generate_readme_content()\n    readme_content = readme_path.read_text(\"utf-8\")\n    if generated_content != readme_content:\n        typer.secho(\n            \"README.md outdated from the latest index.md\", color=typer.colors.RED\n        )\n        raise typer.Abort()\n    typer.echo(\"Valid README ✅\")\n\n\n@app.command()\ndef live(dirty: bool = False) -> None:\n    \"\"\"\n    Serve with livereload a docs site for a specific language.\n\n    This only shows the actual translated files, not the placeholders created with\n    build-all.\n\n    Takes an optional LANG argument with the name of the language to serve, by default\n    en.\n    \"\"\"\n    # Enable line numbers during local development to make it easier to highlight\n    args = [\"mkdocs\", \"serve\", \"--dev-addr\", \"127.0.0.1:8008\"]\n    if dirty:\n        args.append(\"--dirty\")\n    subprocess.run(args, env={**os.environ, \"LINENUMS\": \"true\"}, check=True)\n\n\n@app.command()\ndef build() -> None:\n    \"\"\"\n    Build the docs.\n    \"\"\"\n    print(\"Building docs\")\n    subprocess.run([\"mkdocs\", \"build\"], check=True)\n    typer.secho(\"Successfully built docs\", color=typer.colors.GREEN)\n\n\n@app.command()\ndef serve() -> None:\n    \"\"\"\n    A quick server to preview a built site.\n\n    For development, prefer the command live (or just mkdocs serve).\n\n    This is here only to preview the documentation site.\n\n    Make sure you run the build command first.\n    \"\"\"\n    typer.echo(\"Warning: this is a very simple server.\")\n    typer.echo(\"For development, use the command live instead.\")\n    typer.echo(\"This is here only to preview the documentation site.\")\n    typer.echo(\"Make sure you run the build command first.\")\n    os.chdir(\"site\")\n    server_address = (\"\", 8008)\n    server = HTTPServer(server_address, SimpleHTTPRequestHandler)\n    typer.echo(\"Serving at: http://127.0.0.1:8008\")\n    server.serve_forever()\n\n\n@app.command()\ndef generate_docs_src_versions_for_file(file_path: Path) -> None:\n    target_versions = [\"py39\", \"py310\"]\n    full_path_str = str(file_path)\n    for target_version in target_versions:\n        if f\"_{target_version}\" in full_path_str:\n            logging.info(\n                f\"Skipping {file_path}, already a version file for {target_version}\"\n            )\n            return\n    base_content = file_path.read_text(encoding=\"utf-8\")\n    previous_content = {base_content}\n    for target_version in target_versions:\n        version_result = subprocess.run(\n            [\n                find_ruff_bin(),\n                \"check\",\n                \"--target-version\",\n                target_version,\n                \"--fix\",\n                \"--unsafe-fixes\",\n                \"-\",\n            ],\n            input=base_content.encode(\"utf-8\"),\n            capture_output=True,\n        )\n        content_target = version_result.stdout.decode(\"utf-8\")\n        format_result = subprocess.run(\n            [find_ruff_bin(), \"format\", \"-\"],\n            input=content_target.encode(\"utf-8\"),\n            capture_output=True,\n        )\n        content_format = format_result.stdout.decode(\"utf-8\")\n        if content_format in previous_content:\n            continue\n        previous_content.add(content_format)\n        # Determine where the version label should go: in the parent directory\n        # name or in the file name, matching the source structure.\n        label_in_parent = False\n        for v in target_versions:\n            if f\"_{v}\" in file_path.parent.name:\n                label_in_parent = True\n                break\n        if label_in_parent:\n            parent_name = file_path.parent.name\n            for v in target_versions:\n                parent_name = parent_name.replace(f\"_{v}\", \"\")\n            new_parent = file_path.parent.parent / f\"{parent_name}_{target_version}\"\n            new_parent.mkdir(parents=True, exist_ok=True)\n            version_file = new_parent / file_path.name\n        else:\n            base_name = file_path.stem\n            for v in target_versions:\n                if base_name.endswith(f\"_{v}\"):\n                    base_name = base_name[: -len(f\"_{v}\")]\n                    break\n            version_file = file_path.with_name(f\"{base_name}_{target_version}.py\")\n        logging.info(f\"Writing to {version_file}\")\n        version_file.write_text(content_format, encoding=\"utf-8\")\n\n\n@app.command()\ndef generate_docs_src_versions() -> None:\n    \"\"\"\n    Generate Python version-specific files for all .py files in docs_src.\n    \"\"\"\n    docs_src_path = Path(\"docs_src\")\n    for py_file in sorted(docs_src_path.rglob(\"*.py\")):\n        generate_docs_src_versions_for_file(py_file)\n\n\n@app.command()\ndef copy_py39_to_py310() -> None:\n    \"\"\"\n    For each docs_src file/directory with a _py39 label that has no _py310\n    counterpart, copy it with the _py310 label.\n    \"\"\"\n    docs_src_path = Path(\"docs_src\")\n    # Handle directory-level labels (e.g. app_b_an_py39/)\n    for dir_path in sorted(docs_src_path.rglob(\"*_py39\")):\n        if not dir_path.is_dir():\n            continue\n        py310_dir = dir_path.parent / dir_path.name.replace(\"_py39\", \"_py310\")\n        if py310_dir.exists():\n            continue\n        logging.info(f\"Copying directory {dir_path} -> {py310_dir}\")\n        shutil.copytree(dir_path, py310_dir)\n    # Handle file-level labels (e.g. tutorial001_py39.py)\n    for file_path in sorted(docs_src_path.rglob(\"*_py39.py\")):\n        if not file_path.is_file():\n            continue\n        # Skip files inside _py39 directories (already handled above)\n        if \"_py39\" in file_path.parent.name:\n            continue\n        py310_file = file_path.with_name(\n            file_path.name.replace(\"_py39.py\", \"_py310.py\")\n        )\n        if py310_file.exists():\n            continue\n        logging.info(f\"Copying file {file_path} -> {py310_file}\")\n        shutil.copy2(file_path, py310_file)\n\n\n@app.command()\ndef update_docs_includes_py39_to_py310() -> None:\n    \"\"\"\n    Update .md files in docs/en/ to replace _py39 includes with _py310 versions.\n\n    For each include line referencing a _py39 file or directory in docs_src, replace\n    the _py39 label with _py310.\n    \"\"\"\n    include_pattern = re.compile(r\"\\{[^}]*docs_src/[^}]*_py39[^}]*\\.py[^}]*\\}\")\n    count = 0\n    for md_file in sorted(en_docs_path.rglob(\"*.md\")):\n        content = md_file.read_text(encoding=\"utf-8\")\n        if \"_py39\" not in content:\n            continue\n        new_content = include_pattern.sub(\n            lambda m: m.group(0).replace(\"_py39\", \"_py310\"), content\n        )\n        if new_content != content:\n            md_file.write_text(new_content, encoding=\"utf-8\")\n            count += 1\n            logging.info(f\"Updated includes in {md_file}\")\n    print(f\"Updated {count} file(s) ✅\")\n\n\n@app.command()\ndef remove_unused_docs_src() -> None:\n    \"\"\"\n    Delete .py files in docs_src that are not included in any .md file under docs/.\n    \"\"\"\n    docs_src_path = Path(\"docs_src\")\n    # Collect all docs .md content referencing docs_src\n    all_docs_content = \"\"\n    for md_file in docs_path.rglob(\"*.md\"):\n        all_docs_content += md_file.read_text(encoding=\"utf-8\")\n    # Build a set of directory-based package roots (e.g. docs_src/bigger_applications/app_py39)\n    # where at least one file is referenced in docs. All files in these directories\n    # should be kept since they may be internally imported by the referenced files.\n    used_package_dirs: set[Path] = set()\n    for py_file in docs_src_path.rglob(\"*.py\"):\n        if py_file.name == \"__init__.py\":\n            continue\n        rel_path = str(py_file)\n        if rel_path in all_docs_content:\n            # Walk up from the file's parent to find the package root\n            # (a subdirectory under docs_src/<topic>/)\n            parts = py_file.relative_to(docs_src_path).parts\n            if len(parts) > 2 and not py_file.name.startswith(\"tutorial\"):\n                # File is inside a sub-package like docs_src/topic/app_xxx/...\n                # but not a standalone tutorial file in a topic subdirectory\n                package_root = docs_src_path / parts[0] / parts[1]\n                used_package_dirs.add(package_root)\n    removed = 0\n    for py_file in sorted(docs_src_path.rglob(\"*.py\")):\n        if py_file.name == \"__init__.py\":\n            continue\n        # Build the relative path as it appears in includes (e.g. docs_src/first_steps/tutorial001.py)\n        rel_path = str(py_file)\n        if rel_path in all_docs_content:\n            continue\n        # If this file is inside a directory-based package where any sibling is\n        # referenced, keep it (it's likely imported internally).\n        parts = py_file.relative_to(docs_src_path).parts\n        if len(parts) > 2:\n            package_root = docs_src_path / parts[0] / parts[1]\n            if package_root in used_package_dirs:\n                continue\n        # Check if the _an counterpart (or non-_an counterpart) is referenced.\n        # If either variant is included, keep both.\n        # Handle both file-level _an (tutorial001_an.py) and directory-level _an\n        # (app_an/main.py)\n        counterpart_found = False\n        full_path_str = str(py_file)\n        if \"_an\" in py_file.stem:\n            # This is an _an file, check if the non-_an version is referenced\n            counterpart = full_path_str.replace(\n                f\"/{py_file.stem}\", f\"/{py_file.stem.replace('_an', '', 1)}\"\n            )\n            if counterpart in all_docs_content:\n                counterpart_found = True\n        else:\n            # This is a non-_an file, check if there's an _an version referenced\n            # Insert _an before any version suffix or at the end of the stem\n            stem = py_file.stem\n            for suffix in (\"_py39\", \"_py310\"):\n                if suffix in stem:\n                    an_stem = stem.replace(suffix, f\"_an{suffix}\", 1)\n                    break\n            else:\n                an_stem = f\"{stem}_an\"\n            counterpart = full_path_str.replace(f\"/{stem}.\", f\"/{an_stem}.\")\n            if counterpart in all_docs_content:\n                counterpart_found = True\n        # Also check directory-level _an counterparts\n        if not counterpart_found:\n            parent_name = py_file.parent.name\n            if \"_an\" in parent_name:\n                counterpart_parent = parent_name.replace(\"_an\", \"\", 1)\n                counterpart_dir = str(py_file).replace(\n                    f\"/{parent_name}/\", f\"/{counterpart_parent}/\"\n                )\n                if counterpart_dir in all_docs_content:\n                    counterpart_found = True\n            else:\n                # Try inserting _an into parent directory name\n                for suffix in (\"_py39\", \"_py310\"):\n                    if suffix in parent_name:\n                        an_parent = parent_name.replace(suffix, f\"_an{suffix}\", 1)\n                        break\n                else:\n                    an_parent = f\"{parent_name}_an\"\n                counterpart_dir = str(py_file).replace(\n                    f\"/{parent_name}/\", f\"/{an_parent}/\"\n                )\n                if counterpart_dir in all_docs_content:\n                    counterpart_found = True\n        if counterpart_found:\n            continue\n        logging.info(f\"Removing unused file: {py_file}\")\n        py_file.unlink()\n        removed += 1\n    # Clean up directories that are empty or only contain __init__.py / __pycache__\n    for dir_path in sorted(docs_src_path.rglob(\"*\"), reverse=True):\n        if not dir_path.is_dir():\n            continue\n        remaining = [\n            f\n            for f in dir_path.iterdir()\n            if f.name != \"__pycache__\" and f.name != \"__init__.py\"\n        ]\n        if not remaining:\n            logging.info(f\"Removing empty/init-only directory: {dir_path}\")\n            shutil.rmtree(dir_path)\n    print(f\"Removed {removed} unused file(s) ✅\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "scripts/format.sh",
    "content": "#!/bin/sh -e\nset -x\nset -e\n\nruff check typer tests docs_src scripts --fix\nruff format typer tests docs_src scripts\n"
  },
  {
    "path": "scripts/get-pwsh-activate.sh",
    "content": "curl https://raw.githubusercontent.com/python/cpython/main/Lib/venv/scripts/common/Activate.ps1 -o Activate.ps1\n"
  },
  {
    "path": "scripts/lint.sh",
    "content": "#!/usr/bin/env bash\n\nset -e\nset -x\n\nmypy typer\nty check typer\nruff check typer tests docs_src scripts\nruff format typer tests docs_src scripts --check\n"
  },
  {
    "path": "scripts/mkdocs_hooks.py",
    "content": "from typing import Any\n\nfrom mkdocs.config.defaults import MkDocsConfig\nfrom mkdocs.structure.files import Files\nfrom mkdocs.structure.nav import Link, Navigation, Section\nfrom mkdocs.structure.pages import Page\n\n\ndef generate_renamed_section_items(\n    items: list[Page | Section | Link], *, config: MkDocsConfig\n) -> list[Page | Section | Link]:\n    new_items: list[Page | Section | Link] = []\n    for item in items:\n        if isinstance(item, Section):\n            new_title = item.title\n            new_children = generate_renamed_section_items(item.children, config=config)\n            first_child = new_children[0]\n            if isinstance(first_child, Page):\n                if first_child.file.src_path.endswith(\"index.md\"):\n                    # Read the source so that the title is parsed and available\n                    first_child.read_source(config=config)\n                    new_title = first_child.title or new_title\n            # Creating a new section makes it render it collapsed by default\n            # no idea why, so, let's just modify the existing one\n            # new_section = Section(title=new_title, children=new_children)\n            item.title = new_title\n            item.children = new_children\n            new_items.append(item)\n        else:\n            new_items.append(item)\n    return new_items\n\n\ndef on_nav(\n    nav: Navigation, *, config: MkDocsConfig, files: Files, **kwargs: Any\n) -> Navigation:\n    new_items = generate_renamed_section_items(nav.items, config=config)\n    return Navigation(items=new_items, pages=nav.pages)\n"
  },
  {
    "path": "scripts/test-cov-html.sh",
    "content": "#!/usr/bin/env bash\n\nset -e\nset -x\n\nbash scripts/test.sh --cov-report=html ${@}\n"
  },
  {
    "path": "scripts/test-files.sh",
    "content": "#!/usr/bin/env bash\n\nset -e\nset -x\n\n# Check copy paste errors in tutorials\nif grep -r --include \"*.md\" \"Usage: tutorial\" ./docs ; then echo \"Incorrect console demo\"; exit 1 ; fi\nif grep -r --include \"*.md\" \"python tutorial\" ./docs ; then echo \"Incorrect console demo\"; exit 1 ; fi\n"
  },
  {
    "path": "scripts/test.sh",
    "content": "#!/usr/bin/env bash\n\nset -e\nset -x\n\n# For tests, a large terminal width\nexport TERMINAL_WIDTH=3000\n# Force disable terminal for tests inside of pytest, takes precedence over GITHUB_ACTIONS env var\nexport _TYPER_FORCE_DISABLE_TERMINAL=1\n# Run autocompletion install tests in the CI\nexport _TYPER_RUN_INSTALL_COMPLETION_TESTS=1\n# It seems xdist-pytest ensures modified sys.path to import relative modules in examples keeps working\npytest --cov --cov-report=term-missing -o console_output_style=progress --showlocals --numprocesses=auto ${@}\n"
  },
  {
    "path": "tests/__init__.py",
    "content": ""
  },
  {
    "path": "tests/assets/__init__.py",
    "content": ""
  },
  {
    "path": "tests/assets/cli/__init__.py",
    "content": ""
  },
  {
    "path": "tests/assets/cli/app_other_name.py",
    "content": "import typer\n\napplication = typer.Typer()\n\n\n@application.command()\ndef callback(name: str = \"World\"):\n    typer.echo(f\"Hello {name}\")\n"
  },
  {
    "path": "tests/assets/cli/empty_script.py",
    "content": ""
  },
  {
    "path": "tests/assets/cli/extended_app_cli.py",
    "content": "import typer\n\nsub_sub_app = typer.Typer()\n\n\n@sub_sub_app.command()\ndef sub_sub_command():\n    typer.echo(\"sub_sub_command\")\n\n\nsub_app = typer.Typer()\nsub_app.add_typer(sub_sub_app, name=\"sub\")\n\n\n@sub_app.command()\ndef hello():\n    typer.echo(\"hello there\")\n\n\n@sub_app.command()\ndef bye():\n    typer.echo(\"bye bye\")\n\n\ncli = typer.Typer()\ncli.add_typer(sub_app)\n\n\n@cli.command()\ndef top():\n    typer.echo(\"top\")\n"
  },
  {
    "path": "tests/assets/cli/extended_empty_app_cli.py",
    "content": "import typer\n\ncli = typer.Typer()\nsub_app = typer.Typer()\ncli.add_typer(sub_app)\n\n\n@sub_app.command()\ndef hello():\n    typer.echo(\"hello there\")\n\n\n@sub_app.command()\ndef bye():\n    typer.echo(\"bye bye\")\n"
  },
  {
    "path": "tests/assets/cli/func_other_name.py",
    "content": "def some_function(name: str = \"World\"):\n    print(f\"Hello {name}\")\n"
  },
  {
    "path": "tests/assets/cli/multi_app.py",
    "content": "import typer\n\nsub_app = typer.Typer()\n\nvariable = \"Some text\"\n\n\n@sub_app.command()\ndef hello(name: str = \"World\", age: int = typer.Option(0, help=\"The age of the user\")):\n    \"\"\"\n    Say Hello\n    \"\"\"\n    typer.echo(f\"Hello {name}\")\n\n\n@sub_app.command()\ndef hi(user: str = typer.Argument(\"World\", help=\"The name of the user to greet\")):\n    \"\"\"\n    Say Hi\n    \"\"\"\n\n\n@sub_app.command()\ndef bye():\n    \"\"\"\n    Say bye\n    \"\"\"\n    typer.echo(\"sub bye\")\n\n\napp = typer.Typer(help=\"Demo App\", epilog=\"The end\")\napp.add_typer(sub_app, name=\"sub\")\n\n\n@app.command()\ndef top():\n    \"\"\"\n    Top command\n    \"\"\"\n    typer.echo(\"top\")\n"
  },
  {
    "path": "tests/assets/cli/multi_app_cli.py",
    "content": "import typer\n\nsub_app = typer.Typer()\n\n\n@sub_app.command()\ndef hello():\n    typer.echo(\"sub hello\")\n\n\n@sub_app.command()\ndef bye():\n    typer.echo(\"sub bye\")\n\n\ncli = typer.Typer()\ncli.add_typer(sub_app, name=\"sub\")\n\n\n@cli.command()\ndef top():\n    typer.echo(\"top\")\n"
  },
  {
    "path": "tests/assets/cli/multi_app_norich.py",
    "content": "import typer\n\nsub_app = typer.Typer()\n\nvariable = \"Some text\"\n\n\n@sub_app.command()\ndef hello(name: str = \"World\", age: int = typer.Option(0, help=\"The age of the user\")):\n    \"\"\"\n    Say Hello\n    \"\"\"\n\n\n@sub_app.command()\ndef hi(user: str = typer.Argument(\"World\", help=\"The name of the user to greet\")):\n    \"\"\"\n    Say Hi\n    \"\"\"\n\n\n@sub_app.command()\ndef bye():\n    \"\"\"\n    Say bye\n    \"\"\"\n\n\napp = typer.Typer(help=\"Demo App\", epilog=\"The end\", rich_markup_mode=None)\napp.add_typer(sub_app, name=\"sub\")\n\n\n@app.command()\ndef top():\n    \"\"\"\n    Top command\n    \"\"\"\n"
  },
  {
    "path": "tests/assets/cli/multi_func.py",
    "content": "message = \"Stuff\"\n\n\ndef say_stuff():\n    print(message)\n\n\ndef main(name: str = \"World\"):\n    \"\"\"\n    Say hi to someone, by default to the World.\n    \"\"\"\n    print(f\"Hello {name}\")\n"
  },
  {
    "path": "tests/assets/cli/multiapp-docs-title.md",
    "content": "# Awesome CLI\n\nDemo App\n\n**Usage**:\n\n```console\n$ multiapp [OPTIONS] COMMAND [ARGS]...\n```\n\n**Options**:\n\n* `--install-completion`: Install completion for the current shell.\n* `--show-completion`: Show completion for the current shell, to copy it or customize the installation.\n* `--help`: Show this message and exit.\n\nThe end\n\n**Commands**:\n\n* `top`: Top command\n* `sub`\n\n## `multiapp top`\n\nTop command\n\n**Usage**:\n\n```console\n$ multiapp top [OPTIONS]\n```\n\n**Options**:\n\n* `--help`: Show this message and exit.\n\n## `multiapp sub`\n\n**Usage**:\n\n```console\n$ multiapp sub [OPTIONS] COMMAND [ARGS]...\n```\n\n**Options**:\n\n* `--help`: Show this message and exit.\n\n**Commands**:\n\n* `hello`: Say Hello\n* `hi`: Say Hi\n* `bye`: Say bye\n\n### `multiapp sub hello`\n\nSay Hello\n\n**Usage**:\n\n```console\n$ multiapp sub hello [OPTIONS]\n```\n\n**Options**:\n\n* `--name TEXT`: [default: World]\n* `--age INTEGER`: The age of the user  [default: 0]\n* `--help`: Show this message and exit.\n\n### `multiapp sub hi`\n\nSay Hi\n\n**Usage**:\n\n```console\n$ multiapp sub hi [OPTIONS] [USER]\n```\n\n**Arguments**:\n\n* `[USER]`: The name of the user to greet  [default: World]\n\n**Options**:\n\n* `--help`: Show this message and exit.\n\n### `multiapp sub bye`\n\nSay bye\n\n**Usage**:\n\n```console\n$ multiapp sub bye [OPTIONS]\n```\n\n**Options**:\n\n* `--help`: Show this message and exit.\n"
  },
  {
    "path": "tests/assets/cli/multiapp-docs.md",
    "content": "# `multiapp`\n\nDemo App\n\n**Usage**:\n\n```console\n$ multiapp [OPTIONS] COMMAND [ARGS]...\n```\n\n**Options**:\n\n* `--install-completion`: Install completion for the current shell.\n* `--show-completion`: Show completion for the current shell, to copy it or customize the installation.\n* `--help`: Show this message and exit.\n\nThe end\n\n**Commands**:\n\n* `top`: Top command\n* `sub`\n\n## `multiapp top`\n\nTop command\n\n**Usage**:\n\n```console\n$ multiapp top [OPTIONS]\n```\n\n**Options**:\n\n* `--help`: Show this message and exit.\n\n## `multiapp sub`\n\n**Usage**:\n\n```console\n$ multiapp sub [OPTIONS] COMMAND [ARGS]...\n```\n\n**Options**:\n\n* `--help`: Show this message and exit.\n\n**Commands**:\n\n* `hello`: Say Hello\n* `hi`: Say Hi\n* `bye`: Say bye\n\n### `multiapp sub hello`\n\nSay Hello\n\n**Usage**:\n\n```console\n$ multiapp sub hello [OPTIONS]\n```\n\n**Options**:\n\n* `--name TEXT`: [default: World]\n* `--age INTEGER`: The age of the user  [default: 0]\n* `--help`: Show this message and exit.\n\n### `multiapp sub hi`\n\nSay Hi\n\n**Usage**:\n\n```console\n$ multiapp sub hi [OPTIONS] [USER]\n```\n\n**Arguments**:\n\n* `[USER]`: The name of the user to greet  [default: World]\n\n**Options**:\n\n* `--help`: Show this message and exit.\n\n### `multiapp sub bye`\n\nSay bye\n\n**Usage**:\n\n```console\n$ multiapp sub bye [OPTIONS]\n```\n\n**Options**:\n\n* `--help`: Show this message and exit.\n"
  },
  {
    "path": "tests/assets/cli/not_python.txt",
    "content": "This is not Python\n"
  },
  {
    "path": "tests/assets/cli/rich_formatted_app.py",
    "content": "from typing import Annotated\n\nimport typer\n\napp = typer.Typer(rich_markup_mode=\"rich\")\n\n\n@app.command(help=\"Say [bold red]hello[/bold red] to the user.\")\ndef hello(\n    user_1: Annotated[\n        str,\n        typer.Argument(help=\"The [bold]cool[/bold] name of the [green]user[/green]\"),\n    ],\n    user_2: Annotated[str, typer.Argument(help=\"The world\")] = \"The World\",\n    force: Annotated[\n        bool, typer.Option(help=\"Force the welcome [red]message[/red]\")\n    ] = False,\n):\n    print(f\"Hello {user_1} and {user_2}\")  # pragma: no cover\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "tests/assets/cli/richformattedapp-docs.md",
    "content": "# Awesome CLI\n\nSay <span style=\"color: #800000; text-decoration-color: #800000; font-weight: bold\">hello</span> to the user.\n\n**Usage**:\n\n```console\n$ hello [OPTIONS] USER_1 [USER_2]\n```\n\n**Arguments**:\n\n* `USER_1`: The <span style=\"font-weight: bold\">cool</span> name of the <span style=\"color: #008000; text-decoration-color: #008000\">user</span>  [required]\n* `[USER_2]`: The world  [default: The World]\n\n**Options**:\n\n* `--force / --no-force`: Force the welcome <span style=\"color: #800000; text-decoration-color: #800000\">message</span>  [default: no-force]\n* `--install-completion`: Install completion for the current shell.\n* `--show-completion`: Show completion for the current shell, to copy it or customize the installation.\n* `--help`: Show this message and exit.\n"
  },
  {
    "path": "tests/assets/cli/sample.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef hello(name: str = \"World\", formal: bool = False):\n    \"\"\"\n    Say hi\n    \"\"\"\n    if formal:\n        typer.echo(f\"Good morning Ms. {name}\")\n    else:\n        typer.echo(f\"Hello {name}!\")\n\n\n@app.command()\ndef bye(friend: bool = False):\n    \"\"\"\n    Say bye\n    \"\"\"\n    if friend:\n        typer.echo(\"Goodbye my friend\")\n    else:\n        typer.echo(\"Goodbye\")\n"
  },
  {
    "path": "tests/assets/completion_argument.py",
    "content": "import click\nimport typer\n\napp = typer.Typer()\n\n\ndef shell_complete(ctx: click.Context, param: click.Parameter, incomplete: str):\n    typer.echo(f\"ctx: {ctx.info_name}\", err=True)\n    typer.echo(f\"arg is: {param.name}\", err=True)\n    typer.echo(f\"incomplete is: {incomplete}\", err=True)\n    return [\"Emma\"]\n\n\n@app.command(context_settings={\"auto_envvar_prefix\": \"TEST\"})\ndef main(name: str = typer.Argument(shell_complete=shell_complete)):\n    \"\"\"\n    Say hello.\n    \"\"\"\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "tests/assets/completion_no_types.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\ndef complete(ctx, args, incomplete):\n    typer.echo(f\"info name is: {ctx.info_name}\", err=True)\n    typer.echo(f\"args is: {args}\", err=True)\n    typer.echo(f\"incomplete is: {incomplete}\", err=True)\n    return [\n        (\"Camila\", \"The reader of books.\"),\n        (\"Carlos\", \"The writer of scripts.\"),\n        (\"Sebastian\", \"The type hints guy.\"),\n    ]\n\n\n@app.command()\ndef main(name: str = typer.Option(\"World\", autocompletion=complete)):\n    print(f\"Hello {name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "tests/assets/completion_no_types_order.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\ndef complete(args, incomplete, ctx):\n    typer.echo(f\"info name is: {ctx.info_name}\", err=True)\n    typer.echo(f\"args is: {args}\", err=True)\n    typer.echo(f\"incomplete is: {incomplete}\", err=True)\n    return [\n        (\"Camila\", \"The reader of books.\"),\n        (\"Carlos\", \"The writer of scripts.\"),\n        (\"Sebastian\", \"The type hints guy.\"),\n    ]\n\n\n@app.command()\ndef main(name: str = typer.Option(\"World\", autocompletion=complete)):\n    print(f\"Hello {name}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "tests/assets/corner_cases.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command(context_settings={\"auto_envvar_prefix\": \"TEST\"})\ndef main(\n    name: str = typer.Option(\"John\", hidden=True),\n    lastname: str = typer.Option(\"Doe\", \"/lastname\", show_default=\"Mr. Doe\"),\n    age: int = typer.Option(lambda: 42, show_default=True),\n):\n    \"\"\"\n    Say hello.\n    \"\"\"\n    print(f\"Hello {name} {lastname}, it seems you have {age}\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "tests/assets/print_modules.py",
    "content": "import sys\n\nimport typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main():\n    for m in sys.modules:\n        print(m)\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "tests/assets/prog_name.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(i: int):  # pragma: no cover\n    pass\n\n\nif __name__ == \"__main__\":\n    app(prog_name=\"custom-name\")\n"
  },
  {
    "path": "tests/assets/type_error_no_rich.py",
    "content": "import typer\nimport typer.main\n\ntyper.main.HAS_RICH = False\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(name: str = \"morty\"):\n    print(name + 3)\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "tests/assets/type_error_no_rich_short_disable.py",
    "content": "import typer\nimport typer.main\n\ntyper.main.HAS_RICH = False\n\n\napp = typer.Typer(pretty_exceptions_short=False)\n\n\n@app.command()\ndef main(name: str = \"morty\"):\n    print(name + 3)\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "tests/assets/type_error_normal_traceback.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef main(name: str = \"morty\"):\n    print(name)\n\n\nbroken_app = typer.Typer()\n\n\n@broken_app.command()\ndef broken(name: str = \"morty\"):\n    print(name + 3)\n\n\nif __name__ == \"__main__\":\n    app(standalone_mode=False)\n\n    typer.main.get_command(broken_app)()\n"
  },
  {
    "path": "tests/test_ambiguous_params.py",
    "content": "from typing import Annotated\n\nimport pytest\nimport typer\nfrom typer.testing import CliRunner\nfrom typer.utils import (\n    AnnotatedParamWithDefaultValueError,\n    DefaultFactoryAndDefaultValueError,\n    MixedAnnotatedAndDefaultStyleError,\n    MultipleTyperAnnotationsError,\n    _split_annotation_from_typer_annotations,\n)\n\nrunner = CliRunner()\n\n\ndef test_split_annotations_from_typer_annotations_simple():\n    # Simple sanity check that this utility works. If this isn't working on a given\n    # python version, then no other tests for Annotated will work.\n    given = Annotated[str, typer.Argument()]\n    base, typer_annotations = _split_annotation_from_typer_annotations(given)\n    assert base is str\n    # No equality check on the param types. Checking the length is sufficient.\n    assert len(typer_annotations) == 1\n\n\ndef test_forbid_default_value_in_annotated_argument():\n    app = typer.Typer()\n\n    # This test case only works with `typer.Argument`. `typer.Option` uses positionals\n    # for param_decls too.\n    @app.command()\n    def cmd(my_param: Annotated[str, typer.Argument(\"foo\")]): ...  # pragma: no cover\n\n    with pytest.raises(AnnotatedParamWithDefaultValueError) as excinfo:\n        runner.invoke(app)\n\n    assert vars(excinfo.value) == {\n        \"param_type\": typer.models.ArgumentInfo,\n        \"argument_name\": \"my_param\",\n    }\n\n\ndef test_allow_options_to_have_names():\n    app = typer.Typer()\n\n    @app.command()\n    def cmd(my_param: Annotated[str, typer.Option(\"--some-opt\")]):\n        print(my_param)\n\n    result = runner.invoke(app, [\"--some-opt\", \"hello\"])\n    assert result.exit_code == 0, result.output\n    assert \"hello\" in result.output\n\n\n@pytest.mark.parametrize(\n    [\"param\", \"param_info_type\"],\n    [\n        (typer.Argument, typer.models.ArgumentInfo),\n        (typer.Option, typer.models.OptionInfo),\n    ],\n)\ndef test_forbid_annotated_param_and_default_param(param, param_info_type):\n    app = typer.Typer()\n\n    @app.command()\n    def cmd(my_param: Annotated[str, param()] = param(\"foo\")): ...  # pragma: no cover\n\n    with pytest.raises(MixedAnnotatedAndDefaultStyleError) as excinfo:\n        runner.invoke(app)\n\n    assert vars(excinfo.value) == {\n        \"argument_name\": \"my_param\",\n        \"annotated_param_type\": param_info_type,\n        \"default_param_type\": param_info_type,\n    }\n\n\ndef test_forbid_multiple_typer_params_in_annotated():\n    app = typer.Typer()\n\n    @app.command()\n    def cmd(\n        my_param: Annotated[str, typer.Argument(), typer.Argument()],\n    ): ...  # pragma: no cover\n\n    with pytest.raises(MultipleTyperAnnotationsError) as excinfo:\n        runner.invoke(app)\n\n    assert vars(excinfo.value) == {\"argument_name\": \"my_param\"}\n\n\ndef test_allow_multiple_non_typer_params_in_annotated():\n    app = typer.Typer()\n\n    @app.command()\n    def cmd(my_param: Annotated[str, \"someval\", typer.Argument(), 4] = \"hello\"):\n        print(my_param)\n\n    result = runner.invoke(app)\n    # Should behave like normal\n    assert result.exit_code == 0, result.output\n    assert \"hello\" in result.output\n\n\n@pytest.mark.parametrize(\n    [\"param\", \"param_info_type\"],\n    [\n        (typer.Argument, typer.models.ArgumentInfo),\n        (typer.Option, typer.models.OptionInfo),\n    ],\n)\ndef test_forbid_default_factory_and_default_value_in_annotated(param, param_info_type):\n    def make_string():\n        return \"foo\"  # pragma: no cover\n\n    app = typer.Typer()\n\n    @app.command()\n    def cmd(\n        my_param: Annotated[str, param(default_factory=make_string)] = \"hello\",\n    ): ...  # pragma: no cover\n\n    with pytest.raises(DefaultFactoryAndDefaultValueError) as excinfo:\n        runner.invoke(app)\n\n    assert vars(excinfo.value) == {\n        \"argument_name\": \"my_param\",\n        \"param_type\": param_info_type,\n    }\n\n\n@pytest.mark.parametrize(\n    \"param\",\n    [\n        typer.Argument,\n        typer.Option,\n    ],\n)\ndef test_allow_default_factory_with_default_param(param):\n    def make_string():\n        return \"foo\"\n\n    app = typer.Typer()\n\n    @app.command()\n    def cmd(my_param: str = param(default_factory=make_string)):\n        print(my_param)\n\n    result = runner.invoke(app)\n    assert result.exit_code == 0, result.output\n    assert \"foo\" in result.output\n\n\n@pytest.mark.parametrize(\n    [\"param\", \"param_info_type\"],\n    [\n        (typer.Argument, typer.models.ArgumentInfo),\n        (typer.Option, typer.models.OptionInfo),\n    ],\n)\ndef test_forbid_default_and_default_factory_with_default_param(param, param_info_type):\n    def make_string():\n        return \"foo\"  # pragma: no cover\n\n    app = typer.Typer()\n\n    @app.command()\n    def cmd(\n        my_param: str = param(\"hi\", default_factory=make_string),\n    ): ...  # pragma: no cover\n\n    with pytest.raises(DefaultFactoryAndDefaultValueError) as excinfo:\n        runner.invoke(app)\n\n    assert vars(excinfo.value) == {\n        \"argument_name\": \"my_param\",\n        \"param_type\": param_info_type,\n    }\n\n\n@pytest.mark.parametrize(\n    [\"error\", \"message\"],\n    [\n        (\n            AnnotatedParamWithDefaultValueError(\n                argument_name=\"my_argument\",\n                param_type=typer.models.ArgumentInfo,\n            ),\n            \"`Argument` default value cannot be set in `Annotated` for 'my_argument'. Set the default value with `=` instead.\",\n        ),\n        (\n            MixedAnnotatedAndDefaultStyleError(\n                argument_name=\"my_argument\",\n                annotated_param_type=typer.models.OptionInfo,\n                default_param_type=typer.models.ArgumentInfo,\n            ),\n            \"Cannot specify `Option` in `Annotated` and `Argument` as a default value together for 'my_argument'\",\n        ),\n        (\n            MixedAnnotatedAndDefaultStyleError(\n                argument_name=\"my_argument\",\n                annotated_param_type=typer.models.OptionInfo,\n                default_param_type=typer.models.OptionInfo,\n            ),\n            \"Cannot specify `Option` in `Annotated` and default value together for 'my_argument'\",\n        ),\n        (\n            MixedAnnotatedAndDefaultStyleError(\n                argument_name=\"my_argument\",\n                annotated_param_type=typer.models.ArgumentInfo,\n                default_param_type=typer.models.ArgumentInfo,\n            ),\n            \"Cannot specify `Argument` in `Annotated` and default value together for 'my_argument'\",\n        ),\n        (\n            MultipleTyperAnnotationsError(\n                argument_name=\"my_argument\",\n            ),\n            \"Cannot specify multiple `Annotated` Typer arguments for 'my_argument'\",\n        ),\n        (\n            DefaultFactoryAndDefaultValueError(\n                argument_name=\"my_argument\",\n                param_type=typer.models.OptionInfo,\n            ),\n            \"Cannot specify `default_factory` and a default value together for `Option`\",\n        ),\n    ],\n)\ndef test_error_rendering(error, message):\n    assert str(error) == message\n"
  },
  {
    "path": "tests/test_annotated.py",
    "content": "import sys\nfrom pathlib import Path\nfrom typing import Annotated\n\nimport typer\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\n\n\ndef test_annotated_argument_with_default():\n    app = typer.Typer()\n\n    @app.command()\n    def cmd(val: Annotated[int, typer.Argument()] = 0):\n        print(f\"hello {val}\")\n\n    result = runner.invoke(app)\n    assert result.exit_code == 0, result.output\n    assert \"hello 0\" in result.output\n\n    result = runner.invoke(app, [\"42\"])\n    assert result.exit_code == 0, result.output\n    assert \"hello 42\" in result.output\n\n\ndef test_annotated_argument_in_string_type_with_default():\n    app = typer.Typer()\n\n    @app.command()\n    def cmd(val: \"Annotated[int, typer.Argument()]\" = 0):\n        print(f\"hello {val}\")\n\n    result = runner.invoke(app)\n    assert result.exit_code == 0, result.output\n    assert \"hello 0\" in result.output\n\n    result = runner.invoke(app, [\"42\"])\n    assert result.exit_code == 0, result.output\n    assert \"hello 42\" in result.output\n\n\ndef test_annotated_argument_with_default_factory():\n    app = typer.Typer()\n\n    def make_string():\n        return \"I made it\"\n\n    @app.command()\n    def cmd(val: Annotated[str, typer.Argument(default_factory=make_string)]):\n        print(val)\n\n    result = runner.invoke(app)\n    assert result.exit_code == 0, result.output\n    assert \"I made it\" in result.output\n\n    result = runner.invoke(app, [\"overridden\"])\n    assert result.exit_code == 0, result.output\n    assert \"overridden\" in result.output\n\n\ndef test_annotated_option_with_argname_doesnt_mutate_multiple_calls():\n    app = typer.Typer()\n\n    @app.command()\n    def cmd(force: Annotated[bool, typer.Option(\"--force\")] = False):\n        if force:\n            print(\"Forcing operation\")\n        else:\n            print(\"Not forcing\")\n\n    result = runner.invoke(app)\n    assert result.exit_code == 0, result.output\n    assert \"Not forcing\" in result.output\n\n    result = runner.invoke(app, [\"--force\"])\n    assert result.exit_code == 0, result.output\n    assert \"Forcing operation\" in result.output\n\n\ndef test_annotated_custom_path():\n    app = typer.Typer()\n\n    class CustomPath(Path):\n        # Subclassing Path was not fully supported before 3.12\n        # https://docs.python.org/3.12/whatsnew/3.12.html\n        if sys.version_info < (3, 12):\n            _flavour = type(Path())._flavour\n\n    @app.command()\n    def custom_parser(\n        my_path: Annotated[CustomPath, typer.Argument(parser=CustomPath)],\n    ):\n        assert isinstance(my_path, CustomPath)\n\n    result = runner.invoke(app, \"/some/quirky/path/implementation\")\n    assert result.exit_code == 0\n"
  },
  {
    "path": "tests/test_callback_warning.py",
    "content": "import pytest\nimport typer\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\n\n\ndef test_warns_when_callback_is_not_supported():\n    app = typer.Typer()\n\n    sub_app = typer.Typer()\n\n    @sub_app.callback()\n    def callback():\n        \"\"\"This help text is not used.\"\"\"\n\n    app.add_typer(sub_app)\n\n    with pytest.warns(\n        match=\"The 'callback' parameter is not supported by Typer when using `add_typer` without a name\"\n    ):\n        try:\n            app()\n        except SystemExit:\n            pass\n\n\ndef test_warns_when_callback_is_not_supported_added_after_add_typer():\n    app = typer.Typer()\n\n    sub_app = typer.Typer()\n    app.add_typer(sub_app)\n\n    @sub_app.callback()\n    def callback():\n        \"\"\"This help text is not used.\"\"\"\n\n    with pytest.warns(\n        match=\"The 'callback' parameter is not supported by Typer when using `add_typer` without a name\"\n    ):\n        try:\n            app()\n        except SystemExit:\n            pass\n"
  },
  {
    "path": "tests/test_cli/__init__.py",
    "content": ""
  },
  {
    "path": "tests/test_cli/test_app_other_name.py",
    "content": "import subprocess\nimport sys\n\n\ndef test_script_help():\n    result = subprocess.run(\n        [\n            sys.executable,\n            \"-m\",\n            \"coverage\",\n            \"run\",\n            \"-m\",\n            \"typer\",\n            \"tests/assets/cli/app_other_name.py\",\n            \"run\",\n            \"--help\",\n        ],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"--name\" in result.stdout\n\n\ndef test_script():\n    result = subprocess.run(\n        [\n            sys.executable,\n            \"-m\",\n            \"coverage\",\n            \"run\",\n            \"-m\",\n            \"typer\",\n            \"tests/assets/cli/app_other_name.py\",\n            \"run\",\n            \"--name\",\n            \"Camila\",\n        ],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Hello Camila\" in result.stdout\n"
  },
  {
    "path": "tests/test_cli/test_completion_run.py",
    "content": "import os\nimport subprocess\nimport sys\n\n\ndef test_script_completion_run():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", \"-m\", \"typer\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={\n            **os.environ,\n            \"___MAIN__.PY_COMPLETE\": \"complete_bash\",\n            \"_PYTHON _M TYPER_COMPLETE\": \"complete_bash\",\n            \"COMP_WORDS\": \"typer tests/assets/cli/sample.py\",\n            \"COMP_CWORD\": \"2\",\n        },\n    )\n    assert \"run\" in result.stdout\n"
  },
  {
    "path": "tests/test_cli/test_doc.py",
    "content": "import os\nimport subprocess\nimport sys\nfrom pathlib import Path\n\n\ndef test_doc():\n    result = subprocess.run(\n        [\n            sys.executable,\n            \"-m\",\n            \"coverage\",\n            \"run\",\n            \"-m\",\n            \"typer\",\n            \"tests.assets.cli.multi_app\",\n            \"utils\",\n            \"docs\",\n            \"--name\",\n            \"multiapp\",\n        ],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    docs_path: Path = Path(__file__).parent.parent / \"assets/cli/multiapp-docs.md\"\n    docs = docs_path.read_text()\n    assert docs in result.stdout\n    assert \"**Arguments**\" in result.stdout\n\n\ndef test_doc_output(tmp_path: Path):\n    out_file: Path = tmp_path / \"out.md\"\n    result = subprocess.run(\n        [\n            sys.executable,\n            \"-m\",\n            \"coverage\",\n            \"run\",\n            \"-m\",\n            \"typer\",\n            \"tests.assets.cli.multi_app\",\n            \"utils\",\n            \"docs\",\n            \"--name\",\n            \"multiapp\",\n            \"--output\",\n            str(out_file),\n        ],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    docs_path: Path = Path(__file__).parent.parent / \"assets/cli/multiapp-docs.md\"\n    docs = docs_path.read_text()\n    written_docs = out_file.read_text()\n    assert docs in written_docs\n    assert \"Docs saved to:\" in result.stdout\n\n\ndef test_doc_title_output(tmp_path: Path):\n    out_file: Path = tmp_path / \"out.md\"\n    result = subprocess.run(\n        [\n            sys.executable,\n            \"-m\",\n            \"coverage\",\n            \"run\",\n            \"-m\",\n            \"typer\",\n            \"tests.assets.cli.multi_app\",\n            \"utils\",\n            \"docs\",\n            \"--name\",\n            \"multiapp\",\n            \"--title\",\n            \"Awesome CLI\",\n            \"--output\",\n            str(out_file),\n        ],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    docs_path: Path = Path(__file__).parent.parent / \"assets/cli/multiapp-docs-title.md\"\n    docs = docs_path.read_text()\n    written_docs = out_file.read_text()\n    assert docs in written_docs\n    assert \"Docs saved to:\" in result.stdout\n\n\ndef test_doc_no_rich():\n    result = subprocess.run(\n        [\n            sys.executable,\n            \"-m\",\n            \"coverage\",\n            \"run\",\n            \"-m\",\n            \"typer\",\n            \"tests.assets.cli.multi_app_norich\",\n            \"utils\",\n            \"docs\",\n            \"--name\",\n            \"multiapp\",\n        ],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    docs_path: Path = Path(__file__).parent.parent / \"assets/cli/multiapp-docs.md\"\n    docs = docs_path.read_text()\n    assert docs in result.stdout\n    assert \"**Arguments**\" in result.stdout\n\n\ndef test_doc_not_existing():\n    result = subprocess.run(\n        [\n            sys.executable,\n            \"-m\",\n            \"coverage\",\n            \"run\",\n            \"-m\",\n            \"typer\",\n            \"no_typer\",\n            \"utils\",\n            \"docs\",\n        ],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Could not import as Python module:\" in result.stderr\n\n\ndef test_doc_no_typer():\n    result = subprocess.run(\n        [\n            sys.executable,\n            \"-m\",\n            \"coverage\",\n            \"run\",\n            \"-m\",\n            \"typer\",\n            \"tests/assets/cli/empty_script.py\",\n            \"utils\",\n            \"docs\",\n        ],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"No Typer app found\" in result.stderr\n\n\ndef test_doc_file_not_existing():\n    result = subprocess.run(\n        [\n            sys.executable,\n            \"-m\",\n            \"coverage\",\n            \"run\",\n            \"-m\",\n            \"typer\",\n            \"assets/cli/not_existing.py\",\n            \"utils\",\n            \"docs\",\n        ],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Not a valid file or Python module:\" in result.stderr\n\n\ndef test_doc_html_output(tmp_path: Path):\n    out_file: Path = tmp_path / \"out.md\"\n    result = subprocess.run(\n        [\n            sys.executable,\n            \"-m\",\n            \"coverage\",\n            \"run\",\n            \"-m\",\n            \"typer\",\n            \"tests.assets.cli.rich_formatted_app\",\n            \"utils\",\n            \"docs\",\n            \"--title\",\n            \"Awesome CLI\",\n            \"--output\",\n            str(out_file),\n        ],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={**os.environ, \"PYTHONIOENCODING\": \"utf-8\"},\n    )\n    docs_path: Path = (\n        Path(__file__).parent.parent / \"assets\" / \"cli\" / \"richformattedapp-docs.md\"\n    )\n    docs = docs_path.read_text(encoding=\"utf-8\")\n    written_docs = out_file.read_text(encoding=\"utf-8\")\n    assert docs in written_docs\n    assert \"Docs saved to:\" in result.stdout\n"
  },
  {
    "path": "tests/test_cli/test_empty_script.py",
    "content": "import subprocess\nimport sys\n\n\ndef test_script_help():\n    result = subprocess.run(\n        [\n            sys.executable,\n            \"-m\",\n            \"coverage\",\n            \"run\",\n            \"-m\",\n            \"typer\",\n            \"tests/assets/cli/empty_script.py\",\n            \"--help\",\n        ],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"run\" not in result.stdout\n"
  },
  {
    "path": "tests/test_cli/test_extending_app.py",
    "content": "import subprocess\nimport sys\n\n\ndef test_script_help():\n    result = subprocess.run(\n        [\n            sys.executable,\n            \"-m\",\n            \"coverage\",\n            \"run\",\n            \"-m\",\n            \"typer\",\n            \"tests/assets/cli/extended_app_cli.py\",\n            \"run\",\n            \"--help\",\n        ],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"top\" in result.stdout\n    assert \"hello\" in result.stdout\n    assert \"sub\" in result.stdout\n\n\ndef test_script_top():\n    result = subprocess.run(\n        [\n            sys.executable,\n            \"-m\",\n            \"coverage\",\n            \"run\",\n            \"-m\",\n            \"typer\",\n            \"tests/assets/cli/extended_app_cli.py\",\n            \"run\",\n            \"top\",\n        ],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"top\" in result.stdout\n\n\ndef test_script_hello():\n    result = subprocess.run(\n        [\n            sys.executable,\n            \"-m\",\n            \"coverage\",\n            \"run\",\n            \"-m\",\n            \"typer\",\n            \"tests/assets/cli/extended_app_cli.py\",\n            \"run\",\n            \"hello\",\n        ],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"hello there\" in result.stdout\n\n\ndef test_script_bye():\n    result = subprocess.run(\n        [\n            sys.executable,\n            \"-m\",\n            \"coverage\",\n            \"run\",\n            \"-m\",\n            \"typer\",\n            \"tests/assets/cli/extended_app_cli.py\",\n            \"run\",\n            \"bye\",\n        ],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"bye\" in result.stdout\n\n\ndef test_script_sub_command_help():\n    result = subprocess.run(\n        [\n            sys.executable,\n            \"-m\",\n            \"coverage\",\n            \"run\",\n            \"-m\",\n            \"typer\",\n            \"tests/assets/cli/extended_app_cli.py\",\n            \"run\",\n            \"sub\",\n            \"--help\",\n        ],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"sub-sub-command\" in result.stdout\n\n\ndef test_script_sub_sub_command():\n    result = subprocess.run(\n        [\n            sys.executable,\n            \"-m\",\n            \"coverage\",\n            \"run\",\n            \"-m\",\n            \"typer\",\n            \"tests/assets/cli/extended_app_cli.py\",\n            \"run\",\n            \"sub\",\n            \"sub-sub-command\",\n        ],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"sub_sub_command\" in result.stdout\n"
  },
  {
    "path": "tests/test_cli/test_extending_empty_app.py",
    "content": "import subprocess\nimport sys\n\n\ndef test_script_help():\n    result = subprocess.run(\n        [\n            sys.executable,\n            \"-m\",\n            \"coverage\",\n            \"run\",\n            \"-m\",\n            \"typer\",\n            \"tests/assets/cli/extended_empty_app_cli.py\",\n            \"run\",\n            \"--help\",\n        ],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"hello\" in result.stdout\n\n\ndef test_script_hello():\n    result = subprocess.run(\n        [\n            sys.executable,\n            \"-m\",\n            \"coverage\",\n            \"run\",\n            \"-m\",\n            \"typer\",\n            \"tests/assets/cli/extended_empty_app_cli.py\",\n            \"run\",\n            \"hello\",\n        ],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"hello there\" in result.stdout\n\n\ndef test_script_bye():\n    result = subprocess.run(\n        [\n            sys.executable,\n            \"-m\",\n            \"coverage\",\n            \"run\",\n            \"-m\",\n            \"typer\",\n            \"tests/assets/cli/extended_empty_app_cli.py\",\n            \"run\",\n            \"bye\",\n        ],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"bye\" in result.stdout\n"
  },
  {
    "path": "tests/test_cli/test_func_other_name.py",
    "content": "import subprocess\nimport sys\n\n\ndef test_script():\n    result = subprocess.run(\n        [\n            sys.executable,\n            \"-m\",\n            \"coverage\",\n            \"run\",\n            \"-m\",\n            \"typer\",\n            \"tests/assets/cli/func_other_name.py\",\n            \"run\",\n            \"--name\",\n            \"Camila\",\n        ],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Hello Camila\" in result.stdout\n"
  },
  {
    "path": "tests/test_cli/test_help.py",
    "content": "import subprocess\nimport sys\n\n\ndef test_script_help():\n    result = subprocess.run(\n        [\n            sys.executable,\n            \"-m\",\n            \"coverage\",\n            \"run\",\n            \"-m\",\n            \"typer\",\n            \"tests/assets/cli/sample.py\",\n            \"--help\",\n        ],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"run\" in result.stdout\n\n\ndef test_not_python():\n    result = subprocess.run(\n        [\n            sys.executable,\n            \"-m\",\n            \"coverage\",\n            \"run\",\n            \"-m\",\n            \"typer\",\n            \"tests/assets/cli/not_python.txt\",\n            \"run\",\n        ],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Could not import as Python file\" in result.stderr\n"
  },
  {
    "path": "tests/test_cli/test_multi_app.py",
    "content": "import subprocess\nimport sys\n\n\ndef test_script_help():\n    result = subprocess.run(\n        [\n            sys.executable,\n            \"-m\",\n            \"coverage\",\n            \"run\",\n            \"-m\",\n            \"typer\",\n            \"tests/assets/cli/multi_app.py\",\n            \"run\",\n            \"--help\",\n        ],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"sub\" in result.stdout\n    assert \"top\" in result.stdout\n\n\ndef test_script_app_non_existent():\n    result = subprocess.run(\n        [\n            sys.executable,\n            \"-m\",\n            \"coverage\",\n            \"run\",\n            \"-m\",\n            \"typer\",\n            \"--app\",\n            \"non_existent\",\n            \"tests/assets/cli/multi_app.py\",\n            \"run\",\n            \"--help\",\n        ],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Not a Typer object:\" in result.stderr\n\n\ndef test_script_sub():\n    result = subprocess.run(\n        [\n            sys.executable,\n            \"-m\",\n            \"coverage\",\n            \"run\",\n            \"-m\",\n            \"typer\",\n            \"tests/assets/cli/multi_app.py\",\n            \"run\",\n            \"sub\",\n            \"--help\",\n        ],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"bye\" in result.stdout\n    assert \"hello\" in result.stdout\n\n\ndef test_script_top():\n    result = subprocess.run(\n        [\n            sys.executable,\n            \"-m\",\n            \"coverage\",\n            \"run\",\n            \"-m\",\n            \"typer\",\n            \"tests/assets/cli/multi_app.py\",\n            \"run\",\n            \"top\",\n        ],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"top\" in result.stdout\n\n\ndef test_script_sub_hello():\n    result = subprocess.run(\n        [\n            sys.executable,\n            \"-m\",\n            \"coverage\",\n            \"run\",\n            \"-m\",\n            \"typer\",\n            \"tests/assets/cli/multi_app.py\",\n            \"run\",\n            \"sub\",\n            \"hello\",\n        ],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Hello World\" in result.stdout\n\n\ndef test_script_sub_bye():\n    result = subprocess.run(\n        [\n            sys.executable,\n            \"-m\",\n            \"coverage\",\n            \"run\",\n            \"-m\",\n            \"typer\",\n            \"tests/assets/cli/multi_app.py\",\n            \"run\",\n            \"sub\",\n            \"bye\",\n        ],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"sub bye\" in result.stdout\n"
  },
  {
    "path": "tests/test_cli/test_multi_app_cli.py",
    "content": "import subprocess\nimport sys\n\n\ndef test_script_help():\n    result = subprocess.run(\n        [\n            sys.executable,\n            \"-m\",\n            \"coverage\",\n            \"run\",\n            \"-m\",\n            \"typer\",\n            \"tests/assets/cli/multi_app_cli.py\",\n            \"run\",\n            \"--help\",\n        ],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"sub\" in result.stdout\n    assert \"top\" in result.stdout\n\n\ndef test_script_sub():\n    result = subprocess.run(\n        [\n            sys.executable,\n            \"-m\",\n            \"coverage\",\n            \"run\",\n            \"-m\",\n            \"typer\",\n            \"tests/assets/cli/multi_app_cli.py\",\n            \"run\",\n            \"sub\",\n            \"--help\",\n        ],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"bye\" in result.stdout\n    assert \"hello\" in result.stdout\n\n\ndef test_script_top():\n    result = subprocess.run(\n        [\n            sys.executable,\n            \"-m\",\n            \"coverage\",\n            \"run\",\n            \"-m\",\n            \"typer\",\n            \"tests/assets/cli/multi_app_cli.py\",\n            \"run\",\n            \"top\",\n        ],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"top\" in result.stdout\n\n\ndef test_script_sub_hello():\n    result = subprocess.run(\n        [\n            sys.executable,\n            \"-m\",\n            \"coverage\",\n            \"run\",\n            \"-m\",\n            \"typer\",\n            \"tests/assets/cli/multi_app_cli.py\",\n            \"run\",\n            \"sub\",\n            \"hello\",\n        ],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"sub hello\" in result.stdout\n\n\ndef test_script_sub_bye():\n    result = subprocess.run(\n        [\n            sys.executable,\n            \"-m\",\n            \"coverage\",\n            \"run\",\n            \"-m\",\n            \"typer\",\n            \"tests/assets/cli/multi_app_cli.py\",\n            \"run\",\n            \"sub\",\n            \"bye\",\n        ],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"sub bye\" in result.stdout\n"
  },
  {
    "path": "tests/test_cli/test_multi_app_sub.py",
    "content": "import subprocess\nimport sys\n\n\ndef test_script_help():\n    result = subprocess.run(\n        [\n            sys.executable,\n            \"-m\",\n            \"coverage\",\n            \"run\",\n            \"-m\",\n            \"typer\",\n            \"--app\",\n            \"sub_app\",\n            \"tests/assets/cli/multi_app.py\",\n            \"run\",\n            \"--help\",\n        ],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"bye\" in result.stdout\n    assert \"hello\" in result.stdout\n    assert \"top\" not in result.stdout\n\n\ndef test_script():\n    result = subprocess.run(\n        [\n            sys.executable,\n            \"-m\",\n            \"coverage\",\n            \"run\",\n            \"-m\",\n            \"typer\",\n            \"--app\",\n            \"sub_app\",\n            \"tests/assets/cli/multi_app.py\",\n            \"run\",\n            \"hello\",\n        ],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Hello World\" in result.stdout\n"
  },
  {
    "path": "tests/test_cli/test_multi_func.py",
    "content": "import subprocess\nimport sys\n\n\ndef test_help():\n    result = subprocess.run(\n        [\n            sys.executable,\n            \"-m\",\n            \"coverage\",\n            \"run\",\n            \"-m\",\n            \"typer\",\n            \"tests/assets/cli/multi_func.py\",\n            \"run\",\n            \"--help\",\n        ],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Say hi to someone, by default to the World.\" in result.stdout\n\n\ndef test_script():\n    result = subprocess.run(\n        [\n            sys.executable,\n            \"-m\",\n            \"coverage\",\n            \"run\",\n            \"-m\",\n            \"typer\",\n            \"tests/assets/cli/multi_func.py\",\n            \"run\",\n            \"--name\",\n            \"Camila\",\n        ],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Hello Camila\" in result.stdout\n\n\ndef test_script_func_non_existent():\n    result = subprocess.run(\n        [\n            sys.executable,\n            \"-m\",\n            \"coverage\",\n            \"run\",\n            \"-m\",\n            \"typer\",\n            \"--func\",\n            \"non_existent\",\n            \"tests/assets/cli/multi_func.py\",\n            \"run\",\n            \"--name\",\n            \"Camila\",\n        ],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Not a function:\" in result.stderr\n\n\ndef test_script_func_not_function():\n    result = subprocess.run(\n        [\n            sys.executable,\n            \"-m\",\n            \"coverage\",\n            \"run\",\n            \"-m\",\n            \"typer\",\n            \"--func\",\n            \"message\",\n            \"tests/assets/cli/multi_func.py\",\n            \"run\",\n            \"--name\",\n            \"Camila\",\n        ],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Not a function:\" in result.stderr\n\n\ndef test_script_func():\n    result = subprocess.run(\n        [\n            sys.executable,\n            \"-m\",\n            \"coverage\",\n            \"run\",\n            \"-m\",\n            \"typer\",\n            \"--func\",\n            \"say_stuff\",\n            \"tests/assets/cli/multi_func.py\",\n            \"run\",\n        ],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Hello\" not in result.stdout\n    assert \"Stuff\" in result.stdout\n"
  },
  {
    "path": "tests/test_cli/test_not_python.py",
    "content": "import subprocess\nimport sys\n\n\ndef test_not_python():\n    result = subprocess.run(\n        [\n            sys.executable,\n            \"-m\",\n            \"coverage\",\n            \"run\",\n            \"-m\",\n            \"typer\",\n            \"tests/assets/cli/not_python.txt\",\n            \"run\",\n        ],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Could not import as Python file\" in result.stderr\n"
  },
  {
    "path": "tests/test_cli/test_sub.py",
    "content": "import subprocess\nimport sys\n\n\ndef test_script_hello():\n    result = subprocess.run(\n        [\n            sys.executable,\n            \"-m\",\n            \"coverage\",\n            \"run\",\n            \"-m\",\n            \"typer\",\n            \"tests/assets/cli/sample.py\",\n            \"run\",\n            \"hello\",\n        ],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Hello World!\" in result.stdout\n\n\ndef test_script_hello_name():\n    result = subprocess.run(\n        [\n            sys.executable,\n            \"-m\",\n            \"coverage\",\n            \"run\",\n            \"-m\",\n            \"typer\",\n            \"tests/assets/cli/sample.py\",\n            \"run\",\n            \"hello\",\n            \"--name\",\n            \"Camila\",\n        ],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Hello Camila!\" in result.stdout\n\n\ndef test_script_hello_name_formal():\n    result = subprocess.run(\n        [\n            sys.executable,\n            \"-m\",\n            \"coverage\",\n            \"run\",\n            \"-m\",\n            \"typer\",\n            \"tests/assets/cli/sample.py\",\n            \"run\",\n            \"hello\",\n            \"--name\",\n            \"Camila\",\n            \"--formal\",\n        ],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Good morning Ms. Camila\" in result.stdout\n\n\ndef test_script_bye():\n    result = subprocess.run(\n        [\n            sys.executable,\n            \"-m\",\n            \"coverage\",\n            \"run\",\n            \"-m\",\n            \"typer\",\n            \"tests/assets/cli/sample.py\",\n            \"run\",\n            \"bye\",\n        ],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Goodbye\" in result.stdout\n\n\ndef test_script_bye_friend():\n    result = subprocess.run(\n        [\n            sys.executable,\n            \"-m\",\n            \"coverage\",\n            \"run\",\n            \"-m\",\n            \"typer\",\n            \"tests/assets/cli/sample.py\",\n            \"run\",\n            \"bye\",\n            \"--friend\",\n        ],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Goodbye my friend\" in result.stdout\n\n\ndef test_script_help():\n    result = subprocess.run(\n        [\n            sys.executable,\n            \"-m\",\n            \"coverage\",\n            \"run\",\n            \"-m\",\n            \"typer\",\n            \"tests/assets/cli/sample.py\",\n            \"--help\",\n        ],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"run\" in result.stdout\n\n\ndef test_not_python():\n    result = subprocess.run(\n        [\n            sys.executable,\n            \"-m\",\n            \"coverage\",\n            \"run\",\n            \"-m\",\n            \"typer\",\n            \"tests/assets/cli/not_python.txt\",\n            \"run\",\n        ],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Could not import as Python file\" in result.stderr\n"
  },
  {
    "path": "tests/test_cli/test_sub_completion.py",
    "content": "import os\nimport subprocess\nimport sys\n\n\ndef test_script_completion_run():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", \"-m\", \"typer\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={\n            **os.environ,\n            \"___MAIN__.PY_COMPLETE\": \"complete_bash\",\n            \"_PYTHON _M TYPER_COMPLETE\": \"complete_bash\",\n            \"COMP_WORDS\": \"typer tests/assets/cli/sample.py run hello --\",\n            \"COMP_CWORD\": \"4\",\n        },\n    )\n    assert \"--name\" in result.stdout\n"
  },
  {
    "path": "tests/test_cli/test_sub_help.py",
    "content": "import subprocess\nimport sys\n\n\ndef test_script_help():\n    result = subprocess.run(\n        [\n            sys.executable,\n            \"-m\",\n            \"coverage\",\n            \"run\",\n            \"-m\",\n            \"typer\",\n            \"tests/assets/cli/sample.py\",\n            \"run\",\n            \"--help\",\n        ],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"bye\" in result.stdout\n    assert \"Say bye\" in result.stdout\n    assert \"hello\" in result.stdout\n    assert \"Say hi\" in result.stdout\n"
  },
  {
    "path": "tests/test_cli/test_version.py",
    "content": "import subprocess\nimport sys\n\n\ndef test_script_help():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", \"-m\", \"typer\", \"--version\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Typer version:\" in result.stdout\n"
  },
  {
    "path": "tests/test_completion/__init__.py",
    "content": ""
  },
  {
    "path": "tests/test_completion/colon_example.py",
    "content": "import typer\n\nimage_desc = [\n    (\"alpine:latest\", \"latest alpine image\"),\n    (\"alpine:hello\", \"fake image: for testing\"),\n    (\"nvidia/cuda:10.0-devel-ubuntu18.04\", \"\"),\n]\n\n\ndef _complete(incomplete: str) -> str:\n    for image, desc in image_desc:\n        if image.startswith(incomplete):\n            yield image, desc\n\n\napp = typer.Typer()\n\n\n@app.command()\ndef image(name: str = typer.Option(autocompletion=_complete)):\n    typer.echo(name)\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "tests/test_completion/example_rich_tags.py",
    "content": "import typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef create(username: str):\n    \"\"\"\n    Create a [green]new[green/] user with USERNAME.\n    \"\"\"\n    print(f\"Creating user: {username}\")\n\n\n@app.command()\ndef delete(username: str):\n    \"\"\"\n    Delete a user with [bold]USERNAME[/].\n    \"\"\"\n    print(f\"Deleting user: {username}\")\n\n\n@app.command()\ndef delete_all():\n    \"\"\"\n    [red]Delete ALL users[/red] in the database.\n    \"\"\"\n    print(\"Deleting all users\")\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "tests/test_completion/path_example.py",
    "content": "from pathlib import Path\n\nimport typer\n\napp = typer.Typer()\n\n\n@app.command()\ndef f(p: Path):\n    print(p)\n\n\nif __name__ == \"__main__\":\n    app()\n"
  },
  {
    "path": "tests/test_completion/test_completion.py",
    "content": "import os\nimport subprocess\nimport sys\nfrom pathlib import Path\n\nfrom docs_src.typer_app import tutorial001_py310 as mod\n\nfrom ..utils import needs_bash, needs_linux, requires_completion_permission\n\n\n@needs_bash\n@needs_linux\ndef test_show_completion():\n    result = subprocess.run(\n        [\n            \"bash\",\n            \"-c\",\n            f\"'{sys.executable}' -m coverage run '{mod.__file__}' --show-completion\",\n        ],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={**os.environ, \"SHELL\": \"/bin/bash\", \"_TYPER_COMPLETE_TESTING\": \"True\"},\n    )\n    assert \"_TUTORIAL001_PY310.PY_COMPLETE=complete_bash\" in result.stdout\n\n\n@needs_bash\n@needs_linux\n@requires_completion_permission\ndef test_install_completion():\n    bash_completion_path: Path = Path.home() / \".bashrc\"\n    text = \"\"\n    if bash_completion_path.is_file():  # pragma: no cover\n        text = bash_completion_path.read_text()\n    result = subprocess.run(\n        [\n            \"bash\",\n            \"-c\",\n            f\"'{sys.executable}' -m coverage run '{mod.__file__}' --install-completion\",\n        ],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={**os.environ, \"SHELL\": \"/bin/bash\", \"_TYPER_COMPLETE_TESTING\": \"True\"},\n    )\n    new_text = bash_completion_path.read_text()\n    bash_completion_path.write_text(text)\n    assert \"source\" in new_text\n    assert str(Path(\".bash_completions/tutorial001_py310.py.sh\")) in new_text\n    assert \"completion installed in\" in result.stdout\n    assert \"Completion will take effect once you restart the terminal\" in result.stdout\n\n\ndef test_completion_invalid_instruction():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={\n            **os.environ,\n            \"_TUTORIAL001_PY310.PY_COMPLETE\": \"sourcebash\",\n        },\n    )\n    assert result.returncode != 0\n    assert \"Invalid completion instruction.\" in result.stderr\n\n\ndef test_completion_source_bash():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={\n            **os.environ,\n            \"_TUTORIAL001_PY310.PY_COMPLETE\": \"source_bash\",\n        },\n    )\n    assert (\n        \"complete -o default -F _tutorial001_py310py_completion tutorial001_py310.py\"\n        in result.stdout\n    )\n\n\ndef test_completion_source_invalid_shell():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={\n            **os.environ,\n            \"_TUTORIAL001_PY310.PY_COMPLETE\": \"source_xxx\",\n        },\n    )\n    assert \"Shell xxx not supported.\" in result.stderr\n\n\ndef test_completion_source_invalid_instruction():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={\n            **os.environ,\n            \"_TUTORIAL001_PY310.PY_COMPLETE\": \"explode_bash\",\n        },\n    )\n    assert 'Completion instruction \"explode\" not supported.' in result.stderr\n\n\ndef test_completion_source_zsh():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={\n            **os.environ,\n            \"_TUTORIAL001_PY310.PY_COMPLETE\": \"source_zsh\",\n        },\n    )\n    assert (\n        \"compdef _tutorial001_py310py_completion tutorial001_py310.py\" in result.stdout\n    )\n\n\ndef test_completion_source_fish():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={\n            **os.environ,\n            \"_TUTORIAL001_PY310.PY_COMPLETE\": \"source_fish\",\n        },\n    )\n    assert \"complete --command tutorial001_py310.py --no-files\" in result.stdout\n\n\ndef test_completion_source_powershell():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={\n            **os.environ,\n            \"_TUTORIAL001_PY310.PY_COMPLETE\": \"source_powershell\",\n        },\n    )\n    assert (\n        \"Register-ArgumentCompleter -Native -CommandName tutorial001_py310.py -ScriptBlock $scriptblock\"\n        in result.stdout\n    )\n\n\ndef test_completion_source_pwsh():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={\n            **os.environ,\n            \"_TUTORIAL001_PY310.PY_COMPLETE\": \"source_pwsh\",\n        },\n    )\n    assert (\n        \"Register-ArgumentCompleter -Native -CommandName tutorial001_py310.py -ScriptBlock $scriptblock\"\n        in result.stdout\n    )\n"
  },
  {
    "path": "tests/test_completion/test_completion_complete.py",
    "content": "import importlib\nimport os\nimport subprocess\nimport sys\nfrom pathlib import Path\nfrom types import ModuleType\n\nimport pytest\n\n\n@pytest.fixture(\n    name=\"mod\",\n    params=[\n        pytest.param(\"tutorial001_py310\"),\n        pytest.param(\"tutorial001_an_py310\"),\n    ],\n)\ndef get_mod(request: pytest.FixtureRequest) -> ModuleType:\n    module_name = f\"docs_src.commands.help.{request.param}\"\n    mod = importlib.import_module(module_name)\n    return mod\n\n\ndef test_completion_complete_subcommand_bash(mod: ModuleType):\n    file_name = Path(mod.__file__).name\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \" \"],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={\n            **os.environ,\n            f\"_{file_name.upper()}_COMPLETE\": \"complete_bash\",\n            \"COMP_WORDS\": f\"{file_name} del\",\n            \"COMP_CWORD\": \"1\",\n        },\n    )\n    print(result)\n    assert \"delete\\ndelete-all\" in result.stdout\n\n\ndef test_completion_complete_subcommand_bash_invalid(mod: ModuleType):\n    file_name = Path(mod.__file__).name\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \" \"],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={\n            **os.environ,\n            f\"_{file_name.upper()}_COMPLETE\": \"complete_bash\",\n            \"COMP_WORDS\": f\"{file_name} del\",\n            \"COMP_CWORD\": \"42\",\n        },\n    )\n    assert \"create\\ndelete\\ndelete-all\\ninit\" in result.stdout\n\n\ndef test_completion_complete_subcommand_zsh(mod: ModuleType):\n    file_name = Path(mod.__file__).name\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \" \"],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={\n            **os.environ,\n            f\"_{file_name.upper()}_COMPLETE\": \"complete_zsh\",\n            \"_TYPER_COMPLETE_ARGS\": f\"{file_name} del\",\n        },\n    )\n    assert (\n        \"\"\"_arguments '*: :((\"delete\":\"Delete a user with USERNAME.\"\\n\"\"\"\n        \"\"\"\\\"delete-all\":\"Delete ALL users in the database.\"))'\"\"\"\n    ) in result.stdout\n\n\ndef test_completion_complete_subcommand_zsh_files(mod: ModuleType):\n    file_name = Path(mod.__file__).name\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \" \"],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={\n            **os.environ,\n            f\"_{file_name.upper()}_COMPLETE\": \"complete_zsh\",\n            \"_TYPER_COMPLETE_ARGS\": f\"{file_name} delete \",\n        },\n    )\n    assert (\"_files\") in result.stdout\n\n\ndef test_completion_complete_subcommand_fish(mod: ModuleType):\n    file_name = Path(mod.__file__).name\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \" \"],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={\n            **os.environ,\n            f\"_{file_name.upper()}_COMPLETE\": \"complete_fish\",\n            \"_TYPER_COMPLETE_ARGS\": f\"{file_name} del\",\n            \"_TYPER_COMPLETE_FISH_ACTION\": \"get-args\",\n        },\n    )\n    assert (\n        \"delete\\tDelete a user with USERNAME.\\ndelete-all\\tDelete ALL users in the database.\"\n        in result.stdout\n    )\n\n\ndef test_completion_complete_subcommand_fish_should_complete(mod: ModuleType):\n    file_name = Path(mod.__file__).name\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \" \"],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={\n            **os.environ,\n            f\"_{file_name.upper()}_COMPLETE\": \"complete_fish\",\n            \"_TYPER_COMPLETE_ARGS\": f\"{file_name} del\",\n            \"_TYPER_COMPLETE_FISH_ACTION\": \"is-args\",\n        },\n    )\n    assert result.returncode == 0\n\n\ndef test_completion_complete_subcommand_fish_should_complete_no(mod: ModuleType):\n    file_name = Path(mod.__file__).name\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \" \"],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={\n            **os.environ,\n            f\"_{file_name.upper()}_COMPLETE\": \"complete_fish\",\n            \"_TYPER_COMPLETE_ARGS\": f\"{file_name} delete \",\n            \"_TYPER_COMPLETE_FISH_ACTION\": \"is-args\",\n        },\n    )\n    assert result.returncode != 0\n\n\ndef test_completion_complete_subcommand_powershell(mod: ModuleType):\n    file_name = Path(mod.__file__).name\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \" \"],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={\n            **os.environ,\n            f\"_{file_name.upper()}_COMPLETE\": \"complete_powershell\",\n            \"_TYPER_COMPLETE_ARGS\": f\"{file_name} del\",\n        },\n    )\n    assert (\n        \"delete:::Delete a user with USERNAME.\\ndelete-all:::Delete ALL users in the database.\"\n    ) in result.stdout\n\n\ndef test_completion_complete_subcommand_pwsh(mod: ModuleType):\n    file_name = Path(mod.__file__).name\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \" \"],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={\n            **os.environ,\n            f\"_{file_name.upper()}_COMPLETE\": \"complete_pwsh\",\n            \"_TYPER_COMPLETE_ARGS\": f\"{file_name} del\",\n        },\n    )\n    assert (\n        \"delete:::Delete a user with USERNAME.\\ndelete-all:::Delete ALL users in the database.\"\n    ) in result.stdout\n\n\ndef test_completion_complete_subcommand_noshell(mod: ModuleType):\n    file_name = Path(mod.__file__).name\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \" \"],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={\n            **os.environ,\n            f\"_{file_name.upper()}_COMPLETE\": \"complete_noshell\",\n            \"_TYPER_COMPLETE_ARGS\": f\"{file_name} del\",\n        },\n    )\n    assert (\"\") in result.stdout\n"
  },
  {
    "path": "tests/test_completion/test_completion_complete_no_help.py",
    "content": "import os\nimport subprocess\nimport sys\n\nfrom docs_src.commands.index import tutorial002_py310 as mod\n\n\ndef test_completion_complete_subcommand_zsh():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \" \"],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={\n            **os.environ,\n            \"_TUTORIAL002_PY310.PY_COMPLETE\": \"complete_zsh\",\n            \"_TYPER_COMPLETE_ARGS\": \"tutorial002_py310.py \",\n        },\n    )\n    assert \"create\" in result.stdout\n    assert \"delete\" in result.stdout\n\n\ndef test_completion_complete_subcommand_fish():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \" \"],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={\n            **os.environ,\n            \"_TUTORIAL002_PY310.PY_COMPLETE\": \"complete_fish\",\n            \"_TYPER_COMPLETE_ARGS\": \"tutorial002_py310.py \",\n            \"_TYPER_COMPLETE_FISH_ACTION\": \"get-args\",\n        },\n    )\n    assert \"create\\ndelete\" in result.stdout\n\n\ndef test_completion_complete_subcommand_powershell():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \" \"],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={\n            **os.environ,\n            \"_TUTORIAL002_PY310.PY_COMPLETE\": \"complete_powershell\",\n            \"_TYPER_COMPLETE_ARGS\": \"tutorial002_py310.py \",\n        },\n    )\n    assert (\"create::: \\ndelete::: \") in result.stdout\n\n\ndef test_completion_complete_subcommand_pwsh():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \" \"],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={\n            **os.environ,\n            \"_TUTORIAL002_PY310.PY_COMPLETE\": \"complete_pwsh\",\n            \"_TYPER_COMPLETE_ARGS\": \"tutorial002_py310.py \",\n        },\n    )\n    assert (\"create::: \\ndelete::: \") in result.stdout\n"
  },
  {
    "path": "tests/test_completion/test_completion_complete_rich.py",
    "content": "import os\nimport subprocess\nimport sys\n\nfrom . import example_rich_tags as mod\n\n\ndef test_script():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"create\", \"DeadPool\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert result.returncode == 0\n    assert \"Creating user: DeadPool\" in result.stdout\n\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"delete\", \"DeadPool\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert result.returncode == 0\n    assert \"Deleting user: DeadPool\" in result.stdout\n\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"delete-all\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert result.returncode == 0\n    assert \"Deleting all users\" in result.stdout\n\n\ndef test_completion_complete_subcommand_bash():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \" \"],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={\n            **os.environ,\n            \"_EXAMPLE_RICH_TAGS.PY_COMPLETE\": \"complete_bash\",\n            \"COMP_WORDS\": \"example_rich_tags.py del\",\n            \"COMP_CWORD\": \"1\",\n        },\n    )\n    assert \"delete\\ndelete-all\" in result.stdout\n\n\ndef test_completion_complete_subcommand_zsh():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \" \"],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={\n            **os.environ,\n            \"_EXAMPLE_RICH_TAGS.PY_COMPLETE\": \"complete_zsh\",\n            \"_TYPER_COMPLETE_ARGS\": \"example_rich_tags.py del\",\n        },\n    )\n    assert (\n        \"\"\"_arguments '*: :((\"delete\":\"Delete a user with USERNAME.\"\\n\"\"\"\n        \"\"\"\\\"delete-all\":\"Delete ALL users in the database.\"))'\"\"\"\n    ) in result.stdout\n\n\ndef test_completion_complete_subcommand_fish():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \" \"],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={\n            **os.environ,\n            \"_EXAMPLE_RICH_TAGS.PY_COMPLETE\": \"complete_fish\",\n            \"_TYPER_COMPLETE_ARGS\": \"example_rich_tags.py del\",\n            \"_TYPER_COMPLETE_FISH_ACTION\": \"get-args\",\n        },\n    )\n    assert (\n        \"delete\\tDelete a user with USERNAME.\\ndelete-all\\tDelete ALL users in the database.\"\n        in result.stdout\n    )\n\n\ndef test_completion_complete_subcommand_powershell():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \" \"],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={\n            **os.environ,\n            \"_EXAMPLE_RICH_TAGS.PY_COMPLETE\": \"complete_powershell\",\n            \"_TYPER_COMPLETE_ARGS\": \"example_rich_tags.py del\",\n        },\n    )\n    assert (\n        \"delete:::Delete a user with USERNAME.\\ndelete-all:::Delete ALL users in the database.\"\n    ) in result.stdout\n\n\ndef test_completion_complete_subcommand_pwsh():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \" \"],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={\n            **os.environ,\n            \"_EXAMPLE_RICH_TAGS.PY_COMPLETE\": \"complete_pwsh\",\n            \"_TYPER_COMPLETE_ARGS\": \"example_rich_tags.py del\",\n        },\n    )\n    assert (\n        \"delete:::Delete a user with USERNAME.\\ndelete-all:::Delete ALL users in the database.\"\n    ) in result.stdout\n"
  },
  {
    "path": "tests/test_completion/test_completion_install.py",
    "content": "import os\nimport subprocess\nimport sys\nfrom pathlib import Path\nfrom unittest import mock\n\nimport shellingham\nfrom typer.testing import CliRunner\n\nfrom docs_src.typer_app import tutorial001_py310 as mod\n\nfrom ..utils import requires_completion_permission\n\nrunner = CliRunner()\napp = mod.app\n\n\n@requires_completion_permission\ndef test_completion_install_no_shell():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--install-completion\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={\n            **os.environ,\n            \"_TYPER_COMPLETE_TEST_DISABLE_SHELL_DETECTION\": \"True\",\n        },\n    )\n    assert \"Option '--install-completion' requires an argument\" in result.stderr\n\n\n@requires_completion_permission\ndef test_completion_install_bash():\n    bash_completion_path: Path = Path.home() / \".bashrc\"\n    text = \"\"\n    if bash_completion_path.is_file():\n        text = bash_completion_path.read_text()\n    result = subprocess.run(\n        [\n            sys.executable,\n            \"-m\",\n            \"coverage\",\n            \"run\",\n            mod.__file__,\n            \"--install-completion\",\n            \"bash\",\n        ],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={\n            **os.environ,\n            \"_TYPER_COMPLETE_TEST_DISABLE_SHELL_DETECTION\": \"True\",\n        },\n    )\n    new_text = bash_completion_path.read_text()\n    bash_completion_path.write_text(text)\n    install_source = Path(\".bash_completions/tutorial001_py310.py.sh\")\n    assert str(install_source) not in text\n    assert str(install_source) in new_text\n    assert \"completion installed in\" in result.stdout\n    assert \"Completion will take effect once you restart the terminal\" in result.stdout\n    install_source_path = Path.home() / install_source\n    assert install_source_path.is_file()\n    install_content = install_source_path.read_text()\n    install_source_path.unlink()\n    assert (\n        \"complete -o default -F _tutorial001_py310py_completion tutorial001_py310.py\"\n        in install_content\n    )\n\n\n@requires_completion_permission\ndef test_completion_install_zsh():\n    completion_path: Path = Path.home() / \".zshrc\"\n    text = \"\"\n    if not completion_path.is_file():  # pragma: no cover\n        completion_path.write_text('echo \"custom .zshrc\"')\n    if completion_path.is_file():\n        text = completion_path.read_text()\n    result = subprocess.run(\n        [\n            sys.executable,\n            \"-m\",\n            \"coverage\",\n            \"run\",\n            mod.__file__,\n            \"--install-completion\",\n            \"zsh\",\n        ],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={\n            **os.environ,\n            \"_TYPER_COMPLETE_TEST_DISABLE_SHELL_DETECTION\": \"True\",\n        },\n    )\n    new_text = completion_path.read_text()\n    completion_path.write_text(text)\n    zfunc_fragment = \"fpath+=~/.zfunc\"\n    assert zfunc_fragment in new_text\n    assert \"completion installed in\" in result.stdout\n    assert \"Completion will take effect once you restart the terminal\" in result.stdout\n    install_source_path = Path.home() / \".zfunc/_tutorial001_py310.py\"\n    assert install_source_path.is_file()\n    install_content = install_source_path.read_text()\n    install_source_path.unlink()\n    assert (\n        \"compdef _tutorial001_py310py_completion tutorial001_py310.py\"\n        in install_content\n    )\n\n\n@requires_completion_permission\ndef test_completion_install_fish():\n    script_path = Path(mod.__file__)\n    completion_path: Path = (\n        Path.home() / f\".config/fish/completions/{script_path.name}.fish\"\n    )\n    result = subprocess.run(\n        [\n            sys.executable,\n            \"-m\",\n            \"coverage\",\n            \"run\",\n            mod.__file__,\n            \"--install-completion\",\n            \"fish\",\n        ],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={\n            **os.environ,\n            \"_TYPER_COMPLETE_TEST_DISABLE_SHELL_DETECTION\": \"True\",\n        },\n    )\n    new_text = completion_path.read_text()\n    completion_path.unlink()\n    assert \"complete --command tutorial001_py310.py\" in new_text\n    assert \"completion installed in\" in result.stdout\n    assert \"Completion will take effect once you restart the terminal\" in result.stdout\n\n\n@requires_completion_permission\ndef test_completion_install_powershell():\n    completion_path: Path = (\n        Path.home() / \".config/powershell/Microsoft.PowerShell_profile.ps1\"\n    )\n    completion_path_bytes = f\"{completion_path}\\n\".encode(\"windows-1252\")\n    text = \"\"\n    if completion_path.is_file():  # pragma: no cover\n        text = completion_path.read_text()\n\n    with mock.patch.object(\n        shellingham, \"detect_shell\", return_value=(\"pwsh\", \"/usr/bin/pwsh\")\n    ):\n        with mock.patch.object(\n            subprocess,\n            \"run\",\n            return_value=subprocess.CompletedProcess(\n                [\"pwsh\"], returncode=0, stdout=completion_path_bytes\n            ),\n        ):\n            result = runner.invoke(app, [\"--install-completion\"])\n    install_script = \"Register-ArgumentCompleter -Native -CommandName mocked-typer-testing-app -ScriptBlock $scriptblock\"\n    parent: Path = completion_path.parent\n    parent.mkdir(parents=True, exist_ok=True)\n    completion_path.write_text(install_script)\n    new_text = completion_path.read_text()\n    completion_path.write_text(text)\n    assert install_script not in text\n    assert install_script in new_text\n    assert \"completion installed in\" in result.stdout\n    assert \"Completion will take effect once you restart the terminal\" in result.stdout\n"
  },
  {
    "path": "tests/test_completion/test_completion_option_colon.py",
    "content": "import os\nimport subprocess\nimport sys\n\nfrom . import colon_example as mod\n\n\ndef test_script():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--name\", \"DeadPool\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert result.returncode == 0\n    assert \"DeadPool\" in result.stdout\n\n\ndef test_completion_colon_bash_all():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \" \"],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={\n            **os.environ,\n            \"_COLON_EXAMPLE.PY_COMPLETE\": \"complete_bash\",\n            \"COMP_WORDS\": \"colon_example.py --name \",\n            \"COMP_CWORD\": \"2\",\n        },\n    )\n    assert \"alpine:hello\" in result.stdout\n    assert \"alpine:latest\" in result.stdout\n    assert \"nvidia/cuda:10.0-devel-ubuntu18.04\" in result.stdout\n\n\ndef test_completion_colon_bash_partial():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \" \"],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={\n            **os.environ,\n            \"_COLON_EXAMPLE.PY_COMPLETE\": \"complete_bash\",\n            \"COMP_WORDS\": \"colon_example.py --name alpine \",\n            \"COMP_CWORD\": \"2\",\n        },\n    )\n    assert \"alpine:hello\" in result.stdout\n    assert \"alpine:latest\" in result.stdout\n    assert \"nvidia/cuda:10.0-devel-ubuntu18.04\" not in result.stdout\n\n\ndef test_completion_colon_bash_single():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \" \"],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={\n            **os.environ,\n            \"_COLON_EXAMPLE.PY_COMPLETE\": \"complete_bash\",\n            \"COMP_WORDS\": \"colon_example.py --name alpine:hell \",\n            \"COMP_CWORD\": \"2\",\n        },\n    )\n    assert \"alpine:hello\" in result.stdout\n    assert \"alpine:latest\" not in result.stdout\n    assert \"nvidia/cuda:10.0-devel-ubuntu18.04\" not in result.stdout\n\n\ndef test_completion_colon_zsh_all():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \" \"],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={\n            **os.environ,\n            \"_COLON_EXAMPLE.PY_COMPLETE\": \"complete_zsh\",\n            \"_TYPER_COMPLETE_ARGS\": \"colon_example.py --name \",\n        },\n    )\n    assert \"alpine\\\\\\\\:hello\" in result.stdout\n    assert \"alpine\\\\\\\\:latest\" in result.stdout\n    assert \"nvidia/cuda\\\\\\\\:10.0-devel-ubuntu18.04\" in result.stdout\n\n\ndef test_completion_colon_zsh_partial():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \" \"],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={\n            **os.environ,\n            \"_COLON_EXAMPLE.PY_COMPLETE\": \"complete_zsh\",\n            \"_TYPER_COMPLETE_ARGS\": \"colon_example.py --name alpine\",\n        },\n    )\n    assert \"alpine\\\\\\\\:hello\" in result.stdout\n    assert \"alpine\\\\\\\\:latest\" in result.stdout\n    assert \"nvidia/cuda\\\\\\\\:10.0-devel-ubuntu18.04\" not in result.stdout\n\n\ndef test_completion_colon_zsh_single():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \" \"],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={\n            **os.environ,\n            \"_COLON_EXAMPLE.PY_COMPLETE\": \"complete_zsh\",\n            \"_TYPER_COMPLETE_ARGS\": \"colon_example.py --name alpine:hell\",\n        },\n    )\n    assert \"alpine\\\\\\\\:hello\" in result.stdout\n    assert \"alpine\\\\\\\\:latest\" not in result.stdout\n    assert \"nvidia/cuda\\\\\\\\:10.0-devel-ubuntu18.04\" not in result.stdout\n\n\ndef test_completion_colon_powershell_all():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \" \"],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={\n            **os.environ,\n            \"_COLON_EXAMPLE.PY_COMPLETE\": \"complete_powershell\",\n            \"_TYPER_COMPLETE_ARGS\": \"colon_example.py --name \",\n            \"_TYPER_COMPLETE_WORD_TO_COMPLETE\": \"\",\n        },\n    )\n    assert \"alpine:hello\" in result.stdout\n    assert \"alpine:latest\" in result.stdout\n    assert \"nvidia/cuda:10.0-devel-ubuntu18.04\" in result.stdout\n\n\ndef test_completion_colon_powershell_partial():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \" \"],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={\n            **os.environ,\n            \"_COLON_EXAMPLE.PY_COMPLETE\": \"complete_powershell\",\n            \"_TYPER_COMPLETE_ARGS\": \"colon_example.py --name alpine\",\n            \"_TYPER_COMPLETE_WORD_TO_COMPLETE\": \"alpine\",\n        },\n    )\n    assert \"alpine:hello\" in result.stdout\n    assert \"alpine:latest\" in result.stdout\n    assert \"nvidia/cuda:10.0-devel-ubuntu18.04\" not in result.stdout\n\n\ndef test_completion_colon_powershell_single():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \" \"],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={\n            **os.environ,\n            \"_COLON_EXAMPLE.PY_COMPLETE\": \"complete_powershell\",\n            \"_TYPER_COMPLETE_ARGS\": \"colon_example.py --name alpine:hell\",\n            \"_TYPER_COMPLETE_WORD_TO_COMPLETE\": \"alpine:hell\",\n        },\n    )\n    assert \"alpine:hello\" in result.stdout\n    assert \"alpine:latest\" not in result.stdout\n    assert \"nvidia/cuda:10.0-devel-ubuntu18.04\" not in result.stdout\n\n\ndef test_completion_colon_pwsh_all():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \" \"],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={\n            **os.environ,\n            \"_COLON_EXAMPLE.PY_COMPLETE\": \"complete_pwsh\",\n            \"_TYPER_COMPLETE_ARGS\": \"colon_example.py --name\",\n        },\n    )\n\n    assert \"alpine:hello\" in result.stdout\n    assert \"alpine:latest\" in result.stdout\n    assert \"nvidia/cuda:10.0-devel-ubuntu18.04\" in result.stdout\n\n\ndef test_completion_colon_pwsh_partial():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \" \"],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={\n            **os.environ,\n            \"_COLON_EXAMPLE.PY_COMPLETE\": \"complete_pwsh\",\n            \"_TYPER_COMPLETE_ARGS\": \"colon_example.py --name alpine\",\n            \"_TYPER_COMPLETE_WORD_TO_COMPLETE\": \"alpine\",\n        },\n    )\n    assert \"alpine:hello\" in result.stdout\n    assert \"alpine:latest\" in result.stdout\n    assert \"nvidia/cuda:10.0-devel-ubuntu18.04\" not in result.stdout\n\n\ndef test_completion_colon_pwsh_single():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \" \"],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={\n            **os.environ,\n            \"_COLON_EXAMPLE.PY_COMPLETE\": \"complete_pwsh\",\n            \"_TYPER_COMPLETE_ARGS\": \"colon_example.py --name alpine:hell\",\n            \"_TYPER_COMPLETE_WORD_TO_COMPLETE\": \"alpine:hell\",\n        },\n    )\n    assert \"alpine:hello\" in result.stdout\n    assert \"alpine:latest\" not in result.stdout\n    assert \"nvidia/cuda:10.0-devel-ubuntu18.04\" not in result.stdout\n\n\n# TODO: tests for complete_fish\n"
  },
  {
    "path": "tests/test_completion/test_completion_path.py",
    "content": "import os\nimport subprocess\nimport sys\n\nfrom . import path_example as mod\n\n\ndef test_script():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"path/to/deadpool\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert result.returncode == 0\n    assert \"deadpool\" in result.stdout\n\n\ndef test_completion_path_bash():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \" \"],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={\n            **os.environ,\n            \"_PATH_EXAMPLE.PY_COMPLETE\": \"complete_bash\",\n            \"COMP_WORDS\": \"path_example.py \",\n            \"COMP_CWORD\": \"2\",\n        },\n    )\n    assert result.returncode == 0\n"
  },
  {
    "path": "tests/test_completion/test_completion_show.py",
    "content": "import os\nimport subprocess\nimport sys\nfrom unittest import mock\n\nimport typer\nimport typer.completion\nfrom typer.testing import CliRunner\n\nfrom docs_src.typer_app import tutorial001_py310 as mod\n\nrunner = CliRunner()\napp = mod.app\n\n\ndef test_completion_show_no_shell():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--show-completion\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={\n            **os.environ,\n            \"_TYPER_COMPLETE_TEST_DISABLE_SHELL_DETECTION\": \"True\",\n        },\n    )\n    assert \"Option '--show-completion' requires an argument\" in result.stderr\n\n\ndef test_completion_show_bash():\n    result = subprocess.run(\n        [\n            sys.executable,\n            \"-m\",\n            \"coverage\",\n            \"run\",\n            mod.__file__,\n            \"--show-completion\",\n            \"bash\",\n        ],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={\n            **os.environ,\n            \"_TYPER_COMPLETE_TEST_DISABLE_SHELL_DETECTION\": \"True\",\n        },\n    )\n    assert (\n        \"complete -o default -F _tutorial001_py310py_completion tutorial001_py310.py\"\n        in result.stdout\n    )\n\n\ndef test_completion_source_zsh():\n    result = subprocess.run(\n        [\n            sys.executable,\n            \"-m\",\n            \"coverage\",\n            \"run\",\n            mod.__file__,\n            \"--show-completion\",\n            \"zsh\",\n        ],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={\n            **os.environ,\n            \"_TYPER_COMPLETE_TEST_DISABLE_SHELL_DETECTION\": \"True\",\n        },\n    )\n    assert (\n        \"compdef _tutorial001_py310py_completion tutorial001_py310.py\" in result.stdout\n    )\n\n\ndef test_completion_source_fish():\n    result = subprocess.run(\n        [\n            sys.executable,\n            \"-m\",\n            \"coverage\",\n            \"run\",\n            mod.__file__,\n            \"--show-completion\",\n            \"fish\",\n        ],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={\n            **os.environ,\n            \"_TYPER_COMPLETE_TEST_DISABLE_SHELL_DETECTION\": \"True\",\n        },\n    )\n    assert \"complete --command tutorial001_py310.py --no-files\" in result.stdout\n\n\ndef test_completion_source_powershell():\n    result = subprocess.run(\n        [\n            sys.executable,\n            \"-m\",\n            \"coverage\",\n            \"run\",\n            mod.__file__,\n            \"--show-completion\",\n            \"powershell\",\n        ],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={\n            **os.environ,\n            \"_TYPER_COMPLETE_TEST_DISABLE_SHELL_DETECTION\": \"True\",\n        },\n    )\n    assert (\n        \"Register-ArgumentCompleter -Native -CommandName tutorial001_py310.py -ScriptBlock $scriptblock\"\n        in result.stdout\n    )\n\n\ndef test_completion_source_pwsh():\n    result = subprocess.run(\n        [\n            sys.executable,\n            \"-m\",\n            \"coverage\",\n            \"run\",\n            mod.__file__,\n            \"--show-completion\",\n            \"pwsh\",\n        ],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={\n            **os.environ,\n            \"_TYPER_COMPLETE_TEST_DISABLE_SHELL_DETECTION\": \"True\",\n        },\n    )\n    assert (\n        \"Register-ArgumentCompleter -Native -CommandName tutorial001_py310.py -ScriptBlock $scriptblock\"\n        in result.stdout\n    )\n\n\ndef test_completion_show_invalid_shell():\n    with mock.patch.object(typer.completion, \"_get_shell_name\", return_value=\"xshell\"):\n        result = runner.invoke(app, [\"--show-completion\"])\n    assert \"Shell xshell not supported\" in result.output\n"
  },
  {
    "path": "tests/test_completion/test_sanitization.py",
    "content": "from importlib.machinery import ModuleSpec\nfrom unittest.mock import patch\n\nimport pytest\nfrom typer._completion_classes import _sanitize_help_text\n\n\n@pytest.mark.parametrize(\n    \"find_spec, help_text, expected\",\n    [\n        (\n            ModuleSpec(\"rich\", loader=None),\n            \"help text without rich tags\",\n            \"help text without rich tags\",\n        ),\n        (\n            None,\n            \"help text without rich tags\",\n            \"help text without rich tags\",\n        ),\n        (\n            ModuleSpec(\"rich\", loader=None),\n            \"help [bold]with[/] rich tags\",\n            \"help with rich tags\",\n        ),\n        (\n            None,\n            \"help [bold]with[/] rich tags\",\n            \"help [bold]with[/] rich tags\",\n        ),\n    ],\n)\ndef test_sanitize_help_text(\n    find_spec: ModuleSpec | None, help_text: str, expected: str\n):\n    with patch(\"importlib.util.find_spec\", return_value=find_spec) as mock_find_spec:\n        assert _sanitize_help_text(help_text) == expected\n    mock_find_spec.assert_called_once_with(\"rich\")\n"
  },
  {
    "path": "tests/test_corner_cases.py",
    "content": "import pytest\nimport typer.core\nfrom typer.testing import CliRunner\n\nfrom tests.assets import corner_cases as mod\n\nrunner = CliRunner()\n\n\ndef test_hidden_option():\n    result = runner.invoke(mod.app, [\"--help\"])\n    assert result.exit_code == 0\n    assert \"Say hello\" in result.output\n    assert \"--name\" not in result.output\n    assert \"/lastname\" in result.output\n    assert \"TEST_LASTNAME\" in result.output\n    assert \"(dynamic)\" in result.output\n\n\ndef test_hidden_option_no_rich(monkeypatch: pytest.MonkeyPatch):\n    monkeypatch.setattr(typer.core, \"HAS_RICH\", False)\n\n    result = runner.invoke(mod.app, [\"--help\"])\n    assert result.exit_code == 0\n    assert \"Say hello\" in result.output\n    assert \"--name\" not in result.output\n    assert \"/lastname\" in result.output\n    assert \"TEST_LASTNAME\" in result.output\n    assert \"(dynamic)\" in result.output\n\n\ndef test_coverage_call():\n    result = runner.invoke(mod.app)\n    assert result.exit_code == 0\n    assert \"Hello John Doe, it seems you have 42\" in result.output\n"
  },
  {
    "path": "tests/test_deprecation.py",
    "content": "import pytest\nimport typer\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\n\n\ndef test_deprecation():\n    app = typer.Typer()\n\n    def add_command():\n        @app.command()\n        def cmd(\n            opt: float | None = typer.Option(\n                3.14,\n                is_flag=True,\n                flag_value=\"42\",\n                help=\"Some wonderful number\",\n            ),\n        ): ...  # pragma: no cover\n\n    with pytest.warns(\n        match=\"The 'is_flag' and 'flag_value' parameters are not supported by Typer\"\n    ):\n        add_command()\n"
  },
  {
    "path": "tests/test_exit_errors.py",
    "content": "import errno\n\nimport typer\nimport typer.completion\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\n\n\ndef test_eoferror():\n    # Mainly for coverage/completeness\n    app = typer.Typer()\n\n    @app.command()\n    def main():\n        raise EOFError()\n\n    result = runner.invoke(app)\n    assert result.exit_code == 1\n\n\ndef test_keyboardinterrupt():\n    # Mainly for coverage/completeness\n    app = typer.Typer()\n\n    @app.command()\n    def main():\n        raise KeyboardInterrupt()\n\n    result = runner.invoke(app)\n    assert result.exit_code == 130\n    assert result.stdout == \"\"\n\n\ndef test_oserror():\n    # Mainly for coverage/completeness\n    app = typer.Typer()\n\n    @app.command()\n    def main():\n        e = OSError()\n        e.errno = errno.EPIPE\n        raise e\n\n    result = runner.invoke(app)\n    assert result.exit_code == 1\n\n\ndef test_oserror_no_epipe():\n    # Mainly for coverage/completeness\n    app = typer.Typer()\n\n    @app.command()\n    def main():\n        raise OSError()\n\n    result = runner.invoke(app)\n    assert result.exit_code == 1\n"
  },
  {
    "path": "tests/test_future_annotations.py",
    "content": "from __future__ import annotations\n\nfrom typing import Annotated\n\nimport typer\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\n\n\ndef test_annotated():\n    app = typer.Typer()\n\n    @app.command()\n    def cmd(force: Annotated[bool, typer.Option(\"--force\")] = False):\n        if force:\n            print(\"Forcing operation\")\n        else:\n            print(\"Not forcing\")\n\n    result = runner.invoke(app)\n    assert result.exit_code == 0, result.output\n    assert \"Not forcing\" in result.output\n\n    result = runner.invoke(app, [\"--force\"])\n    assert result.exit_code == 0, result.output\n    assert \"Forcing operation\" in result.output\n"
  },
  {
    "path": "tests/test_launch.py",
    "content": "import subprocess\nfrom unittest.mock import patch\n\nimport pytest\nimport typer\n\nurl = \"http://example.com\"\n\n\n@pytest.mark.parametrize(\n    \"system, command\",\n    [\n        (\"Darwin\", \"open\"),\n        (\"Linux\", \"xdg-open\"),\n        (\"FreeBSD\", \"xdg-open\"),\n    ],\n)\ndef test_launch_url_unix(system: str, command: str):\n    with (\n        patch(\"platform.system\", return_value=system),\n        patch(\"shutil.which\", return_value=True),\n        patch(\"subprocess.Popen\") as mock_popen,\n    ):\n        typer.launch(url)\n\n    mock_popen.assert_called_once_with(\n        [command, url], stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT\n    )\n\n\ndef test_launch_url_windows():\n    with (\n        patch(\"platform.system\", return_value=\"Windows\"),\n        patch(\"webbrowser.open\") as mock_webbrowser_open,\n    ):\n        typer.launch(url)\n\n    mock_webbrowser_open.assert_called_once_with(url)\n\n\ndef test_launch_url_no_xdg_open():\n    with (\n        patch(\"platform.system\", return_value=\"Linux\"),\n        patch(\"shutil.which\", return_value=None),\n        patch(\"webbrowser.open\") as mock_webbrowser_open,\n    ):\n        typer.launch(url)\n\n    mock_webbrowser_open.assert_called_once_with(url)\n\n\ndef test_calls_original_launch_when_not_passing_urls():\n    with patch(\"typer.main.click.launch\", return_value=0) as launch_mock:\n        typer.launch(\"not a url\")\n\n    launch_mock.assert_called_once_with(\"not a url\")\n"
  },
  {
    "path": "tests/test_others.py",
    "content": "import os\nimport subprocess\nimport sys\nimport typing\nfrom pathlib import Path\nfrom typing import Annotated\nfrom unittest import mock\n\nimport click\nimport pytest\nimport typer\nimport typer._completion_shared\nimport typer.completion\nfrom typer.core import _split_opt\nfrom typer.main import solve_typer_info_defaults, solve_typer_info_help\nfrom typer.models import ParameterInfo, TyperInfo\nfrom typer.testing import CliRunner\n\nfrom .utils import requires_completion_permission\n\nrunner = CliRunner()\n\n\ndef test_help_from_info():\n    # Mainly for coverage/completeness\n    value = solve_typer_info_help(TyperInfo())\n    assert value is None\n\n\ndef test_defaults_from_info():\n    # Mainly for coverage/completeness\n    value = solve_typer_info_defaults(TyperInfo())\n    assert value\n\n\ndef test_too_many_parsers():\n    def custom_parser(value: str) -> int:\n        return int(value)  # pragma: no cover\n\n    class CustomClickParser(click.ParamType):\n        name = \"custom_parser\"\n\n        def convert(\n            self,\n            value: str,\n            param: click.Parameter | None,\n            ctx: click.Context | None,\n        ) -> typing.Any:\n            return int(value)  # pragma: no cover\n\n    expected_error = (\n        \"Multiple custom type parsers provided. \"\n        \"`parser` and `click_type` may not both be provided.\"\n    )\n\n    with pytest.raises(ValueError, match=expected_error):\n        ParameterInfo(parser=custom_parser, click_type=CustomClickParser())\n\n\ndef test_valid_parser_permutations():\n    def custom_parser(value: str) -> int:\n        return int(value)  # pragma: no cover\n\n    class CustomClickParser(click.ParamType):\n        name = \"custom_parser\"\n\n        def convert(\n            self,\n            value: str,\n            param: click.Parameter | None,\n            ctx: click.Context | None,\n        ) -> typing.Any:\n            return int(value)  # pragma: no cover\n\n    ParameterInfo()\n    ParameterInfo(parser=custom_parser)\n    ParameterInfo(click_type=CustomClickParser())\n\n\n@requires_completion_permission\ndef test_install_invalid_shell():\n    app = typer.Typer()\n\n    @app.command()\n    def main():\n        print(\"Hello World\")\n\n    with mock.patch.object(\n        typer._completion_shared, \"_get_shell_name\", return_value=\"xshell\"\n    ):\n        result = runner.invoke(app, [\"--install-completion\"])\n        assert \"Shell xshell is not supported.\" in result.stdout\n    result = runner.invoke(app)\n    assert \"Hello World\" in result.stdout\n\n\ndef test_callback_too_many_parameters():\n    app = typer.Typer()\n\n    def name_callback(ctx, param, val1, val2):\n        pass  # pragma: no cover\n\n    @app.command()\n    def main(name: str = typer.Option(..., callback=name_callback)):\n        pass  # pragma: no cover\n\n    with pytest.raises(click.ClickException) as exc_info:\n        runner.invoke(app, [\"--name\", \"Camila\"])\n    assert (\n        exc_info.value.message == \"Too many CLI parameter callback function parameters\"\n    )\n\n\ndef test_callback_2_untyped_parameters():\n    app = typer.Typer()\n\n    def name_callback(ctx, value):\n        print(f\"info name is: {ctx.info_name}\")\n        print(f\"value is: {value}\")\n\n    @app.command()\n    def main(name: str = typer.Option(..., callback=name_callback)):\n        print(\"Hello World\")\n\n    result = runner.invoke(app, [\"--name\", \"Camila\"])\n    assert \"info name is: main\" in result.stdout\n    assert \"value is: Camila\" in result.stdout\n\n\ndef test_callback_3_untyped_parameters():\n    app = typer.Typer()\n\n    def name_callback(ctx, param, value):\n        print(f\"info name is: {ctx.info_name}\")\n        print(f\"param name is: {param.name}\")\n        print(f\"value is: {value}\")\n\n    @app.command()\n    def main(name: str = typer.Option(..., callback=name_callback)):\n        print(\"Hello World\")\n\n    result = runner.invoke(app, [\"--name\", \"Camila\"])\n    assert \"info name is: main\" in result.stdout\n    assert \"param name is: name\" in result.stdout\n    assert \"value is: Camila\" in result.stdout\n\n\ndef test_callback_4_list_none():\n    app = typer.Typer()\n\n    def names_callback(ctx, param, values: list[str] | None):\n        if values is None:\n            return values\n        return [value.upper() for value in values]\n\n    @app.command()\n    def main(\n        names: list[str] | None = typer.Option(None, \"--name\", callback=names_callback),\n    ):\n        if names is None:\n            print(\"Hello World\")\n        else:\n            print(f\"Hello {', '.join(names)}\")\n\n    result = runner.invoke(app, [\"--name\", \"Sideshow\", \"--name\", \"Bob\"])\n    assert \"Hello SIDESHOW, BOB\" in result.stdout\n\n    result = runner.invoke(app, [])\n    assert \"Hello World\" in result.stdout\n\n\ndef test_empty_list_default_generator():\n    def empty_list() -> list[str]:\n        return []\n\n    app = typer.Typer()\n\n    @app.command()\n    def main(\n        names: Annotated[list[str], typer.Option(default_factory=empty_list)],\n    ):\n        print(names)\n\n    result = runner.invoke(app)\n    assert \"[]\" in result.output\n\n\ndef test_completion_argument():\n    file_path = Path(__file__).parent / \"assets/completion_argument.py\"\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", str(file_path), \"E\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={\n            **os.environ,\n            \"_COMPLETION_ARGUMENT.PY_COMPLETE\": \"complete_zsh\",\n            \"_TYPER_COMPLETE_ARGS\": \"completion_argument.py E\",\n            \"_TYPER_COMPLETE_TESTING\": \"True\",\n        },\n    )\n    assert \"Emma\" in result.stdout or \"_files\" in result.stdout\n    assert \"ctx: completion_argument\" in result.stderr\n    assert \"arg is: name\" in result.stderr\n    assert \"incomplete is: E\" in result.stderr\n\n\ndef test_completion_untyped_parameters():\n    file_path = Path(__file__).parent / \"assets/completion_no_types.py\"\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", str(file_path)],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={\n            **os.environ,\n            \"_COMPLETION_NO_TYPES.PY_COMPLETE\": \"complete_zsh\",\n            \"_TYPER_COMPLETE_ARGS\": \"completion_no_types.py --name Sebastian --name Ca\",\n        },\n    )\n    assert \"info name is: completion_no_types.py\" in result.stderr\n    assert \"args is: []\" in result.stderr\n    assert \"incomplete is: Ca\" in result.stderr\n    assert '\"Camila\":\"The reader of books.\"' in result.stdout\n    assert '\"Carlos\":\"The writer of scripts.\"' in result.stdout\n\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", str(file_path)],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Hello World\" in result.stdout\n\n\ndef test_completion_untyped_parameters_different_order_correct_names():\n    file_path = Path(__file__).parent / \"assets/completion_no_types_order.py\"\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", str(file_path)],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={\n            **os.environ,\n            \"_COMPLETION_NO_TYPES_ORDER.PY_COMPLETE\": \"complete_zsh\",\n            \"_TYPER_COMPLETE_ARGS\": \"completion_no_types_order.py --name Sebastian --name Ca\",\n        },\n    )\n    assert \"info name is: completion_no_types_order.py\" in result.stderr\n    assert \"args is: []\" in result.stderr\n    assert \"incomplete is: Ca\" in result.stderr\n    assert '\"Camila\":\"The reader of books.\"' in result.stdout\n    assert '\"Carlos\":\"The writer of scripts.\"' in result.stdout\n\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", str(file_path)],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Hello World\" in result.stdout\n\n\ndef test_autocompletion_too_many_parameters():\n    app = typer.Typer()\n\n    def name_callback(ctx, args, incomplete, val2):\n        pass  # pragma: no cover\n\n    @app.command()\n    def main(name: str = typer.Option(..., autocompletion=name_callback)):\n        pass  # pragma: no cover\n\n    with pytest.raises(click.ClickException) as exc_info:\n        runner.invoke(app, [\"--name\", \"Camila\"])\n    assert exc_info.value.message == \"Invalid autocompletion callback parameters: val2\"\n\n\ndef test_forward_references():\n    app = typer.Typer()\n\n    @app.command()\n    def main(arg1, arg2: int, arg3: \"int\", arg4: bool = False, arg5: \"bool\" = False):\n        print(f\"arg1: {type(arg1)} {arg1}\")\n        print(f\"arg2: {type(arg2)} {arg2}\")\n        print(f\"arg3: {type(arg3)} {arg3}\")\n        print(f\"arg4: {type(arg4)} {arg4}\")\n        print(f\"arg5: {type(arg5)} {arg5}\")\n\n    result = runner.invoke(app, [\"Hello\", \"2\", \"invalid\"])\n\n    assert \"Invalid value for 'ARG3': 'invalid' is not a valid integer\" in result.output\n    result = runner.invoke(app, [\"Hello\", \"2\", \"3\", \"--arg4\", \"--arg5\"])\n    assert (\n        \"arg1: <class 'str'> Hello\\narg2: <class 'int'> 2\\narg3: <class 'int'> 3\\narg4: <class 'bool'> True\\narg5: <class 'bool'> True\\n\"\n        in result.stdout\n    )\n\n\ndef test_context_settings_inheritance_single_command():\n    app = typer.Typer(context_settings={\"help_option_names\": [\"-h\", \"--help\"]})\n\n    @app.command()\n    def main(name: str):\n        pass  # pragma: no cover\n\n    result = runner.invoke(app, [\"main\", \"-h\"])\n    assert \"Show this message and exit.\" in result.stdout\n\n\ndef test_split_opt():\n    prefix, opt = _split_opt(\"--verbose\")\n    assert prefix == \"--\"\n    assert opt == \"verbose\"\n\n    prefix, opt = _split_opt(\"//verbose\")\n    assert prefix == \"//\"\n    assert opt == \"verbose\"\n\n    prefix, opt = _split_opt(\"-verbose\")\n    assert prefix == \"-\"\n    assert opt == \"verbose\"\n\n    prefix, opt = _split_opt(\"verbose\")\n    assert prefix == \"\"\n    assert opt == \"verbose\"\n\n\ndef test_options_metadata_typer_default():\n    app = typer.Typer(options_metavar=\"[options]\")\n\n    @app.command()\n    def c1():\n        pass  # pragma: no cover\n\n    @app.command(options_metavar=\"[OPTS]\")\n    def c2():\n        pass  # pragma: no cover\n\n    result = runner.invoke(app, [\"c1\", \"--help\"])\n    assert \"Usage: root c1 [options]\" in result.stdout\n\n    result = runner.invoke(app, [\"c2\", \"--help\"])\n    assert \"Usage: root c2 [OPTS]\" in result.stdout\n"
  },
  {
    "path": "tests/test_param_meta_empty.py",
    "content": "import typer\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\n\n\ndef test_default_with_class_with_custom_eq():\n    app = typer.Typer()\n\n    from typer.models import ParamMeta\n\n    class StupidClass:\n        def __init__(self, a):\n            self.a = a\n\n        def __eq__(self, other) -> bool:\n            if other is ParamMeta.empty:\n                return True\n            try:\n                return self.a == other.a\n            except Exception:\n                return False\n\n        def __ne__(self, other: object) -> bool:\n            return not self.__eq__(other)\n\n    @app.command()\n    def cmd(val=StupidClass(42)):\n        print(val)\n\n    assert StupidClass(666) == ParamMeta.empty\n    assert StupidClass(666) != StupidClass(1)\n\n    result = runner.invoke(app)\n    assert result.exit_code == 0, result.output\n    assert \"StupidClass\" in result.output\n"
  },
  {
    "path": "tests/test_prog_name.py",
    "content": "import subprocess\nimport sys\nfrom pathlib import Path\n\n\ndef test_custom_prog_name():\n    file_path = Path(__file__).parent / \"assets/prog_name.py\"\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", str(file_path), \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage: custom-name [OPTIONS] I\" in result.stdout\n"
  },
  {
    "path": "tests/test_rich_import.py",
    "content": "import subprocess\nimport sys\nfrom pathlib import Path\n\nACCEPTED_MODULES = {\"rich._extension\", \"rich\"}\n\n\ndef test_rich_not_imported_unnecessary():\n    file_path = Path(__file__).parent / \"assets/print_modules.py\"\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", str(file_path)],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    modules = result.stdout.splitlines()\n    modules = [\n        module\n        for module in modules\n        if module not in ACCEPTED_MODULES and module.startswith(\"rich\")\n    ]\n    assert not modules\n"
  },
  {
    "path": "tests/test_rich_markup_mode.py",
    "content": "import os\nimport subprocess\nimport sys\n\nimport pytest\nimport typer\nimport typer.completion\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\nrounded = [\"╭\", \"─\", \"┬\", \"╮\", \"│\", \"├\", \"┼\", \"┤\", \"╰\", \"┴\", \"╯\"]\n\n\ndef test_rich_markup_mode_none():\n    app = typer.Typer(rich_markup_mode=None)\n\n    @app.command()\n    def main(arg: str):\n        \"\"\"Main function\"\"\"\n        print(f\"Hello {arg}\")\n\n    assert app.rich_markup_mode is None\n\n    result = runner.invoke(app, [\"World\"])\n    assert \"Hello World\" in result.stdout\n\n    result = runner.invoke(app, [\"--help\"])\n    assert \"ARG  [required]\" in result.stdout\n    assert all(c not in result.stdout for c in rounded)\n\n\ndef test_rich_markup_mode_rich():\n    app = typer.Typer(rich_markup_mode=\"rich\")\n\n    @app.command()\n    def main(arg: str):\n        \"\"\"Main function\"\"\"\n        print(f\"Hello {arg}\")\n\n    assert app.rich_markup_mode == \"rich\"\n\n    result = runner.invoke(app, [\"World\"])\n    assert \"Hello World\" in result.stdout\n\n    result = runner.invoke(app, [\"--help\"])\n    assert any(c in result.stdout for c in rounded)\n\n\n@pytest.mark.parametrize(\n    \"env_var_value, expected_result\",\n    [\n        (\"1\", True),\n        (\"True\", True),\n        (\"TRUE\", True),\n        (\"true\", True),\n        (\"0\", False),\n        (\"False\", False),\n        (\"FALSE\", False),\n        (\"false\", False),\n        (\"somerandomvalue\", True),\n    ],\n)\ndef test_rich_markup_mode_envvar(env_var_value: str, expected_result: bool):\n    result = subprocess.run(\n        [\n            sys.executable,\n            \"-m\",\n            \"coverage\",\n            \"run\",\n            \"-m\",\n            \"typer\",\n            \"tests/assets/cli/sample.py\",\n            \"--help\",\n        ],\n        capture_output=True,\n        env={\n            **os.environ,\n            \"TYPER_USE_RICH\": env_var_value,\n            \"PYTHONIOENCODING\": \"utf-8\",\n        },\n        encoding=\"utf-8\",\n    )\n    assert any(c in result.stdout for c in rounded) == expected_result\n\n\n@pytest.mark.parametrize(\n    \"mode,lines\",\n    [\n        pytest.param(\n            \"markdown\",\n            [\"First line\", \"\", \"Line 1\", \"\", \"Line 2\", \"\", \"Line 3\", \"\"],\n        ),\n        pytest.param(\n            \"rich\", [\"First line\", \"\", \"Line 1\", \"\", \"Line 2\", \"\", \"Line 3\", \"\"]\n        ),\n        pytest.param(\n            None, [\"First line\", \"\", \"Line 1\", \"\", \"Line 2\", \"\", \"Line 3\", \"\"]\n        ),\n    ],\n)\ndef test_markup_mode_newline_pr815(mode: str, lines: list[str]):\n    app = typer.Typer(rich_markup_mode=mode)\n\n    @app.command()\n    def main(arg: str):\n        \"\"\"First line\n\n        Line 1\n\n        Line 2\n\n        Line 3\n        \"\"\"\n        print(f\"Hello {arg}\")\n\n    assert app.rich_markup_mode == mode\n\n    result = runner.invoke(app, [\"World\"])\n    assert \"Hello World\" in result.stdout\n\n    result = runner.invoke(app, [\"--help\"])\n    result_lines = [line.strip() for line in result.stdout.split(\"\\n\")]\n    if mode:\n        assert any(c in result.stdout for c in rounded)\n    help_start = result_lines.index(\"First line\")\n    arg_start = [i for i, row in enumerate(result_lines) if \"Arguments\" in row][0]\n    assert help_start != -1\n    assert result_lines[help_start:arg_start] == lines\n\n\n@pytest.mark.parametrize(\n    \"mode,lines\",\n    [\n        pytest.param(\"markdown\", [\"First line\", \"\", \"Line 1 Line 2 Line 3\", \"\"]),\n        pytest.param(\"rich\", [\"First line\", \"\", \"Line 1\", \"Line 2\", \"Line 3\", \"\"]),\n        pytest.param(None, [\"First line\", \"\", \"Line 1 Line 2 Line 3\", \"\"]),\n    ],\n)\ndef test_markup_mode_newline_issue447(mode: str, lines: list[str]):\n    app = typer.Typer(rich_markup_mode=mode)\n\n    @app.command()\n    def main(arg: str):\n        \"\"\"First line\n\n        Line 1\n        Line 2\n        Line 3\n        \"\"\"\n        print(f\"Hello {arg}\")\n\n    assert app.rich_markup_mode == mode\n\n    result = runner.invoke(app, [\"World\"])\n    assert \"Hello World\" in result.stdout\n\n    result = runner.invoke(app, [\"--help\"])\n    result_lines = [line.strip() for line in result.stdout.split(\"\\n\")]\n    if mode:\n        assert any(c in result.stdout for c in rounded)\n    help_start = result_lines.index(\"First line\")\n    arg_start = [i for i, row in enumerate(result_lines) if \"Arguments\" in row][0]\n    assert help_start != -1\n    assert result_lines[help_start:arg_start] == lines\n\n\n@pytest.mark.parametrize(\n    \"mode,lines\",\n    [\n        pytest.param(\n            \"markdown\",\n            [\n                \"This header is long\",\n                \"\",\n                \"Line 1\",\n                \"\",\n                \"Line 2 continues here\",\n                \"\",\n                \"Line 3\",\n                \"\",\n            ],\n        ),\n        pytest.param(\n            \"rich\",\n            [\n                \"This header is long\",\n                \"\",\n                \"Line 1\",\n                \"\",\n                \"Line 2\",\n                \"continues here\",\n                \"\",\n                \"Line 3\",\n                \"\",\n            ],\n        ),\n        pytest.param(\n            None,\n            [\n                \"This header is long\",\n                \"\",\n                \"Line 1\",\n                \"\",\n                \"Line 2 continues here\",\n                \"\",\n                \"Line 3\",\n                \"\",\n            ],\n        ),\n    ],\n)\ndef test_markup_mode_newline_mixed(mode: str, lines: list[str]):\n    app = typer.Typer(rich_markup_mode=mode)\n\n    @app.command()\n    def main(arg: str):\n        \"\"\"This header\n        is long\n\n        Line 1\n\n        Line 2\n        continues here\n\n        Line 3\n        \"\"\"\n        print(f\"Hello {arg}\")\n\n    assert app.rich_markup_mode == mode\n\n    result = runner.invoke(app, [\"World\"])\n    assert \"Hello World\" in result.stdout\n\n    result = runner.invoke(app, [\"--help\"])\n    result_lines = [line.strip() for line in result.stdout.split(\"\\n\")]\n    if mode:\n        assert any(c in result.stdout for c in rounded)\n    help_start = [i for i, row in enumerate(result_lines) if \"This header\" in row][0]\n    arg_start = [i for i, row in enumerate(result_lines) if \"Arguments\" in row][0]\n    assert help_start != -1\n    assert result_lines[help_start:arg_start] == lines\n\n\n@pytest.mark.parametrize(\n    \"mode,lines\",\n    [\n        pytest.param(\n            \"markdown\",\n            [\"First line\", \"\", \"• 1\", \"• 2\", \"• 3\", \"\"],\n            marks=pytest.mark.xfail,\n        ),\n        pytest.param(\"rich\", [\"First line\", \"\", \"- 1\", \"- 2\", \"- 3\", \"\"]),\n        pytest.param(None, [\"First line\", \"\", \"- 1 - 2 - 3\", \"\"]),\n    ],\n)\ndef test_markup_mode_bullets_single_newline(mode: str, lines: list[str]):\n    app = typer.Typer(rich_markup_mode=mode)\n\n    @app.command()\n    def main(arg: str):\n        \"\"\"First line\n\n        - 1\n        - 2\n        - 3\n        \"\"\"\n        print(f\"Hello {arg}\")\n\n    assert app.rich_markup_mode == mode\n\n    result = runner.invoke(app, [\"World\"])\n    assert \"Hello World\" in result.stdout\n\n    result = runner.invoke(app, [\"--help\"])\n    result_lines = [line.strip() for line in result.stdout.split(\"\\n\")]\n    if mode:\n        assert any(c in result.stdout for c in rounded)\n    help_start = result_lines.index(\"First line\")\n    arg_start = [i for i, row in enumerate(result_lines) if \"Arguments\" in row][0]\n    assert help_start != -1\n    assert result_lines[help_start:arg_start] == lines\n\n\n@pytest.mark.parametrize(\n    \"mode,lines\",\n    [\n        pytest.param(\n            \"markdown\",\n            [\"First line\", \"\", \"• 1\", \"• 2\", \"• 3\", \"\"],\n            marks=pytest.mark.xfail,\n        ),\n        (\n            \"rich\",\n            [\"First line\", \"\", \"- 1\", \"\", \"- 2\", \"\", \"- 3\", \"\"],\n        ),\n        (None, [\"First line\", \"\", \"- 1\", \"\", \"- 2\", \"\", \"- 3\", \"\"]),\n    ],\n)\ndef test_markup_mode_bullets_double_newline(mode: str, lines: list[str]):\n    app = typer.Typer(rich_markup_mode=mode)\n\n    @app.command()\n    def main(arg: str):\n        \"\"\"First line\n\n        - 1\n\n        - 2\n\n        - 3\n        \"\"\"\n        print(f\"Hello {arg}\")\n\n    assert app.rich_markup_mode == mode\n\n    result = runner.invoke(app, [\"World\"])\n    assert \"Hello World\" in result.stdout\n\n    result = runner.invoke(app, [\"--help\"])\n    result_lines = [line.strip() for line in result.stdout.split(\"\\n\")]\n    if mode:\n        assert any(c in result.stdout for c in rounded)\n    help_start = result_lines.index(\"First line\")\n    arg_start = [i for i, row in enumerate(result_lines) if \"Arguments\" in row][0]\n    assert help_start != -1\n    assert result_lines[help_start:arg_start] == lines\n\n\ndef test_markup_mode_default():\n    # We're assuming the test suite is run with rich installed\n    app = typer.Typer()\n    assert app.rich_markup_mode == \"rich\"\n"
  },
  {
    "path": "tests/test_rich_utils.py",
    "content": "import sys\n\nimport pytest\nimport typer\nimport typer.completion\nfrom typer.testing import CliRunner\n\nfrom tests.utils import needs_rich\n\nrunner = CliRunner()\n\n\ndef test_rich_utils_click_rewrapp():\n    app = typer.Typer(rich_markup_mode=\"markdown\")\n\n    @app.command()\n    def main():\n        \"\"\"\n        \\b\n        Some text\n\n        Some unwrapped text\n        \"\"\"\n        print(\"Hello World\")\n\n    @app.command()\n    def secondary():\n        \"\"\"\n        \\b\n        Secondary text\n\n        Some unwrapped text\n        \"\"\"\n        print(\"Hello Secondary World\")\n\n    result = runner.invoke(app, [\"--help\"])\n    assert \"Some text\" in result.stdout\n    assert \"Secondary text\" in result.stdout\n    assert \"\\b\" not in result.stdout\n    result = runner.invoke(app, [\"main\"])\n    assert \"Hello World\" in result.stdout\n    result = runner.invoke(app, [\"secondary\"])\n    assert \"Hello Secondary World\" in result.stdout\n\n\ndef test_rich_help_no_commands():\n    \"\"\"Ensure that the help still works for a Typer instance with no commands, but with a callback.\"\"\"\n    app = typer.Typer(help=\"My cool Typer app\")\n\n    @app.callback(invoke_without_command=True, no_args_is_help=True)\n    def main() -> None:\n        return None  # pragma: no cover\n\n    result = runner.invoke(app, [\"--help\"])\n\n    assert result.exit_code == 0\n    assert \"Show this message\" in result.stdout\n\n\ndef test_rich_doesnt_print_None_default():\n    app = typer.Typer(rich_markup_mode=\"rich\")\n\n    @app.command()\n    def main(\n        name: str,\n        option_1: str = typer.Option(\n            \"option_1_default\",\n        ),\n        option_2: str = typer.Option(\n            ...,\n        ),\n    ):\n        print(f\"Hello {name}\")\n        print(f\"First: {option_1}\")\n        print(f\"Second: {option_2}\")\n\n    result = runner.invoke(app, [\"--help\"])\n    assert \"Usage\" in result.stdout\n    assert \"name\" in result.stdout\n    assert \"option-1\" in result.stdout\n    assert \"option-2\" in result.stdout\n    assert result.stdout.count(\"[default: None]\") == 0\n    result = runner.invoke(app, [\"Rick\", \"--option-2=Morty\"])\n    assert \"Hello Rick\" in result.stdout\n    assert \"First: option_1_default\" in result.stdout\n    assert \"Second: Morty\" in result.stdout\n\n\ndef test_rich_markup_import_regression():\n    # Remove rich.markup if it was imported by other tests\n    if \"rich\" in sys.modules:\n        rich_module = sys.modules[\"rich\"]\n        if hasattr(rich_module, \"markup\"):\n            delattr(rich_module, \"markup\")\n\n    app = typer.Typer(rich_markup_mode=None)\n\n    @app.command()\n    def main(bar: str):\n        pass  # pragma: no cover\n\n    result = runner.invoke(app, [\"--help\"])\n    assert \"Usage\" in result.stdout\n    assert \"BAR\" in result.stdout\n\n\n@needs_rich\n@pytest.mark.parametrize(\"input_text\", [\"[ARGS]\", \"[ARGS]...\"])\ndef test_metavar_highlighter(input_text: str):\n    \"\"\"\n    Test that the MetavarHighlighter works correctly.\n    cf PR 1508\n    \"\"\"\n    from typer.rich_utils import (\n        STYLE_METAVAR_SEPARATOR,\n        Text,\n        _get_rich_console,\n        metavar_highlighter,\n    )\n\n    console = _get_rich_console()\n\n    text = Text(input_text)\n    highlighted = metavar_highlighter(text)\n    console.print(highlighted)\n\n    # Get the style for each bracket\n    opening_bracket_style = highlighted.get_style_at_offset(console, 0)\n    closing_bracket_style = highlighted.get_style_at_offset(console, 5)\n\n    # The opening bracket should have metavar_sep style\n    assert str(opening_bracket_style) == STYLE_METAVAR_SEPARATOR\n\n    # The closing bracket should have metavar_sep style (fails before PR 1508 when there are 3 dots)\n    assert str(closing_bracket_style) == STYLE_METAVAR_SEPARATOR\n\n\ndef test_make_rich_text_with_ansi_escape_sequences():\n    from typer.rich_utils import Text, _make_rich_text\n\n    ansi_text = \"This is \\x1b[4munderlined\\x1b[0m text\"\n    result = _make_rich_text(text=ansi_text, markup_mode=\"rich\")\n\n    assert isinstance(result, Text)\n    assert \"\\x1b[\" not in result.plain\n    assert \"underlined\" in result.plain\n\n    mixed_text = \"Start \\x1b[31mred\\x1b[0m middle \\x1b[32mgreen\\x1b[0m end\"\n    result = _make_rich_text(text=mixed_text, markup_mode=\"rich\")\n    assert isinstance(result, Text)\n    assert \"\\x1b[\" not in result.plain\n    assert \"red\" in result.plain\n    assert \"green\" in result.plain\n\n    fake_ansi = \"This contains \\x1b[ but not a complete sequence\"\n    result = _make_rich_text(text=fake_ansi, markup_mode=\"rich\")\n    assert isinstance(result, Text)\n    assert \"\\x1b[\" not in result.plain\n    assert \"This contains \" in result.plain\n\n\ndef test_make_rich_text_with_typer_style_in_help():\n    app = typer.Typer()\n\n    @app.command()\n    def example(\n        a: str = typer.Option(help=\"This is A\"),\n        b: str = typer.Option(help=f\"This is {typer.style('B', underline=True)}\"),\n    ):\n        \"\"\"Example command with styled help text.\"\"\"\n        pass  # pragma: no cover\n\n    result = runner.invoke(app, [\"--help\"])\n\n    assert result.exit_code == 0\n    assert \"This is A\" in result.stdout\n    assert \"This is B\" in result.stdout\n    assert \"\\x1b[\" not in result.stdout\n\n\ndef test_help_table_alignment_with_styled_text():\n    app = typer.Typer()\n\n    @app.command()\n    def example(\n        a: str = typer.Option(help=\"This is A\"),\n        b: str = typer.Option(help=f\"This is {typer.style('B', underline=True)}\"),\n        c: str = typer.Option(help=\"This is C\"),\n    ):\n        \"\"\"Example command with styled help text.\"\"\"\n        pass  # pragma: no cover\n\n    result = runner.invoke(app, [\"--help\"])\n\n    assert result.exit_code == 0\n\n    lines = result.stdout.split(\"\\n\")\n\n    option_a_line = None\n    option_b_line = None\n    option_c_line = None\n\n    for line in lines:\n        if \"--a\" in line and \"This is A\" in line:\n            option_a_line = line\n        elif \"--b\" in line and \"This is B\" in line:\n            option_b_line = line\n        elif \"--c\" in line and \"This is C\" in line:\n            option_c_line = line\n\n    assert option_a_line is not None, \"Option A line not found\"\n    assert option_b_line is not None, \"Option B line not found\"\n    assert option_c_line is not None, \"Option C line not found\"\n\n    def find_right_boundary_pos(line):\n        return line.rfind(\"|\")\n\n    pos_a = find_right_boundary_pos(option_a_line)\n    pos_b = find_right_boundary_pos(option_b_line)\n    pos_c = find_right_boundary_pos(option_c_line)\n\n    assert pos_a == pos_b == pos_c, (\n        f\"Right boundaries not aligned: A={pos_a}, B={pos_b}, C={pos_c}\"\n    )\n"
  },
  {
    "path": "tests/test_suggest_commands.py",
    "content": "import typer\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\n\n\ndef test_typo_suggestion_enabled():\n    \"\"\"Test that typo suggestions work when enabled\"\"\"\n    app = typer.Typer()\n\n    @app.command()\n    def create():  # pragma: no cover\n        typer.echo(\"Creating...\")\n\n    @app.command()\n    def delete():  # pragma: no cover\n        typer.echo(\"Deleting...\")\n\n    result = runner.invoke(app, [\"crate\"])\n    assert result.exit_code != 0\n    assert \"No such command\" in result.output\n    assert \"Did you mean 'create'?\" in result.output\n\n\ndef test_typo_suggestion_multiple_matches():\n    \"\"\"Test that multiple suggestions are shown when there are multiple close matches\"\"\"\n    app = typer.Typer()\n\n    @app.command()\n    def create():  # pragma: no cover\n        typer.echo(\"Creating...\")\n\n    @app.command()\n    def createnew():  # pragma: no cover\n        typer.echo(\"Creating new...\")\n\n    result = runner.invoke(app, [\"crate\"])\n    assert result.exit_code != 0\n    assert \"No such command\" in result.output\n    assert \"Did you mean\" in result.output\n    assert \"create\" in result.output and \"createnew\" in result.output\n\n\ndef test_typo_suggestion_no_matches():\n    \"\"\"Test that no suggestions are shown when there are no close matches\"\"\"\n    app = typer.Typer()\n\n    @app.command()\n    def create():  # pragma: no cover\n        typer.echo(\"Creating...\")\n\n    @app.command()\n    def delete():  # pragma: no cover\n        typer.echo(\"Deleting...\")\n\n    result = runner.invoke(app, [\"xyz\"])\n    assert result.exit_code != 0\n    assert \"No such command\" in result.output\n    assert \"Did you mean\" not in result.output\n\n\ndef test_typo_suggestion_exact_match_works():\n    \"\"\"Test that exact matches still work normally\"\"\"\n    app = typer.Typer()\n\n    @app.command()\n    def create():\n        typer.echo(\"Creating...\")\n\n    @app.command()\n    def delete():\n        typer.echo(\"Deleting...\")\n\n    result = runner.invoke(app, [\"create\"])\n    assert result.exit_code == 0\n    assert \"Creating...\" in result.output\n\n    result = runner.invoke(app, [\"delete\"])\n    assert result.exit_code == 0\n    assert \"Deleting...\" in result.output\n\n\ndef test_typo_suggestion_disabled():\n    \"\"\"Test that typo suggestions can be explicitly disabled\"\"\"\n    app = typer.Typer(suggest_commands=False)\n\n    @app.command()\n    def create():  # pragma: no cover\n        typer.echo(\"Creating...\")\n\n    @app.command()\n    def delete():  # pragma: no cover\n        typer.echo(\"Deleting...\")\n\n    result = runner.invoke(app, [\"crate\"])\n    assert result.exit_code != 0\n    assert \"No such command\" in result.output\n    assert \"Did you mean\" not in result.output\n"
  },
  {
    "path": "tests/test_tracebacks.py",
    "content": "import os\nimport subprocess\nimport sys\nfrom pathlib import Path\n\n\ndef test_traceback_no_rich():\n    file_path = Path(__file__).parent / \"assets/type_error_no_rich.py\"\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", str(file_path)],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={\n            **os.environ,\n            \"TYPER_STANDARD_TRACEBACK\": \"\",\n            \"_TYPER_STANDARD_TRACEBACK\": \"\",\n        },\n    )\n    assert \"return get_command(self)(*args, **kwargs)\" not in result.stderr\n\n    assert \"app()\" in result.stderr\n    assert \"print(name + 3)\" in result.stderr\n    assert 'TypeError: can only concatenate str (not \"int\") to str' in result.stderr\n\n\ndef test_traceback_no_rich_short_disable():\n    file_path = Path(__file__).parent / \"assets/type_error_no_rich_short_disable.py\"\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", str(file_path)],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={\n            **os.environ,\n            \"TYPER_STANDARD_TRACEBACK\": \"\",\n            \"_TYPER_STANDARD_TRACEBACK\": \"\",\n        },\n    )\n    assert \"return get_command(self)(*args, **kwargs)\" not in result.stderr\n\n    assert \"app()\" in result.stderr\n    assert \"print(name + 3)\" in result.stderr\n    assert 'TypeError: can only concatenate str (not \"int\") to str' in result.stderr\n\n\ndef test_unmodified_traceback():\n    file_path = Path(__file__).parent / \"assets/type_error_normal_traceback.py\"\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", str(file_path)],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={\n            **os.environ,\n            \"TYPER_STANDARD_TRACEBACK\": \"\",\n            \"_TYPER_STANDARD_TRACEBACK\": \"\",\n        },\n    )\n    assert \"morty\" in result.stdout, \"the call to the first app should work normally\"\n    assert \"return callback(**use_params)\" in result.stderr, (\n        \"calling outside of Typer should show the normal traceback, \"\n        \"even after the hook is installed\"\n    )\n    assert \"typer.main.get_command(broken_app)()\" in result.stderr\n    assert \"print(name + 3)\" in result.stderr\n    assert 'TypeError: can only concatenate str (not \"int\") to str' in result.stderr\n"
  },
  {
    "path": "tests/test_tutorial/__init__.py",
    "content": ""
  },
  {
    "path": "tests/test_tutorial/test_app_dir/__init__.py",
    "content": ""
  },
  {
    "path": "tests/test_tutorial/test_app_dir/test_tutorial001.py",
    "content": "import subprocess\nimport sys\nfrom pathlib import Path\n\nimport pytest\nimport typer\nfrom typer.testing import CliRunner\n\nfrom docs_src.app_dir import tutorial001_py310 as mod\n\nrunner = CliRunner()\n\n\n@pytest.fixture(name=\"config_file\")\ndef create_config_file():\n    app_dir = Path(typer.get_app_dir(\"my-super-cli-app\"))\n    app_dir.mkdir(parents=True, exist_ok=True)\n    config_path = app_dir / \"config.json\"\n    config_path.touch(exist_ok=True)\n\n    yield config_path\n\n    config_path.unlink()\n    app_dir.rmdir()\n\n\ndef test_cli_config_doesnt_exist():\n    result = runner.invoke(mod.app)\n    assert result.exit_code == 0\n    assert \"Config file doesn't exist yet\" in result.output\n\n\ndef test_cli_config_exists(config_file: Path):\n    result = runner.invoke(mod.app)\n    assert result.exit_code == 0\n    assert \"Config file doesn't exist yet\" not in result.output\n\n\ndef test_script():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_arguments/__init__.py",
    "content": ""
  },
  {
    "path": "tests/test_tutorial/test_arguments/test_default/__init__.py",
    "content": ""
  },
  {
    "path": "tests/test_tutorial/test_arguments/test_default/test_tutorial001.py",
    "content": "import importlib\nimport subprocess\nimport sys\nfrom types import ModuleType\n\nimport pytest\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\n\n\n@pytest.fixture(\n    name=\"mod\",\n    params=[\n        pytest.param(\"tutorial001_py310\"),\n        pytest.param(\"tutorial001_an_py310\"),\n    ],\n)\ndef get_mod(request: pytest.FixtureRequest) -> ModuleType:\n    module_name = f\"docs_src.arguments.default.{request.param}\"\n    mod = importlib.import_module(module_name)\n    return mod\n\n\ndef test_help(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--help\"])\n    assert result.exit_code == 0\n    assert \"[OPTIONS] [NAME]\" in result.output\n    assert \"Arguments\" in result.output\n    assert \"[default: Wade Wilson]\" in result.output\n\n\ndef test_call_no_arg(mod: ModuleType):\n    result = runner.invoke(mod.app)\n    assert result.exit_code == 0\n    assert \"Hello Wade Wilson\" in result.output\n\n\ndef test_call_arg(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"Camila\"])\n    assert result.exit_code == 0\n    assert \"Hello Camila\" in result.output\n\n\ndef test_script(mod: ModuleType):\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_arguments/test_default/test_tutorial002.py",
    "content": "import importlib\nimport subprocess\nimport sys\nfrom types import ModuleType\n\nimport pytest\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\n\n\n@pytest.fixture(\n    name=\"mod\",\n    params=[\n        pytest.param(\"tutorial002_py310\"),\n        pytest.param(\"tutorial002_an_py310\"),\n    ],\n)\ndef get_mod(request: pytest.FixtureRequest) -> ModuleType:\n    module_name = f\"docs_src.arguments.default.{request.param}\"\n    mod = importlib.import_module(module_name)\n    return mod\n\n\ndef test_help(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--help\"])\n    assert result.exit_code == 0\n    assert \"[OPTIONS] [NAME]\" in result.output\n    assert \"Arguments\" in result.output\n    assert \"[default: (dynamic)]\" in result.output\n\n\ndef test_call_no_arg(mod: ModuleType):\n    greetings = [\"Hello Deadpool\", \"Hello Rick\", \"Hello Morty\", \"Hello Hiro\"]\n    for _i in range(3):\n        result = runner.invoke(mod.app)\n        assert result.exit_code == 0\n        assert any(greet in result.output for greet in greetings)\n\n\ndef test_call_arg(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"Camila\"])\n    assert result.exit_code == 0\n    assert \"Hello Camila\" in result.output\n\n\ndef test_script(mod: ModuleType):\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_arguments/test_envvar/__init__.py",
    "content": ""
  },
  {
    "path": "tests/test_tutorial/test_arguments/test_envvar/test_tutorial001.py",
    "content": "import importlib\nimport subprocess\nimport sys\nfrom types import ModuleType\n\nimport pytest\nimport typer\nimport typer.core\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\n\n\n@pytest.fixture(\n    name=\"mod\",\n    params=[\n        pytest.param(\"tutorial001_py310\"),\n        pytest.param(\"tutorial001_an_py310\"),\n    ],\n)\ndef get_mod(request: pytest.FixtureRequest) -> ModuleType:\n    module_name = f\"docs_src.arguments.envvar.{request.param}\"\n    mod = importlib.import_module(module_name)\n    return mod\n\n\ndef test_help(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--help\"])\n    assert result.exit_code == 0\n    assert \"[OPTIONS] [NAME]\" in result.output\n    assert \"Arguments\" in result.output\n    assert \"env var: AWESOME_NAME\" in result.output\n    assert \"default: World\" in result.output\n\n\ndef test_help_no_rich(monkeypatch: pytest.MonkeyPatch, mod: ModuleType):\n    monkeypatch.setattr(typer.core, \"HAS_RICH\", False)\n    result = runner.invoke(mod.app, [\"--help\"])\n    assert result.exit_code == 0\n    assert \"[OPTIONS] [NAME]\" in result.output\n    assert \"Arguments\" in result.output\n    assert \"env var: AWESOME_NAME\" in result.output\n    assert \"default: World\" in result.output\n\n\ndef test_call_arg(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"Wednesday\"])\n    assert result.exit_code == 0\n    assert \"Hello Mr. Wednesday\" in result.output\n\n\ndef test_call_env_var(mod: ModuleType):\n    result = runner.invoke(mod.app, env={\"AWESOME_NAME\": \"Wednesday\"})\n    assert result.exit_code == 0\n    assert \"Hello Mr. Wednesday\" in result.output\n\n\ndef test_call_env_var_arg(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"Czernobog\"], env={\"AWESOME_NAME\": \"Wednesday\"})\n    assert result.exit_code == 0\n    assert \"Hello Mr. Czernobog\" in result.output\n\n\ndef test_script(mod: ModuleType):\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_arguments/test_envvar/test_tutorial002.py",
    "content": "import importlib\nimport subprocess\nimport sys\nfrom types import ModuleType\n\nimport pytest\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\n\n\n@pytest.fixture(\n    name=\"mod\",\n    params=[\n        pytest.param(\"tutorial002_py310\"),\n        pytest.param(\"tutorial002_an_py310\"),\n    ],\n)\ndef get_mod(request: pytest.FixtureRequest) -> ModuleType:\n    module_name = f\"docs_src.arguments.envvar.{request.param}\"\n    mod = importlib.import_module(module_name)\n    return mod\n\n\ndef test_help(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--help\"])\n    assert result.exit_code == 0\n    assert \"[OPTIONS] [NAME]\" in result.output\n    assert \"Arguments\" in result.output\n    assert \"env var: AWESOME_NAME, GOD_NAME\" in result.output\n    assert \"default: World\" in result.output\n\n\ndef test_call_arg(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"Wednesday\"])\n    assert result.exit_code == 0\n    assert \"Hello Mr. Wednesday\" in result.output\n\n\ndef test_call_env_var1(mod: ModuleType):\n    result = runner.invoke(mod.app, env={\"AWESOME_NAME\": \"Wednesday\"})\n    assert result.exit_code == 0\n    assert \"Hello Mr. Wednesday\" in result.output\n\n\ndef test_call_env_var2(mod: ModuleType):\n    result = runner.invoke(mod.app, env={\"GOD_NAME\": \"Anubis\"})\n    assert result.exit_code == 0\n    assert \"Hello Mr. Anubis\" in result.output\n\n\ndef test_script(mod: ModuleType):\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_arguments/test_envvar/test_tutorial003.py",
    "content": "import importlib\nimport subprocess\nimport sys\nfrom types import ModuleType\n\nimport pytest\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\n\n\n@pytest.fixture(\n    name=\"mod\",\n    params=[\n        pytest.param(\"tutorial003_py310\"),\n        pytest.param(\"tutorial003_an_py310\"),\n    ],\n)\ndef get_mod(request: pytest.FixtureRequest) -> ModuleType:\n    module_name = f\"docs_src.arguments.envvar.{request.param}\"\n    mod = importlib.import_module(module_name)\n    return mod\n\n\ndef test_help(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--help\"])\n    assert result.exit_code == 0\n    assert \"[OPTIONS] [NAME]\" in result.output\n    assert \"Arguments\" in result.output\n    assert \"env var: AWESOME_NAME\" not in result.output\n    assert \"default: World\" in result.output\n\n\ndef test_call_arg(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"Wednesday\"])\n    assert result.exit_code == 0\n    assert \"Hello Mr. Wednesday\" in result.output\n\n\ndef test_call_env_var(mod: ModuleType):\n    result = runner.invoke(mod.app, env={\"AWESOME_NAME\": \"Wednesday\"})\n    assert result.exit_code == 0\n    assert \"Hello Mr. Wednesday\" in result.output\n\n\ndef test_call_env_var_arg(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"Czernobog\"], env={\"AWESOME_NAME\": \"Wednesday\"})\n    assert result.exit_code == 0\n    assert \"Hello Mr. Czernobog\" in result.output\n\n\ndef test_script(mod: ModuleType):\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_arguments/test_help/__init__.py",
    "content": ""
  },
  {
    "path": "tests/test_tutorial/test_arguments/test_help/test_tutorial001.py",
    "content": "import importlib\nimport subprocess\nimport sys\nfrom types import ModuleType\n\nimport pytest\nimport typer\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\n\n\n@pytest.fixture(\n    name=\"mod\",\n    params=[\n        pytest.param(\"tutorial001_py310\"),\n        pytest.param(\"tutorial001_an_py310\"),\n    ],\n)\ndef get_mod(request: pytest.FixtureRequest) -> ModuleType:\n    module_name = f\"docs_src.arguments.help.{request.param}\"\n    mod = importlib.import_module(module_name)\n    return mod\n\n\ndef test_help(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--help\"])\n    assert result.exit_code == 0\n    assert \"[OPTIONS] NAME\" in result.output\n    assert \"Arguments\" in result.output\n    assert \"NAME\" in result.output\n    assert \"The name of the user to greet\" in result.output\n    assert \"[required]\" in result.output\n\n\ndef test_help_no_rich(monkeypatch: pytest.MonkeyPatch, mod: ModuleType):\n    monkeypatch.setattr(typer.core, \"HAS_RICH\", False)\n    result = runner.invoke(mod.app, [\"--help\"])\n    assert result.exit_code == 0\n    assert \"[OPTIONS] NAME\" in result.output\n    assert \"Arguments\" in result.output\n    assert \"NAME\" in result.output\n    assert \"The name of the user to greet\" in result.output\n    assert \"[required]\" in result.output\n\n\ndef test_call_arg(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"Camila\"])\n    assert result.exit_code == 0\n    assert \"Hello Camila\" in result.output\n\n\ndef test_script(mod: ModuleType):\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_arguments/test_help/test_tutorial002.py",
    "content": "import importlib\nimport subprocess\nimport sys\nfrom types import ModuleType\n\nimport pytest\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\n\n\n@pytest.fixture(\n    name=\"mod\",\n    params=[\n        pytest.param(\"tutorial002_py310\"),\n        pytest.param(\"tutorial002_an_py310\"),\n    ],\n)\ndef get_mod(request: pytest.FixtureRequest) -> ModuleType:\n    module_name = f\"docs_src.arguments.help.{request.param}\"\n    mod = importlib.import_module(module_name)\n    return mod\n\n\ndef test_help(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--help\"])\n    assert result.exit_code == 0\n    assert \"[OPTIONS] NAME\" in result.output\n    assert \"Say hi to NAME very gently, like Dirk.\" in result.output\n    assert \"Arguments\" in result.output\n    assert \"NAME\" in result.output\n    assert \"The name of the user to greet\" in result.output\n    assert \"[required]\" in result.output\n\n\ndef test_call_arg(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"Camila\"])\n    assert result.exit_code == 0\n    assert \"Hello Camila\" in result.output\n\n\ndef test_script(mod: ModuleType):\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_arguments/test_help/test_tutorial003.py",
    "content": "import importlib\nimport subprocess\nimport sys\nfrom types import ModuleType\n\nimport pytest\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\n\n\n@pytest.fixture(\n    name=\"mod\",\n    params=[\n        pytest.param(\"tutorial003_py310\"),\n        pytest.param(\"tutorial003_an_py310\"),\n    ],\n)\ndef get_mod(request: pytest.FixtureRequest) -> ModuleType:\n    module_name = f\"docs_src.arguments.help.{request.param}\"\n    mod = importlib.import_module(module_name)\n    return mod\n\n\ndef test_help(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--help\"])\n    assert result.exit_code == 0\n    assert \"[OPTIONS] [NAME]\" in result.output\n    assert \"Say hi to NAME very gently, like Dirk.\" in result.output\n    assert \"Arguments\" in result.output\n    assert \"NAME\" in result.output\n    assert \"Who to greet\" in result.output\n    assert \"[default: World]\" in result.output\n\n\ndef test_call_arg(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"Camila\"])\n    assert result.exit_code == 0\n    assert \"Hello Camila\" in result.output\n\n\ndef test_script(mod: ModuleType):\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_arguments/test_help/test_tutorial004.py",
    "content": "import importlib\nimport subprocess\nimport sys\nfrom types import ModuleType\n\nimport pytest\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\n\n\n@pytest.fixture(\n    name=\"mod\",\n    params=[\n        pytest.param(\"tutorial004_py310\"),\n        pytest.param(\"tutorial004_an_py310\"),\n    ],\n)\ndef get_mod(request: pytest.FixtureRequest) -> ModuleType:\n    module_name = f\"docs_src.arguments.help.{request.param}\"\n    mod = importlib.import_module(module_name)\n    return mod\n\n\ndef test_help(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--help\"])\n    assert result.exit_code == 0\n    assert \"[OPTIONS] [NAME]\" in result.output\n    assert \"Say hi to NAME very gently, like Dirk.\" in result.output\n    assert \"Arguments\" in result.output\n    assert \"NAME\" in result.output\n    assert \"Who to greet\" in result.output\n    assert \"[default: World]\" not in result.output\n\n\ndef test_call_arg(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"Camila\"])\n    assert result.exit_code == 0\n    assert \"Hello Camila\" in result.output\n\n\ndef test_script(mod: ModuleType):\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_arguments/test_help/test_tutorial005.py",
    "content": "import importlib\nimport subprocess\nimport sys\nfrom types import ModuleType\n\nimport pytest\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\n\n\n@pytest.fixture(\n    name=\"mod\",\n    params=[\n        pytest.param(\"tutorial005_py310\"),\n        pytest.param(\"tutorial005_an_py310\"),\n    ],\n)\ndef get_mod(request: pytest.FixtureRequest) -> ModuleType:\n    module_name = f\"docs_src.arguments.help.{request.param}\"\n    mod = importlib.import_module(module_name)\n    return mod\n\n\ndef test_help(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--help\"])\n    assert result.exit_code == 0\n    assert \"Usage: main [OPTIONS] [NAME]\" in result.output\n    assert \"Arguments\" in result.output\n    assert \"Who to greet\" in result.output\n    assert \"[default: (Deadpoolio the amazing's name)]\" in result.output\n\n\ndef test_call_arg(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"Camila\"])\n    assert result.exit_code == 0\n    assert \"Hello Camila\" in result.output\n\n\ndef test_script(mod: ModuleType):\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_arguments/test_help/test_tutorial006.py",
    "content": "import importlib\nimport subprocess\nimport sys\nfrom types import ModuleType\n\nimport pytest\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\n\n\n@pytest.fixture(\n    name=\"mod\",\n    params=[\n        pytest.param(\"tutorial006_py310\"),\n        pytest.param(\"tutorial006_an_py310\"),\n    ],\n)\ndef get_mod(request: pytest.FixtureRequest) -> ModuleType:\n    module_name = f\"docs_src.arguments.help.{request.param}\"\n    mod = importlib.import_module(module_name)\n    return mod\n\n\ndef test_help(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--help\"])\n    assert result.exit_code == 0\n    assert \"Usage: main [OPTIONS] [✨username✨]\" in result.output\n    assert \"Arguments\" in result.output\n    assert \"[default: World]\" in result.output\n\n\ndef test_call_arg(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"Camila\"])\n    assert result.exit_code == 0\n    assert \"Hello Camila\" in result.output\n\n\ndef test_script(mod: ModuleType):\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_arguments/test_help/test_tutorial007.py",
    "content": "import importlib\nimport subprocess\nimport sys\nfrom types import ModuleType\n\nimport pytest\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\n\n\n@pytest.fixture(\n    name=\"mod\",\n    params=[\n        pytest.param(\"tutorial007_py310\"),\n        pytest.param(\"tutorial007_an_py310\"),\n    ],\n)\ndef get_mod(request: pytest.FixtureRequest) -> ModuleType:\n    module_name = f\"docs_src.arguments.help.{request.param}\"\n    mod = importlib.import_module(module_name)\n    return mod\n\n\ndef test_help(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--help\"])\n    assert result.exit_code == 0\n    assert \"Say hi to NAME very gently, like Dirk.\" in result.output\n    assert \"Arguments\" in result.output\n    assert \"Secondary Arguments\" in result.output\n\n\ndef test_call_arg(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"Camila\"])\n    assert result.exit_code == 0\n    assert \"Hello Camila\" in result.output\n\n\ndef test_script(mod: ModuleType):\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_arguments/test_help/test_tutorial008.py",
    "content": "import importlib\nimport subprocess\nimport sys\nfrom types import ModuleType\n\nimport pytest\nimport typer\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\n\n\n@pytest.fixture(\n    name=\"mod\",\n    params=[\n        pytest.param(\"tutorial008_py310\"),\n        pytest.param(\"tutorial008_an_py310\"),\n    ],\n)\ndef get_mod(request: pytest.FixtureRequest) -> ModuleType:\n    module_name = f\"docs_src.arguments.help.{request.param}\"\n    mod = importlib.import_module(module_name)\n    return mod\n\n\ndef test_help(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--help\"])\n    assert result.exit_code == 0\n    assert \"[OPTIONS] [NAME]\" in result.output\n    assert \"Say hi to NAME very gently, like Dirk.\" in result.output\n    assert \"Arguments\" not in result.output\n    assert \"[default: World]\" not in result.output\n\n\ndef test_help_no_rich(monkeypatch: pytest.MonkeyPatch, mod: ModuleType):\n    monkeypatch.setattr(typer.core, \"HAS_RICH\", False)\n    result = runner.invoke(mod.app, [\"--help\"])\n    assert result.exit_code == 0\n    assert \"[OPTIONS] [NAME]\" in result.output\n    assert \"Say hi to NAME very gently, like Dirk.\" in result.output\n    assert \"Arguments\" not in result.output\n    assert \"[default: World]\" not in result.output\n\n\ndef test_call_arg(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"Camila\"])\n    assert result.exit_code == 0\n    assert \"Hello Camila\" in result.output\n\n\ndef test_script(mod: ModuleType):\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_arguments/test_optional/__init__.py",
    "content": ""
  },
  {
    "path": "tests/test_tutorial/test_arguments/test_optional/test_tutorial000.py",
    "content": "import importlib\nimport subprocess\nimport sys\nfrom types import ModuleType\n\nimport pytest\nimport typer\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\n\n\n@pytest.fixture(\n    name=\"mod\",\n    params=[\n        pytest.param(\"tutorial000_py310\"),\n        pytest.param(\"tutorial000_an_py310\"),\n    ],\n)\ndef get_mod(request: pytest.FixtureRequest) -> ModuleType:\n    module_name = f\"docs_src.arguments.optional.{request.param}\"\n    mod = importlib.import_module(module_name)\n    return mod\n\n\n@pytest.fixture(name=\"app\")\ndef get_app(mod: ModuleType) -> typer.Typer:\n    app = typer.Typer()\n    app.command()(mod.main)\n    return app\n\n\ndef test_cli(app: typer.Typer):\n    result = runner.invoke(app, [\"World\"])\n    assert result.exit_code == 0\n    assert \"Hello World\" in result.output\n\n\ndef test_cli_missing_argument(app: typer.Typer):\n    result = runner.invoke(app)\n    assert result.exit_code == 2\n    assert \"Missing argument 'NAME'\" in result.output\n\n\ndef test_script(mod: ModuleType):\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_arguments/test_optional/test_tutorial001.py",
    "content": "import importlib\nimport subprocess\nimport sys\nfrom types import ModuleType\n\nimport pytest\nimport typer\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\n\n\n@pytest.fixture(\n    name=\"mod\",\n    params=[\n        pytest.param(\"tutorial001_py310\"),\n        pytest.param(\"tutorial001_an_py310\"),\n    ],\n)\ndef get_mod(request: pytest.FixtureRequest) -> ModuleType:\n    module_name = f\"docs_src.arguments.optional.{request.param}\"\n    mod = importlib.import_module(module_name)\n    return mod\n\n\ndef test_call_no_arg(mod: ModuleType):\n    result = runner.invoke(mod.app)\n    assert result.exit_code != 0\n    assert \"Missing argument 'NAME'.\" in result.output\n\n\ndef test_call_no_arg_standalone(mod: ModuleType):\n    # Mainly for coverage\n    result = runner.invoke(mod.app, standalone_mode=False)\n    assert result.exit_code != 0\n\n\ndef test_call_no_arg_no_rich(monkeypatch: pytest.MonkeyPatch, mod: ModuleType):\n    # Mainly for coverage\n    monkeypatch.setattr(typer.core, \"HAS_RICH\", False)\n    result = runner.invoke(mod.app)\n    assert result.exit_code != 0\n    assert \"Error: Missing argument 'NAME'\" in result.output\n\n\ndef test_call_arg(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"Camila\"])\n    assert result.exit_code == 0\n    assert \"Hello Camila\" in result.output\n\n\ndef test_script(mod: ModuleType):\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_arguments/test_optional/test_tutorial002.py",
    "content": "import importlib\nimport subprocess\nimport sys\nfrom types import ModuleType\n\nimport pytest\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\n\n\n@pytest.fixture(\n    name=\"mod\",\n    params=[\n        pytest.param(\"tutorial002_py310\"),\n        pytest.param(\"tutorial002_an_py310\"),\n    ],\n)\ndef get_mod(request: pytest.FixtureRequest) -> ModuleType:\n    module_name = f\"docs_src.arguments.optional.{request.param}\"\n    mod = importlib.import_module(module_name)\n    return mod\n\n\ndef test_help(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--help\"])\n    assert result.exit_code == 0\n    assert \"[OPTIONS] [NAME]\" in result.output\n\n\ndef test_call_no_arg(mod: ModuleType):\n    result = runner.invoke(mod.app)\n    assert result.exit_code == 0\n    assert \"Hello World!\" in result.output\n\n\ndef test_call_arg(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"Camila\"])\n    assert result.exit_code == 0\n    assert \"Hello Camila\" in result.output\n\n\ndef test_script(mod: ModuleType):\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_arguments/test_optional/test_tutorial003.py",
    "content": "import subprocess\nimport sys\n\nimport pytest\nimport typer\nimport typer.core\nfrom typer.testing import CliRunner\n\nfrom docs_src.arguments.optional import tutorial003_py310 as mod\n\nrunner = CliRunner()\napp = mod.app\n\n\ndef test_call_no_arg():\n    result = runner.invoke(app)\n    assert result.exit_code != 0\n    assert \"Missing argument 'NAME'.\" in result.output\n\n\ndef test_call_no_arg_standalone():\n    # Mainly for coverage\n    result = runner.invoke(app, standalone_mode=False)\n    assert result.exit_code != 0\n\n\ndef test_call_no_arg_no_rich(monkeypatch: pytest.MonkeyPatch):\n    # Mainly for coverage\n    monkeypatch.setattr(typer.core, \"HAS_RICH\", False)\n    result = runner.invoke(app)\n    assert result.exit_code != 0\n    assert \"Error: Missing argument 'NAME'\" in result.output\n\n\ndef test_call_arg():\n    result = runner.invoke(app, [\"Camila\"])\n    assert result.exit_code == 0\n    assert \"Hello Camila\" in result.output\n\n\ndef test_script():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_commands/__init__.py",
    "content": ""
  },
  {
    "path": "tests/test_tutorial/test_commands/test_arguments/__init__.py",
    "content": ""
  },
  {
    "path": "tests/test_tutorial/test_commands/test_arguments/test_tutorial001.py",
    "content": "import subprocess\nimport sys\n\nfrom typer.testing import CliRunner\n\nfrom docs_src.commands.arguments import tutorial001_py310 as mod\n\napp = mod.app\n\nrunner = CliRunner()\n\n\ndef test_help_create():\n    result = runner.invoke(app, [\"create\", \"--help\"])\n    assert result.exit_code == 0\n    assert \"create [OPTIONS] USERNAME\" in result.output\n\n\ndef test_help_delete():\n    result = runner.invoke(app, [\"delete\", \"--help\"])\n    assert result.exit_code == 0\n    assert \"delete [OPTIONS] USERNAME\" in result.output\n\n\ndef test_create():\n    result = runner.invoke(app, [\"create\", \"Camila\"])\n    assert result.exit_code == 0\n    assert \"Creating user: Camila\" in result.output\n\n\ndef test_delete():\n    result = runner.invoke(app, [\"delete\", \"Camila\"])\n    assert result.exit_code == 0\n    assert \"Deleting user: Camila\" in result.output\n\n\ndef test_script():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_commands/test_callback/__init__.py",
    "content": ""
  },
  {
    "path": "tests/test_tutorial/test_commands/test_callback/test_tutorial001.py",
    "content": "import subprocess\nimport sys\n\nimport pytest\nimport typer.core\nfrom typer.testing import CliRunner\n\nfrom docs_src.commands.callback import tutorial001_py310 as mod\n\napp = mod.app\n\nrunner = CliRunner()\n\n\ndef test_help():\n    result = runner.invoke(app, [\"--help\"])\n    assert result.exit_code == 0\n    assert \"Manage users in the awesome CLI app.\" in result.output\n    assert \"--verbose\" in result.output\n    assert \"--no-verbose\" in result.output\n\n\ndef test_help_no_rich(monkeypatch: pytest.MonkeyPatch):\n    monkeypatch.setattr(typer.core, \"HAS_RICH\", False)\n    result = runner.invoke(app, [\"--help\"])\n    assert result.exit_code == 0\n    assert \"Manage users in the awesome CLI app.\" in result.output\n    assert \"--verbose\" in result.output\n    assert \"--no-verbose\" in result.output\n\n\ndef test_create():\n    result = runner.invoke(app, [\"create\", \"Camila\"])\n    assert result.exit_code == 0\n    assert \"Creating user: Camila\" in result.output\n\n\ndef test_create_verbose():\n    result = runner.invoke(app, [\"--verbose\", \"create\", \"Camila\"])\n    assert result.exit_code == 0\n    assert \"Will write verbose output\" in result.output\n    assert \"About to create a user\" in result.output\n    assert \"Creating user: Camila\" in result.output\n    assert \"Just created a user\" in result.output\n\n\ndef test_delete():\n    result = runner.invoke(app, [\"delete\", \"Camila\"])\n    assert result.exit_code == 0\n    assert \"Deleting user: Camila\" in result.output\n\n\ndef test_delete_verbose():\n    result = runner.invoke(app, [\"--verbose\", \"delete\", \"Camila\"])\n    assert result.exit_code == 0\n    assert \"Will write verbose output\" in result.output\n    assert \"About to delete a user\" in result.output\n    assert \"Deleting user: Camila\" in result.output\n    assert \"Just deleted a user\" in result.output\n\n\ndef test_wrong_verbose():\n    result = runner.invoke(app, [\"delete\", \"--verbose\", \"Camila\"])\n    assert result.exit_code != 0\n    assert \"No such option: --verbose\" in result.output\n\n\ndef test_script():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_commands/test_callback/test_tutorial002.py",
    "content": "import subprocess\nimport sys\n\nfrom typer.testing import CliRunner\n\nfrom docs_src.commands.callback import tutorial002_py310 as mod\n\napp = mod.app\n\nrunner = CliRunner()\n\n\ndef test_app():\n    result = runner.invoke(app, [\"create\", \"Camila\"])\n    assert result.exit_code == 0\n    assert \"Running a command\" in result.output\n    assert \"Creating user: Camila\" in result.output\n\n\ndef test_script():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_commands/test_callback/test_tutorial003.py",
    "content": "import subprocess\nimport sys\n\nfrom typer.testing import CliRunner\n\nfrom docs_src.commands.callback import tutorial003_py310 as mod\n\napp = mod.app\n\nrunner = CliRunner()\n\n\ndef test_app():\n    result = runner.invoke(app, [\"create\", \"Camila\"])\n    assert result.exit_code == 0\n    assert \"Override callback, running a command\" in result.output\n    assert \"Running a command\" not in result.output\n    assert \"Creating user: Camila\" in result.output\n\n\ndef test_for_coverage():\n    mod.callback()\n\n\ndef test_script():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_commands/test_callback/test_tutorial004.py",
    "content": "import subprocess\nimport sys\n\nfrom typer.testing import CliRunner\n\nfrom docs_src.commands.callback import tutorial004_py310 as mod\n\napp = mod.app\n\nrunner = CliRunner()\n\n\ndef test_help():\n    result = runner.invoke(app, [\"--help\"])\n    assert result.exit_code == 0\n    assert \"Manage users CLI app.\" in result.output\n    assert \"Use it with the create command.\" in result.output\n    assert \"A new user with the given NAME will be created.\" in result.output\n\n\ndef test_app():\n    result = runner.invoke(app, [\"create\", \"Camila\"])\n    assert result.exit_code == 0\n    assert \"Creating user: Camila\" in result.output\n\n\ndef test_script():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_commands/test_context/__init__.py",
    "content": ""
  },
  {
    "path": "tests/test_tutorial/test_commands/test_context/test_tutorial001.py",
    "content": "import subprocess\nimport sys\n\nfrom typer.testing import CliRunner\n\nfrom docs_src.commands.context import tutorial001_py310 as mod\n\napp = mod.app\n\nrunner = CliRunner()\n\n\ndef test_create():\n    result = runner.invoke(app, [\"create\", \"Camila\"])\n    assert result.exit_code == 0\n    assert \"About to execute command: create\" in result.output\n    assert \"Creating user: Camila\" in result.output\n\n\ndef test_delete():\n    result = runner.invoke(app, [\"delete\", \"Camila\"])\n    assert result.exit_code == 0\n    assert \"About to execute command: delete\" in result.output\n    assert \"Deleting user: Camila\" in result.output\n\n\ndef test_script():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_commands/test_context/test_tutorial002.py",
    "content": "import subprocess\nimport sys\n\nfrom typer.testing import CliRunner\n\nfrom docs_src.commands.context import tutorial002_py310 as mod\n\napp = mod.app\n\nrunner = CliRunner()\n\n\ndef test_create():\n    result = runner.invoke(app, [\"create\", \"Camila\"])\n    assert result.exit_code == 0\n    assert \"Initializing database\" in result.output\n    assert \"Creating user: Camila\" in result.output\n\n\ndef test_delete():\n    result = runner.invoke(app, [\"delete\", \"Camila\"])\n    assert result.exit_code == 0\n    assert \"Initializing database\" in result.output\n    assert \"Deleting user: Camila\" in result.output\n\n\ndef test_callback():\n    result = runner.invoke(app)\n    assert result.exit_code == 0\n    assert \"Initializing database\" in result.output\n\n\ndef test_script():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_commands/test_context/test_tutorial003.py",
    "content": "import subprocess\nimport sys\n\nfrom typer.testing import CliRunner\n\nfrom docs_src.commands.context import tutorial003_py310 as mod\n\napp = mod.app\n\nrunner = CliRunner()\n\n\ndef test_create():\n    result = runner.invoke(app, [\"create\", \"Camila\"])\n    assert result.exit_code == 0\n    assert \"Initializing database\" not in result.output\n    assert \"Creating user: Camila\" in result.output\n\n\ndef test_delete():\n    result = runner.invoke(app, [\"delete\", \"Camila\"])\n    assert result.exit_code == 0\n    assert \"Initializing database\" not in result.output\n    assert \"Deleting user: Camila\" in result.output\n\n\ndef test_callback():\n    result = runner.invoke(app)\n    assert result.exit_code == 0\n    assert \"Initializing database\" in result.output\n\n\ndef test_script():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_commands/test_context/test_tutorial004.py",
    "content": "import subprocess\nimport sys\n\nfrom typer.testing import CliRunner\n\nfrom docs_src.commands.context import tutorial004_py310 as mod\n\napp = mod.app\n\nrunner = CliRunner()\n\n\ndef test_1():\n    result = runner.invoke(app, [\"--name\", \"Camila\", \"--city\", \"Berlin\"])\n    assert result.exit_code == 0\n    assert \"Got extra arg: --name\" in result.output\n    assert \"Got extra arg: Camila\" in result.output\n    assert \"Got extra arg: --city\" in result.output\n    assert \"Got extra arg: Berlin\" in result.output\n\n\ndef test_script():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_commands/test_help/__init__.py",
    "content": ""
  },
  {
    "path": "tests/test_tutorial/test_commands/test_help/test_tutorial001.py",
    "content": "import importlib\nimport subprocess\nimport sys\nfrom types import ModuleType\n\nimport pytest\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\n\n\n@pytest.fixture(\n    name=\"mod\",\n    params=[\n        pytest.param(\"tutorial001_py310\"),\n        pytest.param(\"tutorial001_an_py310\"),\n    ],\n)\ndef get_mod(request: pytest.FixtureRequest) -> ModuleType:\n    module_name = f\"docs_src.commands.help.{request.param}\"\n    mod = importlib.import_module(module_name)\n    return mod\n\n\ndef test_help(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--help\"])\n    assert result.exit_code == 0\n    assert \"Awesome CLI user manager.\" in result.output\n    assert \"create\" in result.output\n    assert \"Create a new user with USERNAME.\" in result.output\n    assert \"delete\" in result.output\n    assert \"Delete a user with USERNAME.\" in result.output\n    assert \"delete-all\" in result.output\n    assert \"Delete ALL users in the database.\" in result.output\n    assert \"init\" in result.output\n    assert \"Initialize the users database.\" in result.output\n\n\ndef test_help_create(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"create\", \"--help\"])\n    assert result.exit_code == 0\n    assert \"create [OPTIONS] USERNAME\" in result.output\n    assert \"Create a new user with USERNAME.\" in result.output\n\n\ndef test_help_delete(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"delete\", \"--help\"])\n    assert result.exit_code == 0\n    assert \"delete [OPTIONS] USERNAME\" in result.output\n    assert \"Delete a user with USERNAME.\" in result.output\n    assert \"--force\" in result.output\n    assert \"--no-force\" in result.output\n    assert \"Force deletion without confirmation.\" in result.output\n\n\ndef test_help_delete_all(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"delete-all\", \"--help\"])\n    assert result.exit_code == 0\n    assert \"delete-all [OPTIONS]\" in result.output\n    assert \"Delete ALL users in the database.\" in result.output\n    assert \"If --force is not used, will ask for confirmation.\" in result.output\n    assert \"[required]\" in result.output\n    assert \"--force\" in result.output\n    assert \"--no-force\" in result.output\n    assert \"Force deletion without confirmation.\" in result.output\n\n\ndef test_help_init(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"init\", \"--help\"])\n    assert result.exit_code == 0\n    assert \"init [OPTIONS]\" in result.output\n    assert \"Initialize the users database.\" in result.output\n\n\ndef test_create(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"create\", \"Camila\"])\n    assert result.exit_code == 0\n    assert \"Creating user: Camila\" in result.output\n\n\ndef test_delete(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"delete\", \"Camila\"], input=\"y\\n\")\n    assert result.exit_code == 0\n    assert \"Are you sure you want to delete the user? [y/n]:\" in result.output\n    assert \"Deleting user: Camila\" in result.output\n\n\ndef test_no_delete(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"delete\", \"Camila\"], input=\"n\\n\")\n    assert result.exit_code == 0\n    assert \"Are you sure you want to delete the user? [y/n]:\" in result.output\n    assert \"Operation cancelled\" in result.output\n\n\ndef test_delete_all(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"delete-all\"], input=\"y\\n\")\n    assert result.exit_code == 0\n    assert \"Are you sure you want to delete ALL users? [y/n]:\" in result.output\n    assert \"Deleting all users\" in result.output\n\n\ndef test_no_delete_all(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"delete-all\"], input=\"n\\n\")\n    assert result.exit_code == 0\n    assert \"Are you sure you want to delete ALL users? [y/n]:\" in result.output\n    assert \"Operation cancelled\" in result.output\n\n\ndef test_init(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"init\"])\n    assert result.exit_code == 0\n    assert \"Initializing user database\" in result.output\n\n\ndef test_script(mod: ModuleType):\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_commands/test_help/test_tutorial002.py",
    "content": "import subprocess\nimport sys\n\nfrom typer.testing import CliRunner\n\nfrom docs_src.commands.help import tutorial002_py310 as mod\n\napp = mod.app\n\nrunner = CliRunner()\n\n\ndef test_help():\n    result = runner.invoke(app, [\"--help\"])\n    assert result.exit_code == 0\n    assert \"create\" in result.output\n    assert \"Create a new user with USERNAME.\" in result.output\n    assert \"delete\" in result.output\n    assert \"Delete a user with USERNAME.\" in result.output\n    assert \"Some internal utility function to create.\" not in result.output\n    assert \"Some internal utility function to delete.\" not in result.output\n\n\ndef test_help_create():\n    result = runner.invoke(app, [\"create\", \"--help\"])\n    assert result.exit_code == 0\n    assert \"Create a new user with USERNAME.\" in result.output\n    assert \"Some internal utility function to create.\" not in result.output\n\n\ndef test_help_delete():\n    result = runner.invoke(app, [\"delete\", \"--help\"])\n    assert result.exit_code == 0\n    assert \"Delete a user with USERNAME.\" in result.output\n    assert \"Some internal utility function to delete.\" not in result.output\n\n\ndef test_create():\n    result = runner.invoke(app, [\"create\", \"Camila\"])\n    assert result.exit_code == 0\n    assert \"Creating user: Camila\" in result.output\n\n\ndef test_delete():\n    result = runner.invoke(app, [\"delete\", \"Camila\"])\n    assert result.exit_code == 0\n    assert \"Deleting user: Camila\" in result.output\n\n\ndef test_script():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_commands/test_help/test_tutorial003.py",
    "content": "import subprocess\nimport sys\n\nfrom typer.testing import CliRunner\n\nfrom docs_src.commands.help import tutorial003_py310 as mod\n\napp = mod.app\n\nrunner = CliRunner()\n\n\ndef test_help():\n    result = runner.invoke(app, [\"--help\"])\n    assert result.exit_code == 0\n    assert \"create\" in result.output\n    assert \"Create a user.\" in result.output\n    assert \"delete\" in result.output\n    assert \"(deprecated)\" in result.output\n    assert \"Delete a user.\" in result.output\n\n\ndef test_help_delete():\n    result = runner.invoke(app, [\"delete\", \"--help\"])\n    assert result.exit_code == 0\n    assert \"(deprecated)\" in result.output\n    assert \"Delete a user.\" in result.output\n\n\ndef test_call():\n    # Mainly for coverage\n    result = runner.invoke(app, [\"create\", \"Camila\"])\n    assert result.exit_code == 0\n    result = runner.invoke(app, [\"delete\", \"Camila\"])\n    assert result.exit_code == 0\n\n\ndef test_script():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_commands/test_help/test_tutorial004.py",
    "content": "import importlib\nimport os\nimport subprocess\nimport sys\nfrom types import ModuleType\n\nimport pytest\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\n\n\n@pytest.fixture(\n    name=\"mod\",\n    params=[\n        pytest.param(\"tutorial004_py310\"),\n        pytest.param(\"tutorial004_an_py310\"),\n    ],\n)\ndef get_mod(request: pytest.FixtureRequest) -> ModuleType:\n    module_name = f\"docs_src.commands.help.{request.param}\"\n    mod = importlib.import_module(module_name)\n    return mod\n\n\ndef test_help(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--help\"])\n    assert result.exit_code == 0\n    assert \"create\" in result.output\n    assert \"Create a new shiny user. ✨\" in result.output\n    assert \"delete\" in result.output\n    assert \"Delete a user with USERNAME.\" in result.output\n    assert \"Some internal utility function to create.\" not in result.output\n    assert \"Some internal utility function to delete.\" not in result.output\n\n\ndef test_help_create(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"create\", \"--help\"])\n    assert result.exit_code == 0\n    assert \"Create a new shiny user. ✨\" in result.output\n    assert \"The username to be created\" in result.output\n    assert \"Some internal utility function to create.\" not in result.output\n\n\ndef test_help_delete(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"delete\", \"--help\"])\n    assert result.exit_code == 0\n    assert \"Delete a user with USERNAME.\" in result.output\n    assert \"The username to be deleted\" in result.output\n    assert \"Force the deletion 💥\" in result.output\n    assert \"Some internal utility function to delete.\" not in result.output\n\n\ndef test_create(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"create\", \"Camila\"])\n    assert result.exit_code == 0\n    assert \"Creating user: Camila\" in result.output\n\n\ndef test_delete(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"delete\", \"Camila\"])\n    assert result.exit_code == 0\n    assert \"Deleting user: Camila\" in result.output\n\n\ndef test_script(mod: ModuleType):\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={**os.environ, \"PYTHONIOENCODING\": \"utf-8\"},\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_commands/test_help/test_tutorial005.py",
    "content": "import importlib\nimport os\nimport subprocess\nimport sys\nfrom types import ModuleType\n\nimport pytest\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\n\n\n@pytest.fixture(\n    name=\"mod\",\n    params=[\n        pytest.param(\"tutorial005_py310\"),\n        pytest.param(\"tutorial005_an_py310\"),\n    ],\n)\ndef get_mod(request: pytest.FixtureRequest) -> ModuleType:\n    module_name = f\"docs_src.commands.help.{request.param}\"\n    mod = importlib.import_module(module_name)\n    return mod\n\n\ndef test_help(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--help\"])\n    assert result.exit_code == 0\n    assert \"create\" in result.output\n    assert \"Create a new shiny user. ✨\" in result.output\n    assert \"delete\" in result.output\n    assert \"Delete a user with USERNAME.\" in result.output\n    assert \"Some internal utility function to create.\" not in result.output\n    assert \"Some internal utility function to delete.\" not in result.output\n\n\ndef test_help_create(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"create\", \"--help\"])\n    assert result.exit_code == 0\n    assert \"Create a new shiny user. ✨\" in result.output\n    assert \"The username to be created\" in result.output\n    assert \"Learn more at the Typer docs website\" in result.output\n    assert \"Some internal utility function to create.\" not in result.output\n\n\ndef test_help_delete(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"delete\", \"--help\"])\n    assert result.exit_code == 0\n    assert \"Delete a user with USERNAME.\" in result.output\n    assert \"The username to be deleted\" in result.output\n    assert \"Force the deletion 💥\" in result.output\n    assert \"Some internal utility function to delete.\" not in result.output\n\n\ndef test_create(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"create\", \"Camila\"])\n    assert result.exit_code == 0\n    assert \"Creating user: Camila\" in result.output\n\n\ndef test_delete(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"delete\", \"Camila\"])\n    assert result.exit_code == 0\n    assert \"Deleting user: Camila\" in result.output\n\n\ndef test_script(mod: ModuleType):\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={**os.environ, \"PYTHONIOENCODING\": \"utf-8\"},\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_commands/test_help/test_tutorial006.py",
    "content": "import os\nimport subprocess\nimport sys\n\nfrom typer.testing import CliRunner\n\nfrom docs_src.commands.help import tutorial006_py310 as mod\n\napp = mod.app\n\nrunner = CliRunner()\n\n\ndef test_main_help():\n    result = runner.invoke(app, [\"--help\"])\n    assert result.exit_code == 0\n    assert \"create\" in result.output\n    assert \"Create a new user. ✨\" in result.output\n    assert \"delete\" in result.output\n    assert \"Delete a user. ❌\" in result.output\n    assert \"Utils and Configs\" in result.output\n    assert \"config\" in result.output\n    assert \"Configure the system. ⚙\" in result.output\n    assert \"Synchronize the system or something fancy like that. ♻\" in result.output\n    assert \"Help and Others\" in result.output\n    assert \"Get help with the system. ❓\" in result.output\n    assert \"Report an issue. ❗\" in result.output\n\n\ndef test_call():\n    # Mainly for coverage\n    result = runner.invoke(app, [\"create\", \"Morty\"])\n    assert result.exit_code == 0\n    result = runner.invoke(app, [\"delete\", \"Morty\"])\n    assert result.exit_code == 0\n    result = runner.invoke(app, [\"config\", \"Morty\"])\n    assert result.exit_code == 0\n    result = runner.invoke(app, [\"sync\"])\n    assert result.exit_code == 0\n    result = runner.invoke(app, [\"help\"])\n    assert result.exit_code == 0\n    result = runner.invoke(app, [\"report\"])\n    assert result.exit_code == 0\n\n\ndef test_script():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={**os.environ, \"PYTHONIOENCODING\": \"utf-8\"},\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_commands/test_help/test_tutorial007.py",
    "content": "import importlib\nimport os\nimport subprocess\nimport sys\nfrom types import ModuleType\n\nimport pytest\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\n\n\n@pytest.fixture(\n    name=\"mod\",\n    params=[\n        pytest.param(\"tutorial007_py310\"),\n        pytest.param(\"tutorial007_an_py310\"),\n    ],\n)\ndef get_mod(request: pytest.FixtureRequest) -> ModuleType:\n    module_name = f\"docs_src.commands.help.{request.param}\"\n    mod = importlib.import_module(module_name)\n    return mod\n\n\ndef test_main_help(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--help\"])\n    assert result.exit_code == 0\n    assert \"create\" in result.output\n    assert \"Create a new user. ✨\" in result.output\n    assert \"Utils and Configs\" in result.output\n    assert \"config\" in result.output\n    assert \"Configure the system. ⚙\" in result.output\n\n\ndef test_create_help(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"create\", \"--help\"])\n    assert result.exit_code == 0\n    assert \"username\" in result.output\n    assert \"The username to create\" in result.output\n    assert \"Secondary Arguments\" in result.output\n    assert \"lastname\" in result.output\n    assert \"The last name of the new user\" in result.output\n    assert \"--force\" in result.output\n    assert \"--no-force\" in result.output\n    assert \"Force the creation of the user\" in result.output\n    assert \"Additional Data\" in result.output\n    assert \"--age\" in result.output\n    assert \"The age of the new user\" in result.output\n    assert \"--favorite-color\" in result.output\n    assert \"The favorite color of the new user\" in result.output\n\n\ndef test_call(mod: ModuleType):\n    # Mainly for coverage\n    result = runner.invoke(mod.app, [\"create\", \"Morty\"])\n    assert result.exit_code == 0\n    result = runner.invoke(mod.app, [\"config\", \"Morty\"])\n    assert result.exit_code == 0\n\n\ndef test_script(mod: ModuleType):\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={**os.environ, \"PYTHONIOENCODING\": \"utf-8\"},\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_commands/test_help/test_tutorial008.py",
    "content": "import subprocess\nimport sys\n\nfrom typer.testing import CliRunner\n\nfrom docs_src.commands.help import tutorial008_py310 as mod\n\napp = mod.app\n\nrunner = CliRunner()\n\n\ndef test_main_help():\n    result = runner.invoke(app, [\"--help\"])\n    assert result.exit_code == 0\n    assert \"Create a new user. ✨\" in result.output\n    assert \"Made with ❤ in Venus\" in result.output\n\n\ndef test_call():\n    # Mainly for coverage\n    result = runner.invoke(app, [\"Morty\"])\n    assert result.exit_code == 0\n\n\ndef test_script():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_commands/test_index/__init__.py",
    "content": ""
  },
  {
    "path": "tests/test_tutorial/test_commands/test_index/test_tutorial002.py",
    "content": "import subprocess\nimport sys\n\nfrom typer.testing import CliRunner\n\nfrom docs_src.commands.index import tutorial002_py310 as mod\n\napp = mod.app\n\nrunner = CliRunner()\n\n\ndef test_help():\n    result = runner.invoke(app, [\"--help\"])\n    assert result.exit_code == 0\n    assert \"[OPTIONS] COMMAND [ARGS]...\" in result.output\n    assert \"Commands\" in result.output\n    assert \"create\" in result.output\n    assert \"delete\" in result.output\n\n\ndef test_create():\n    result = runner.invoke(app, [\"create\"])\n    assert result.exit_code == 0\n    assert \"Creating user: Hiro Hamada\" in result.output\n\n\ndef test_delete():\n    result = runner.invoke(app, [\"delete\"])\n    assert result.exit_code == 0\n    assert \"Deleting user: Hiro Hamada\" in result.output\n\n\ndef test_script():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_commands/test_index/test_tutorial003.py",
    "content": "import subprocess\nimport sys\n\nfrom typer.testing import CliRunner\n\nfrom docs_src.commands.index import tutorial003_py310 as mod\n\napp = mod.app\n\nrunner = CliRunner()\n\n\ndef test_no_arg():\n    result = runner.invoke(app)\n    assert \"[OPTIONS] COMMAND [ARGS]...\" in result.output\n    assert \"Commands\" in result.output\n    assert \"create\" in result.output\n    assert \"delete\" in result.output\n\n\ndef test_no_additional_output():\n    \"\"\"Ensure that no additional output was generated (cf. PR #1262)\"\"\"\n    result = runner.invoke(app)\n    assert result.output.count(\"Usage\") == 1\n    assert \"Error\" not in result.output\n\n\ndef test_create():\n    result = runner.invoke(app, [\"create\"])\n    assert result.exit_code == 0\n    assert \"Creating user: Hiro Hamada\" in result.output\n\n\ndef test_delete():\n    result = runner.invoke(app, [\"delete\"])\n    assert result.exit_code == 0\n    assert \"Deleting user: Hiro Hamada\" in result.output\n\n\ndef test_script():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_commands/test_index/test_tutorial004.py",
    "content": "import subprocess\nimport sys\n\nfrom typer.testing import CliRunner\n\nfrom docs_src.commands.index import tutorial004_py310 as mod\n\napp = mod.app\n\nrunner = CliRunner()\n\n\ndef test_help():\n    result = runner.invoke(app, [\"--help\"])\n    assert result.exit_code == 0\n    assert \"[OPTIONS] COMMAND [ARGS]...\" in result.output\n    print(result.output)\n    assert \"Commands\" in result.output\n    assert \"create\" in result.output\n    assert \"delete\" in result.output\n    # Test that the 'delete' command precedes the 'create' command in the help output\n    create_char = result.output.index(\"create\")\n    delete_char = result.output.index(\"delete\")\n    assert delete_char < create_char\n\n\ndef test_create():\n    result = runner.invoke(app, [\"create\"])\n    assert result.exit_code == 0\n    assert \"Creating user: Hiro Hamada\" in result.output\n\n\ndef test_delete():\n    result = runner.invoke(app, [\"delete\"])\n    assert result.exit_code == 0\n    assert \"Deleting user: Hiro Hamada\" in result.output\n\n\ndef test_script():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_commands/test_index/test_tutorial005.py",
    "content": "from typer.testing import CliRunner\n\nfrom docs_src.commands.index import tutorial005_py310 as mod\n\napp = mod.app\nrunner = CliRunner()\n\n\ndef test_creates_successfully():\n    \"\"\"Verify the example runs without errors\"\"\"\n    result = runner.invoke(app, [\"create\"])\n    assert result.exit_code == 0\n    assert \"Creating...\" in result.output\n\n    result = runner.invoke(app, [\"delete\"])\n    assert result.exit_code == 0\n    assert \"Deleting...\" in result.output\n\n\ndef test_shows_suggestion():\n    \"\"\"Verify command suggestions appear for typos\"\"\"\n    result = runner.invoke(app, [\"crate\"])\n    assert result.exit_code != 0\n    assert \"Did you mean 'create'?\" in result.output\n"
  },
  {
    "path": "tests/test_tutorial/test_commands/test_name/__init__.py",
    "content": ""
  },
  {
    "path": "tests/test_tutorial/test_commands/test_name/test_tutorial001.py",
    "content": "import subprocess\nimport sys\n\nfrom typer.testing import CliRunner\n\nfrom docs_src.commands.name import tutorial001_py310 as mod\n\napp = mod.app\n\nrunner = CliRunner()\n\n\ndef test_help():\n    result = runner.invoke(app, [\"--help\"])\n    assert result.exit_code == 0\n    assert \"Commands\" in result.output\n    assert \"create\" in result.output\n    assert \"delete\" in result.output\n\n\ndef test_create():\n    result = runner.invoke(app, [\"create\", \"Camila\"])\n    assert result.exit_code == 0\n    assert \"Creating user: Camila\" in result.output\n\n\ndef test_delete():\n    result = runner.invoke(app, [\"delete\", \"Camila\"])\n    assert result.exit_code == 0\n    assert \"Deleting user: Camila\" in result.output\n\n\ndef test_script():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_commands/test_one_or_multiple/__init__.py",
    "content": ""
  },
  {
    "path": "tests/test_tutorial/test_commands/test_one_or_multiple/test_tutorial001.py",
    "content": "import subprocess\nimport sys\n\nfrom typer.testing import CliRunner\n\nfrom docs_src.commands.one_or_multiple import tutorial001_py310 as mod\n\napp = mod.app\n\nrunner = CliRunner()\n\n\ndef test_help():\n    result = runner.invoke(app, [\"--help\"])\n    assert result.exit_code == 0\n    assert \"Commands\" in result.output\n    assert \"create\" in result.output\n\n\ndef test_command():\n    result = runner.invoke(app, [\"create\"])\n    assert result.exit_code == 0\n    assert \"Creating user: Hiro Hamada\" in result.output\n\n\ndef test_script():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_commands/test_one_or_multiple/test_tutorial002.py",
    "content": "import subprocess\nimport sys\n\nfrom typer.testing import CliRunner\n\nfrom docs_src.commands.one_or_multiple import tutorial002_py310 as mod\n\napp = mod.app\n\nrunner = CliRunner()\n\n\ndef test_help():\n    result = runner.invoke(app, [\"--help\"])\n    assert result.exit_code == 0\n    assert \"Creates a single user Hiro Hamada.\" in result.output\n    assert \"In the next version it will create 5 more users.\" in result.output\n    assert \"Commands\" in result.output\n    assert \"create\" in result.output\n\n\ndef test_command():\n    result = runner.invoke(app, [\"create\"])\n    assert result.exit_code == 0\n    assert \"Creating user: Hiro Hamada\" in result.output\n\n\ndef test_script():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_commands/test_options/__init__.py",
    "content": ""
  },
  {
    "path": "tests/test_tutorial/test_commands/test_options/test_tutorial001.py",
    "content": "import importlib\nimport subprocess\nimport sys\nfrom types import ModuleType\n\nimport pytest\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\n\n\n@pytest.fixture(\n    name=\"mod\",\n    params=[\n        pytest.param(\"tutorial001_py310\"),\n        pytest.param(\"tutorial001_an_py310\"),\n    ],\n)\ndef get_mod(request: pytest.FixtureRequest) -> ModuleType:\n    module_name = f\"docs_src.commands.options.{request.param}\"\n    mod = importlib.import_module(module_name)\n    return mod\n\n\ndef test_help(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--help\"])\n    assert result.exit_code == 0\n    assert \"Commands\" in result.output\n    assert \"create\" in result.output\n    assert \"delete\" in result.output\n    assert \"delete-all\" in result.output\n    assert \"init\" in result.output\n\n\ndef test_create(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"create\", \"Camila\"])\n    assert result.exit_code == 0\n    assert \"Creating user: Camila\" in result.output\n\n\ndef test_delete(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"delete\", \"Camila\"], input=\"y\\n\")\n    assert result.exit_code == 0\n    assert \"Are you sure you want to delete the user? [y/n]:\" in result.output\n    assert \"Deleting user: Camila\" in result.output\n\n\ndef test_no_delete(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"delete\", \"Camila\"], input=\"n\\n\")\n    assert result.exit_code == 0\n    assert \"Are you sure you want to delete the user? [y/n]:\" in result.output\n    assert \"Operation cancelled\" in result.output\n\n\ndef test_delete_all(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"delete-all\"], input=\"y\\n\")\n    assert result.exit_code == 0\n    assert \"Are you sure you want to delete ALL users? [y/n]:\" in result.output\n    assert \"Deleting all users\" in result.output\n\n\ndef test_no_delete_all(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"delete-all\"], input=\"n\\n\")\n    assert result.exit_code == 0\n    assert \"Are you sure you want to delete ALL users? [y/n]:\" in result.output\n    assert \"Operation cancelled\" in result.output\n\n\ndef test_delete_all_force(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"delete-all\", \"--force\"])\n    assert result.exit_code == 0\n    assert \"Are you sure you want to delete ALL users? [y/n]:\" not in result.output\n    assert \"Deleting all users\" in result.output\n\n\ndef test_init(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"init\"])\n    assert result.exit_code == 0\n    assert \"Initializing user database\" in result.output\n\n\ndef test_script(mod: ModuleType):\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_exceptions/__init__.py",
    "content": ""
  },
  {
    "path": "tests/test_tutorial/test_exceptions/test_tutorial001.py",
    "content": "import os\nimport subprocess\nimport sys\nfrom pathlib import Path\n\nimport pytest\nfrom typer.testing import CliRunner\n\nfrom docs_src.exceptions import tutorial001_py310 as mod\n\nrunner = CliRunner()\n\n\ndef test_traceback_rich():\n    file_path = Path(mod.__file__)\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", str(file_path)],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={\n            **os.environ,\n            \"TYPER_STANDARD_TRACEBACK\": \"\",\n            \"_TYPER_STANDARD_TRACEBACK\": \"\",\n        },\n    )\n    assert \"return get_command(self)(*args, **kwargs)\" not in result.stderr\n\n    assert \"app()\" not in result.stderr\n    assert \"print(name + 3)\" in result.stderr\n    assert 'TypeError: can only concatenate str (not \"int\") to str' in result.stderr\n    assert \"name = 'morty'\" not in result.stderr\n\n\n@pytest.mark.parametrize(\n    \"env_var\", [\"TYPER_STANDARD_TRACEBACK\", \"_TYPER_STANDARD_TRACEBACK\"]\n)\ndef test_standard_traceback_env_var(env_var: str):\n    file_path = Path(mod.__file__)\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", str(file_path)],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={**os.environ, env_var: \"1\"},\n    )\n    assert \"return get_command(self)(*args, **kwargs)\" in result.stderr\n\n    assert \"app()\" in result.stderr\n    assert \"print(name + 3)\" in result.stderr\n    assert 'TypeError: can only concatenate str (not \"int\") to str' in result.stderr\n    assert \"name = 'morty'\" not in result.stderr\n\n\ndef test_script():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_exceptions/test_tutorial002.py",
    "content": "import os\nimport subprocess\nimport sys\nfrom pathlib import Path\n\nimport pytest\nfrom typer.testing import CliRunner\n\nfrom docs_src.exceptions import tutorial002_py310 as mod\n\nrunner = CliRunner()\n\n\ndef test_traceback_rich():\n    file_path = Path(mod.__file__)\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", str(file_path)],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={\n            **os.environ,\n            \"TYPER_STANDARD_TRACEBACK\": \"\",\n            \"_TYPER_STANDARD_TRACEBACK\": \"\",\n        },\n    )\n    assert \"return get_command(self)(*args, **kwargs)\" not in result.stderr\n\n    assert \"app()\" not in result.stderr\n    assert \"print(name + 3)\" in result.stderr\n    assert 'TypeError: can only concatenate str (not \"int\") to str' in result.stderr\n    assert \"name = 'morty'\" in result.stderr\n\n\n@pytest.mark.parametrize(\n    \"env_var\", [\"TYPER_STANDARD_TRACEBACK\", \"_TYPER_STANDARD_TRACEBACK\"]\n)\ndef test_standard_traceback_env_var(env_var: str):\n    file_path = Path(mod.__file__)\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", str(file_path)],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={**os.environ, env_var: \"1\"},\n    )\n    assert \"return get_command(self)(*args, **kwargs)\" in result.stderr\n\n    assert \"app()\" in result.stderr\n    assert \"print(name + 3)\" in result.stderr\n    assert 'TypeError: can only concatenate str (not \"int\") to str' in result.stderr\n    assert \"name = 'morty'\" not in result.stderr\n\n\ndef test_script():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_exceptions/test_tutorial003.py",
    "content": "import os\nimport subprocess\nimport sys\nfrom pathlib import Path\n\nfrom typer.testing import CliRunner\n\nfrom docs_src.exceptions import tutorial003_py310 as mod\n\nrunner = CliRunner()\n\n\ndef test_traceback_rich_pretty_short_disable():\n    file_path = Path(mod.__file__)\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", str(file_path)],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={\n            **os.environ,\n            \"TYPER_STANDARD_TRACEBACK\": \"\",\n            \"_TYPER_STANDARD_TRACEBACK\": \"\",\n        },\n    )\n    assert \"return get_command(self)(*args, **kwargs)\" not in result.stderr\n\n    assert \"app()\" in result.stderr\n    assert \"print(name + 3)\" in result.stderr\n    assert 'TypeError: can only concatenate str (not \"int\") to str' in result.stderr\n    assert \"name = 'morty'\" not in result.stderr\n\n\ndef test_script():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_exceptions/test_tutorial004.py",
    "content": "import os\nimport subprocess\nimport sys\nfrom pathlib import Path\n\nfrom typer.testing import CliRunner\n\nfrom docs_src.exceptions import tutorial004_py310 as mod\n\nrunner = CliRunner()\n\n\ndef test_rich_pretty_exceptions_disable():\n    file_path = Path(mod.__file__)\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", str(file_path)],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={\n            **os.environ,\n            \"TYPER_STANDARD_TRACEBACK\": \"\",\n            \"_TYPER_STANDARD_TRACEBACK\": \"\",\n        },\n    )\n    assert \"return get_command(self)(*args, **kwargs)\" in result.stderr\n\n    assert \"app()\" in result.stderr\n    assert \"print(name + 3)\" in result.stderr\n    assert 'TypeError: can only concatenate str (not \"int\") to str' in result.stderr\n\n\ndef test_script():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_first_steps/__init__.py",
    "content": ""
  },
  {
    "path": "tests/test_tutorial/test_first_steps/test_tutorial001.py",
    "content": "import subprocess\nimport sys\n\nimport typer\nfrom typer.testing import CliRunner\n\nfrom docs_src.first_steps import tutorial001_py310 as mod\n\nrunner = CliRunner()\n\n\ndef test_cli():\n    app = typer.Typer()\n    app.command()(mod.main)\n    result = runner.invoke(app, [])\n    assert result.output == \"Hello World\\n\"\n\n\ndef test_script():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_first_steps/test_tutorial002.py",
    "content": "import subprocess\nimport sys\n\nimport typer\nfrom typer.testing import CliRunner\n\nfrom docs_src.first_steps import tutorial002_py310 as mod\n\nrunner = CliRunner()\n\napp = typer.Typer()\napp.command()(mod.main)\n\n\ndef test_1():\n    result = runner.invoke(app, [])\n    assert result.exit_code != 0\n    assert \"Missing argument 'NAME'\" in result.output\n\n\ndef test_2():\n    result = runner.invoke(app, [\"Camila\"])\n    assert result.exit_code == 0\n    assert \"Hello Camila\" in result.output\n\n\ndef test_script():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_first_steps/test_tutorial003.py",
    "content": "import subprocess\nimport sys\n\nimport typer\nfrom typer.testing import CliRunner\n\nfrom docs_src.first_steps import tutorial003_py310 as mod\n\nrunner = CliRunner()\n\napp = typer.Typer()\napp.command()(mod.main)\n\n\ndef test_1():\n    result = runner.invoke(app, [\"Camila\"])\n    assert result.exit_code != 0\n    assert \"Missing argument 'LASTNAME'\" in result.output\n\n\ndef test_2():\n    result = runner.invoke(app, [\"Camila\", \"Gutiérrez\"])\n    assert result.exit_code == 0\n    assert \"Hello Camila Gutiérrez\" in result.output\n\n\ndef test_script():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_first_steps/test_tutorial004.py",
    "content": "import subprocess\nimport sys\n\nimport typer\nfrom typer.testing import CliRunner\n\nfrom docs_src.first_steps import tutorial004_py310 as mod\n\nrunner = CliRunner()\n\napp = typer.Typer()\napp.command()(mod.main)\n\n\ndef test_help():\n    result = runner.invoke(app, [\"--help\"])\n    assert result.exit_code == 0\n    assert \"Arguments\" in result.output\n    assert \"NAME\" in result.output\n    assert \"[required]\" in result.output\n    assert \"LASTNAME\" in result.output\n    assert \"[required]\" in result.output\n    assert \"--formal\" in result.output\n    assert \"--no-formal\" in result.output\n\n\ndef test_1():\n    result = runner.invoke(app, [\"Camila\", \"Gutiérrez\"])\n    assert result.exit_code == 0\n    assert \"Hello Camila Gutiérrez\" in result.output\n\n\ndef test_formal_1():\n    result = runner.invoke(app, [\"Camila\", \"Gutiérrez\", \"--formal\"])\n    assert result.exit_code == 0\n    assert \"Good day Ms. Camila Gutiérrez.\" in result.output\n\n\ndef test_formal_2():\n    result = runner.invoke(app, [\"Camila\", \"--formal\", \"Gutiérrez\"])\n    assert result.exit_code == 0\n    assert \"Good day Ms. Camila Gutiérrez.\" in result.output\n\n\ndef test_formal_3():\n    result = runner.invoke(app, [\"--formal\", \"Camila\", \"Gutiérrez\"])\n    assert result.exit_code == 0\n    assert \"Good day Ms. Camila Gutiérrez.\" in result.output\n\n\ndef test_script():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_first_steps/test_tutorial005.py",
    "content": "import subprocess\nimport sys\n\nimport typer\nfrom typer.testing import CliRunner\n\nfrom docs_src.first_steps import tutorial005_py310 as mod\n\nrunner = CliRunner()\n\napp = typer.Typer()\napp.command()(mod.main)\n\n\ndef test_help():\n    result = runner.invoke(app, [\"--help\"])\n    assert result.exit_code == 0\n    assert \"Arguments\" in result.output\n    assert \"NAME\" in result.output\n    assert \"[required]\" in result.output\n    assert \"--lastname\" in result.output\n    assert \"TEXT\" in result.output\n    assert \"--formal\" in result.output\n    assert \"--no-formal\" in result.output\n\n\ndef test_1():\n    result = runner.invoke(app, [\"Camila\"])\n    assert result.exit_code == 0\n    assert \"Hello Camila\" in result.output\n\n\ndef test_option_lastname():\n    result = runner.invoke(app, [\"Camila\", \"--lastname\", \"Gutiérrez\"])\n    assert result.exit_code == 0\n    assert \"Hello Camila Gutiérrez\" in result.output\n\n\ndef test_option_lastname_2():\n    result = runner.invoke(app, [\"--lastname\", \"Gutiérrez\", \"Camila\"])\n    assert result.exit_code == 0\n    assert \"Hello Camila Gutiérrez\" in result.output\n\n\ndef test_formal_1():\n    result = runner.invoke(app, [\"Camila\", \"--lastname\", \"Gutiérrez\", \"--formal\"])\n    assert result.exit_code == 0\n    assert \"Good day Ms. Camila Gutiérrez.\" in result.output\n\n\ndef test_script():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_first_steps/test_tutorial006.py",
    "content": "import subprocess\nimport sys\n\nimport typer\nfrom typer.testing import CliRunner\n\nfrom docs_src.first_steps import tutorial006_py310 as mod\n\nrunner = CliRunner()\n\napp = typer.Typer()\napp.command()(mod.main)\n\n\ndef test_help():\n    result = runner.invoke(app, [\"--help\"])\n    assert result.exit_code == 0\n    assert \"Say hi to NAME, optionally with a --lastname.\" in result.output\n    assert \"If --formal is used, say hi very formally.\" in result.output\n\n\ndef test_1():\n    result = runner.invoke(app, [\"Camila\"])\n    assert result.exit_code == 0\n    assert \"Hello Camila\" in result.output\n\n\ndef test_option_lastname():\n    result = runner.invoke(app, [\"Camila\", \"--lastname\", \"Gutiérrez\"])\n    assert result.exit_code == 0\n    assert \"Hello Camila Gutiérrez\" in result.output\n\n\ndef test_option_lastname_2():\n    result = runner.invoke(app, [\"--lastname\", \"Gutiérrez\", \"Camila\"])\n    assert result.exit_code == 0\n    assert \"Hello Camila Gutiérrez\" in result.output\n\n\ndef test_formal_1():\n    result = runner.invoke(app, [\"Camila\", \"--lastname\", \"Gutiérrez\", \"--formal\"])\n    assert result.exit_code == 0\n    assert \"Good day Ms. Camila Gutiérrez.\" in result.output\n\n\ndef test_script():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_launch/test_tutorial001.py",
    "content": "import subprocess\nimport sys\nfrom unittest.mock import patch\n\nfrom typer.testing import CliRunner\n\nfrom docs_src.launch import tutorial001_py310 as mod\n\nrunner = CliRunner()\n\n\ndef test_cli():\n    with patch(\"typer.launch\") as launch_mock:\n        result = runner.invoke(mod.app)\n\n    assert result.exit_code == 0\n    assert result.output.strip() == \"Opening Typer's docs\"\n    launch_mock.assert_called_once_with(\"https://typer.tiangolo.com\")\n\n\ndef test_script():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_launch/test_tutorial002.py",
    "content": "import subprocess\nimport sys\nfrom pathlib import Path\nfrom unittest.mock import patch\n\nimport pytest\nimport typer\nfrom typer.testing import CliRunner\n\nfrom docs_src.launch import tutorial002_py310 as mod\n\nrunner = CliRunner()\n\n\n@pytest.fixture(name=\"app_dir\")\ndef app_dir():\n    app_dir = Path(typer.get_app_dir(\"my-super-cli-app\"))\n    if app_dir.exists():  # pragma: no cover\n        for item in app_dir.iterdir():\n            if item.is_file():\n                item.unlink()\n\n    yield app_dir\n\n    if app_dir.exists():\n        for item in app_dir.iterdir():\n            if item.is_file():\n                item.unlink()\n        app_dir.rmdir()\n\n\ndef test_cli(app_dir: Path):\n    with patch(\"typer.launch\") as launch_mock:\n        result = runner.invoke(mod.app)\n\n    assert result.exit_code == 0\n    assert \"Opening config directory\" in result.output\n    launch_mock.assert_called_with(str(app_dir / \"config.json\"), locate=True)\n\n\ndef test_script():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_multiple_values/__init__.py",
    "content": ""
  },
  {
    "path": "tests/test_tutorial/test_multiple_values/test_arguments_with_multiple_values/__init__.py",
    "content": ""
  },
  {
    "path": "tests/test_tutorial/test_multiple_values/test_arguments_with_multiple_values/test_tutorial001.py",
    "content": "import subprocess\nimport sys\n\nfrom typer.testing import CliRunner\n\nfrom docs_src.multiple_values.arguments_with_multiple_values import (\n    tutorial001_py310 as mod,\n)\n\nrunner = CliRunner()\napp = mod.app\n\n\ndef test_main():\n    result = runner.invoke(app, [\"README.md\", \"pyproject.toml\", \"woohoo!\"])\n    assert result.exit_code == 0\n    assert \"This file exists: README.md\\nwoohoo!\" in result.output\n    assert \"This file exists: pyproject.toml\\nwoohoo!\" in result.output\n\n\ndef test_script():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_multiple_values/test_arguments_with_multiple_values/test_tutorial002.py",
    "content": "import importlib\nimport subprocess\nimport sys\nfrom types import ModuleType\n\nimport pytest\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\n\n\n@pytest.fixture(\n    name=\"mod\",\n    params=[\n        pytest.param(\"tutorial002_py310\"),\n        pytest.param(\"tutorial002_an_py310\"),\n    ],\n)\ndef get_mod(request: pytest.FixtureRequest) -> ModuleType:\n    module_name = (\n        f\"docs_src.multiple_values.arguments_with_multiple_values.{request.param}\"\n    )\n    mod = importlib.import_module(module_name)\n    return mod\n\n\ndef test_help(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--help\"])\n    assert result.exit_code == 0\n    assert \"[OPTIONS] [NAMES]...\" in result.output\n    assert \"Arguments\" in result.output\n    assert \"[default: Harry, Hermione, Ron]\" in result.output\n\n\ndef test_defaults(mod: ModuleType):\n    result = runner.invoke(mod.app)\n    assert result.exit_code == 0\n    assert \"Hello Harry\" in result.output\n    assert \"Hello Hermione\" in result.output\n    assert \"Hello Ron\" in result.output\n\n\ndef test_invalid_args(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"Draco\", \"Hagrid\"])\n    assert result.exit_code != 0\n    assert \"Argument 'names' takes 3 values\" in result.output\n\n\ndef test_valid_args(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"Draco\", \"Hagrid\", \"Dobby\"])\n    assert result.exit_code == 0\n    assert \"Hello Draco\" in result.stdout\n    assert \"Hello Hagrid\" in result.stdout\n    assert \"Hello Dobby\" in result.stdout\n\n\ndef test_script(mod: ModuleType):\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_multiple_values/test_multiple_options/__init__.py",
    "content": ""
  },
  {
    "path": "tests/test_tutorial/test_multiple_values/test_multiple_options/test_tutorial001.py",
    "content": "import importlib\nimport subprocess\nimport sys\nfrom types import ModuleType\n\nimport pytest\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\n\n\n@pytest.fixture(\n    name=\"mod\",\n    params=[\n        pytest.param(\"tutorial001_py310\"),\n        pytest.param(\"tutorial001_an_py310\"),\n    ],\n)\ndef get_mod(request: pytest.FixtureRequest) -> ModuleType:\n    module_name = f\"docs_src.multiple_values.multiple_options.{request.param}\"\n    mod = importlib.import_module(module_name)\n    return mod\n\n\ndef test_main(mod: ModuleType):\n    result = runner.invoke(mod.app)\n    assert result.exit_code != 0\n    assert \"No provided users\" in result.output\n    assert \"raw input = None\" in result.output\n    assert \"Aborted\" in result.output\n\n\ndef test_1_user(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--user\", \"Camila\"])\n    assert result.exit_code == 0\n    assert \"Processing user: Camila\" in result.output\n\n\ndef test_3_user(mod: ModuleType):\n    result = runner.invoke(\n        mod.app, [\"--user\", \"Camila\", \"--user\", \"Rick\", \"--user\", \"Morty\"]\n    )\n    assert result.exit_code == 0\n    assert \"Processing user: Camila\" in result.output\n    assert \"Processing user: Rick\" in result.output\n    assert \"Processing user: Morty\" in result.output\n\n\ndef test_script(mod: ModuleType):\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_multiple_values/test_multiple_options/test_tutorial002.py",
    "content": "import importlib\nimport subprocess\nimport sys\nfrom types import ModuleType\n\nimport pytest\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\n\n\n@pytest.fixture(\n    name=\"mod\",\n    params=[\n        pytest.param(\"tutorial002_py310\"),\n        pytest.param(\"tutorial002_an_py310\"),\n    ],\n)\ndef get_mod(request: pytest.FixtureRequest) -> ModuleType:\n    module_name = f\"docs_src.multiple_values.multiple_options.{request.param}\"\n    mod = importlib.import_module(module_name)\n    return mod\n\n\ndef test_main(mod: ModuleType):\n    result = runner.invoke(mod.app)\n    assert result.exit_code == 0\n    assert \"The sum is 0\" in result.output\n\n\ndef test_1_number(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--number\", \"2\"])\n    assert result.exit_code == 0\n    assert \"The sum is 2.0\" in result.output\n\n\ndef test_2_number(mod: ModuleType):\n    result = runner.invoke(\n        mod.app, [\"--number\", \"2\", \"--number\", \"3\", \"--number\", \"4.5\"]\n    )\n    assert result.exit_code == 0\n    assert \"The sum is 9.5\" in result.output\n\n\ndef test_script(mod: ModuleType):\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_multiple_values/test_options_with_multiple_values/__init__.py",
    "content": ""
  },
  {
    "path": "tests/test_tutorial/test_multiple_values/test_options_with_multiple_values/test_tutorial001.py",
    "content": "import importlib\nimport subprocess\nimport sys\nfrom types import ModuleType\n\nimport pytest\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\n\n\n@pytest.fixture(\n    name=\"mod\",\n    params=[\n        pytest.param(\"tutorial001_py310\"),\n        pytest.param(\"tutorial001_an_py310\"),\n    ],\n)\ndef get_mod(request: pytest.FixtureRequest) -> ModuleType:\n    module_name = (\n        f\"docs_src.multiple_values.options_with_multiple_values.{request.param}\"\n    )\n    mod = importlib.import_module(module_name)\n    return mod\n\n\ndef test_main(mod: ModuleType):\n    result = runner.invoke(mod.app)\n    assert result.exit_code != 0\n    assert \"No user provided\" in result.output\n    assert \"Aborted\" in result.output\n\n\ndef test_user_1(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--user\", \"Camila\", \"50\", \"yes\"])\n    assert result.exit_code == 0\n    assert \"The username Camila has 50 coins\" in result.output\n    assert \"And this user is a wizard!\" in result.output\n\n\ndef test_user_2(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--user\", \"Morty\", \"3\", \"no\"])\n    assert result.exit_code == 0\n    assert \"The username Morty has 3 coins\" in result.output\n    assert \"And this user is a wizard!\" not in result.output\n\n\ndef test_invalid_user(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--user\", \"Camila\", \"50\"])\n    assert result.exit_code != 0\n    assert \"Option '--user' requires 3 arguments\" in result.output\n\n\ndef test_script(mod: ModuleType):\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_one_file_per_command/__init__.py",
    "content": ""
  },
  {
    "path": "tests/test_tutorial/test_one_file_per_command/test_tutorial.py",
    "content": "from typer.testing import CliRunner\n\nfrom docs_src.one_file_per_command.app_py310 import main as mod\n\nrunner = CliRunner()\n\n\ndef test_help():\n    result = runner.invoke(mod.app, [\"--help\"])\n\n    assert result.exit_code == 0\n\n    assert \"version\" in result.output\n    assert \"users\" in result.output\n\n\ndef test_version():\n    result = runner.invoke(mod.app, [\"version\"])\n\n    assert result.exit_code == 0\n    assert \"My CLI Version 1.0\" in result.output\n\n\ndef test_users_help():\n    result = runner.invoke(mod.app, [\"users\", \"--help\"])\n\n    assert result.exit_code == 0\n\n    assert \"add\" in result.output\n    assert \"delete\" in result.output\n\n\ndef test_add_user():\n    result = runner.invoke(mod.app, [\"users\", \"add\", \"Camila\"])\n\n    assert result.exit_code == 0\n    assert \"Adding user: Camila\" in result.output\n\n\ndef test_delete_user():\n    result = runner.invoke(mod.app, [\"users\", \"delete\", \"Camila\"])\n\n    assert result.exit_code == 0\n    assert \"Deleting user: Camila\" in result.output\n"
  },
  {
    "path": "tests/test_tutorial/test_options/__init__.py",
    "content": ""
  },
  {
    "path": "tests/test_tutorial/test_options/test_callback/__init__.py",
    "content": ""
  },
  {
    "path": "tests/test_tutorial/test_options/test_callback/test_tutorial001.py",
    "content": "import importlib\nimport subprocess\nimport sys\nfrom types import ModuleType\n\nimport pytest\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\n\n\n@pytest.fixture(\n    name=\"mod\",\n    params=[\n        pytest.param(\"tutorial001_py310\"),\n        pytest.param(\"tutorial001_an_py310\"),\n    ],\n)\ndef get_mod(request: pytest.FixtureRequest) -> ModuleType:\n    module_name = f\"docs_src.options.callback.{request.param}\"\n    mod = importlib.import_module(module_name)\n    return mod\n\n\ndef test_1(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--name\", \"Camila\"])\n    assert result.exit_code == 0\n    assert \"Hello Camila\" in result.output\n\n\ndef test_2(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--name\", \"rick\"])\n    assert result.exit_code != 0\n    assert \"Invalid value for '--name'\" in result.output\n    assert \"Only Camila is allowed\" in result.output\n\n\ndef test_script(mod: ModuleType):\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_options/test_callback/test_tutorial002.py",
    "content": "import importlib\nimport subprocess\nimport sys\nfrom types import ModuleType\n\nimport pytest\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\n\n\n@pytest.fixture(\n    name=\"mod\",\n    params=[\n        pytest.param(\"tutorial002_py310\"),\n        pytest.param(\"tutorial002_an_py310\"),\n    ],\n)\ndef get_mod(request: pytest.FixtureRequest) -> ModuleType:\n    module_name = f\"docs_src.options.callback.{request.param}\"\n    mod = importlib.import_module(module_name)\n    return mod\n\n\ndef test_1(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--name\", \"Camila\"])\n    assert result.exit_code == 0\n    assert \"Validating name\" in result.output\n    assert \"Hello Camila\" in result.output\n\n\ndef test_2(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--name\", \"rick\"])\n    assert result.exit_code != 0\n    assert \"Validating name\" in result.output\n    assert \"Invalid value for '--name'\" in result.output\n    assert \"Only Camila is allowed\" in result.output\n\n\ndef test_script(mod: ModuleType):\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_options/test_callback/test_tutorial003.py",
    "content": "import importlib\nimport os\nimport subprocess\nimport sys\nfrom pathlib import Path\nfrom types import ModuleType\n\nimport pytest\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\n\n\n@pytest.fixture(\n    name=\"mod\",\n    params=[\n        pytest.param(\"tutorial003_py310\"),\n        pytest.param(\"tutorial003_an_py310\"),\n    ],\n)\ndef get_mod(request: pytest.FixtureRequest) -> ModuleType:\n    module_name = f\"docs_src.options.callback.{request.param}\"\n    mod = importlib.import_module(module_name)\n    return mod\n\n\ndef test_1(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--name\", \"Camila\"])\n    assert result.exit_code == 0\n    assert \"Validating name\" in result.output\n    assert \"Hello Camila\" in result.output\n\n\ndef test_2(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--name\", \"rick\"])\n    assert result.exit_code != 0\n    assert \"Invalid value for '--name'\" in result.output\n    assert \"Only Camila is allowed\" in result.output\n\n\ndef test_script(mod: ModuleType):\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n\n\ndef test_completion(mod: ModuleType):\n    file_name = Path(mod.__file__).name\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \" \"],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={\n            **os.environ,\n            f\"_{file_name.upper()}_COMPLETE\": \"complete_bash\",\n            \"COMP_WORDS\": f\"{file_name} --\",\n            \"COMP_CWORD\": \"1\",\n        },\n    )\n    assert \"--name\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_options/test_callback/test_tutorial004.py",
    "content": "import importlib\nimport os\nimport subprocess\nimport sys\nfrom pathlib import Path\nfrom types import ModuleType\n\nimport pytest\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\n\n\n@pytest.fixture(\n    name=\"mod\",\n    params=[\n        pytest.param(\"tutorial004_py310\"),\n        pytest.param(\"tutorial004_an_py310\"),\n    ],\n)\ndef get_mod(request: pytest.FixtureRequest) -> ModuleType:\n    module_name = f\"docs_src.options.callback.{request.param}\"\n    mod = importlib.import_module(module_name)\n    return mod\n\n\ndef test_1(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--name\", \"Camila\"])\n    assert result.exit_code == 0\n    assert \"Validating param: name\" in result.output\n    assert \"Hello Camila\" in result.output\n\n\ndef test_2(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--name\", \"rick\"])\n    assert result.exit_code != 0\n    assert \"Invalid value for '--name'\" in result.output\n    assert \"Only Camila is allowed\" in result.output\n\n\ndef test_script(mod: ModuleType):\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n\n\ndef test_completion(mod: ModuleType):\n    file_name = Path(mod.__file__).name\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \" \"],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={\n            **os.environ,\n            f\"_{file_name.upper()}_COMPLETE\": \"complete_bash\",\n            \"COMP_WORDS\": f\"{file_name} --\",\n            \"COMP_CWORD\": \"1\",\n        },\n    )\n    assert \"--name\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_options/test_help/__init__.py",
    "content": ""
  },
  {
    "path": "tests/test_tutorial/test_options/test_help/test_tutorial001.py",
    "content": "import importlib\nimport subprocess\nimport sys\nfrom types import ModuleType\n\nimport pytest\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\n\n\n@pytest.fixture(\n    name=\"mod\",\n    params=[\n        pytest.param(\"tutorial001_py310\"),\n        pytest.param(\"tutorial001_an_py310\"),\n    ],\n)\ndef get_mod(request: pytest.FixtureRequest) -> ModuleType:\n    module_name = f\"docs_src.options.help.{request.param}\"\n    mod = importlib.import_module(module_name)\n    return mod\n\n\ndef test_help(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--help\"])\n    assert result.exit_code == 0\n    assert \"Say hi to NAME, optionally with a --lastname.\" in result.output\n    assert \"If --formal is used, say hi very formally.\" in result.output\n    assert \"Last name of person to greet.\" in result.output\n    assert \"Say hi formally.\" in result.output\n\n\ndef test_1(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"Camila\"])\n    assert result.exit_code == 0\n    assert \"Hello Camila\" in result.output\n\n\ndef test_option_lastname(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"Camila\", \"--lastname\", \"Gutiérrez\"])\n    assert result.exit_code == 0\n    assert \"Hello Camila Gutiérrez\" in result.output\n\n\ndef test_formal(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"Camila\", \"--lastname\", \"Gutiérrez\", \"--formal\"])\n    assert result.exit_code == 0\n    assert \"Good day Ms. Camila Gutiérrez.\" in result.output\n\n\ndef test_script(mod: ModuleType):\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_options/test_help/test_tutorial002.py",
    "content": "import importlib\nimport subprocess\nimport sys\nfrom types import ModuleType\n\nimport pytest\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\n\n\n@pytest.fixture(\n    name=\"mod\",\n    params=[\n        pytest.param(\"tutorial002_py310\"),\n        pytest.param(\"tutorial002_an_py310\"),\n    ],\n)\ndef get_mod(request: pytest.FixtureRequest) -> ModuleType:\n    module_name = f\"docs_src.options.help.{request.param}\"\n    mod = importlib.import_module(module_name)\n    return mod\n\n\ndef test_call(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"World\"])\n    assert result.exit_code == 0\n    assert \"Hello World\" in result.output\n\n\ndef test_formal(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"World\", \"--formal\"])\n    assert result.exit_code == 0\n    assert \"Good day Ms. World\" in result.output\n\n\ndef test_help(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--help\"])\n    assert result.exit_code == 0\n    assert \"--lastname\" in result.output\n    assert \"Customization and Utils\" in result.output\n    assert \"--formal\" in result.output\n    assert \"--no-formal\" in result.output\n    assert \"--debug\" in result.output\n    assert \"--no-debug\" in result.output\n\n\ndef test_script(mod: ModuleType):\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_options/test_help/test_tutorial003.py",
    "content": "import importlib\nimport subprocess\nimport sys\nfrom types import ModuleType\n\nimport pytest\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\n\n\n@pytest.fixture(\n    name=\"mod\",\n    params=[\n        pytest.param(\"tutorial003_py310\"),\n        pytest.param(\"tutorial003_an_py310\"),\n    ],\n)\ndef get_mod(request: pytest.FixtureRequest) -> ModuleType:\n    module_name = f\"docs_src.options.help.{request.param}\"\n    mod = importlib.import_module(module_name)\n    return mod\n\n\ndef test_call(mod: ModuleType):\n    result = runner.invoke(mod.app)\n    assert result.exit_code == 0\n    assert \"Hello Wade Wilson\" in result.output\n\n\ndef test_help(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--help\"])\n    assert result.exit_code == 0\n    assert \"--fullname\" in result.output\n    assert \"TEXT\" in result.output\n    assert \"[default: Wade Wilson]\" not in result.output\n\n\ndef test_script(mod: ModuleType):\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_options/test_help/test_tutorial004.py",
    "content": "import importlib\nimport subprocess\nimport sys\nfrom types import ModuleType\n\nimport pytest\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\n\n\n@pytest.fixture(\n    name=\"mod\",\n    params=[\n        pytest.param(\"tutorial004_py310\"),\n        pytest.param(\"tutorial004_an_py310\"),\n    ],\n)\ndef get_mod(request: pytest.FixtureRequest) -> ModuleType:\n    module_name = f\"docs_src.options.help.{request.param}\"\n    mod = importlib.import_module(module_name)\n    return mod\n\n\ndef test_call(mod: ModuleType):\n    result = runner.invoke(mod.app)\n    assert result.exit_code == 0\n    assert \"Hello Wade Wilson\" in result.output\n\n\ndef test_help(monkeypatch, mod: ModuleType):\n    # avoid default width of 80 for non-attached consoles during testing\n    monkeypatch.setenv(\"COLUMNS\", \"200\")\n    result = runner.invoke(mod.app, [\"--help\"])\n    assert result.exit_code == 0\n    assert \"--fullname\" in result.output\n    assert \"TEXT\" in result.output\n    assert \"[default: (Deadpoolio the amazing's name)]\" in result.output\n\n\ndef test_script(mod: ModuleType):\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_options/test_name/__init__.py",
    "content": ""
  },
  {
    "path": "tests/test_tutorial/test_options/test_name/test_tutorial001.py",
    "content": "import importlib\nimport subprocess\nimport sys\nfrom types import ModuleType\n\nimport pytest\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\n\n\n@pytest.fixture(\n    name=\"mod\",\n    params=[\n        pytest.param(\"tutorial001_py310\"),\n        pytest.param(\"tutorial001_an_py310\"),\n    ],\n)\ndef get_mod(request: pytest.FixtureRequest) -> ModuleType:\n    module_name = f\"docs_src.options.name.{request.param}\"\n    mod = importlib.import_module(module_name)\n    return mod\n\n\ndef test_option_help(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--help\"])\n    assert result.exit_code == 0\n    assert \"--name\" in result.output\n    assert \"TEXT\" in result.output\n    assert \"--user-name\" not in result.output\n\n\ndef test_call(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--name\", \"Camila\"])\n    assert result.exit_code == 0\n    assert \"Hello Camila\" in result.output\n\n\ndef test_call_no_args(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--name\"])\n    assert result.exit_code != 0\n    assert \"Option '--name' requires an argument\" in result.output\n\n\ndef test_script(mod: ModuleType):\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_options/test_name/test_tutorial002.py",
    "content": "import importlib\nimport subprocess\nimport sys\nfrom types import ModuleType\n\nimport pytest\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\n\n\n@pytest.fixture(\n    name=\"mod\",\n    params=[\n        pytest.param(\"tutorial002_py310\"),\n        pytest.param(\"tutorial002_an_py310\"),\n    ],\n)\ndef get_mod(request: pytest.FixtureRequest) -> ModuleType:\n    module_name = f\"docs_src.options.name.{request.param}\"\n    mod = importlib.import_module(module_name)\n    return mod\n\n\ndef test_option_help(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--help\"])\n    assert result.exit_code == 0\n    assert \"-n\" in result.output\n    assert \"--name\" in result.output\n    assert \"TEXT\" in result.output\n    assert \"--user-name\" not in result.output\n\n\ndef test_call(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"-n\", \"Camila\"])\n    assert result.exit_code == 0\n    assert \"Hello Camila\" in result.output\n\n\ndef test_call_long(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--name\", \"Camila\"])\n    assert result.exit_code == 0\n    assert \"Hello Camila\" in result.output\n\n\ndef test_script(mod: ModuleType):\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_options/test_name/test_tutorial003.py",
    "content": "import importlib\nimport subprocess\nimport sys\nfrom types import ModuleType\n\nimport pytest\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\n\n\n@pytest.fixture(\n    name=\"mod\",\n    params=[\n        pytest.param(\"tutorial003_py310\"),\n        pytest.param(\"tutorial003_an_py310\"),\n    ],\n)\ndef get_mod(request: pytest.FixtureRequest) -> ModuleType:\n    module_name = f\"docs_src.options.name.{request.param}\"\n    mod = importlib.import_module(module_name)\n    return mod\n\n\ndef test_option_help(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--help\"])\n    assert result.exit_code == 0\n    assert \"-n\" in result.output\n    assert \"TEXT\" in result.output\n    assert \"--user-name\" not in result.output\n    assert \"--name\" not in result.output\n\n\ndef test_call(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"-n\", \"Camila\"])\n    assert result.exit_code == 0\n    assert \"Hello Camila\" in result.output\n\n\ndef test_script(mod: ModuleType):\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_options/test_name/test_tutorial004.py",
    "content": "import importlib\nimport subprocess\nimport sys\nfrom types import ModuleType\n\nimport pytest\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\n\n\n@pytest.fixture(\n    name=\"mod\",\n    params=[\n        pytest.param(\"tutorial004_py310\"),\n        pytest.param(\"tutorial004_an_py310\"),\n    ],\n)\ndef get_mod(request: pytest.FixtureRequest) -> ModuleType:\n    module_name = f\"docs_src.options.name.{request.param}\"\n    mod = importlib.import_module(module_name)\n    return mod\n\n\ndef test_option_help(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--help\"])\n    assert result.exit_code == 0\n    assert \"-n\" in result.output\n    assert \"--user-name\" in result.output\n    assert \"TEXT\" in result.output\n    assert \"--name\" not in result.output\n\n\ndef test_call(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"-n\", \"Camila\"])\n    assert result.exit_code == 0\n    assert \"Hello Camila\" in result.output\n\n\ndef test_call_long(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--user-name\", \"Camila\"])\n    assert result.exit_code == 0\n    assert \"Hello Camila\" in result.output\n\n\ndef test_script(mod: ModuleType):\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_options/test_name/test_tutorial005.py",
    "content": "import importlib\nimport subprocess\nimport sys\nfrom types import ModuleType\n\nimport pytest\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\n\n\n@pytest.fixture(\n    name=\"mod\",\n    params=[\n        pytest.param(\"tutorial005_py310\"),\n        pytest.param(\"tutorial005_an_py310\"),\n    ],\n)\ndef get_mod(request: pytest.FixtureRequest) -> ModuleType:\n    module_name = f\"docs_src.options.name.{request.param}\"\n    mod = importlib.import_module(module_name)\n    return mod\n\n\ndef test_option_help(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--help\"])\n    assert result.exit_code == 0\n    assert \"-n\" in result.output\n    assert \"--name\" in result.output\n    assert \"TEXT\" in result.output\n    assert \"-f\" in result.output\n    assert \"--formal\" in result.output\n\n\ndef test_call(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"-n\", \"Camila\"])\n    assert result.exit_code == 0\n    assert \"Hello Camila\" in result.output\n\n\ndef test_call_formal(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"-n\", \"Camila\", \"-f\"])\n    assert result.exit_code == 0\n    assert \"Good day Ms. Camila.\" in result.output\n\n\ndef test_call_formal_condensed(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"-fn\", \"Camila\"])\n    assert result.exit_code == 0\n    assert \"Good day Ms. Camila.\" in result.output\n\n\ndef test_call_condensed_wrong_order(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"-nf\", \"Camila\"])\n    assert result.exit_code != 0\n\n\ndef test_script(mod: ModuleType):\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_options/test_password/__init__.py",
    "content": ""
  },
  {
    "path": "tests/test_tutorial/test_options/test_password/test_tutorial001.py",
    "content": "import importlib\nimport subprocess\nimport sys\nfrom types import ModuleType\n\nimport pytest\nfrom typer.testing import CliRunner\n\nfrom tests.utils import strip_double_spaces\n\nrunner = CliRunner()\n\n\n@pytest.fixture(\n    name=\"mod\",\n    params=[\n        pytest.param(\"tutorial001_py310\"),\n        pytest.param(\"tutorial001_an_py310\"),\n    ],\n)\ndef get_mod(request: pytest.FixtureRequest) -> ModuleType:\n    module_name = f\"docs_src.options.password.{request.param}\"\n    mod = importlib.import_module(module_name)\n    return mod\n\n\ndef test_option_email(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"Camila\", \"--email\", \"camila@example.com\"])\n    assert result.exit_code == 0\n    assert \"Hello Camila, your email is camila@example.com\" in result.output\n\n\ndef test_option_email_prompt(mod: ModuleType):\n    result = runner.invoke(\n        mod.app, [\"Camila\"], input=\"camila@example.com\\ncamila@example.com\\n\"\n    )\n    assert result.exit_code == 0\n    assert \"Email: camila@example.com\" in result.output\n    assert \"Repeat for confirmation: camila@example.com\" in result.output\n    assert \"Hello Camila, your email is camila@example.com\" in result.output\n\n\ndef test_help(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--help\"])\n    assert result.exit_code == 0\n    output_without_double_spaces = strip_double_spaces(result.output)\n    assert \"--email TEXT [required]\" in output_without_double_spaces\n\n\ndef test_script(mod: ModuleType):\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_options/test_password/test_tutorial002.py",
    "content": "import importlib\nimport subprocess\nimport sys\nfrom types import ModuleType\n\nimport pytest\nfrom typer.testing import CliRunner\n\nfrom tests.utils import strip_double_spaces\n\nrunner = CliRunner()\n\n\n@pytest.fixture(\n    name=\"mod\",\n    params=[\n        pytest.param(\"tutorial002_py310\"),\n        pytest.param(\"tutorial002_an_py310\"),\n    ],\n)\ndef get_mod(request: pytest.FixtureRequest) -> ModuleType:\n    module_name = f\"docs_src.options.password.{request.param}\"\n    mod = importlib.import_module(module_name)\n    return mod\n\n\ndef test_option_password(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"Camila\", \"--password\", \"secretpassword\"])\n    assert result.exit_code == 0\n    assert \"Hello Camila. Doing something very secure with password.\" in result.output\n    assert \"...just kidding, here it is, very insecure: secretpassword\" in result.output\n\n\ndef test_option_password_prompt(mod: ModuleType):\n    result = runner.invoke(\n        mod.app, [\"Camila\"], input=\"secretpassword\\nsecretpassword\\n\"\n    )\n    assert result.exit_code == 0\n    assert \"Password: \" in result.output\n    assert \"Password: secretpassword\" not in result.output\n    assert \"Repeat for confirmation: \" in result.output\n    assert \"Repeat for confirmation: secretpassword\" not in result.output\n    assert \"Hello Camila. Doing something very secure with password.\" in result.output\n    assert \"...just kidding, here it is, very insecure: secretpassword\" in result.output\n\n\ndef test_help(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--help\"])\n    assert result.exit_code == 0\n    output_without_double_spaces = strip_double_spaces(result.output)\n    assert \"--password TEXT [required]\" in output_without_double_spaces\n\n\ndef test_script(mod: ModuleType):\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_options/test_prompt/__init__.py",
    "content": ""
  },
  {
    "path": "tests/test_tutorial/test_options/test_prompt/test_tutorial001.py",
    "content": "import importlib\nimport subprocess\nimport sys\nfrom types import ModuleType\n\nimport pytest\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\n\n\n@pytest.fixture(\n    name=\"mod\",\n    params=[\n        pytest.param(\"tutorial001_py310\"),\n        pytest.param(\"tutorial001_an_py310\"),\n    ],\n)\ndef get_mod(request: pytest.FixtureRequest) -> ModuleType:\n    module_name = f\"docs_src.options.prompt.{request.param}\"\n    mod = importlib.import_module(module_name)\n    return mod\n\n\ndef test_option_lastname(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"Camila\", \"--lastname\", \"Gutiérrez\"])\n    assert result.exit_code == 0\n    assert \"Hello Camila Gutiérrez\" in result.output\n\n\ndef test_option_lastname_prompt(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"Camila\"], input=\"Gutiérrez\")\n    assert result.exit_code == 0\n    assert \"Lastname: \" in result.output\n    assert \"Hello Camila Gutiérrez\" in result.output\n\n\ndef test_help(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--help\"])\n    assert result.exit_code == 0\n    assert \"--lastname\" in result.output\n    assert \"TEXT\" in result.output\n    assert \"[required]\" in result.output\n\n\ndef test_script(mod: ModuleType):\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_options/test_prompt/test_tutorial002.py",
    "content": "import importlib\nimport subprocess\nimport sys\nfrom types import ModuleType\n\nimport pytest\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\n\n\n@pytest.fixture(\n    name=\"mod\",\n    params=[\n        pytest.param(\"tutorial002_py310\"),\n        pytest.param(\"tutorial002_an_py310\"),\n    ],\n)\ndef get_mod(request: pytest.FixtureRequest) -> ModuleType:\n    module_name = f\"docs_src.options.prompt.{request.param}\"\n    mod = importlib.import_module(module_name)\n    return mod\n\n\ndef test_option_lastname(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"Camila\", \"--lastname\", \"Gutiérrez\"])\n    assert result.exit_code == 0\n    assert \"Hello Camila Gutiérrez\" in result.output\n\n\ndef test_option_lastname_prompt(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"Camila\"], input=\"Gutiérrez\")\n    assert result.exit_code == 0\n    assert \"Please tell me your last name: \" in result.output\n    assert \"Hello Camila Gutiérrez\" in result.output\n\n\ndef test_help(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--help\"])\n    assert result.exit_code == 0\n    assert \"--lastname\" in result.output\n    assert \"TEXT\" in result.output\n    assert \"[required]\" in result.output\n\n\ndef test_script(mod: ModuleType):\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_options/test_prompt/test_tutorial003.py",
    "content": "import importlib\nimport subprocess\nimport sys\nfrom types import ModuleType\n\nimport pytest\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\n\n\n@pytest.fixture(\n    name=\"mod\",\n    params=[\n        pytest.param(\"tutorial003_py310\"),\n        pytest.param(\"tutorial003_an_py310\"),\n    ],\n)\ndef get_mod(request: pytest.FixtureRequest) -> ModuleType:\n    module_name = f\"docs_src.options.prompt.{request.param}\"\n    mod = importlib.import_module(module_name)\n    return mod\n\n\ndef test_prompt(mod: ModuleType):\n    result = runner.invoke(mod.app, input=\"Old Project\\nOld Project\\n\")\n    assert result.exit_code == 0\n    assert \"Deleting project Old Project\" in result.output\n\n\ndef test_prompt_not_equal(mod: ModuleType):\n    result = runner.invoke(\n        mod.app, input=\"Old Project\\nNew Spice\\nOld Project\\nOld Project\\n\"\n    )\n    assert result.exit_code == 0\n    assert \"Error: The two entered values do not match\" in result.output\n    assert \"Deleting project Old Project\" in result.output\n\n\ndef test_option(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--project-name\", \"Old Project\"])\n    assert result.exit_code == 0\n    assert \"Deleting project Old Project\" in result.output\n    assert \"Project name: \" not in result.output\n\n\ndef test_help(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--help\"])\n    assert result.exit_code == 0\n    assert \"--project-name\" in result.output\n    assert \"TEXT\" in result.output\n    assert \"[required]\" in result.output\n\n\ndef test_script(mod: ModuleType):\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_options/test_required/__init__.py",
    "content": ""
  },
  {
    "path": "tests/test_tutorial/test_options/test_required/test_tutorial001_tutorial002.py",
    "content": "import importlib\nimport subprocess\nimport sys\nfrom types import ModuleType\n\nimport pytest\nimport typer\nimport typer.core\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\n\n\n@pytest.fixture(\n    name=\"mod\",\n    params=[\n        pytest.param(\"tutorial001_py310\"),\n        pytest.param(\"tutorial001_an_py310\"),\n        pytest.param(\"tutorial002_py310\"),\n    ],\n)\ndef get_mod(request: pytest.FixtureRequest) -> ModuleType:\n    module_name = f\"docs_src.options.required.{request.param}\"\n    mod = importlib.import_module(module_name)\n    return mod\n\n\ndef test_1(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"Camila\"])\n    assert result.exit_code != 0\n    assert \"Missing option '--lastname'\" in result.output\n\n\ndef test_option_lastname(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"Camila\", \"--lastname\", \"Gutiérrez\"])\n    assert result.exit_code == 0\n    assert \"Hello Camila Gutiérrez\" in result.output\n\n\ndef test_help(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--help\"])\n    assert result.exit_code == 0\n    assert \"--lastname\" in result.output\n    assert \"TEXT\" in result.output\n    assert \"[required]\" in result.output\n\n\ndef test_help_no_rich(monkeypatch: pytest.MonkeyPatch, mod: ModuleType):\n    monkeypatch.setattr(typer.core, \"HAS_RICH\", False)\n    result = runner.invoke(mod.app, [\"--help\"])\n    assert result.exit_code == 0\n    assert \"--lastname\" in result.output\n    assert \"TEXT\" in result.output\n    assert \"[required]\" in result.output\n\n\ndef test_script(mod: ModuleType):\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_options/test_version/__init__.py",
    "content": ""
  },
  {
    "path": "tests/test_tutorial/test_options/test_version/test_tutorial001.py",
    "content": "import importlib\nimport os\nimport subprocess\nimport sys\nfrom pathlib import Path\nfrom types import ModuleType\n\nimport pytest\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\n\n\n@pytest.fixture(\n    name=\"mod\",\n    params=[\n        pytest.param(\"tutorial001_py310\"),\n        pytest.param(\"tutorial001_an_py310\"),\n    ],\n)\ndef get_mod(request: pytest.FixtureRequest) -> ModuleType:\n    module_name = f\"docs_src.options.version.{request.param}\"\n    mod = importlib.import_module(module_name)\n    return mod\n\n\ndef test_version(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--version\"])\n    assert result.exit_code == 0\n    assert \"Awesome CLI Version: 0.1.0\" in result.output\n\n\ndef test_version_with_name(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--name\", \"rick\", \"--version\"])\n    assert result.exit_code == 0\n    assert \"Awesome CLI Version: 0.1.0\" in result.output\n    assert \"Hello\" not in result.output\n\n\ndef test_no_version(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--name\", \"Camila\"])\n    assert result.exit_code == 0\n    assert \"Hello Camila\" in result.output\n\n\ndef test_script(mod: ModuleType):\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n\n\ndef test_completion(mod: ModuleType):\n    file_name = Path(mod.__file__).name\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \" \"],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={\n            **os.environ,\n            f\"_{file_name.upper()}_COMPLETE\": \"complete_bash\",\n            \"COMP_WORDS\": f\"{file_name} --name Rick --v\",\n            \"COMP_CWORD\": \"3\",\n        },\n    )\n    assert \"--version\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_options/test_version/test_tutorial002.py",
    "content": "import importlib\nimport os\nimport subprocess\nimport sys\nfrom pathlib import Path\nfrom types import ModuleType\n\nimport pytest\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\n\n\n@pytest.fixture(\n    name=\"mod\",\n    params=[\n        pytest.param(\"tutorial002_py310\"),\n        pytest.param(\"tutorial002_an_py310\"),\n    ],\n)\ndef get_mod(request: pytest.FixtureRequest) -> ModuleType:\n    module_name = f\"docs_src.options.version.{request.param}\"\n    mod = importlib.import_module(module_name)\n    return mod\n\n\ndef test_version_camila(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--name\", \"Camila\", \"--version\"])\n    assert result.exit_code == 0\n    assert \"Awesome CLI Version: 0.1.0\" in result.output\n\n\ndef test_version_not_camila(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--name\", \"rick\", \"--version\"])\n    assert result.exit_code != 0\n    assert \"Invalid value for '--name'\" in result.output\n    assert \"Only Camila is allowed\" in result.output\n\n\ndef test_version_camila_no_version(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--name\", \"Camila\"])\n    assert result.exit_code == 0\n    assert \"Hello Camila\" in result.output\n\n\ndef test_script(mod: ModuleType):\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n\n\ndef test_completion(mod: ModuleType):\n    file_name = Path(mod.__file__).name\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \" \"],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={\n            **os.environ,\n            f\"_{file_name.upper()}_COMPLETE\": \"complete_bash\",\n            \"COMP_WORDS\": f\"{file_name} --name Rick --v\",\n            \"COMP_CWORD\": \"3\",\n        },\n    )\n    assert \"--version\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_options/test_version/test_tutorial003.py",
    "content": "import importlib\nimport os\nimport subprocess\nimport sys\nfrom pathlib import Path\nfrom types import ModuleType\n\nimport pytest\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\n\n\n@pytest.fixture(\n    name=\"mod\",\n    params=[\n        pytest.param(\"tutorial003_py310\"),\n        pytest.param(\"tutorial003_an_py310\"),\n    ],\n)\ndef get_mod(request: pytest.FixtureRequest) -> ModuleType:\n    module_name = f\"docs_src.options.version.{request.param}\"\n    mod = importlib.import_module(module_name)\n    return mod\n\n\ndef test_1(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--name\", \"Rick\", \"--version\"])\n    assert result.exit_code == 0\n    assert \"Awesome CLI Version: 0.1.0\" in result.output\n\n\ndef test_2(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--name\", \"rick\"])\n    assert result.exit_code != 0\n    assert \"Invalid value for '--name'\" in result.output\n    assert \"Only Camila is allowed\" in result.output\n\n\ndef test_3(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--name\", \"Camila\"])\n    assert result.exit_code == 0\n    assert \"Hello Camila\" in result.output\n\n\ndef test_script(mod: ModuleType):\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n\n\ndef test_completion(mod: ModuleType):\n    file_name = Path(mod.__file__).name\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \" \"],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={\n            **os.environ,\n            f\"_{file_name.upper()}_COMPLETE\": \"complete_bash\",\n            \"COMP_WORDS\": f\"{file_name} --name Rick --v\",\n            \"COMP_CWORD\": \"3\",\n        },\n    )\n    assert \"--version\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_options_autocompletion/__init__.py",
    "content": ""
  },
  {
    "path": "tests/test_tutorial/test_options_autocompletion/test_tutorial001.py",
    "content": "import importlib\nimport subprocess\nimport sys\nfrom types import ModuleType\n\nimport pytest\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\n\n\n@pytest.fixture(\n    name=\"mod\",\n    params=[\n        pytest.param(\"tutorial001_py310\"),\n        pytest.param(\"tutorial001_an_py310\"),\n    ],\n)\ndef get_mod(request: pytest.FixtureRequest) -> ModuleType:\n    module_name = f\"docs_src.options_autocompletion.{request.param}\"\n    mod = importlib.import_module(module_name)\n    return mod\n\n\ndef test_1(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--name\", \"Camila\"])\n    assert result.exit_code == 0\n    assert \"Hello Camila\" in result.output\n\n\ndef test_script(mod: ModuleType):\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_options_autocompletion/test_tutorial002.py",
    "content": "import importlib\nimport os\nimport subprocess\nimport sys\nfrom pathlib import Path\nfrom types import ModuleType\n\nimport pytest\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\n\n\n@pytest.fixture(\n    name=\"mod\",\n    params=[\n        pytest.param(\"tutorial002_py310\"),\n        pytest.param(\"tutorial002_an_py310\"),\n    ],\n)\ndef get_mod(request: pytest.FixtureRequest) -> ModuleType:\n    module_name = f\"docs_src.options_autocompletion.{request.param}\"\n    mod = importlib.import_module(module_name)\n    return mod\n\n\ndef test_completion(mod: ModuleType):\n    file_name = Path(mod.__file__).name\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \" \"],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={\n            **os.environ,\n            f\"_{file_name.upper()}_COMPLETE\": \"complete_zsh\",\n            \"_TYPER_COMPLETE_ARGS\": f\"{file_name} --name \",\n        },\n    )\n    assert \"Camila\" in result.stdout\n    assert \"Carlos\" in result.stdout\n    assert \"Sebastian\" in result.stdout\n\n\ndef test_1(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--name\", \"Camila\"])\n    assert result.exit_code == 0\n    assert \"Hello Camila\" in result.output\n\n\ndef test_script(mod: ModuleType):\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_options_autocompletion/test_tutorial003.py",
    "content": "import importlib\nimport os\nimport subprocess\nimport sys\nfrom pathlib import Path\nfrom types import ModuleType\n\nimport pytest\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\n\n\n@pytest.fixture(\n    name=\"mod\",\n    params=[\n        pytest.param(\"tutorial003_py310\"),\n        pytest.param(\"tutorial003_an_py310\"),\n    ],\n)\ndef get_mod(request: pytest.FixtureRequest) -> ModuleType:\n    module_name = f\"docs_src.options_autocompletion.{request.param}\"\n    mod = importlib.import_module(module_name)\n    return mod\n\n\ndef test_completion_zsh(mod: ModuleType):\n    file_name = Path(mod.__file__).name\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \" \"],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={\n            **os.environ,\n            f\"_{file_name.upper()}_COMPLETE\": \"complete_zsh\",\n            \"_TYPER_COMPLETE_ARGS\": f\"{file_name} --name Seb\",\n        },\n    )\n    assert \"Camila\" not in result.stdout\n    assert \"Carlos\" not in result.stdout\n    assert \"Sebastian\" in result.stdout\n\n\ndef test_completion_powershell(mod: ModuleType):\n    file_name = Path(mod.__file__).name\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \" \"],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={\n            **os.environ,\n            f\"_{file_name.upper()}_COMPLETE\": \"complete_powershell\",\n            \"_TYPER_COMPLETE_ARGS\": f\"{file_name} --name Seb\",\n            \"_TYPER_COMPLETE_WORD_TO_COMPLETE\": \"Seb\",\n        },\n    )\n    assert \"Camila\" not in result.stdout\n    assert \"Carlos\" not in result.stdout\n    assert \"Sebastian\" in result.stdout\n\n\ndef test_1(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--name\", \"Camila\"])\n    assert result.exit_code == 0\n    assert \"Hello Camila\" in result.output\n\n\ndef test_script(mod: ModuleType):\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_options_autocompletion/test_tutorial004_tutorial005.py",
    "content": "import importlib\nimport os\nimport subprocess\nimport sys\nfrom pathlib import Path\nfrom types import ModuleType\n\nimport pytest\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\n\n\n@pytest.fixture(\n    name=\"mod\",\n    params=[\n        pytest.param(\"tutorial004_py310\"),\n        pytest.param(\"tutorial004_an_py310\"),\n        pytest.param(\"tutorial005_py310\"),\n        pytest.param(\"tutorial005_an_py310\"),\n    ],\n)\ndef get_mod(request: pytest.FixtureRequest) -> ModuleType:\n    module_name = f\"docs_src.options_autocompletion.{request.param}\"\n    mod = importlib.import_module(module_name)\n    return mod\n\n\ndef test_completion(mod: ModuleType):\n    file_name = Path(mod.__file__).name\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \" \"],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={\n            **os.environ,\n            f\"_{file_name.upper()}_COMPLETE\": \"complete_zsh\",\n            \"_TYPER_COMPLETE_ARGS\": f\"{file_name} --name \",\n        },\n    )\n    assert '\"Camila\":\"The reader of books.\"' in result.stdout\n    assert '\"Carlos\":\"The writer of scripts.\"' in result.stdout\n    assert '\"Sebastian\":\"The type hints guy.\"' in result.stdout\n\n\ndef test_1(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--name\", \"Camila\"])\n    assert result.exit_code == 0\n    assert \"Hello Camila\" in result.output\n\n\ndef test_script(mod: ModuleType):\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_options_autocompletion/test_tutorial006.py",
    "content": "import importlib\nimport subprocess\nimport sys\nfrom types import ModuleType\n\nimport pytest\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\n\n\n@pytest.fixture(\n    name=\"mod\",\n    params=[\n        pytest.param(\"tutorial006_py310\"),\n        pytest.param(\"tutorial006_an_py310\"),\n    ],\n)\ndef get_mod(request: pytest.FixtureRequest) -> ModuleType:\n    module_name = f\"docs_src.options_autocompletion.{request.param}\"\n    mod = importlib.import_module(module_name)\n    return mod\n\n\ndef test_1(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--name\", \"Camila\"])\n    assert result.exit_code == 0\n    assert \"Hello Camila\" in result.output\n\n\ndef test_2(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--name\", \"Camila\", \"--name\", \"Sebastian\"])\n    assert result.exit_code == 0\n    assert \"Hello Camila\" in result.output\n    assert \"Hello Sebastian\" in result.output\n\n\ndef test_script(mod: ModuleType):\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_options_autocompletion/test_tutorial007.py",
    "content": "import importlib\nimport os\nimport subprocess\nimport sys\nfrom pathlib import Path\nfrom types import ModuleType\n\nimport pytest\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\n\n\n@pytest.fixture(\n    name=\"mod\",\n    params=[\n        pytest.param(\"tutorial007_py310\"),\n        pytest.param(\"tutorial007_an_py310\"),\n    ],\n)\ndef get_mod(request: pytest.FixtureRequest) -> ModuleType:\n    module_name = f\"docs_src.options_autocompletion.{request.param}\"\n    mod = importlib.import_module(module_name)\n    return mod\n\n\ndef test_completion(mod: ModuleType):\n    file_name = Path(mod.__file__).name\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \" \"],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={\n            **os.environ,\n            f\"_{file_name.upper()}_COMPLETE\": \"complete_zsh\",\n            \"_TYPER_COMPLETE_ARGS\": f\"{file_name} --name Sebastian --name \",\n        },\n    )\n    assert '\"Camila\":\"The reader of books.\"' in result.stdout\n    assert '\"Carlos\":\"The writer of scripts.\"' in result.stdout\n    assert '\"Sebastian\":\"The type hints guy.\"' not in result.stdout\n\n\ndef test_1(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--name\", \"Camila\", \"--name\", \"Sebastian\"])\n    assert result.exit_code == 0\n    assert \"Hello Camila\" in result.output\n    assert \"Hello Sebastian\" in result.output\n\n\ndef test_script(mod: ModuleType):\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_options_autocompletion/test_tutorial008.py",
    "content": "import importlib\nimport os\nimport subprocess\nimport sys\nfrom pathlib import Path\nfrom types import ModuleType\n\nimport pytest\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\n\n\n@pytest.fixture(\n    name=\"mod\",\n    params=[\n        pytest.param(\"tutorial008_py310\"),\n        pytest.param(\"tutorial008_an_py310\"),\n    ],\n)\ndef get_mod(request: pytest.FixtureRequest) -> ModuleType:\n    module_name = f\"docs_src.options_autocompletion.{request.param}\"\n    mod = importlib.import_module(module_name)\n    return mod\n\n\ndef test_completion(mod: ModuleType):\n    file_name = Path(mod.__file__).name\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \" \"],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={\n            **os.environ,\n            f\"_{file_name.upper()}_COMPLETE\": \"complete_zsh\",\n            \"_TYPER_COMPLETE_ARGS\": f\"{file_name} --name \",\n        },\n    )\n    assert '\"Camila\":\"The reader of books.\"' in result.stdout\n    assert '\"Carlos\":\"The writer of scripts.\"' in result.stdout\n    assert '\"Sebastian\":\"The type hints guy.\"' in result.stdout\n    assert \"[]\" in result.stderr\n\n\ndef test_1(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--name\", \"Camila\", \"--name\", \"Sebastian\"])\n    assert result.exit_code == 0\n    assert \"Hello Camila\" in result.output\n    assert \"Hello Sebastian\" in result.output\n\n\ndef test_script(mod: ModuleType):\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_options_autocompletion/test_tutorial009.py",
    "content": "import importlib\nimport os\nimport subprocess\nimport sys\nfrom pathlib import Path\nfrom types import ModuleType\n\nimport pytest\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\n\n\n@pytest.fixture(\n    name=\"mod\",\n    params=[\n        pytest.param(\"tutorial009_py310\"),\n        pytest.param(\"tutorial009_an_py310\"),\n    ],\n)\ndef get_mod(request: pytest.FixtureRequest) -> ModuleType:\n    module_name = f\"docs_src.options_autocompletion.{request.param}\"\n    mod = importlib.import_module(module_name)\n    return mod\n\n\ndef test_completion(mod: ModuleType):\n    file_name = Path(mod.__file__).name\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \" \"],\n        capture_output=True,\n        encoding=\"utf-8\",\n        env={\n            **os.environ,\n            f\"_{file_name.upper()}_COMPLETE\": \"complete_zsh\",\n            \"_TYPER_COMPLETE_ARGS\": f\"{file_name} --name Sebastian --name \",\n        },\n    )\n    assert '\"Camila\":\"The reader of books.\"' in result.stdout\n    assert '\"Carlos\":\"The writer of scripts.\"' in result.stdout\n    assert '\"Sebastian\":\"The type hints guy.\"' not in result.stdout\n    assert \"[]\" in result.stderr\n\n\ndef test_1(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--name\", \"Camila\", \"--name\", \"Sebastian\"])\n    assert result.exit_code == 0\n    assert \"Hello Camila\" in result.output\n    assert \"Hello Sebastian\" in result.output\n\n\ndef test_script(mod: ModuleType):\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_parameter_types/__init__.py",
    "content": ""
  },
  {
    "path": "tests/test_tutorial/test_parameter_types/test_bool/__init__.py",
    "content": ""
  },
  {
    "path": "tests/test_tutorial/test_parameter_types/test_bool/test_tutorial001.py",
    "content": "import importlib\nimport subprocess\nimport sys\nfrom types import ModuleType\n\nimport pytest\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\n\n\n@pytest.fixture(\n    name=\"mod\",\n    params=[\n        pytest.param(\"tutorial001_py310\"),\n        pytest.param(\"tutorial001_an_py310\"),\n    ],\n)\ndef get_mod(request: pytest.FixtureRequest) -> ModuleType:\n    module_name = f\"docs_src.parameter_types.bool.{request.param}\"\n    mod = importlib.import_module(module_name)\n    return mod\n\n\ndef test_help(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--help\"])\n    assert result.exit_code == 0\n    assert \"--force\" in result.output\n    assert \"--no-force\" not in result.output\n\n\ndef test_no_force(mod: ModuleType):\n    result = runner.invoke(mod.app)\n    assert result.exit_code == 0\n    assert \"Not forcing\" in result.output\n\n\ndef test_force(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--force\"])\n    assert result.exit_code == 0\n    assert \"Forcing operation\" in result.output\n\n\ndef test_invalid_no_force(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--no-force\"])\n    assert result.exit_code != 0\n    assert \"No such option: --no-force\" in result.output\n\n\ndef test_script(mod: ModuleType):\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_parameter_types/test_bool/test_tutorial002.py",
    "content": "import importlib\nimport subprocess\nimport sys\nfrom types import ModuleType\n\nimport pytest\nimport typer\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\n\n\n@pytest.fixture(\n    name=\"mod\",\n    params=[\n        pytest.param(\"tutorial002_py310\"),\n        pytest.param(\"tutorial002_an_py310\"),\n    ],\n)\ndef get_mod(request: pytest.FixtureRequest) -> ModuleType:\n    module_name = f\"docs_src.parameter_types.bool.{request.param}\"\n    mod = importlib.import_module(module_name)\n    return mod\n\n\ndef test_help(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--help\"])\n    assert result.exit_code == 0\n    assert \"--accept\" in result.output\n    assert \"--reject\" in result.output\n    assert \"--no-accept\" not in result.output\n\n\ndef test_help_no_rich(monkeypatch: pytest.MonkeyPatch, mod: ModuleType):\n    monkeypatch.setattr(typer.core, \"HAS_RICH\", False)\n    result = runner.invoke(mod.app, [\"--help\"])\n    assert result.exit_code == 0\n    assert \"--accept\" in result.output\n    assert \"--reject\" in result.output\n    assert \"--no-accept\" not in result.output\n\n\ndef test_main(mod: ModuleType):\n    result = runner.invoke(mod.app)\n    assert result.exit_code == 0\n    assert \"I don't know what you want yet\" in result.output\n\n\ndef test_accept(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--accept\"])\n    assert result.exit_code == 0\n    assert \"Accepting!\" in result.output\n\n\ndef test_reject(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--reject\"])\n    assert result.exit_code == 0\n    assert \"Rejecting!\" in result.output\n\n\ndef test_invalid_no_accept(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--no-accept\"])\n    assert result.exit_code != 0\n    assert \"No such option: --no-accept\" in result.output\n\n\ndef test_script(mod: ModuleType):\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_parameter_types/test_bool/test_tutorial003.py",
    "content": "import importlib\nimport subprocess\nimport sys\nfrom types import ModuleType\n\nimport pytest\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\n\n\n@pytest.fixture(\n    name=\"mod\",\n    params=[\n        pytest.param(\"tutorial003_py310\"),\n        pytest.param(\"tutorial003_an_py310\"),\n    ],\n)\ndef get_mod(request: pytest.FixtureRequest) -> ModuleType:\n    module_name = f\"docs_src.parameter_types.bool.{request.param}\"\n    mod = importlib.import_module(module_name)\n    return mod\n\n\ndef test_help(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--help\"])\n    assert result.exit_code == 0\n    assert \"-f\" in result.output\n    assert \"--force\" in result.output\n    assert \"-F\" in result.output\n    assert \"--no-force\" in result.output\n\n\ndef test_force(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"-f\"])\n    assert result.exit_code == 0\n    assert \"Forcing operation\" in result.output\n\n\ndef test_no_force(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"-F\"])\n    assert result.exit_code == 0\n    assert \"Not forcing\" in result.output\n\n\ndef test_script(mod: ModuleType):\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_parameter_types/test_bool/test_tutorial004.py",
    "content": "import importlib\nimport subprocess\nimport sys\nfrom types import ModuleType\n\nimport pytest\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\n\n\n@pytest.fixture(\n    name=\"mod\",\n    params=[\n        pytest.param(\"tutorial004_py310\"),\n        pytest.param(\"tutorial004_an_py310\"),\n    ],\n)\ndef get_mod(request: pytest.FixtureRequest) -> ModuleType:\n    module_name = f\"docs_src.parameter_types.bool.{request.param}\"\n    mod = importlib.import_module(module_name)\n    return mod\n\n\ndef test_help(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--help\"])\n    assert result.exit_code == 0\n    assert \"-d\" in result.output\n    assert \"--demo\" in result.output\n\n\ndef test_main(mod: ModuleType):\n    result = runner.invoke(mod.app)\n    assert result.exit_code == 0\n    assert \"Running in production\" in result.output\n\n\ndef test_demo(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--demo\"])\n    assert result.exit_code == 0\n    assert \"Running demo\" in result.output\n\n\ndef test_short_demo(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"-d\"])\n    assert result.exit_code == 0\n    assert \"Running demo\" in result.output\n\n\ndef test_script(mod: ModuleType):\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_parameter_types/test_custom_types/__init__.py",
    "content": ""
  },
  {
    "path": "tests/test_tutorial/test_parameter_types/test_custom_types/test_tutorial001.py",
    "content": "import importlib\nimport subprocess\nimport sys\nfrom types import ModuleType\n\nimport pytest\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\n\n\n@pytest.fixture(\n    name=\"mod\",\n    params=[\n        pytest.param(\"tutorial001_py310\"),\n        pytest.param(\"tutorial001_an_py310\"),\n    ],\n)\ndef get_mod(request: pytest.FixtureRequest) -> ModuleType:\n    module_name = f\"docs_src.parameter_types.custom_types.{request.param}\"\n    mod = importlib.import_module(module_name)\n    return mod\n\n\ndef test_help(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--help\"])\n    assert result.exit_code == 0\n\n\ndef test_parse_custom_type(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"0\", \"--custom-opt\", \"1\"])\n    assert \"custom_arg is <CustomClass: value=00>\" in result.output\n    assert \"custom-opt is <CustomClass: value=11>\" in result.output\n\n\ndef test_parse_custom_type_with_default(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"0\"])\n    assert \"custom_arg is <CustomClass: value=00>\" in result.output\n    assert \"custom-opt is <CustomClass: value=FooFoo>\" in result.output\n\n\ndef test_script(mod: ModuleType):\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_parameter_types/test_datetime/__init__.py",
    "content": ""
  },
  {
    "path": "tests/test_tutorial/test_parameter_types/test_datetime/test_tutorial001.py",
    "content": "import subprocess\nimport sys\n\nfrom typer.testing import CliRunner\n\nfrom docs_src.parameter_types.datetime import tutorial001_py310 as mod\n\nrunner = CliRunner()\napp = mod.app\n\n\ndef test_help():\n    result = runner.invoke(app, [\"--help\"])\n    assert result.exit_code == 0\n    assert \"[%Y-%m-%d|%Y-%m-%dT%H:%M:%S|%Y-%m-%d %H:%M:%S]\" in result.output\n\n\ndef test_main():\n    result = runner.invoke(app, [\"1956-01-31T10:00:00\"])\n    assert result.exit_code == 0\n    assert \"Interesting day to be born: 1956-01-31 10:00:00\" in result.output\n    assert \"Birth hour: 10\" in result.output\n\n\ndef test_invalid():\n    result = runner.invoke(app, [\"july-19-1989\"])\n    assert result.exit_code != 0\n    assert (\n        \"Invalid value for 'BIRTH:[%Y-%m-%d|%Y-%m-%dT%H:%M:%S|%Y-%m-%d %H:%M:%S]':\"\n        in result.output\n    )\n    assert \"'july-19-1989' does not match the formats\" in result.output\n    assert \"%Y-%m-%d\" in result.output\n    assert \"%Y-%m-%dT%H:%M:%S\" in result.output\n    assert \"%Y-%m-%d %H:%M:%S\" in result.output\n\n\ndef test_script():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_parameter_types/test_datetime/test_tutorial002.py",
    "content": "import importlib\nimport subprocess\nimport sys\nfrom types import ModuleType\n\nimport pytest\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\n\n\n@pytest.fixture(\n    name=\"mod\",\n    params=[\n        pytest.param(\"tutorial002_py310\"),\n        pytest.param(\"tutorial002_an_py310\"),\n    ],\n)\ndef get_mod(request: pytest.FixtureRequest) -> ModuleType:\n    module_name = f\"docs_src.parameter_types.datetime.{request.param}\"\n    mod = importlib.import_module(module_name)\n    return mod\n\n\ndef test_main(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"1969-10-29\"])\n    assert result.exit_code == 0\n    assert \"Launch will be at: 1969-10-29 00:00:00\" in result.output\n\n\ndef test_usa_weird_date_format(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"10/29/1969\"])\n    assert result.exit_code == 0\n    assert \"Launch will be at: 1969-10-29 00:00:00\" in result.output\n\n\ndef test_script(mod: ModuleType):\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_parameter_types/test_enum/__init__.py",
    "content": ""
  },
  {
    "path": "tests/test_tutorial/test_parameter_types/test_enum/test_tutorial001.py",
    "content": "import subprocess\nimport sys\n\nfrom typer.testing import CliRunner\n\nfrom docs_src.parameter_types.enum import tutorial001_py310 as mod\n\nrunner = CliRunner()\napp = mod.app\n\n\ndef test_help():\n    result = runner.invoke(app, [\"--help\"])\n    assert result.exit_code == 0\n    assert \"--network\" in result.output\n    assert \"[simple|conv|lstm]\" in result.output\n    assert \"default: simple\" in result.output\n\n\ndef test_main():\n    result = runner.invoke(app, [\"--network\", \"conv\"])\n    assert result.exit_code == 0\n    assert \"Training neural network of type: conv\" in result.output\n\n\ndef test_main_default():\n    result = runner.invoke(app)\n    assert result.exit_code == 0\n    assert \"Training neural network of type: simple\" in result.output\n\n\ndef test_invalid_case():\n    result = runner.invoke(app, [\"--network\", \"CONV\"])\n    assert result.exit_code != 0\n    assert \"Invalid value for '--network'\" in result.output\n    assert \"'CONV' is not one of\" in result.output\n    assert \"simple\" in result.output\n    assert \"conv\" in result.output\n    assert \"lstm\" in result.output\n\n\ndef test_invalid_other():\n    result = runner.invoke(app, [\"--network\", \"capsule\"])\n    assert result.exit_code != 0\n    assert \"Invalid value for '--network'\" in result.output\n    assert \"'capsule' is not one of\" in result.output\n    assert \"simple\" in result.output\n    assert \"conv\" in result.output\n    assert \"lstm\" in result.output\n\n\ndef test_script():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_parameter_types/test_enum/test_tutorial002.py",
    "content": "import importlib\nimport subprocess\nimport sys\nfrom types import ModuleType\n\nimport pytest\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\n\n\n@pytest.fixture(\n    name=\"mod\",\n    params=[\n        pytest.param(\"tutorial002_py310\"),\n        pytest.param(\"tutorial002_an_py310\"),\n    ],\n)\ndef get_mod(request: pytest.FixtureRequest) -> ModuleType:\n    module_name = f\"docs_src.parameter_types.enum.{request.param}\"\n    mod = importlib.import_module(module_name)\n    return mod\n\n\ndef test_upper(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--network\", \"CONV\"])\n    assert result.exit_code == 0\n    assert \"Training neural network of type: conv\" in result.output\n\n\ndef test_mix(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--network\", \"LsTm\"])\n    assert result.exit_code == 0\n    assert \"Training neural network of type: lstm\" in result.output\n\n\ndef test_script(mod: ModuleType):\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_parameter_types/test_enum/test_tutorial003.py",
    "content": "import importlib\nimport subprocess\nimport sys\nfrom types import ModuleType\n\nimport pytest\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\n\n\n@pytest.fixture(\n    name=\"mod\",\n    params=[\n        pytest.param(\"tutorial003_py310\"),\n        pytest.param(\"tutorial003_an_py310\"),\n    ],\n)\ndef get_mod(request: pytest.FixtureRequest) -> ModuleType:\n    module_name = f\"docs_src.parameter_types.enum.{request.param}\"\n    mod = importlib.import_module(module_name)\n    return mod\n\n\ndef test_help(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--help\"])\n    assert result.exit_code == 0\n    assert \"--groceries\" in result.output\n    assert \"[Eggs|Bacon|Cheese]\" in result.output\n    assert \"default: Eggs, Cheese\" in result.output\n\n\ndef test_call_no_arg(mod: ModuleType):\n    result = runner.invoke(mod.app)\n    assert result.exit_code == 0\n    assert \"Buying groceries: Eggs, Cheese\" in result.output\n\n\ndef test_call_single_arg(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--groceries\", \"Bacon\"])\n    assert result.exit_code == 0\n    assert \"Buying groceries: Bacon\" in result.output\n\n\ndef test_call_multiple_arg(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--groceries\", \"Eggs\", \"--groceries\", \"Bacon\"])\n    assert result.exit_code == 0\n    assert \"Buying groceries: Eggs, Bacon\" in result.output\n\n\ndef test_script(mod: ModuleType):\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_parameter_types/test_enum/test_tutorial004.py",
    "content": "import importlib\nimport subprocess\nimport sys\nfrom types import ModuleType\n\nimport pytest\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\n\n\n@pytest.fixture(\n    name=\"mod\",\n    params=[\n        pytest.param(\"tutorial004_py310\"),\n        pytest.param(\"tutorial004_an_py310\"),\n    ],\n)\ndef get_mod(request: pytest.FixtureRequest) -> ModuleType:\n    module_name = f\"docs_src.parameter_types.enum.{request.param}\"\n    mod = importlib.import_module(module_name)\n    return mod\n\n\ndef test_help(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--help\"])\n    assert result.exit_code == 0\n    assert \"--network [simple|conv|lstm]\" in result.output.replace(\"  \", \"\")\n\n\ndef test_main(mod):\n    result = runner.invoke(mod.app, [\"--network\", \"conv\"])\n    assert result.exit_code == 0\n    assert \"Training neural network of type: conv\" in result.output\n\n\ndef test_invalid(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--network\", \"capsule\"])\n    assert result.exit_code != 0\n    assert \"Invalid value for '--network'\" in result.output\n    assert (\n        \"invalid choice: capsule. (choose from\" in result.output\n        or \"'capsule' is not one of\" in result.output\n    )\n    assert \"simple\" in result.output\n    assert \"conv\" in result.output\n    assert \"lstm\" in result.output\n\n\ndef test_script(mod: ModuleType):\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_parameter_types/test_file/__init__.py",
    "content": ""
  },
  {
    "path": "tests/test_tutorial/test_parameter_types/test_file/test_tutorial001.py",
    "content": "import importlib\nimport subprocess\nimport sys\nfrom pathlib import Path\nfrom types import ModuleType\n\nimport pytest\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\n\n\n@pytest.fixture(\n    name=\"mod\",\n    params=[\n        pytest.param(\"tutorial001_py310\"),\n        pytest.param(\"tutorial001_an_py310\"),\n    ],\n)\ndef get_mod(request: pytest.FixtureRequest) -> ModuleType:\n    module_name = f\"docs_src.parameter_types.file.{request.param}\"\n    mod = importlib.import_module(module_name)\n    return mod\n\n\ndef test_main(tmpdir, mod: ModuleType):\n    config_file = Path(tmpdir) / \"config.txt\"\n    config_file.write_text(\"some settings\\nsome more settings\")\n    result = runner.invoke(mod.app, [\"--config\", f\"{config_file}\"])\n    config_file.unlink()\n    assert result.exit_code == 0\n    assert \"Config line: some settings\" in result.output\n    assert \"Config line: some more settings\" in result.output\n\n\ndef test_script(mod: ModuleType):\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_parameter_types/test_file/test_tutorial002.py",
    "content": "import importlib\nimport subprocess\nimport sys\nfrom pathlib import Path\nfrom types import ModuleType\n\nimport pytest\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\n\n\n@pytest.fixture(\n    name=\"mod\",\n    params=[\n        pytest.param(\"tutorial002_py310\"),\n        pytest.param(\"tutorial002_an_py310\"),\n    ],\n)\ndef get_mod(request: pytest.FixtureRequest) -> ModuleType:\n    module_name = f\"docs_src.parameter_types.file.{request.param}\"\n    mod = importlib.import_module(module_name)\n    return mod\n\n\ndef test_main(tmpdir, mod: ModuleType):\n    config_file = Path(tmpdir) / \"config.txt\"\n    if config_file.exists():  # pragma: no cover\n        config_file.unlink()\n    result = runner.invoke(mod.app, [\"--config\", f\"{config_file}\"])\n    text = config_file.read_text()\n    config_file.unlink()\n    assert result.exit_code == 0\n    assert \"Config written\" in result.output\n    assert \"Some config written by the app\" in text\n\n\ndef test_script(mod: ModuleType):\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_parameter_types/test_file/test_tutorial003.py",
    "content": "import importlib\nimport subprocess\nimport sys\nfrom pathlib import Path\nfrom types import ModuleType\n\nimport pytest\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\n\n\n@pytest.fixture(\n    name=\"mod\",\n    params=[\n        pytest.param(\"tutorial003_py310\"),\n        pytest.param(\"tutorial003_an_py310\"),\n    ],\n)\ndef get_mod(request: pytest.FixtureRequest) -> ModuleType:\n    module_name = f\"docs_src.parameter_types.file.{request.param}\"\n    mod = importlib.import_module(module_name)\n    return mod\n\n\ndef test_main(tmpdir, mod: ModuleType):\n    binary_file = Path(tmpdir) / \"config.txt\"\n    binary_file.write_bytes(b\"la cig\\xc3\\xbce\\xc3\\xb1a trae al ni\\xc3\\xb1o\")\n    result = runner.invoke(mod.app, [\"--file\", f\"{binary_file}\"])\n    binary_file.unlink()\n    assert result.exit_code == 0\n    assert \"Processed bytes total:\" in result.output\n\n\ndef test_script(mod: ModuleType):\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_parameter_types/test_file/test_tutorial004.py",
    "content": "import importlib\nimport subprocess\nimport sys\nfrom pathlib import Path\nfrom types import ModuleType\n\nimport pytest\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\n\n\n@pytest.fixture(\n    name=\"mod\",\n    params=[\n        pytest.param(\"tutorial004_py310\"),\n        pytest.param(\"tutorial004_an_py310\"),\n    ],\n)\ndef get_mod(request: pytest.FixtureRequest) -> ModuleType:\n    module_name = f\"docs_src.parameter_types.file.{request.param}\"\n    mod = importlib.import_module(module_name)\n    return mod\n\n\ndef test_main(tmpdir, mod: ModuleType):\n    binary_file = Path(tmpdir) / \"config.txt\"\n    if binary_file.exists():  # pragma: no cover\n        binary_file.unlink()\n    result = runner.invoke(mod.app, [\"--file\", f\"{binary_file}\"])\n    text = binary_file.read_text(encoding=\"utf-8\")\n    binary_file.unlink()\n    assert result.exit_code == 0\n    assert \"Binary file written\" in result.output\n    assert \"some settings\" in text\n    assert \"la cigüeña trae al niño\" in text\n\n\ndef test_script(mod: ModuleType):\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_parameter_types/test_file/test_tutorial005.py",
    "content": "import importlib\nimport subprocess\nimport sys\nfrom pathlib import Path\nfrom types import ModuleType\n\nimport pytest\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\n\n\n@pytest.fixture(\n    name=\"mod\",\n    params=[\n        pytest.param(\"tutorial005_py310\"),\n        pytest.param(\"tutorial005_an_py310\"),\n    ],\n)\ndef get_mod(request: pytest.FixtureRequest) -> ModuleType:\n    module_name = f\"docs_src.parameter_types.file.{request.param}\"\n    mod = importlib.import_module(module_name)\n    return mod\n\n\ndef test_main(tmpdir, mod: ModuleType):\n    config_file = Path(tmpdir) / \"config.txt\"\n    if config_file.exists():  # pragma: no cover\n        config_file.unlink()\n        config_file.write_text(\"\")\n    result = runner.invoke(mod.app, [\"--config\", f\"{config_file}\"])\n    result = runner.invoke(mod.app, [\"--config\", f\"{config_file}\"])\n    result = runner.invoke(mod.app, [\"--config\", f\"{config_file}\"])\n    text = config_file.read_text()\n    config_file.unlink()\n    assert result.exit_code == 0\n    assert \"Config line written\"\n    assert \"This is a single line\\nThis is a single line\\nThis is a single line\" in text\n\n\ndef test_script(mod: ModuleType):\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_parameter_types/test_index/__init__.py",
    "content": ""
  },
  {
    "path": "tests/test_tutorial/test_parameter_types/test_index/test_tutorial001.py",
    "content": "import subprocess\nimport sys\n\nfrom typer.testing import CliRunner\n\nfrom docs_src.parameter_types.index import tutorial001_py310 as mod\n\nrunner = CliRunner()\napp = mod.app\n\n\ndef test_help():\n    result = runner.invoke(app, [\"--help\"])\n    assert result.exit_code == 0\n    assert \"--age\" in result.output\n    assert \"INTEGER\" in result.output\n    assert \"--height-meters\" in result.output\n    assert \"FLOAT\" in result.output\n\n\ndef test_params():\n    result = runner.invoke(\n        app, [\"Camila\", \"--age\", \"15\", \"--height-meters\", \"1.70\", \"--female\"]\n    )\n    assert result.exit_code == 0\n    assert \"NAME is Camila, of type: <class 'str'>\" in result.output\n    assert \"--age is 15, of type: <class 'int'>\" in result.output\n    assert \"--height-meters is 1.7, of type: <class 'float'>\" in result.output\n    assert \"--female is True, of type: <class 'bool'>\" in result.output\n\n\ndef test_invalid():\n    result = runner.invoke(app, [\"Camila\", \"--age\", \"15.3\"])\n    assert result.exit_code != 0\n    assert \"Invalid value for '--age'\" in result.output\n    assert \"'15.3' is not a valid integer\" in result.output\n\n\ndef test_script():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_parameter_types/test_number/__init__.py",
    "content": ""
  },
  {
    "path": "tests/test_tutorial/test_parameter_types/test_number/test_tutorial001.py",
    "content": "import importlib\nimport subprocess\nimport sys\nfrom types import ModuleType\n\nimport pytest\nimport typer\nimport typer.core\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\n\n\n@pytest.fixture(\n    name=\"mod\",\n    params=[\n        pytest.param(\"tutorial001_py310\"),\n        pytest.param(\"tutorial001_an_py310\"),\n    ],\n)\ndef get_mod(request: pytest.FixtureRequest) -> ModuleType:\n    module_name = f\"docs_src.parameter_types.number.{request.param}\"\n    mod = importlib.import_module(module_name)\n    return mod\n\n\ndef test_help(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--help\"])\n    assert result.exit_code == 0\n    assert \"--age\" in result.output\n    assert \"INTEGER RANGE\" in result.output\n    assert \"--score\" in result.output\n    assert \"FLOAT RANGE\" in result.output\n\n\ndef test_help_no_rich(monkeypatch: pytest.MonkeyPatch, mod: ModuleType):\n    monkeypatch.setattr(typer.core, \"HAS_RICH\", False)\n    result = runner.invoke(mod.app, [\"--help\"])\n    assert result.exit_code == 0\n    assert \"--age\" in result.output\n    assert \"INTEGER RANGE\" in result.output\n    assert \"--score\" in result.output\n    assert \"FLOAT RANGE\" in result.output\n\n\ndef test_params(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"5\", \"--age\", \"20\", \"--score\", \"90\"])\n    assert result.exit_code == 0\n    assert \"ID is 5\" in result.output\n    assert \"--age is 20\" in result.output\n    assert \"--score is 90.0\" in result.output\n\n\ndef test_invalid_id(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"1002\"])\n    assert result.exit_code != 0\n    assert (\n        \"Invalid value for 'ID': 1002 is not in the range 0<=x<=1000.\" in result.output\n    )\n\n\ndef test_invalid_age(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"5\", \"--age\", \"15\"])\n    assert result.exit_code != 0\n    assert \"Invalid value for '--age'\" in result.output\n    assert \"15 is not in the range x>=18\" in result.output\n\n\ndef test_invalid_score(monkeypatch: pytest.MonkeyPatch, mod: ModuleType):\n    monkeypatch.setattr(typer.core, \"HAS_RICH\", False)\n    result = runner.invoke(mod.app, [\"5\", \"--age\", \"20\", \"--score\", \"100.5\"])\n    assert result.exit_code != 0\n    assert \"Invalid value for '--score'\" in result.output\n    assert \"100.5 is not in the range x<=100.\" in result.output\n\n\ndef test_negative_score(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"5\", \"--age\", \"20\", \"--score\", \"-5\"])\n    assert result.exit_code == 0\n    assert \"ID is 5\" in result.output\n    assert \"--age is 20\" in result.output\n    assert \"--score is -5.0\" in result.output\n\n\ndef test_script(mod: ModuleType):\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_parameter_types/test_number/test_tutorial002.py",
    "content": "import importlib\nimport subprocess\nimport sys\nfrom types import ModuleType\n\nimport pytest\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\n\n\n@pytest.fixture(\n    name=\"mod\",\n    params=[\n        pytest.param(\"tutorial002_py310\"),\n        pytest.param(\"tutorial002_an_py310\"),\n    ],\n)\ndef get_mod(request: pytest.FixtureRequest) -> ModuleType:\n    module_name = f\"docs_src.parameter_types.number.{request.param}\"\n    mod = importlib.import_module(module_name)\n    return mod\n\n\ndef test_invalid_id(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"1002\"])\n    assert result.exit_code != 0\n    assert (\n        \"Invalid value for 'ID': 1002 is not in the range 0<=x<=1000\" in result.output\n    )\n\n\ndef test_clamped(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"5\", \"--rank\", \"11\", \"--score\", \"-5\"])\n    assert result.exit_code == 0\n    assert \"ID is 5\" in result.output\n    assert \"--rank is 10\" in result.output\n    assert \"--score is 0\" in result.output\n\n\ndef test_script(mod: ModuleType):\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_parameter_types/test_number/test_tutorial003.py",
    "content": "import importlib\nimport subprocess\nimport sys\nfrom types import ModuleType\n\nimport pytest\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\n\n\n@pytest.fixture(\n    name=\"mod\",\n    params=[\n        pytest.param(\"tutorial003_py310\"),\n        pytest.param(\"tutorial003_an_py310\"),\n    ],\n)\ndef get_mod(request: pytest.FixtureRequest) -> ModuleType:\n    module_name = f\"docs_src.parameter_types.number.{request.param}\"\n    mod = importlib.import_module(module_name)\n    return mod\n\n\ndef test_main(mod: ModuleType):\n    result = runner.invoke(mod.app)\n    assert result.exit_code == 0\n    assert \"Verbose level is 0\" in result.output\n\n\ndef test_verbose_1(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--verbose\"])\n    assert result.exit_code == 0\n    assert \"Verbose level is 1\" in result.output\n\n\ndef test_verbose_3(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--verbose\", \"--verbose\", \"--verbose\"])\n    assert result.exit_code == 0\n    assert \"Verbose level is 3\" in result.output\n\n\ndef test_verbose_short_1(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"-v\"])\n    assert result.exit_code == 0\n    assert \"Verbose level is 1\" in result.output\n\n\ndef test_verbose_short_3(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"-v\", \"-v\", \"-v\"])\n    assert result.exit_code == 0\n    assert \"Verbose level is 3\" in result.output\n\n\ndef test_verbose_short_3_condensed(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"-vvv\"])\n    assert result.exit_code == 0\n    assert \"Verbose level is 3\" in result.output\n\n\ndef test_script(mod: ModuleType):\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_parameter_types/test_path/__init__.py",
    "content": ""
  },
  {
    "path": "tests/test_tutorial/test_parameter_types/test_path/test_tutorial001.py",
    "content": "import importlib\nimport subprocess\nimport sys\nfrom pathlib import Path\nfrom types import ModuleType\n\nimport pytest\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\n\n\n@pytest.fixture(\n    name=\"mod\",\n    params=[\n        pytest.param(\"tutorial001_py310\"),\n        pytest.param(\"tutorial001_an_py310\"),\n    ],\n)\ndef get_mod(request: pytest.FixtureRequest) -> ModuleType:\n    module_name = f\"docs_src.parameter_types.path.{request.param}\"\n    mod = importlib.import_module(module_name)\n    return mod\n\n\ndef test_no_path(tmpdir, mod: ModuleType):\n    Path(tmpdir) / \"config.txt\"\n    result = runner.invoke(mod.app)\n    assert result.exit_code == 1\n    assert \"No config file\" in result.output\n    assert \"Aborted\" in result.output\n\n\ndef test_not_exists(tmpdir, mod: ModuleType):\n    config_file = Path(tmpdir) / \"config.txt\"\n    if config_file.exists():  # pragma: no cover\n        config_file.unlink()\n    result = runner.invoke(mod.app, [\"--config\", f\"{config_file}\"])\n    assert result.exit_code == 0\n    assert \"The config doesn't exist\" in result.output\n\n\ndef test_exists(tmpdir, mod: ModuleType):\n    config_file = Path(tmpdir) / \"config.txt\"\n    config_file.write_text(\"some settings\")\n    result = runner.invoke(mod.app, [\"--config\", f\"{config_file}\"])\n    config_file.unlink()\n    assert result.exit_code == 0\n    assert \"Config file contents: some settings\" in result.output\n\n\ndef test_dir(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--config\", \"./\"])\n    assert result.exit_code == 0\n    assert \"Config is a directory, will use all its config files\" in result.output\n\n\ndef test_script(mod: ModuleType):\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_parameter_types/test_path/test_tutorial002.py",
    "content": "import importlib\nimport subprocess\nimport sys\nfrom pathlib import Path\nfrom types import ModuleType\n\nimport pytest\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\n\n\n@pytest.fixture(\n    name=\"mod\",\n    params=[\n        pytest.param(\"tutorial002_py310\"),\n        pytest.param(\"tutorial002_an_py310\"),\n    ],\n)\ndef get_mod(request: pytest.FixtureRequest) -> ModuleType:\n    module_name = f\"docs_src.parameter_types.path.{request.param}\"\n    mod = importlib.import_module(module_name)\n    return mod\n\n\ndef test_not_exists(tmpdir, mod: ModuleType):\n    config_file = Path(tmpdir) / \"config.txt\"\n    if config_file.exists():  # pragma: no cover\n        config_file.unlink()\n    result = runner.invoke(mod.app, [\"--config\", f\"{config_file}\"])\n    assert result.exit_code != 0\n    assert \"Invalid value for '--config'\" in result.output\n    assert \"File\" in result.output\n    assert \"does not exist\" in result.output\n\n\ndef test_exists(tmpdir, mod: ModuleType):\n    config_file = Path(tmpdir) / \"config.txt\"\n    config_file.write_text(\"some settings\")\n    result = runner.invoke(mod.app, [\"--config\", f\"{config_file}\"])\n    config_file.unlink()\n    assert result.exit_code == 0\n    assert \"Config file contents: some settings\" in result.output\n\n\ndef test_dir(mod: ModuleType):\n    result = runner.invoke(mod.app, [\"--config\", \"./\"])\n    assert result.exit_code != 0\n    assert \"Invalid value for '--config'\" in result.output\n    assert \"File './' is a directory.\" in result.output\n\n\ndef test_script(mod: ModuleType):\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_parameter_types/test_uuid/__init__.py",
    "content": ""
  },
  {
    "path": "tests/test_tutorial/test_parameter_types/test_uuid/test_tutorial001.py",
    "content": "import subprocess\nimport sys\n\nfrom typer.testing import CliRunner\n\nfrom docs_src.parameter_types.uuid import tutorial001_py310 as mod\n\nrunner = CliRunner()\napp = mod.app\n\n\ndef test_main():\n    result = runner.invoke(app, [\"d48edaa6-871a-4082-a196-4daab372d4a1\"])\n    assert result.exit_code == 0\n    assert \"USER_ID is d48edaa6-871a-4082-a196-4daab372d4a1\" in result.output\n    assert \"UUID version is: 4\" in result.output\n\n\ndef test_invalid_uuid():\n    result = runner.invoke(app, [\"7479706572-72756c6573\"])\n    assert result.exit_code != 0\n    assert (\n        \"Invalid value for 'USER_ID': '7479706572-72756c6573' is not a valid UUID\"\n        in result.output\n    )\n\n\ndef test_script():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_printing/__init__.py",
    "content": ""
  },
  {
    "path": "tests/test_tutorial/test_printing/test_tutorial001.py",
    "content": "import subprocess\nimport sys\nfrom unittest.mock import patch\n\nfrom rich.console import Console  # noqa: TID251\nfrom typer.testing import CliRunner\n\nimport docs_src.printing.tutorial001_py310 as mod\nfrom tests.utils import normalize_rich_output\n\napp = mod.app\n\nrunner = CliRunner()\n\n\ndef test_cli():\n    console = Console(force_terminal=True, width=100)\n    with patch(\"rich.get_console\", return_value=console):\n        result = runner.invoke(app)\n\n    assert result.exit_code == 0\n\n    # Replace all Rich formatting with `*` characters\n    normalized_output = normalize_rich_output(result.output, squash_whitespaces=False)\n\n    assert (\n        \"Here's the data\\n\"\n        \"*{*\\n\"\n        \"    *'name'*: *'Rick'*,\\n\"\n        \"    *'age'*: *42*,\\n\"\n        \"    *'items'*: *[*{*'name'*: *'Portal Gun'*}*, *{*'name'*: *'Plumbus'*}*]*,\\n\"\n        \"    *'active'*: *True*,\\n\"\n        \"    *'affiliation'*: *None*\\n\"\n        \"*}*\\n\"\n    ) in normalized_output\n\n\ndef test_script():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_printing/test_tutorial002.py",
    "content": "import subprocess\nimport sys\nfrom unittest.mock import patch\n\nfrom rich.console import Console  # noqa: TID251\nfrom typer.testing import CliRunner\n\nimport docs_src.printing.tutorial002_py310 as mod\nfrom tests.utils import normalize_rich_output\n\napp = mod.app\n\nrunner = CliRunner()\n\n\ndef test_cli():\n    console = Console(force_terminal=True, width=100)\n    with patch(\"rich.get_console\", return_value=console):\n        result = runner.invoke(app)\n\n    # Replace all Rich formatting with `*` characters\n    normalized_output = normalize_rich_output(result.output)\n\n    assert result.exit_code == 0\n    assert \"*Alert!* *Portal gun* shooting! *\" in normalized_output\n\n\ndef test_cli_without_formatting():\n    result = runner.invoke(app)\n\n    assert result.exit_code == 0\n    assert \"Alert! Portal gun shooting! 💥\" in result.output\n\n\ndef test_script():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_printing/test_tutorial003.py",
    "content": "import subprocess\nimport sys\n\nfrom typer.testing import CliRunner\n\nimport docs_src.printing.tutorial003_py310 as mod\nfrom tests.utils import normalize_rich_output\n\napp = mod.app\n\nrunner = CliRunner()\n\n\ndef test_cli():\n    result = runner.invoke(app)\n\n    # Replace all Rich formatting with `*` characters\n    normalized_output = normalize_rich_output(result.output)\n\n    assert result.exit_code == 0\n    assert (\n        \"*\\n* Name * Item *\\n*\\n* Rick * Portal Gun *\\n* Morty * Plumbus *\\n*\\n\"\n    ) in normalized_output\n\n\ndef test_script():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_printing/test_tutorial004.py",
    "content": "import subprocess\nimport sys\n\nfrom typer.testing import CliRunner\n\nimport docs_src.printing.tutorial004_py310 as mod\n\napp = mod.app\n\nrunner = CliRunner()\n\n\ndef test_cli():\n    result = runner.invoke(app)\n    assert result.exit_code == 0\n    assert result.stdout == \"\"\n    assert \"Here is something written to standard error\" in result.stderr\n\n\ndef test_script():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_printing/test_tutorial005.py",
    "content": "import subprocess\nimport sys\n\nfrom typer.testing import CliRunner\n\nimport docs_src.printing.tutorial005_py310 as mod\nfrom tests.utils import normalize_rich_output\n\napp = mod.app\n\nrunner = CliRunner()\n\n\ndef test_good_true():\n    result = runner.invoke(app, color=True)\n    assert result.exit_code == 0\n\n    # Replace all Rich formatting with `*` characters\n    normalized_output = normalize_rich_output(result.output)\n\n    assert \"everything is *good*\" in normalized_output\n    # We don't check exact colors here, just that text has formatting\n\n\ndef test_good_false():\n    result = runner.invoke(app, [\"--no-good\"], color=True)\n    assert result.exit_code == 0\n\n    # Replace all Rich formatting with `*` characters\n    normalized_output = normalize_rich_output(result.output)\n\n    assert \"everything is *bad*\" in normalized_output\n    # We don't check exact colors here, just that text has formatting\n\n\ndef test_script():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_printing/test_tutorial006.py",
    "content": "import subprocess\nimport sys\n\nfrom typer.testing import CliRunner\n\nimport docs_src.printing.tutorial006_py310 as mod\nfrom tests.utils import normalize_rich_output\n\napp = mod.app\n\nrunner = CliRunner()\n\n\ndef test_cli():\n    result = runner.invoke(app, [\"everyone\"], color=True)\n    assert result.exit_code == 0\n\n    # Replace all Rich formatting with `*` characters\n    normalized_output = normalize_rich_output(result.output)\n\n    assert \"*Welcome here everyone*\" in normalized_output\n    # We don't check exact colors here, just that text has formatting\n\n\ndef test_script():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_progressbar/__init__.py",
    "content": ""
  },
  {
    "path": "tests/test_tutorial/test_progressbar/test_tutorial001.py",
    "content": "import subprocess\nimport sys\nfrom unittest.mock import patch\n\nimport typer\nfrom typer.testing import CliRunner\n\nimport docs_src.progressbar.tutorial001_py310 as mod\nfrom tests.utils import normalize_rich_output\n\napp = mod.app\n\nrunner = CliRunner()\n\n\ndef test_cli_one_step():\n    with patch(\"time.sleep\") as sleep_mock:\n        sleep_mock.side_effect = typer.Exit()  # Exit on first `time.sleep()` call\n        result = runner.invoke(app)\n\n    # Replace all Rich formatting with `*` characters\n    normalized_output = normalize_rich_output(result.output)\n\n    assert result.exit_code == 0\n    assert \"Processing... 0%\" in normalized_output\n\n\ndef test_cli():\n    with patch(\"time.sleep\") as mock_sleep:\n        result = runner.invoke(app)\n\n    # Replace all Rich formatting with `*` characters\n    normalized_output = normalize_rich_output(result.output)\n\n    assert result.exit_code == 0\n    assert mock_sleep.call_count == 100\n    assert \"Processing...\" in normalized_output\n    assert \"100%\" in normalized_output\n    assert \"Processed 100 things.\" in normalized_output\n\n\ndef test_script():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_progressbar/test_tutorial002.py",
    "content": "import subprocess\nimport sys\nfrom unittest.mock import patch\n\nfrom typer.testing import CliRunner\n\nimport docs_src.progressbar.tutorial002_py310 as mod\nfrom tests.utils import normalize_rich_output\n\napp = mod.app\n\nrunner = CliRunner()\n\n\ndef test_cli():  # Checking only final state of spinner progress bar\n    with patch(\"time.sleep\") as mock_sleep:\n        result = runner.invoke(app)\n\n    # Replace all Rich formatting with `*` characters\n    normalized_output = normalize_rich_output(result.output)\n\n    assert result.exit_code == 0\n    mock_sleep.assert_called_once_with(5)\n    assert \"Done!\" in normalized_output\n\n\ndef test_script():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_progressbar/test_tutorial003.py",
    "content": "import subprocess\nimport sys\nfrom unittest.mock import patch\n\nfrom typer.testing import CliRunner\n\nimport docs_src.progressbar.tutorial003_py310 as mod\nfrom tests.utils import normalize_rich_output\n\napp = mod.app\n\nrunner = CliRunner()\n\n\ndef test_cli():  # Checking only final state of progress bar\n    with patch(\"time.sleep\") as mock_sleep:\n        result = runner.invoke(app)\n\n    # Replace all Rich formatting with `*` characters\n    normalized_output = normalize_rich_output(result.output)\n\n    assert result.exit_code == 0\n    assert mock_sleep.call_count == 100\n    assert \"Processed 100 things.\" in normalized_output\n\n\ndef test_script():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_progressbar/test_tutorial004.py",
    "content": "import subprocess\nimport sys\nfrom unittest.mock import patch\n\nfrom typer.testing import CliRunner\n\nimport docs_src.progressbar.tutorial004_py310 as mod\nfrom tests.utils import normalize_rich_output\n\napp = mod.app\n\nrunner = CliRunner()\n\n\ndef test_cli():  # Checking only final state of progress bar\n    consumed = []\n\n    def fake_iterate_user_ids():\n        for i in range(100):\n            consumed.append(i)\n            yield i\n\n    with (\n        patch(\"time.sleep\") as mock_sleep,\n        patch(\n            \"docs_src.progressbar.tutorial004_py310.iterate_user_ids\",\n            side_effect=fake_iterate_user_ids,\n        ),\n    ):\n        result = runner.invoke(app)\n\n    # Replace all Rich formatting with `*` characters\n    normalized_output = normalize_rich_output(result.output)\n\n    assert result.exit_code == 0\n    assert len(consumed) == 100\n    assert mock_sleep.call_count == 100\n    assert \"Processed 100 user IDs.\" in normalized_output\n\n\ndef test_cli_no_mock_generator():\n    with (\n        patch(\"time.sleep\") as mock_sleep,\n    ):\n        result = runner.invoke(app)\n\n    # Replace all Rich formatting with `*` characters\n    normalized_output = normalize_rich_output(result.output)\n\n    assert result.exit_code == 0\n    assert mock_sleep.call_count == 100\n    assert \"Processed 100 user IDs.\" in normalized_output\n\n\ndef test_script():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_progressbar/test_tutorial005.py",
    "content": "import subprocess\nimport sys\nfrom unittest.mock import patch\n\nfrom typer.testing import CliRunner\n\nimport docs_src.progressbar.tutorial005_py310 as mod\nfrom tests.utils import normalize_rich_output\n\napp = mod.app\n\nrunner = CliRunner()\n\n\ndef test_cli():  # Checking only final state of progress bar\n    with patch(\"time.sleep\") as mock_sleep:\n        result = runner.invoke(app)\n\n    # Replace all Rich formatting with `*` characters\n    normalized_output = normalize_rich_output(result.output)\n\n    assert result.exit_code == 0\n    assert mock_sleep.call_count == 100\n    assert \"Processed 100 things.\" in normalized_output\n\n\ndef test_script():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_progressbar/test_tutorial006.py",
    "content": "import subprocess\nimport sys\nfrom unittest.mock import patch\n\nfrom typer.testing import CliRunner\n\nimport docs_src.progressbar.tutorial006_py310 as mod\nfrom tests.utils import normalize_rich_output\n\napp = mod.app\n\nrunner = CliRunner()\n\n\ndef test_cli():  # Checking only final state of progress bar\n    with patch(\"time.sleep\") as mock_sleep:\n        result = runner.invoke(app)\n\n    # Replace all Rich formatting with `*` characters\n    normalized_output = normalize_rich_output(result.output)\n\n    assert result.exit_code == 0\n    assert mock_sleep.call_count == 4\n    assert \"Processed 1000 things in batches.\" in normalized_output\n\n\ndef test_script():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_prompt/__init__.py",
    "content": ""
  },
  {
    "path": "tests/test_tutorial/test_prompt/test_tutorial001.py",
    "content": "import subprocess\nimport sys\n\nfrom typer.testing import CliRunner\n\nfrom docs_src.prompt import tutorial001_py310 as mod\n\nrunner = CliRunner()\napp = mod.app\n\n\ndef test_cli():\n    result = runner.invoke(app, input=\"Camila\\n\")\n    assert result.exit_code == 0\n    assert \"What's your name?:\" in result.output\n    assert \"Hello Camila\" in result.output\n\n\ndef test_script():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_prompt/test_tutorial002.py",
    "content": "import subprocess\nimport sys\n\nfrom typer.testing import CliRunner\n\nfrom docs_src.prompt import tutorial002_py310 as mod\n\nrunner = CliRunner()\napp = mod.app\n\n\ndef test_cli():\n    result = runner.invoke(app, input=\"y\\n\")\n    assert result.exit_code == 0\n    assert \"Are you sure you want to delete it? [y/N]:\" in result.output\n    assert \"Deleting it!\" in result.output\n\n\ndef test_no_confirm():\n    result = runner.invoke(app, input=\"n\\n\")\n    assert result.exit_code == 1\n    assert \"Are you sure you want to delete it? [y/N]:\" in result.output\n    assert \"Not deleting\" in result.output\n    assert \"Aborted\" in result.output\n\n\ndef test_script():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_prompt/test_tutorial003.py",
    "content": "import subprocess\nimport sys\n\nfrom typer.testing import CliRunner\n\nfrom docs_src.prompt import tutorial003_py310 as mod\n\nrunner = CliRunner()\napp = mod.app\n\n\ndef test_cli():\n    result = runner.invoke(app, input=\"y\\n\")\n    assert result.exit_code == 0\n    assert \"Are you sure you want to delete it? [y/N]:\" in result.output\n    assert \"Deleting it!\" in result.output\n\n\ndef test_no_confirm():\n    result = runner.invoke(app, input=\"n\\n\")\n    assert result.exit_code == 1\n    assert \"Are you sure you want to delete it? [y/N]:\" in result.output\n    assert \"Aborted\" in result.output\n\n\ndef test_script():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_prompt/test_tutorial004.py",
    "content": "import subprocess\nimport sys\n\nfrom typer.testing import CliRunner\n\nfrom docs_src.prompt import tutorial004_py310 as mod\n\nrunner = CliRunner()\napp = mod.app\n\n\ndef test_cli():\n    result = runner.invoke(app, input=\"World\\n\")\n    assert result.exit_code == 0\n    assert \"Enter your name\" in result.output\n    assert \"Hey there World!\" in result.output\n\n\ndef test_script():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_subcommands/__init__.py",
    "content": ""
  },
  {
    "path": "tests/test_tutorial/test_subcommands/test_callback_override/__init__.py",
    "content": ""
  },
  {
    "path": "tests/test_tutorial/test_subcommands/test_callback_override/test_tutorial001.py",
    "content": "import subprocess\nimport sys\n\nfrom typer.testing import CliRunner\n\nfrom docs_src.subcommands.callback_override import tutorial001_py310 as mod\n\nrunner = CliRunner()\n\napp = mod.app\n\n\ndef test_cli():\n    result = runner.invoke(app, [\"users\", \"create\", \"Camila\"])\n    assert result.exit_code == 0\n    assert \"Running a users command\" in result.output\n    assert \"Creating user: Camila\" in result.output\n\n\ndef test_script():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_subcommands/test_callback_override/test_tutorial002.py",
    "content": "import subprocess\nimport sys\n\nfrom typer.testing import CliRunner\n\nfrom docs_src.subcommands.callback_override import tutorial002_py310 as mod\n\nrunner = CliRunner()\n\napp = mod.app\n\n\ndef test_cli():\n    result = runner.invoke(app, [\"users\", \"create\", \"Camila\"])\n    assert result.exit_code == 0\n    assert \"Running a users command\" in result.output\n    assert \"Creating user: Camila\" in result.output\n\n\ndef test_script():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_subcommands/test_callback_override/test_tutorial003.py",
    "content": "import subprocess\nimport sys\n\nfrom typer.testing import CliRunner\n\nfrom docs_src.subcommands.callback_override import tutorial003_py310 as mod\n\nrunner = CliRunner()\n\napp = mod.app\n\n\ndef test_cli():\n    result = runner.invoke(app, [\"users\", \"create\", \"Camila\"])\n    assert result.exit_code == 0\n    assert \"Running a users command\" not in result.output\n    assert \"Callback override, running users command\" in result.output\n    assert \"Creating user: Camila\" in result.output\n\n\ndef test_for_coverage():\n    mod.default_callback()\n\n\ndef test_script():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_subcommands/test_callback_override/test_tutorial004.py",
    "content": "import subprocess\nimport sys\n\nfrom typer.testing import CliRunner\n\nfrom docs_src.subcommands.callback_override import tutorial004_py310 as mod\n\nrunner = CliRunner()\n\napp = mod.app\n\n\ndef test_cli():\n    result = runner.invoke(app, [\"users\", \"create\", \"Camila\"])\n    assert result.exit_code == 0\n    assert \"Running a users command\" not in result.output\n    assert \"Callback override, running users command\" not in result.output\n    assert \"I have the high land! Running users command\" in result.output\n    assert \"Creating user: Camila\" in result.output\n\n\ndef test_for_coverage():\n    mod.default_callback()\n    mod.user_callback()\n\n\ndef test_script():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_subcommands/test_name_help/__init__.py",
    "content": ""
  },
  {
    "path": "tests/test_tutorial/test_subcommands/test_name_help/test_tutorial001.py",
    "content": "import subprocess\nimport sys\n\nfrom typer.testing import CliRunner\n\nfrom docs_src.subcommands.name_help import tutorial001_py310 as mod\n\nrunner = CliRunner()\n\napp = mod.app\n\n\ndef test_help():\n    result = runner.invoke(app, [\"--help\"])\n    assert result.exit_code == 0\n    assert \"Commands\" in result.output\n    assert \"users\" in result.output\n    assert \"Manage users in the app.\" in result.output\n\n\ndef test_command_help():\n    result = runner.invoke(app, [\"users\", \"--help\"])\n    assert result.exit_code == 0\n    assert \"Manage users in the app.\" in result.output\n\n\ndef test_command():\n    result = runner.invoke(app, [\"users\", \"create\", \"Camila\"])\n    assert result.exit_code == 0\n    assert \"Creating user: Camila\" in result.output\n\n\ndef test_script():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_subcommands/test_name_help/test_tutorial002.py",
    "content": "import subprocess\nimport sys\n\nfrom typer.testing import CliRunner\n\nfrom docs_src.subcommands.name_help import tutorial002_py310 as mod\n\nrunner = CliRunner()\n\napp = mod.app\n\n\ndef test_help():\n    result = runner.invoke(app, [\"--help\"])\n    assert result.exit_code == 0\n    assert \"Commands\" in result.output\n    assert \"users\" in result.output\n    assert \"Manage users in the app.\" in result.output\n\n\ndef test_command_help():\n    result = runner.invoke(app, [\"users\", \"--help\"])\n    assert result.exit_code == 0\n    assert \"Manage users in the app.\" in result.output\n\n\ndef test_command():\n    result = runner.invoke(app, [\"users\", \"create\", \"Camila\"])\n    assert result.exit_code == 0\n    assert \"Creating user: Camila\" in result.output\n\n\ndef test_script():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_subcommands/test_name_help/test_tutorial003.py",
    "content": "import subprocess\nimport sys\n\nfrom typer.testing import CliRunner\n\nfrom docs_src.subcommands.name_help import tutorial003_py310 as mod\n\nrunner = CliRunner()\n\napp = mod.app\n\n\ndef test_help():\n    result = runner.invoke(app, [\"--help\"])\n    assert result.exit_code == 0\n    assert \"Commands\" in result.output\n    assert \"users\" in result.output\n    assert \"Manage users in the app.\" in result.output\n\n\ndef test_command_help():\n    result = runner.invoke(app, [\"users\", \"--help\"])\n    assert result.exit_code == 0\n    assert \"Manage users in the app.\" in result.output\n\n\ndef test_command():\n    result = runner.invoke(app, [\"users\", \"create\", \"Camila\"])\n    assert result.exit_code == 0\n    assert \"Creating user: Camila\" in result.output\n\n\ndef test_script():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_subcommands/test_name_help/test_tutorial004.py",
    "content": "import subprocess\nimport sys\n\nfrom typer.testing import CliRunner\n\nfrom docs_src.subcommands.name_help import tutorial004_py310 as mod\n\nrunner = CliRunner()\n\napp = mod.app\n\n\ndef test_help():\n    result = runner.invoke(app, [\"--help\"])\n    assert result.exit_code == 0\n    assert \"Commands\" in result.output\n    assert \"users\" in result.output\n    assert \"Manage users in the app.\" in result.output\n\n\ndef test_command_help():\n    result = runner.invoke(app, [\"users\", \"--help\"])\n    assert result.exit_code == 0\n    assert \"Manage users in the app.\" in result.output\n\n\ndef test_command():\n    result = runner.invoke(app, [\"users\", \"create\", \"Camila\"])\n    assert result.exit_code == 0\n    assert \"Creating user: Camila\" in result.output\n\n\ndef test_script():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_subcommands/test_name_help/test_tutorial005.py",
    "content": "import subprocess\nimport sys\n\nfrom typer.testing import CliRunner\n\nfrom docs_src.subcommands.name_help import tutorial005_py310 as mod\n\nrunner = CliRunner()\n\napp = mod.app\n\n\ndef test_help():\n    result = runner.invoke(app, [\"--help\"])\n    assert result.exit_code == 0\n    assert \"Commands\" in result.output\n    assert \"new-users\" in result.output\n    assert \"I have the highland! Create some users.\" in result.output\n\n\ndef test_command_help():\n    result = runner.invoke(app, [\"new-users\", \"--help\"])\n    assert result.exit_code == 0\n    assert \"I have the highland! Create some users.\" in result.output\n\n\ndef test_command():\n    result = runner.invoke(app, [\"new-users\", \"create\", \"Camila\"])\n    assert result.exit_code == 0\n    assert \"Creating user: Camila\" in result.output\n\n\ndef test_script():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_subcommands/test_name_help/test_tutorial006.py",
    "content": "import subprocess\nimport sys\n\nfrom typer.testing import CliRunner\n\nfrom docs_src.subcommands.name_help import tutorial006_py310 as mod\n\nrunner = CliRunner()\n\napp = mod.app\n\n\ndef test_help():\n    result = runner.invoke(app, [\"--help\"])\n    assert result.exit_code == 0\n    assert \"Commands\" in result.output\n    assert \"exp-users\" in result.output\n    assert \"Explicit help.\" in result.output\n\n\ndef test_command_help():\n    result = runner.invoke(app, [\"exp-users\", \"--help\"])\n    assert result.exit_code == 0\n    assert \"Explicit help.\" in result.output\n\n\ndef test_command():\n    result = runner.invoke(app, [\"exp-users\", \"create\", \"Camila\"])\n    assert result.exit_code == 0\n    assert \"Creating user: Camila\" in result.output\n\n\ndef test_script():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_subcommands/test_name_help/test_tutorial007.py",
    "content": "import subprocess\nimport sys\n\nfrom typer.testing import CliRunner\n\nfrom docs_src.subcommands.name_help import tutorial007_py310 as mod\n\nrunner = CliRunner()\n\napp = mod.app\n\n\ndef test_help():\n    result = runner.invoke(app, [\"--help\"])\n    assert result.exit_code == 0\n    assert \"Commands\" in result.output\n    assert \"users\" in result.output\n    assert \"Help from callback for users.\" in result.output\n\n\ndef test_command_help():\n    result = runner.invoke(app, [\"users\", \"--help\"])\n    assert result.exit_code == 0\n    assert \"Help from callback for users.\" in result.output\n\n\ndef test_command():\n    result = runner.invoke(app, [\"users\", \"create\", \"Camila\"])\n    assert result.exit_code == 0\n    assert \"Creating user: Camila\" in result.output\n\n\ndef test_script():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_subcommands/test_name_help/test_tutorial008.py",
    "content": "import subprocess\nimport sys\n\nfrom typer.testing import CliRunner\n\nfrom docs_src.subcommands.name_help import tutorial008_py310 as mod\n\nrunner = CliRunner()\n\napp = mod.app\n\n\ndef test_help():\n    result = runner.invoke(app, [\"--help\"])\n    assert result.exit_code == 0\n    assert \"Commands\" in result.output\n    assert \"cake-sith-users\" in result.output\n    assert \"Unlimited powder! Eh, users.\" in result.output\n\n\ndef test_command_help():\n    result = runner.invoke(app, [\"cake-sith-users\", \"--help\"])\n    assert result.exit_code == 0\n    assert \"Unlimited powder! Eh, users.\" in result.output\n\n\ndef test_command():\n    result = runner.invoke(app, [\"cake-sith-users\", \"create\", \"Camila\"])\n    assert result.exit_code == 0\n    assert \"Creating user: Camila\" in result.output\n\n\ndef test_script():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_subcommands/test_tutorial001.py",
    "content": "import os\nimport subprocess\nimport sys\n\nimport pytest\nfrom typer.testing import CliRunner\n\nfrom docs_src.subcommands import tutorial001_py310\n\nrunner = CliRunner()\n\n\n@pytest.fixture()\ndef mod(monkeypatch):\n    with monkeypatch.context():\n        monkeypatch.syspath_prepend(list(tutorial001_py310.__path__)[0])\n        from docs_src.subcommands.tutorial001_py310 import main\n\n        return main\n\n\n@pytest.fixture()\ndef app(mod):\n    return mod.app\n\n\ndef test_help(app):\n    result = runner.invoke(app, [\"--help\"])\n    assert result.exit_code == 0\n    assert \"[OPTIONS] COMMAND [ARGS]...\" in result.output\n    assert \"Commands\" in result.output\n    assert \"items\" in result.output\n    assert \"users\" in result.output\n\n\ndef test_help_items(app):\n    result = runner.invoke(app, [\"items\", \"--help\"])\n    assert result.exit_code == 0\n    assert \"[OPTIONS] COMMAND [ARGS]...\" in result.output\n    assert \"Commands\" in result.output\n    assert \"create\" in result.output\n    assert \"delete\" in result.output\n    assert \"sell\" in result.output\n\n\ndef test_items_create(app):\n    result = runner.invoke(app, [\"items\", \"create\", \"Wand\"])\n    assert result.exit_code == 0\n    assert \"Creating item: Wand\" in result.output\n\n\ndef test_items_sell(app):\n    result = runner.invoke(app, [\"items\", \"sell\", \"Vase\"])\n    assert result.exit_code == 0\n    assert \"Selling item: Vase\" in result.output\n\n\ndef test_items_delete(app):\n    result = runner.invoke(app, [\"items\", \"delete\", \"Vase\"])\n    assert result.exit_code == 0\n    assert \"Deleting item: Vase\" in result.output\n\n\ndef test_help_users(app):\n    result = runner.invoke(app, [\"users\", \"--help\"])\n    assert result.exit_code == 0\n    assert \"[OPTIONS] COMMAND [ARGS]...\" in result.output\n    assert \"Commands\" in result.output\n    assert \"create\" in result.output\n    assert \"delete\" in result.output\n    assert \"sell\" not in result.output\n\n\ndef test_users_create(app):\n    result = runner.invoke(app, [\"users\", \"create\", \"Camila\"])\n    assert result.exit_code == 0\n    assert \"Creating user: Camila\" in result.output\n\n\ndef test_users_delete(app):\n    result = runner.invoke(app, [\"users\", \"delete\", \"Camila\"])\n    assert result.exit_code == 0\n    assert \"Deleting user: Camila\" in result.output\n\n\ndef test_scripts(mod):\n    from docs_src.subcommands.tutorial001_py310 import items, users\n\n    env = os.environ.copy()\n    env[\"PYTHONPATH\"] = \":\".join(list(tutorial001_py310.__path__))\n\n    for module in [mod, items, users]:\n        result = subprocess.run(\n            [sys.executable, \"-m\", \"coverage\", \"run\", module.__file__, \"--help\"],\n            capture_output=True,\n            encoding=\"utf-8\",\n            env=env,\n        )\n        assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_subcommands/test_tutorial002.py",
    "content": "import subprocess\nimport sys\n\nfrom typer.testing import CliRunner\n\nfrom docs_src.subcommands.tutorial002_py310 import main as mod\n\napp = mod.app\nrunner = CliRunner()\n\n\ndef test_help():\n    result = runner.invoke(app, [\"--help\"])\n    assert result.exit_code == 0\n    assert \"[OPTIONS] COMMAND [ARGS]...\" in result.output\n    assert \"Commands\" in result.output\n    assert \"items\" in result.output\n    assert \"users\" in result.output\n\n\ndef test_help_items():\n    result = runner.invoke(app, [\"items\", \"--help\"])\n    assert result.exit_code == 0\n    assert \"[OPTIONS] COMMAND [ARGS]...\" in result.output\n    assert \"Commands\" in result.output\n    assert \"create\" in result.output\n    assert \"delete\" in result.output\n    assert \"sell\" in result.output\n\n\ndef test_items_create():\n    result = runner.invoke(app, [\"items\", \"create\", \"Wand\"])\n    assert result.exit_code == 0\n    assert \"Creating item: Wand\" in result.output\n\n\ndef test_items_sell():\n    result = runner.invoke(app, [\"items\", \"sell\", \"Vase\"])\n    assert result.exit_code == 0\n    assert \"Selling item: Vase\" in result.output\n\n\ndef test_items_delete():\n    result = runner.invoke(app, [\"items\", \"delete\", \"Vase\"])\n    assert result.exit_code == 0\n    assert \"Deleting item: Vase\" in result.output\n\n\ndef test_help_users():\n    result = runner.invoke(app, [\"users\", \"--help\"])\n    assert result.exit_code == 0\n    assert \"[OPTIONS] COMMAND [ARGS]...\" in result.output\n    assert \"Commands\" in result.output\n    assert \"create\" in result.output\n    assert \"delete\" in result.output\n    assert \"sell\" not in result.output\n\n\ndef test_users_create():\n    result = runner.invoke(app, [\"users\", \"create\", \"Camila\"])\n    assert result.exit_code == 0\n    assert \"Creating user: Camila\" in result.output\n\n\ndef test_users_delete():\n    result = runner.invoke(app, [\"users\", \"delete\", \"Camila\"])\n    assert result.exit_code == 0\n    assert \"Deleting user: Camila\" in result.output\n\n\ndef test_script():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_subcommands/test_tutorial003.py",
    "content": "import os\nimport subprocess\nimport sys\n\nimport pytest\nfrom typer.testing import CliRunner\n\nfrom docs_src.subcommands import tutorial003_py310\nfrom docs_src.subcommands.tutorial003_py310 import items, users\n\nrunner = CliRunner()\n\n\n@pytest.fixture()\ndef mod(monkeypatch):\n    with monkeypatch.context() as m:\n        m.syspath_prepend(list(tutorial003_py310.__path__)[0])\n        from docs_src.subcommands.tutorial003_py310 import main\n\n        return main\n\n\n@pytest.fixture()\ndef app(mod):\n    return mod.app\n\n\ndef test_help(app):\n    result = runner.invoke(app, [\"--help\"])\n    assert result.exit_code == 0\n    assert \"[OPTIONS] COMMAND [ARGS]...\" in result.output\n    assert \"Commands\" in result.output\n    assert \"items\" in result.output\n    assert \"users\" in result.output\n    assert \"lands\" in result.output\n\n\ndef test_help_items(app):\n    result = runner.invoke(app, [\"items\", \"--help\"])\n    assert result.exit_code == 0\n    assert \"[OPTIONS] COMMAND [ARGS]...\" in result.output\n    assert \"Commands\" in result.output\n    assert \"create\" in result.output\n    assert \"delete\" in result.output\n    assert \"sell\" in result.output\n\n\ndef test_items_create(app):\n    result = runner.invoke(app, [\"items\", \"create\", \"Wand\"])\n    assert result.exit_code == 0\n    assert \"Creating item: Wand\" in result.output\n    # For coverage, because the monkeypatch above sometimes confuses coverage\n    result = runner.invoke(items.app, [\"create\", \"Wand\"])\n    assert result.exit_code == 0\n    assert \"Creating item: Wand\" in result.output\n\n\ndef test_items_sell(app):\n    result = runner.invoke(app, [\"items\", \"sell\", \"Vase\"])\n    assert result.exit_code == 0\n    assert \"Selling item: Vase\" in result.output\n    # For coverage, because the monkeypatch above sometimes confuses coverage\n    result = runner.invoke(items.app, [\"sell\", \"Vase\"])\n    assert result.exit_code == 0\n    assert \"Selling item: Vase\" in result.output\n\n\ndef test_items_delete(app):\n    result = runner.invoke(app, [\"items\", \"delete\", \"Vase\"])\n    assert result.exit_code == 0\n    assert \"Deleting item: Vase\" in result.output\n    # For coverage, because the monkeypatch above sometimes confuses coverage\n    result = runner.invoke(items.app, [\"delete\", \"Vase\"])\n    assert result.exit_code == 0\n    assert \"Deleting item: Vase\" in result.output\n\n\ndef test_help_users(app):\n    result = runner.invoke(app, [\"users\", \"--help\"])\n    assert result.exit_code == 0\n    assert \"[OPTIONS] COMMAND [ARGS]...\" in result.output\n    assert \"Commands\" in result.output\n    assert \"create\" in result.output\n    assert \"delete\" in result.output\n    assert \"sell\" not in result.output\n\n\ndef test_users_create(app):\n    result = runner.invoke(app, [\"users\", \"create\", \"Camila\"])\n    assert result.exit_code == 0\n    assert \"Creating user: Camila\" in result.output\n    # For coverage, because the monkeypatch above sometimes confuses coverage\n    result = runner.invoke(users.app, [\"create\", \"Camila\"])\n    assert result.exit_code == 0\n    assert \"Creating user: Camila\" in result.output\n\n\ndef test_users_delete(app):\n    result = runner.invoke(app, [\"users\", \"delete\", \"Camila\"])\n    assert result.exit_code == 0\n    assert \"Deleting user: Camila\" in result.output\n    # For coverage, because the monkeypatch above sometimes confuses coverage\n    result = runner.invoke(users.app, [\"delete\", \"Camila\"])\n    assert result.exit_code == 0\n    assert \"Deleting user: Camila\" in result.output\n\n\ndef test_help_lands(app):\n    result = runner.invoke(app, [\"lands\", \"--help\"])\n    assert result.exit_code == 0\n    assert \"lands [OPTIONS] COMMAND [ARGS]...\" in result.output\n    assert \"Commands\" in result.output\n    assert \"reigns\" in result.output\n    assert \"towns\" in result.output\n\n\ndef test_help_lands_reigns(app):\n    result = runner.invoke(app, [\"lands\", \"reigns\", \"--help\"])\n    assert result.exit_code == 0\n    assert \"lands reigns [OPTIONS] COMMAND [ARGS]...\" in result.output\n    assert \"Commands\" in result.output\n    assert \"conquer\" in result.output\n    assert \"destroy\" in result.output\n\n\ndef test_lands_reigns_conquer(app):\n    result = runner.invoke(app, [\"lands\", \"reigns\", \"conquer\", \"Gondor\"])\n    assert result.exit_code == 0\n    assert \"Conquering reign: Gondor\" in result.output\n\n\ndef test_lands_reigns_destroy(app):\n    result = runner.invoke(app, [\"lands\", \"reigns\", \"destroy\", \"Mordor\"])\n    assert result.exit_code == 0\n    assert \"Destroying reign: Mordor\" in result.output\n\n\ndef test_help_lands_towns(app):\n    result = runner.invoke(app, [\"lands\", \"towns\", \"--help\"])\n    assert result.exit_code == 0\n    assert \"lands towns [OPTIONS] COMMAND [ARGS]...\" in result.output\n    assert \"Commands\" in result.output\n    assert \"burn\" in result.output\n    assert \"found\" in result.output\n\n\ndef test_lands_towns_found(app):\n    result = runner.invoke(app, [\"lands\", \"towns\", \"found\", \"Cartagena\"])\n    assert result.exit_code == 0\n    assert \"Founding town: Cartagena\" in result.output\n\n\ndef test_lands_towns_burn(app):\n    result = runner.invoke(app, [\"lands\", \"towns\", \"burn\", \"New Asgard\"])\n    assert result.exit_code == 0\n    assert \"Burning town: New Asgard\" in result.output\n\n\ndef test_scripts(mod):\n    from docs_src.subcommands.tutorial003_py310 import (\n        items,\n        lands,\n        reigns,\n        towns,\n        users,\n    )\n\n    env = os.environ.copy()\n    env[\"PYTHONPATH\"] = \":\".join(list(tutorial003_py310.__path__))\n\n    for module in [mod, items, lands, reigns, towns, users]:\n        result = subprocess.run(\n            [sys.executable, \"-m\", \"coverage\", \"run\", module.__file__, \"--help\"],\n            capture_output=True,\n            encoding=\"utf-8\",\n            env=env,\n        )\n        assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_terminating/__init__.py",
    "content": ""
  },
  {
    "path": "tests/test_tutorial/test_terminating/test_tutorial001.py",
    "content": "import subprocess\nimport sys\n\nfrom typer.testing import CliRunner\n\nfrom docs_src.terminating import tutorial001_py310 as mod\n\nrunner = CliRunner()\napp = mod.app\n\n\ndef test_cli():\n    result = runner.invoke(app, [\"Camila\"])\n    assert result.exit_code == 0\n    assert \"User created: Camila\" in result.output\n    assert \"Notification sent for new user: Camila\" in result.output\n\n\ndef test_existing():\n    result = runner.invoke(app, [\"rick\"])\n    assert result.exit_code == 0\n    assert \"The user already exists\" in result.output\n    assert \"Notification sent for new user\" not in result.output\n\n\ndef test_existing_no_standalone():\n    # Mainly for coverage\n    result = runner.invoke(app, [\"rick\"], standalone_mode=False)\n    assert result.exit_code == 0\n    assert \"The user already exists\" in result.output\n    assert \"Notification sent for new user\" not in result.output\n\n\ndef test_script():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_terminating/test_tutorial002.py",
    "content": "import subprocess\nimport sys\n\nfrom typer.testing import CliRunner\n\nfrom docs_src.terminating import tutorial002_py310 as mod\n\nrunner = CliRunner()\napp = mod.app\n\n\ndef test_cli():\n    result = runner.invoke(app, [\"Camila\"])\n    assert result.exit_code == 0\n    assert \"New user created: Camila\" in result.output\n\n\ndef test_root():\n    result = runner.invoke(app, [\"root\"])\n    assert result.exit_code == 1\n    assert \"The root user is reserved\" in result.output\n\n\ndef test_script():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_terminating/test_tutorial003.py",
    "content": "import subprocess\nimport sys\n\nimport pytest\nimport typer\nimport typer.core\nfrom typer.testing import CliRunner\n\nfrom docs_src.terminating import tutorial003_py310 as mod\n\nrunner = CliRunner()\napp = mod.app\n\n\ndef test_cli():\n    result = runner.invoke(app, [\"Camila\"])\n    assert result.exit_code == 0\n    assert \"New user created: Camila\" in result.output\n\n\ndef test_root():\n    result = runner.invoke(app, [\"root\"])\n    assert result.exit_code == 1\n    assert \"The root user is reserved\" in result.output\n    assert \"Aborted\" in result.output\n\n\ndef test_root_no_standalone():\n    # Mainly for coverage\n    result = runner.invoke(app, [\"root\"], standalone_mode=False)\n    assert result.exit_code == 1\n\n\ndef test_root_no_rich(monkeypatch: pytest.MonkeyPatch):\n    # Mainly for coverage\n    monkeypatch.setattr(typer.core, \"HAS_RICH\", False)\n    result = runner.invoke(app, [\"root\"])\n    assert result.exit_code == 1\n    assert \"The root user is reserved\" in result.output\n    assert \"Aborted!\" in result.output\n\n\ndef test_script():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_testing/__init__.py",
    "content": ""
  },
  {
    "path": "tests/test_tutorial/test_testing/test_app01.py",
    "content": "import subprocess\nimport sys\n\nfrom docs_src.testing.app01_py310 import main as mod\nfrom docs_src.testing.app01_py310.test_main import test_app\n\n\ndef test_app01():\n    test_app()\n\n\ndef test_script():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_testing/test_app02.py",
    "content": "import importlib\nimport subprocess\nimport sys\nfrom types import ModuleType\n\nimport pytest\n\n\n@pytest.fixture(\n    name=\"module_paths\",\n    params=[\n        \"app02_py310\",\n        \"app02_an_py310\",\n    ],\n)\ndef get_modules_path(request: pytest.FixtureRequest) -> str:\n    return f\"docs_src.testing.{request.param}\"\n\n\n@pytest.fixture(name=\"main_mod\")\ndef get_main_mod(module_paths: str) -> ModuleType:\n    mod = importlib.import_module(f\"{module_paths}.main\")\n    return mod\n\n\n@pytest.fixture(name=\"test_mod\")\ndef get_test_mod(module_paths: str) -> ModuleType:\n    mod = importlib.import_module(f\"{module_paths}.test_main\")\n    return mod\n\n\ndef test_app02(test_mod: ModuleType):\n    test_mod.test_app()\n\n\ndef test_script(main_mod: ModuleType):\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", main_mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_testing/test_app03.py",
    "content": "import subprocess\nimport sys\n\nfrom docs_src.testing.app03_py310 import main as mod\nfrom docs_src.testing.app03_py310.test_main import test_app\n\n\ndef test_app03():\n    test_app()\n\n\ndef test_script():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_tutorial/test_typer_app/__init__.py",
    "content": ""
  },
  {
    "path": "tests/test_tutorial/test_typer_app/test_tutorial001.py",
    "content": "import subprocess\nimport sys\n\nfrom typer.testing import CliRunner\n\nfrom docs_src.typer_app import tutorial001_py310 as mod\n\napp = mod.app\n\nrunner = CliRunner()\n\n\ndef test_no_arg():\n    result = runner.invoke(app)\n    assert result.exit_code != 0\n    assert \"Missing argument 'NAME'.\" in result.output\n\n\ndef test_arg():\n    result = runner.invoke(app, [\"Camila\"])\n    assert result.exit_code == 0\n    assert \"Hello Camila\" in result.output\n\n\ndef test_script():\n    result = subprocess.run(\n        [sys.executable, \"-m\", \"coverage\", \"run\", mod.__file__, \"--help\"],\n        capture_output=True,\n        encoding=\"utf-8\",\n    )\n    assert \"Usage\" in result.stdout\n"
  },
  {
    "path": "tests/test_type_conversion.py",
    "content": "from enum import Enum\nfrom pathlib import Path\nfrom typing import Any\n\nimport click\nimport pytest\nimport typer\nfrom typer.testing import CliRunner\n\nrunner = CliRunner()\n\n\ndef test_optional():\n    app = typer.Typer()\n\n    @app.command()\n    def opt(user: str | None = None):\n        if user:\n            print(f\"User: {user}\")\n        else:\n            print(\"No user\")\n\n    result = runner.invoke(app)\n    assert result.exit_code == 0\n    assert \"No user\" in result.output\n\n    result = runner.invoke(app, [\"--user\", \"Camila\"])\n    assert result.exit_code == 0\n    assert \"User: Camila\" in result.output\n\n\ndef test_union_type_optional():\n    app = typer.Typer()\n\n    @app.command()\n    def opt(user: str | None = None):\n        if user:\n            print(f\"User: {user}\")\n        else:\n            print(\"No user\")\n\n    result = runner.invoke(app)\n    assert result.exit_code == 0\n    assert \"No user\" in result.output\n\n    result = runner.invoke(app, [\"--user\", \"Camila\"])\n    assert result.exit_code == 0\n    assert \"User: Camila\" in result.output\n\n\ndef test_optional_tuple():\n    app = typer.Typer()\n\n    @app.command()\n    def opt(number: tuple[int, int] | None = None):\n        if number:\n            print(f\"Number: {number}\")\n        else:\n            print(\"No number\")\n\n    result = runner.invoke(app)\n    assert result.exit_code == 0\n    assert \"No number\" in result.output\n\n    result = runner.invoke(app, [\"--number\", \"4\", \"2\"])\n    assert result.exit_code == 0\n    assert \"Number: (4, 2)\" in result.output\n\n\ndef test_no_type():\n    app = typer.Typer()\n\n    @app.command()\n    def no_type(user):\n        print(f\"User: {user}\")\n\n    result = runner.invoke(app, [\"Camila\"])\n    assert result.exit_code == 0\n    assert \"User: Camila\" in result.output\n\n\nclass SomeEnum(Enum):\n    ONE = \"one\"\n    TWO = \"two\"\n    THREE = \"three\"\n\n\n@pytest.mark.parametrize(\n    \"type_annotation\",\n    [list[Path], list[SomeEnum], list[str]],\n)\ndef test_list_parameters_convert_to_lists(type_annotation):\n    # Lists containing objects that are converted by Click (i.e. not Path or Enum)\n    # should not be inadvertently converted to tuples\n    expected_element_type = type_annotation.__args__[0]\n    app = typer.Typer()\n\n    @app.command()\n    def list_conversion(container: type_annotation):\n        assert isinstance(container, list)\n        for element in container:\n            assert isinstance(element, expected_element_type)\n\n    result = runner.invoke(app, [\"one\", \"two\", \"three\"])\n    assert result.exit_code == 0\n\n\n@pytest.mark.parametrize(\n    \"type_annotation\",\n    [\n        tuple[str, str],\n        tuple[str, Path],\n        tuple[Path, Path],\n        tuple[str, SomeEnum],\n        tuple[SomeEnum, SomeEnum],\n    ],\n)\ndef test_tuple_parameter_elements_are_converted_recursively(type_annotation):\n    # Tuple elements that aren't converted by Click (i.e. Path or Enum)\n    # should be recursively converted by Typer\n    expected_element_types = type_annotation.__args__\n    app = typer.Typer()\n\n    @app.command()\n    def tuple_recursive_conversion(container: type_annotation):\n        assert isinstance(container, tuple)\n        for element, expected_type in zip(\n            container, expected_element_types, strict=True\n        ):\n            assert isinstance(element, expected_type)\n\n    result = runner.invoke(app, [\"one\", \"two\"])\n    assert result.exit_code == 0\n\n\ndef test_custom_parse():\n    app = typer.Typer()\n\n    @app.command()\n    def custom_parser(\n        hex_value: int = typer.Argument(None, parser=lambda x: int(x, 0)),\n    ):\n        assert hex_value == 0x56\n\n    result = runner.invoke(app, [\"0x56\"])\n    assert result.exit_code == 0\n\n\ndef test_custom_click_type():\n    class BaseNumberParamType(click.ParamType):\n        name = \"base_integer\"\n\n        def convert(\n            self,\n            value: Any,\n            param: click.Parameter | None,\n            ctx: click.Context | None,\n        ) -> Any:\n            return int(value, 0)\n\n    app = typer.Typer()\n\n    @app.command()\n    def custom_click_type(\n        hex_value: int = typer.Argument(None, click_type=BaseNumberParamType()),\n    ):\n        assert hex_value == 0x56\n\n    result = runner.invoke(app, [\"0x56\"])\n    assert result.exit_code == 0\n"
  },
  {
    "path": "tests/test_types.py",
    "content": "from enum import Enum\n\nimport typer\nfrom typer.testing import CliRunner\n\napp = typer.Typer(context_settings={\"token_normalize_func\": str.lower})\n\n\nclass User(str, Enum):\n    rick = \"Rick\"\n    morty = \"Morty\"\n\n\n@app.command()\ndef hello(name: User = User.rick) -> None:\n    print(f\"Hello {name.value}!\")\n\n\nrunner = CliRunner()\n\n\ndef test_enum_choice() -> None:\n    # This test is only for coverage of the new custom TyperChoice class\n    result = runner.invoke(app, [\"--name\", \"morty\"], catch_exceptions=False)\n    assert result.exit_code == 0\n    assert \"Hello Morty!\" in result.output\n\n    result = runner.invoke(app, [\"--name\", \"Rick\"])\n    assert result.exit_code == 0\n    assert \"Hello Rick!\" in result.output\n\n    result = runner.invoke(app, [\"--name\", \"RICK\"])\n    assert result.exit_code == 0\n    assert \"Hello Rick!\" in result.output\n"
  },
  {
    "path": "tests/utils.py",
    "content": "import re\nimport sys\nfrom os import getenv\n\nimport pytest\nfrom typer._completion_shared import _get_shell_name\nfrom typer.core import HAS_RICH\n\nneeds_linux = pytest.mark.skipif(\n    not sys.platform.startswith(\"linux\"), reason=\"Test requires Linux\"\n)\n\nneeds_rich = pytest.mark.skipif(not HAS_RICH, reason=\"Test requires Rich\")\n\nshell = _get_shell_name()\nneeds_bash = pytest.mark.skipif(\n    shell is None or \"bash\" not in shell, reason=\"Test requires Bash\"\n)\n\nrequires_completion_permission = pytest.mark.skipif(\n    not getenv(\"_TYPER_RUN_INSTALL_COMPLETION_TESTS\", False),\n    reason=\"Test requires permission to run completion installation tests\",\n)\n\n\ndef strip_double_spaces(text: str) -> str:\n    return re.sub(r\" {2,}\", \" \", text)\n\n\ndef normalize_rich_output(\n    text: str, replace_with: str = \"*\", squash_whitespaces: bool = True\n) -> str:\n    \"\"\"\n    Replace all rich formatting characters with a simple placeholder.\n    \"\"\"\n    text = re.sub(r\"\\x1b\\[[0-9;]*m\", replace_with, text)\n    text = re.sub(r\"[\\u2500-\\u257F]\", replace_with, text)\n    text = re.sub(r\"[\\U0001F300-\\U0001F6FF]\", replace_with, text)\n    text = re.sub(f\"{re.escape(replace_with)}+\", replace_with, text)\n    if squash_whitespaces:\n        text = strip_double_spaces(text)\n    return text\n"
  },
  {
    "path": "typer/__init__.py",
    "content": "\"\"\"Typer, build great CLIs. Easy to code. Based on Python type hints.\"\"\"\n\n__version__ = \"0.24.1\"\n\nfrom shutil import get_terminal_size as get_terminal_size\n\nfrom click.exceptions import Abort as Abort\nfrom click.exceptions import BadParameter as BadParameter\nfrom click.exceptions import Exit as Exit\nfrom click.termui import clear as clear\nfrom click.termui import confirm as confirm\nfrom click.termui import echo_via_pager as echo_via_pager\nfrom click.termui import edit as edit\nfrom click.termui import getchar as getchar\nfrom click.termui import pause as pause\nfrom click.termui import progressbar as progressbar\nfrom click.termui import prompt as prompt\nfrom click.termui import secho as secho\nfrom click.termui import style as style\nfrom click.termui import unstyle as unstyle\nfrom click.utils import echo as echo\nfrom click.utils import format_filename as format_filename\nfrom click.utils import get_app_dir as get_app_dir\nfrom click.utils import get_binary_stream as get_binary_stream\nfrom click.utils import get_text_stream as get_text_stream\nfrom click.utils import open_file as open_file\n\nfrom . import colors as colors\nfrom .main import Typer as Typer\nfrom .main import launch as launch\nfrom .main import run as run\nfrom .models import CallbackParam as CallbackParam\nfrom .models import Context as Context\nfrom .models import FileBinaryRead as FileBinaryRead\nfrom .models import FileBinaryWrite as FileBinaryWrite\nfrom .models import FileText as FileText\nfrom .models import FileTextWrite as FileTextWrite\nfrom .params import Argument as Argument\nfrom .params import Option as Option\n"
  },
  {
    "path": "typer/__main__.py",
    "content": "from .cli import main\n\nmain()\n"
  },
  {
    "path": "typer/_completion_classes.py",
    "content": "import importlib.util\nimport os\nimport re\nimport sys\nfrom typing import Any\n\nimport click\nimport click.parser\nimport click.shell_completion\nfrom click.shell_completion import split_arg_string as click_split_arg_string\n\nfrom ._completion_shared import (\n    COMPLETION_SCRIPT_BASH,\n    COMPLETION_SCRIPT_FISH,\n    COMPLETION_SCRIPT_POWER_SHELL,\n    COMPLETION_SCRIPT_ZSH,\n    Shells,\n)\n\n\ndef _sanitize_help_text(text: str) -> str:\n    \"\"\"Sanitizes the help text by removing rich tags\"\"\"\n    if not importlib.util.find_spec(\"rich\"):\n        return text\n    from . import rich_utils\n\n    return rich_utils.rich_render_text(text)\n\n\nclass BashComplete(click.shell_completion.BashComplete):\n    name = Shells.bash.value\n    source_template = COMPLETION_SCRIPT_BASH\n\n    def source_vars(self) -> dict[str, Any]:\n        return {\n            \"complete_func\": self.func_name,\n            \"autocomplete_var\": self.complete_var,\n            \"prog_name\": self.prog_name,\n        }\n\n    def get_completion_args(self) -> tuple[list[str], str]:\n        cwords = click_split_arg_string(os.environ[\"COMP_WORDS\"])\n        cword = int(os.environ[\"COMP_CWORD\"])\n        args = cwords[1:cword]\n\n        try:\n            incomplete = cwords[cword]\n        except IndexError:\n            incomplete = \"\"\n\n        return args, incomplete\n\n    def format_completion(self, item: click.shell_completion.CompletionItem) -> str:\n        # TODO: Explore replicating the new behavior from Click, with item types and\n        # triggering completion for files and directories\n        # return f\"{item.type},{item.value}\"\n        return f\"{item.value}\"\n\n    def complete(self) -> str:\n        args, incomplete = self.get_completion_args()\n        completions = self.get_completions(args, incomplete)\n        out = [self.format_completion(item) for item in completions]\n        return \"\\n\".join(out)\n\n\nclass ZshComplete(click.shell_completion.ZshComplete):\n    name = Shells.zsh.value\n    source_template = COMPLETION_SCRIPT_ZSH\n\n    def source_vars(self) -> dict[str, Any]:\n        return {\n            \"complete_func\": self.func_name,\n            \"autocomplete_var\": self.complete_var,\n            \"prog_name\": self.prog_name,\n        }\n\n    def get_completion_args(self) -> tuple[list[str], str]:\n        completion_args = os.getenv(\"_TYPER_COMPLETE_ARGS\", \"\")\n        cwords = click_split_arg_string(completion_args)\n        args = cwords[1:]\n        if args and not completion_args.endswith(\" \"):\n            incomplete = args[-1]\n            args = args[:-1]\n        else:\n            incomplete = \"\"\n        return args, incomplete\n\n    def format_completion(self, item: click.shell_completion.CompletionItem) -> str:\n        def escape(s: str) -> str:\n            return (\n                s.replace('\"', '\"\"')\n                .replace(\"'\", \"''\")\n                .replace(\"$\", \"\\\\$\")\n                .replace(\"`\", \"\\\\`\")\n                .replace(\":\", r\"\\\\:\")\n            )\n\n        # TODO: Explore replicating the new behavior from Click, pay attention to\n        # the difference with and without escape\n        # return f\"{item.type}\\n{item.value}\\n{item.help if item.help else '_'}\"\n        if item.help:\n            return f'\"{escape(item.value)}\":\"{_sanitize_help_text(escape(item.help))}\"'\n        else:\n            return f'\"{escape(item.value)}\"'\n\n    def complete(self) -> str:\n        args, incomplete = self.get_completion_args()\n        completions = self.get_completions(args, incomplete)\n        res = [self.format_completion(item) for item in completions]\n        if res:\n            args_str = \"\\n\".join(res)\n            return f\"_arguments '*: :(({args_str}))'\"\n        else:\n            return \"_files\"\n\n\nclass FishComplete(click.shell_completion.FishComplete):\n    name = Shells.fish.value\n    source_template = COMPLETION_SCRIPT_FISH\n\n    def source_vars(self) -> dict[str, Any]:\n        return {\n            \"complete_func\": self.func_name,\n            \"autocomplete_var\": self.complete_var,\n            \"prog_name\": self.prog_name,\n        }\n\n    def get_completion_args(self) -> tuple[list[str], str]:\n        completion_args = os.getenv(\"_TYPER_COMPLETE_ARGS\", \"\")\n        cwords = click_split_arg_string(completion_args)\n        args = cwords[1:]\n        if args and not completion_args.endswith(\" \"):\n            incomplete = args[-1]\n            args = args[:-1]\n        else:\n            incomplete = \"\"\n        return args, incomplete\n\n    def format_completion(self, item: click.shell_completion.CompletionItem) -> str:\n        # TODO: Explore replicating the new behavior from Click, pay attention to\n        # the difference with and without formatted help\n        # if item.help:\n        #     return f\"{item.type},{item.value}\\t{item.help}\"\n\n        # return f\"{item.type},{item.value}\n        if item.help:\n            formatted_help = re.sub(r\"\\s\", \" \", item.help)\n            return f\"{item.value}\\t{_sanitize_help_text(formatted_help)}\"\n        else:\n            return f\"{item.value}\"\n\n    def complete(self) -> str:\n        complete_action = os.getenv(\"_TYPER_COMPLETE_FISH_ACTION\", \"\")\n        args, incomplete = self.get_completion_args()\n        completions = self.get_completions(args, incomplete)\n        show_args = [self.format_completion(item) for item in completions]\n        if complete_action == \"get-args\":\n            if show_args:\n                return \"\\n\".join(show_args)\n        elif complete_action == \"is-args\":\n            if show_args:\n                # Activate complete args (no files)\n                sys.exit(0)\n            else:\n                # Deactivate complete args (allow files)\n                sys.exit(1)\n        return \"\"  # pragma: no cover\n\n\nclass PowerShellComplete(click.shell_completion.ShellComplete):\n    name = Shells.powershell.value\n    source_template = COMPLETION_SCRIPT_POWER_SHELL\n\n    def source_vars(self) -> dict[str, Any]:\n        return {\n            \"complete_func\": self.func_name,\n            \"autocomplete_var\": self.complete_var,\n            \"prog_name\": self.prog_name,\n        }\n\n    def get_completion_args(self) -> tuple[list[str], str]:\n        completion_args = os.getenv(\"_TYPER_COMPLETE_ARGS\", \"\")\n        incomplete = os.getenv(\"_TYPER_COMPLETE_WORD_TO_COMPLETE\", \"\")\n        cwords = click_split_arg_string(completion_args)\n        args = cwords[1:-1] if incomplete else cwords[1:]\n        return args, incomplete\n\n    def format_completion(self, item: click.shell_completion.CompletionItem) -> str:\n        return f\"{item.value}:::{_sanitize_help_text(item.help) if item.help else ' '}\"\n\n\ndef completion_init() -> None:\n    click.shell_completion.add_completion_class(BashComplete, Shells.bash.value)\n    click.shell_completion.add_completion_class(ZshComplete, Shells.zsh.value)\n    click.shell_completion.add_completion_class(FishComplete, Shells.fish.value)\n    click.shell_completion.add_completion_class(\n        PowerShellComplete, Shells.powershell.value\n    )\n    click.shell_completion.add_completion_class(PowerShellComplete, Shells.pwsh.value)\n"
  },
  {
    "path": "typer/_completion_shared.py",
    "content": "import os\nimport re\nimport subprocess\nfrom enum import Enum\nfrom pathlib import Path\n\nimport click\nimport shellingham\n\n\nclass Shells(str, Enum):\n    bash = \"bash\"\n    zsh = \"zsh\"\n    fish = \"fish\"\n    powershell = \"powershell\"\n    pwsh = \"pwsh\"\n\n\nCOMPLETION_SCRIPT_BASH = \"\"\"\n%(complete_func)s() {\n    local IFS=$'\\n'\n    COMPREPLY=( $( env COMP_WORDS=\"${COMP_WORDS[*]}\" \\\\\n                   COMP_CWORD=$COMP_CWORD \\\\\n                   %(autocomplete_var)s=complete_bash $1 ) )\n    return 0\n}\n\ncomplete -o default -F %(complete_func)s %(prog_name)s\n\"\"\"\n\nCOMPLETION_SCRIPT_ZSH = \"\"\"\n#compdef %(prog_name)s\n\n%(complete_func)s() {\n  eval $(env _TYPER_COMPLETE_ARGS=\"${words[1,$CURRENT]}\" %(autocomplete_var)s=complete_zsh %(prog_name)s)\n}\n\ncompdef %(complete_func)s %(prog_name)s\n\"\"\"\n\nCOMPLETION_SCRIPT_FISH = 'complete --command %(prog_name)s --no-files --arguments \"(env %(autocomplete_var)s=complete_fish _TYPER_COMPLETE_FISH_ACTION=get-args _TYPER_COMPLETE_ARGS=(commandline -cp) %(prog_name)s)\" --condition \"env %(autocomplete_var)s=complete_fish _TYPER_COMPLETE_FISH_ACTION=is-args _TYPER_COMPLETE_ARGS=(commandline -cp) %(prog_name)s\"'\n\nCOMPLETION_SCRIPT_POWER_SHELL = \"\"\"\nImport-Module PSReadLine\nSet-PSReadLineKeyHandler -Chord Tab -Function MenuComplete\n$scriptblock = {\n    param($wordToComplete, $commandAst, $cursorPosition)\n    $Env:%(autocomplete_var)s = \"complete_powershell\"\n    $Env:_TYPER_COMPLETE_ARGS = $commandAst.ToString()\n    $Env:_TYPER_COMPLETE_WORD_TO_COMPLETE = $wordToComplete\n    %(prog_name)s | ForEach-Object {\n        $commandArray = $_ -Split \":::\"\n        $command = $commandArray[0]\n        $helpString = $commandArray[1]\n        [System.Management.Automation.CompletionResult]::new(\n            $command, $command, 'ParameterValue', $helpString)\n    }\n    $Env:%(autocomplete_var)s = \"\"\n    $Env:_TYPER_COMPLETE_ARGS = \"\"\n    $Env:_TYPER_COMPLETE_WORD_TO_COMPLETE = \"\"\n}\nRegister-ArgumentCompleter -Native -CommandName %(prog_name)s -ScriptBlock $scriptblock\n\"\"\"\n\n_completion_scripts = {\n    \"bash\": COMPLETION_SCRIPT_BASH,\n    \"zsh\": COMPLETION_SCRIPT_ZSH,\n    \"fish\": COMPLETION_SCRIPT_FISH,\n    \"powershell\": COMPLETION_SCRIPT_POWER_SHELL,\n    \"pwsh\": COMPLETION_SCRIPT_POWER_SHELL,\n}\n\n# TODO: Probably refactor this, copied from Click 7.x\n_invalid_ident_char_re = re.compile(r\"[^a-zA-Z0-9_]\")\n\n\ndef get_completion_script(*, prog_name: str, complete_var: str, shell: str) -> str:\n    cf_name = _invalid_ident_char_re.sub(\"\", prog_name.replace(\"-\", \"_\"))\n    script = _completion_scripts.get(shell)\n    if script is None:\n        click.echo(f\"Shell {shell} not supported.\", err=True)\n        raise click.exceptions.Exit(1)\n    return (\n        script\n        % {\n            \"complete_func\": f\"_{cf_name}_completion\",\n            \"prog_name\": prog_name,\n            \"autocomplete_var\": complete_var,\n        }\n    ).strip()\n\n\ndef install_bash(*, prog_name: str, complete_var: str, shell: str) -> Path:\n    # Ref: https://github.com/scop/bash-completion#faq\n    # It seems bash-completion is the official completion system for bash:\n    # Ref: https://www.gnu.org/software/bash/manual/html_node/A-Programmable-Completion-Example.html\n    # But installing in the locations from the docs doesn't seem to have effect\n    completion_path = Path.home() / \".bash_completions\" / f\"{prog_name}.sh\"\n    rc_path = Path.home() / \".bashrc\"\n    rc_path.parent.mkdir(parents=True, exist_ok=True)\n    rc_content = \"\"\n    if rc_path.is_file():\n        rc_content = rc_path.read_text()\n    completion_init_lines = [f\"source '{completion_path}'\"]\n    for line in completion_init_lines:\n        if line not in rc_content:  # pragma: no cover\n            rc_content += f\"\\n{line}\"\n    rc_content += \"\\n\"\n    rc_path.write_text(rc_content)\n    # Install completion\n    completion_path.parent.mkdir(parents=True, exist_ok=True)\n    script_content = get_completion_script(\n        prog_name=prog_name, complete_var=complete_var, shell=shell\n    )\n    completion_path.write_text(script_content)\n    return completion_path\n\n\ndef install_zsh(*, prog_name: str, complete_var: str, shell: str) -> Path:\n    # Setup Zsh and load ~/.zfunc\n    zshrc_path = Path.home() / \".zshrc\"\n    zshrc_path.parent.mkdir(parents=True, exist_ok=True)\n    zshrc_content = \"\"\n    if zshrc_path.is_file():\n        zshrc_content = zshrc_path.read_text()\n    completion_line = \"fpath+=~/.zfunc; autoload -Uz compinit; compinit\"\n    if completion_line not in zshrc_content:\n        zshrc_content += f\"\\n{completion_line}\\n\"\n    style_line = \"zstyle ':completion:*' menu select\"\n    # TODO: consider setting the style only for the current program\n    # style_line = f\"zstyle ':completion:*:*:{prog_name}:*' menu select\"\n    # Install zstyle completion config only if the user doesn't have a customization\n    if \"zstyle\" not in zshrc_content:\n        zshrc_content += f\"\\n{style_line}\\n\"\n    zshrc_content = f\"{zshrc_content.strip()}\\n\"\n    zshrc_path.write_text(zshrc_content)\n    # Install completion under ~/.zfunc/\n    path_obj = Path.home() / f\".zfunc/_{prog_name}\"\n    path_obj.parent.mkdir(parents=True, exist_ok=True)\n    script_content = get_completion_script(\n        prog_name=prog_name, complete_var=complete_var, shell=shell\n    )\n    path_obj.write_text(script_content)\n    return path_obj\n\n\ndef install_fish(*, prog_name: str, complete_var: str, shell: str) -> Path:\n    path_obj = Path.home() / f\".config/fish/completions/{prog_name}.fish\"\n    parent_dir: Path = path_obj.parent\n    parent_dir.mkdir(parents=True, exist_ok=True)\n    script_content = get_completion_script(\n        prog_name=prog_name, complete_var=complete_var, shell=shell\n    )\n    path_obj.write_text(f\"{script_content}\\n\")\n    return path_obj\n\n\ndef install_powershell(*, prog_name: str, complete_var: str, shell: str) -> Path:\n    subprocess.run(\n        [\n            shell,\n            \"-Command\",\n            \"Set-ExecutionPolicy\",\n            \"Unrestricted\",\n            \"-Scope\",\n            \"CurrentUser\",\n        ]\n    )\n    result = subprocess.run(\n        [shell, \"-NoProfile\", \"-Command\", \"echo\", \"$profile\"],\n        check=True,\n        stdout=subprocess.PIPE,\n    )\n    if result.returncode != 0:  # pragma: no cover\n        click.echo(\"Couldn't get PowerShell user profile\", err=True)\n        raise click.exceptions.Exit(result.returncode)\n    path_str = \"\"\n    if isinstance(result.stdout, str):  # pragma: no cover\n        path_str = result.stdout\n    if isinstance(result.stdout, bytes):\n        for encoding in [\"windows-1252\", \"utf8\", \"cp850\"]:\n            try:\n                path_str = result.stdout.decode(encoding)\n                break\n            except UnicodeDecodeError:  # pragma: no cover\n                pass\n        if not path_str:  # pragma: no cover\n            click.echo(\"Couldn't decode the path automatically\", err=True)\n            raise click.exceptions.Exit(1)\n    path_obj = Path(path_str.strip())\n    parent_dir: Path = path_obj.parent\n    parent_dir.mkdir(parents=True, exist_ok=True)\n    script_content = get_completion_script(\n        prog_name=prog_name, complete_var=complete_var, shell=shell\n    )\n    with path_obj.open(mode=\"a\") as f:\n        f.write(f\"{script_content}\\n\")\n    return path_obj\n\n\ndef install(\n    shell: str | None = None,\n    prog_name: str | None = None,\n    complete_var: str | None = None,\n) -> tuple[str, Path]:\n    prog_name = prog_name or click.get_current_context().find_root().info_name\n    assert prog_name\n    if complete_var is None:\n        complete_var = \"_{}_COMPLETE\".format(prog_name.replace(\"-\", \"_\").upper())\n    test_disable_detection = os.getenv(\"_TYPER_COMPLETE_TEST_DISABLE_SHELL_DETECTION\")\n    if shell is None and not test_disable_detection:\n        shell = _get_shell_name()\n    if shell == \"bash\":\n        installed_path = install_bash(\n            prog_name=prog_name, complete_var=complete_var, shell=shell\n        )\n        return shell, installed_path\n    elif shell == \"zsh\":\n        installed_path = install_zsh(\n            prog_name=prog_name, complete_var=complete_var, shell=shell\n        )\n        return shell, installed_path\n    elif shell == \"fish\":\n        installed_path = install_fish(\n            prog_name=prog_name, complete_var=complete_var, shell=shell\n        )\n        return shell, installed_path\n    elif shell in {\"powershell\", \"pwsh\"}:\n        installed_path = install_powershell(\n            prog_name=prog_name, complete_var=complete_var, shell=shell\n        )\n        return shell, installed_path\n    else:\n        click.echo(f\"Shell {shell} is not supported.\")\n        raise click.exceptions.Exit(1)\n\n\ndef _get_shell_name() -> str | None:\n    \"\"\"Get the current shell name, if available.\n\n    The name will always be lowercase. If the shell cannot be detected, None is\n    returned.\n    \"\"\"\n    name: str | None  # N.B. shellingham is untyped\n    try:\n        # N.B. detect_shell returns a tuple of (shell name, shell command).\n        # We only need the name.\n        name, _cmd = shellingham.detect_shell()  # noqa: TID251\n    except shellingham.ShellDetectionFailure:  # pragma: no cover\n        name = None\n\n    return name\n"
  },
  {
    "path": "typer/_types.py",
    "content": "from enum import Enum\nfrom typing import TypeVar\n\nimport click\n\nParamTypeValue = TypeVar(\"ParamTypeValue\")\n\n\nclass TyperChoice(click.Choice[ParamTypeValue]):\n    def normalize_choice(\n        self, choice: ParamTypeValue, ctx: click.Context | None\n    ) -> str:\n        # Click 8.2.0 added a new method `normalize_choice` to the `Choice` class\n        # to support enums, but it uses the enum names, while Typer has always used the\n        # enum values.\n        # This class overrides that method to maintain the previous behavior.\n        # In Click:\n        # normed_value = choice.name if isinstance(choice, Enum) else str(choice)\n        normed_value = str(choice.value) if isinstance(choice, Enum) else str(choice)\n\n        if ctx is not None and ctx.token_normalize_func is not None:\n            normed_value = ctx.token_normalize_func(normed_value)\n\n        if not self.case_sensitive:\n            normed_value = normed_value.casefold()\n\n        return normed_value\n"
  },
  {
    "path": "typer/_typing.py",
    "content": "# Copied from pydantic 1.9.2 (the latest version to support python 3.6.)\n# https://github.com/pydantic/pydantic/blob/v1.9.2/pydantic/typing.py\n# Reduced drastically to only include Typer-specific 3.9+ functionality\n# mypy: ignore-errors\n\nimport types\nfrom collections.abc import Callable\nfrom typing import (\n    Annotated,\n    Any,\n    Literal,\n    Union,\n    get_args,\n    get_origin,\n    get_type_hints,\n)\n\n\ndef is_union(tp: type[Any] | None) -> bool:\n    return tp is Union or tp is types.UnionType  # noqa: E721\n\n\n__all__ = (\n    \"NoneType\",\n    \"is_none_type\",\n    \"is_callable_type\",\n    \"is_literal_type\",\n    \"all_literal_values\",\n    \"is_union\",\n    \"Annotated\",\n    \"Literal\",\n    \"get_args\",\n    \"get_origin\",\n    \"get_type_hints\",\n)\n\n\nNoneType = None.__class__\n\n\nNONE_TYPES: tuple[Any, Any, Any] = (None, NoneType, Literal[None])\n\n\ndef is_none_type(type_: Any) -> bool:\n    for none_type in NONE_TYPES:\n        if type_ is none_type:\n            return True\n    return False\n\n\ndef is_callable_type(type_: type[Any]) -> bool:\n    return type_ is Callable or get_origin(type_) is Callable\n\n\ndef is_literal_type(type_: type[Any]) -> bool:\n    return get_origin(type_) is Literal\n\n\ndef literal_values(type_: type[Any]) -> tuple[Any, ...]:\n    return get_args(type_)\n\n\ndef all_literal_values(type_: type[Any]) -> tuple[Any, ...]:\n    \"\"\"\n    This method is used to retrieve all Literal values as\n    Literal can be used recursively (see https://www.python.org/dev/peps/pep-0586)\n    e.g. `Literal[Literal[Literal[1, 2, 3], \"foo\"], 5, None]`\n    \"\"\"\n    if not is_literal_type(type_):\n        return (type_,)\n\n    values = literal_values(type_)\n    return tuple(x for value in values for x in all_literal_values(value))\n"
  },
  {
    "path": "typer/cli.py",
    "content": "import importlib.util\nimport re\nimport sys\nfrom pathlib import Path\nfrom typing import Any\n\nimport click\nimport typer\nimport typer.core\nfrom click import Command, Group, Option\n\nfrom . import __version__\nfrom .core import HAS_RICH, MARKUP_MODE_KEY\n\ndefault_app_names = (\"app\", \"cli\", \"main\")\ndefault_func_names = (\"main\", \"cli\", \"app\")\n\napp = typer.Typer()\nutils_app = typer.Typer(help=\"Extra utility commands for Typer apps.\")\napp.add_typer(utils_app, name=\"utils\")\n\n\nclass State:\n    def __init__(self) -> None:\n        self.app: str | None = None\n        self.func: str | None = None\n        self.file: Path | None = None\n        self.module: str | None = None\n\n\nstate = State()\n\n\ndef maybe_update_state(ctx: click.Context) -> None:\n    path_or_module = ctx.params.get(\"path_or_module\")\n    if path_or_module:\n        file_path = Path(path_or_module)\n        if file_path.exists() and file_path.is_file():\n            state.file = file_path\n        else:\n            if not re.fullmatch(r\"[a-zA-Z_]\\w*(\\.[a-zA-Z_]\\w*)*\", path_or_module):\n                typer.echo(\n                    f\"Not a valid file or Python module: {path_or_module}\", err=True\n                )\n                sys.exit(1)\n            state.module = path_or_module\n    app_name = ctx.params.get(\"app\")\n    if app_name:\n        state.app = app_name\n    func_name = ctx.params.get(\"func\")\n    if func_name:\n        state.func = func_name\n\n\nclass TyperCLIGroup(typer.core.TyperGroup):\n    def list_commands(self, ctx: click.Context) -> list[str]:\n        self.maybe_add_run(ctx)\n        return super().list_commands(ctx)\n\n    def get_command(self, ctx: click.Context, name: str) -> Command | None:  # ty: ignore[invalid-method-override]\n        self.maybe_add_run(ctx)\n        return super().get_command(ctx, name)\n\n    def invoke(self, ctx: click.Context) -> Any:\n        self.maybe_add_run(ctx)\n        return super().invoke(ctx)\n\n    def maybe_add_run(self, ctx: click.Context) -> None:\n        maybe_update_state(ctx)\n        maybe_add_run_to_cli(self)\n\n\ndef get_typer_from_module(module: Any) -> typer.Typer | None:\n    # Try to get defined app\n    if state.app:\n        obj = getattr(module, state.app, None)\n        if not isinstance(obj, typer.Typer):\n            typer.echo(f\"Not a Typer object: --app {state.app}\", err=True)\n            sys.exit(1)\n        return obj\n    # Try to get defined function\n    if state.func:\n        func_obj = getattr(module, state.func, None)\n        if not callable(func_obj):\n            typer.echo(f\"Not a function: --func {state.func}\", err=True)\n            raise typer.Exit(1)\n        sub_app = typer.Typer()\n        sub_app.command()(func_obj)\n        return sub_app\n    # Iterate and get a default object to use as CLI\n    local_names = dir(module)\n    local_names_set = set(local_names)\n    # Try to get a default Typer app\n    for name in default_app_names:\n        if name in local_names_set:\n            obj = getattr(module, name, None)\n            if isinstance(obj, typer.Typer):\n                return obj\n    # Try to get any Typer app\n    for name in local_names_set - set(default_app_names):\n        obj = getattr(module, name)\n        if isinstance(obj, typer.Typer):\n            return obj\n    # Try to get a default function\n    for func_name in default_func_names:\n        func_obj = getattr(module, func_name, None)\n        if callable(func_obj):\n            sub_app = typer.Typer()\n            sub_app.command()(func_obj)\n            return sub_app\n    # Try to get any func app\n    for func_name in local_names_set - set(default_func_names):\n        func_obj = getattr(module, func_name)\n        if callable(func_obj):\n            sub_app = typer.Typer()\n            sub_app.command()(func_obj)\n            return sub_app\n    return None\n\n\ndef get_typer_from_state() -> typer.Typer | None:\n    spec = None\n    if state.file:\n        module_name = state.file.name\n        spec = importlib.util.spec_from_file_location(module_name, str(state.file))\n    elif state.module:\n        spec = importlib.util.find_spec(state.module)\n    if spec is None:\n        if state.file:\n            typer.echo(f\"Could not import as Python file: {state.file}\", err=True)\n        else:\n            typer.echo(f\"Could not import as Python module: {state.module}\", err=True)\n        sys.exit(1)\n    assert spec is not None\n    module = importlib.util.module_from_spec(spec)\n    spec.loader.exec_module(module)  # type: ignore\n    obj = get_typer_from_module(module)\n    return obj\n\n\ndef maybe_add_run_to_cli(cli: click.Group) -> None:\n    if \"run\" not in cli.commands:\n        if state.file or state.module:\n            obj = get_typer_from_state()\n            if obj:\n                obj._add_completion = False\n                click_obj = typer.main.get_command(obj)\n                click_obj.name = \"run\"\n                if not click_obj.help:\n                    click_obj.help = \"Run the provided Typer app.\"\n                cli.add_command(click_obj)\n\n\ndef print_version(ctx: click.Context, param: Option, value: bool) -> None:\n    if not value or ctx.resilient_parsing:\n        return\n    typer.echo(f\"Typer version: {__version__}\")\n    raise typer.Exit()\n\n\n@app.callback(cls=TyperCLIGroup, no_args_is_help=True)\ndef callback(\n    ctx: typer.Context,\n    *,\n    path_or_module: str = typer.Argument(None),\n    app: str = typer.Option(None, help=\"The typer app object/variable to use.\"),\n    func: str = typer.Option(None, help=\"The function to convert to Typer.\"),\n    version: bool = typer.Option(\n        False,\n        \"--version\",\n        help=\"Print version and exit.\",\n        callback=print_version,\n    ),\n) -> None:\n    \"\"\"\n    Run Typer scripts with completion, without having to create a package.\n\n    You probably want to install completion for the typer command:\n\n    $ typer --install-completion\n\n    https://typer.tiangolo.com/\n    \"\"\"\n    maybe_update_state(ctx)\n\n\ndef get_docs_for_click(\n    *,\n    obj: Command,\n    ctx: typer.Context,\n    indent: int = 0,\n    name: str = \"\",\n    call_prefix: str = \"\",\n    title: str | None = None,\n) -> str:\n    docs = \"#\" * (1 + indent)\n    command_name = name or obj.name\n    if call_prefix:\n        command_name = f\"{call_prefix} {command_name}\"\n    if not title:\n        title = f\"`{command_name}`\" if command_name else \"CLI\"\n    docs += f\" {title}\\n\\n\"\n    rich_markup_mode = None\n    if hasattr(ctx, \"obj\") and isinstance(ctx.obj, dict):\n        rich_markup_mode = ctx.obj.get(MARKUP_MODE_KEY, None)\n    to_parse: bool = bool(HAS_RICH and (rich_markup_mode == \"rich\"))\n    if obj.help:\n        docs += f\"{_parse_html(to_parse, obj.help)}\\n\\n\"\n    usage_pieces = obj.collect_usage_pieces(ctx)\n    if usage_pieces:\n        docs += \"**Usage**:\\n\\n\"\n        docs += \"```console\\n\"\n        docs += \"$ \"\n        if command_name:\n            docs += f\"{command_name} \"\n        docs += f\"{' '.join(usage_pieces)}\\n\"\n        docs += \"```\\n\\n\"\n    args = []\n    opts = []\n    for param in obj.get_params(ctx):\n        rv = param.get_help_record(ctx)\n        if rv is not None:\n            if param.param_type_name == \"argument\":\n                args.append(rv)\n            elif param.param_type_name == \"option\":\n                opts.append(rv)\n    if args:\n        docs += \"**Arguments**:\\n\\n\"\n        for arg_name, arg_help in args:\n            docs += f\"* `{arg_name}`\"\n            if arg_help:\n                docs += f\": {_parse_html(to_parse, arg_help)}\"\n            docs += \"\\n\"\n        docs += \"\\n\"\n    if opts:\n        docs += \"**Options**:\\n\\n\"\n        for opt_name, opt_help in opts:\n            docs += f\"* `{opt_name}`\"\n            if opt_help:\n                docs += f\": {_parse_html(to_parse, opt_help)}\"\n            docs += \"\\n\"\n        docs += \"\\n\"\n    if obj.epilog:\n        docs += f\"{obj.epilog}\\n\\n\"\n    if isinstance(obj, Group):\n        group = obj\n        commands = group.list_commands(ctx)\n        if commands:\n            docs += \"**Commands**:\\n\\n\"\n            for command in commands:\n                command_obj = group.get_command(ctx, command)\n                assert command_obj\n                docs += f\"* `{command_obj.name}`\"\n                command_help = command_obj.get_short_help_str()\n                if command_help:\n                    docs += f\": {_parse_html(to_parse, command_help)}\"\n                docs += \"\\n\"\n            docs += \"\\n\"\n        for command in commands:\n            command_obj = group.get_command(ctx, command)\n            assert command_obj\n            use_prefix = \"\"\n            if command_name:\n                use_prefix += f\"{command_name}\"\n            docs += get_docs_for_click(\n                obj=command_obj, ctx=ctx, indent=indent + 1, call_prefix=use_prefix\n            )\n    return docs\n\n\ndef _parse_html(to_parse: bool, input_text: str) -> str:\n    if not to_parse:\n        return input_text\n    from . import rich_utils\n\n    return rich_utils.rich_to_html(input_text)\n\n\n@utils_app.command()\ndef docs(\n    ctx: typer.Context,\n    name: str = typer.Option(\"\", help=\"The name of the CLI program to use in docs.\"),\n    output: Path | None = typer.Option(\n        None,\n        help=\"An output file to write docs to, like README.md.\",\n        file_okay=True,\n        dir_okay=False,\n    ),\n    title: str | None = typer.Option(\n        None,\n        help=\"The title for the documentation page. If not provided, the name of \"\n        \"the program is used.\",\n    ),\n) -> None:\n    \"\"\"\n    Generate Markdown docs for a Typer app.\n    \"\"\"\n    typer_obj = get_typer_from_state()\n    if not typer_obj:\n        typer.echo(\"No Typer app found\", err=True)\n        raise typer.Abort()\n    if hasattr(typer_obj, \"rich_markup_mode\"):\n        if not hasattr(ctx, \"obj\") or ctx.obj is None:\n            ctx.ensure_object(dict)\n        if isinstance(ctx.obj, dict):\n            ctx.obj[MARKUP_MODE_KEY] = typer_obj.rich_markup_mode\n    click_obj = typer.main.get_command(typer_obj)\n    docs = get_docs_for_click(obj=click_obj, ctx=ctx, name=name, title=title)\n    clean_docs = f\"{docs.strip()}\\n\"\n    if output:\n        output.write_text(clean_docs)\n        typer.echo(f\"Docs saved to: {output}\")\n    else:\n        typer.echo(clean_docs)\n\n\ndef main() -> Any:\n    return app()\n"
  },
  {
    "path": "typer/colors.py",
    "content": "# Variable names to colors, just for completion\nBLACK = \"black\"\nRED = \"red\"\nGREEN = \"green\"\nYELLOW = \"yellow\"\nBLUE = \"blue\"\nMAGENTA = \"magenta\"\nCYAN = \"cyan\"\nWHITE = \"white\"\n\nRESET = \"reset\"\n\nBRIGHT_BLACK = \"bright_black\"\nBRIGHT_RED = \"bright_red\"\nBRIGHT_GREEN = \"bright_green\"\nBRIGHT_YELLOW = \"bright_yellow\"\nBRIGHT_BLUE = \"bright_blue\"\nBRIGHT_MAGENTA = \"bright_magenta\"\nBRIGHT_CYAN = \"bright_cyan\"\nBRIGHT_WHITE = \"bright_white\"\n"
  },
  {
    "path": "typer/completion.py",
    "content": "import os\nimport sys\nfrom collections.abc import MutableMapping\nfrom typing import Any\n\nimport click\n\nfrom ._completion_classes import completion_init\nfrom ._completion_shared import Shells, _get_shell_name, get_completion_script, install\nfrom .models import ParamMeta\nfrom .params import Option\nfrom .utils import get_params_from_function\n\n_click_patched = False\n\n\ndef get_completion_inspect_parameters() -> tuple[ParamMeta, ParamMeta]:\n    completion_init()\n    test_disable_detection = os.getenv(\"_TYPER_COMPLETE_TEST_DISABLE_SHELL_DETECTION\")\n    if not test_disable_detection:\n        parameters = get_params_from_function(_install_completion_placeholder_function)\n    else:\n        parameters = get_params_from_function(\n            _install_completion_no_auto_placeholder_function\n        )\n    install_param, show_param = parameters.values()\n    return install_param, show_param\n\n\ndef install_callback(ctx: click.Context, param: click.Parameter, value: Any) -> Any:\n    if not value or ctx.resilient_parsing:\n        return value  # pragma: no cover\n    if isinstance(value, str):\n        shell, path = install(shell=value)\n    else:\n        shell, path = install()\n    click.secho(f\"{shell} completion installed in {path}\", fg=\"green\")\n    click.echo(\"Completion will take effect once you restart the terminal\")\n    sys.exit(0)\n\n\ndef show_callback(ctx: click.Context, param: click.Parameter, value: Any) -> Any:\n    if not value or ctx.resilient_parsing:\n        return value  # pragma: no cover\n    prog_name = ctx.find_root().info_name\n    assert prog_name\n    complete_var = \"_{}_COMPLETE\".format(prog_name.replace(\"-\", \"_\").upper())\n    shell = \"\"\n    test_disable_detection = os.getenv(\"_TYPER_COMPLETE_TEST_DISABLE_SHELL_DETECTION\")\n    if isinstance(value, str):\n        shell = value\n    elif not test_disable_detection:\n        detected_shell = _get_shell_name()\n        if detected_shell is not None:\n            shell = detected_shell\n    script_content = get_completion_script(\n        prog_name=prog_name, complete_var=complete_var, shell=shell\n    )\n    click.echo(script_content)\n    sys.exit(0)\n\n\n# Create a fake command function to extract the completion parameters\ndef _install_completion_placeholder_function(\n    install_completion: bool = Option(\n        None,\n        \"--install-completion\",\n        callback=install_callback,\n        expose_value=False,\n        help=\"Install completion for the current shell.\",\n    ),\n    show_completion: bool = Option(\n        None,\n        \"--show-completion\",\n        callback=show_callback,\n        expose_value=False,\n        help=\"Show completion for the current shell, to copy it or customize the installation.\",\n    ),\n) -> Any:\n    pass  # pragma: no cover\n\n\ndef _install_completion_no_auto_placeholder_function(\n    install_completion: Shells = Option(\n        None,\n        callback=install_callback,\n        expose_value=False,\n        help=\"Install completion for the specified shell.\",\n    ),\n    show_completion: Shells = Option(\n        None,\n        callback=show_callback,\n        expose_value=False,\n        help=\"Show completion for the specified shell, to copy it or customize the installation.\",\n    ),\n) -> Any:\n    pass  # pragma: no cover\n\n\n# Re-implement Click's shell_complete to add error message with:\n# Invalid completion instruction\n# To use 7.x instruction style for compatibility\n# And to add extra error messages, for compatibility with Typer in previous versions\n# This is only called in new Command method, only used by Click 8.x+\ndef shell_complete(\n    cli: click.Command,\n    ctx_args: MutableMapping[str, Any],\n    prog_name: str,\n    complete_var: str,\n    instruction: str,\n) -> int:\n    import click\n    import click.shell_completion\n\n    if \"_\" not in instruction:\n        click.echo(\"Invalid completion instruction.\", err=True)\n        return 1\n\n    # Click 8 changed the order/style of shell instructions from e.g.\n    # source_bash to bash_source\n    # Typer override to preserve the old style for compatibility\n    # Original in Click 8.x commented:\n    # shell, _, instruction = instruction.partition(\"_\")\n    instruction, _, shell = instruction.partition(\"_\")\n    # Typer override end\n\n    comp_cls = click.shell_completion.get_completion_class(shell)\n\n    if comp_cls is None:\n        click.echo(f\"Shell {shell} not supported.\", err=True)\n        return 1\n\n    comp = comp_cls(cli, ctx_args, prog_name, complete_var)\n\n    if instruction == \"source\":\n        click.echo(comp.source())\n        return 0\n\n    # Typer override to print the completion help msg with Rich\n    if instruction == \"complete\":\n        click.echo(comp.complete())\n        return 0\n    # Typer override end\n\n    click.echo(f'Completion instruction \"{instruction}\" not supported.', err=True)\n    return 1\n"
  },
  {
    "path": "typer/core.py",
    "content": "import errno\nimport inspect\nimport os\nimport sys\nfrom collections.abc import Callable, MutableMapping, Sequence\nfrom difflib import get_close_matches\nfrom enum import Enum\nfrom gettext import gettext as _\nfrom typing import (\n    Any,\n    TextIO,\n    Union,\n    cast,\n)\n\nimport click\nimport click.core\nimport click.formatting\nimport click.shell_completion\nimport click.types\nimport click.utils\n\nfrom ._typing import Literal\nfrom .utils import parse_boolean_env_var\n\nMarkupMode = Literal[\"markdown\", \"rich\", None]\nMARKUP_MODE_KEY = \"TYPER_RICH_MARKUP_MODE\"\n\nHAS_RICH = parse_boolean_env_var(os.getenv(\"TYPER_USE_RICH\"), default=True)\n\nif HAS_RICH:\n    DEFAULT_MARKUP_MODE: MarkupMode = \"rich\"\nelse:\n    DEFAULT_MARKUP_MODE = None\n\n\n# Copy from click.parser._split_opt\ndef _split_opt(opt: str) -> tuple[str, str]:\n    first = opt[:1]\n    if first.isalnum():\n        return \"\", opt\n    if opt[1:2] == first:\n        return opt[:2], opt[2:]\n    return first, opt[1:]\n\n\ndef _typer_param_setup_autocompletion_compat(\n    self: click.Parameter,\n    *,\n    autocompletion: Callable[\n        [click.Context, list[str], str], list[tuple[str, str] | str]\n    ]\n    | None = None,\n) -> None:\n    if self._custom_shell_complete is not None:\n        import warnings\n\n        warnings.warn(\n            \"In Typer, only the parameter 'autocompletion' is supported. \"\n            \"The support for 'shell_complete' is deprecated and will be removed in upcoming versions. \",\n            DeprecationWarning,\n            stacklevel=2,\n        )\n\n    if autocompletion is not None:\n\n        def compat_autocompletion(\n            ctx: click.Context, param: click.core.Parameter, incomplete: str\n        ) -> list[\"click.shell_completion.CompletionItem\"]:\n            from click.shell_completion import CompletionItem\n\n            out = []\n\n            for c in autocompletion(ctx, [], incomplete):\n                if isinstance(c, tuple):\n                    use_completion = CompletionItem(c[0], help=c[1])\n                else:\n                    assert isinstance(c, str)\n                    use_completion = CompletionItem(c)\n\n                if use_completion.value.startswith(incomplete):\n                    out.append(use_completion)\n\n            return out\n\n        self._custom_shell_complete = compat_autocompletion\n\n\ndef _get_default_string(\n    obj: Union[\"TyperArgument\", \"TyperOption\"],\n    *,\n    ctx: click.Context,\n    show_default_is_str: bool,\n    default_value: list[Any] | tuple[Any, ...] | str | Callable[..., Any] | Any,\n) -> str:\n    # Extracted from click.core.Option.get_help_record() to be reused by\n    # rich_utils avoiding RegEx hacks\n    if show_default_is_str:\n        default_string = f\"({obj.show_default})\"\n    elif isinstance(default_value, (list, tuple)):\n        default_string = \", \".join(\n            _get_default_string(\n                obj, ctx=ctx, show_default_is_str=show_default_is_str, default_value=d\n            )\n            for d in default_value\n        )\n    elif isinstance(default_value, Enum):\n        default_string = str(default_value.value)\n    elif inspect.isfunction(default_value):\n        default_string = _(\"(dynamic)\")\n    elif isinstance(obj, TyperOption) and obj.is_bool_flag and obj.secondary_opts:\n        # For boolean flags that have distinct True/False opts,\n        # use the opt without prefix instead of the value.\n        # Typer override, original commented\n        # default_string = click.parser.split_opt(\n        #     (self.opts if self.default else self.secondary_opts)[0]\n        # )[1]\n        if obj.default:\n            if obj.opts:\n                default_string = _split_opt(obj.opts[0])[1]\n            else:\n                default_string = str(default_value)\n        else:\n            default_string = _split_opt(obj.secondary_opts[0])[1]\n        # Typer override end\n    elif (\n        isinstance(obj, TyperOption)\n        and obj.is_bool_flag\n        and not obj.secondary_opts\n        and not default_value\n    ):\n        default_string = \"\"\n    else:\n        default_string = str(default_value)\n    return default_string\n\n\ndef _extract_default_help_str(\n    obj: Union[\"TyperArgument\", \"TyperOption\"], *, ctx: click.Context\n) -> Any | Callable[[], Any] | None:\n    # Extracted from click.core.Option.get_help_record() to be reused by\n    # rich_utils avoiding RegEx hacks\n    # Temporarily enable resilient parsing to avoid type casting\n    # failing for the default. Might be possible to extend this to\n    # help formatting in general.\n    resilient = ctx.resilient_parsing\n    ctx.resilient_parsing = True\n\n    try:\n        default_value = obj.get_default(ctx, call=False)\n    finally:\n        ctx.resilient_parsing = resilient\n    return default_value\n\n\ndef _main(\n    self: click.Command,\n    *,\n    args: Sequence[str] | None = None,\n    prog_name: str | None = None,\n    complete_var: str | None = None,\n    standalone_mode: bool = True,\n    windows_expand_args: bool = True,\n    rich_markup_mode: MarkupMode = DEFAULT_MARKUP_MODE,\n    **extra: Any,\n) -> Any:\n    # Typer override, duplicated from click.main() to handle custom rich exceptions\n    # Verify that the environment is configured correctly, or reject\n    # further execution to avoid a broken script.\n    if args is None:\n        args = sys.argv[1:]\n\n        # Covered in Click tests\n        if os.name == \"nt\" and windows_expand_args:  # pragma: no cover\n            args = click.utils._expand_args(args)\n    else:\n        args = list(args)\n\n    if prog_name is None:\n        prog_name = click.utils._detect_program_name()\n\n    # Process shell completion requests and exit early.\n    self._main_shell_completion(extra, prog_name, complete_var)\n\n    try:\n        try:\n            with self.make_context(prog_name, args, **extra) as ctx:\n                rv = self.invoke(ctx)\n                if not standalone_mode:\n                    return rv\n                # it's not safe to `ctx.exit(rv)` here!\n                # note that `rv` may actually contain data like \"1\" which\n                # has obvious effects\n                # more subtle case: `rv=[None, None]` can come out of\n                # chained commands which all returned `None` -- so it's not\n                # even always obvious that `rv` indicates success/failure\n                # by its truthiness/falsiness\n                ctx.exit()\n        except EOFError as e:\n            click.echo(file=sys.stderr)\n            raise click.Abort() from e\n        except KeyboardInterrupt as e:\n            raise click.exceptions.Exit(130) from e\n        except click.ClickException as e:\n            if not standalone_mode:\n                raise\n            # Typer override\n            if HAS_RICH and rich_markup_mode is not None:\n                from . import rich_utils\n\n                rich_utils.rich_format_error(e)\n            else:\n                e.show()\n            # Typer override end\n            sys.exit(e.exit_code)\n        except OSError as e:\n            if e.errno == errno.EPIPE:\n                sys.stdout = cast(TextIO, click.utils.PacifyFlushWrapper(sys.stdout))\n                sys.stderr = cast(TextIO, click.utils.PacifyFlushWrapper(sys.stderr))\n                sys.exit(1)\n            else:\n                raise\n    except click.exceptions.Exit as e:\n        if standalone_mode:\n            sys.exit(e.exit_code)\n        else:\n            # in non-standalone mode, return the exit code\n            # note that this is only reached if `self.invoke` above raises\n            # an Exit explicitly -- thus bypassing the check there which\n            # would return its result\n            # the results of non-standalone execution may therefore be\n            # somewhat ambiguous: if there are codepaths which lead to\n            # `ctx.exit(1)` and to `return 1`, the caller won't be able to\n            # tell the difference between the two\n            return e.exit_code\n    except click.Abort:\n        if not standalone_mode:\n            raise\n        # Typer override\n        if HAS_RICH and rich_markup_mode is not None:\n            from . import rich_utils\n\n            rich_utils.rich_abort_error()\n        else:\n            click.echo(_(\"Aborted!\"), file=sys.stderr)\n        # Typer override end\n        sys.exit(1)\n\n\nclass TyperArgument(click.core.Argument):\n    def __init__(\n        self,\n        *,\n        # Parameter\n        param_decls: list[str],\n        type: Any | None = None,\n        required: bool | None = None,\n        default: Any | None = None,\n        callback: Callable[..., Any] | None = None,\n        nargs: int | None = None,\n        metavar: str | None = None,\n        expose_value: bool = True,\n        is_eager: bool = False,\n        envvar: str | list[str] | None = None,\n        # Note that shell_complete is not fully supported and will be removed in future versions\n        # TODO: Remove shell_complete in a future version (after 0.16.0)\n        shell_complete: Callable[\n            [click.Context, click.Parameter, str],\n            list[\"click.shell_completion.CompletionItem\"] | list[str],\n        ]\n        | None = None,\n        autocompletion: Callable[..., Any] | None = None,\n        # TyperArgument\n        show_default: bool | str = True,\n        show_choices: bool = True,\n        show_envvar: bool = True,\n        help: str | None = None,\n        hidden: bool = False,\n        # Rich settings\n        rich_help_panel: str | None = None,\n    ):\n        self.help = help\n        self.show_default = show_default\n        self.show_choices = show_choices\n        self.show_envvar = show_envvar\n        self.hidden = hidden\n        self.rich_help_panel = rich_help_panel\n\n        super().__init__(\n            param_decls=param_decls,\n            type=type,\n            required=required,\n            default=default,\n            callback=callback,\n            nargs=nargs,\n            metavar=metavar,\n            expose_value=expose_value,\n            is_eager=is_eager,\n            envvar=envvar,\n            shell_complete=shell_complete,\n        )\n        _typer_param_setup_autocompletion_compat(self, autocompletion=autocompletion)\n\n    def _get_default_string(\n        self,\n        *,\n        ctx: click.Context,\n        show_default_is_str: bool,\n        default_value: list[Any] | tuple[Any, ...] | str | Callable[..., Any] | Any,\n    ) -> str:\n        return _get_default_string(\n            self,\n            ctx=ctx,\n            show_default_is_str=show_default_is_str,\n            default_value=default_value,\n        )\n\n    def _extract_default_help_str(\n        self, *, ctx: click.Context\n    ) -> Any | Callable[[], Any] | None:\n        return _extract_default_help_str(self, ctx=ctx)\n\n    def get_help_record(self, ctx: click.Context) -> tuple[str, str] | None:\n        # Modified version of click.core.Option.get_help_record()\n        # to support Arguments\n        if self.hidden:\n            return None\n        name = self.make_metavar(ctx=ctx)\n        help = self.help or \"\"\n        extra = []\n        if self.show_envvar:\n            envvar = self.envvar\n            # allow_from_autoenv is currently not supported in Typer for CLI Arguments\n            if envvar is not None:\n                var_str = (\n                    \", \".join(str(d) for d in envvar)\n                    if isinstance(envvar, (list, tuple))\n                    else envvar\n                )\n                extra.append(f\"env var: {var_str}\")\n\n        # Typer override:\n        # Extracted to _extract_default_help_str() to allow re-using it in rich_utils\n        default_value = self._extract_default_help_str(ctx=ctx)\n        # Typer override end\n\n        show_default_is_str = isinstance(self.show_default, str)\n\n        if show_default_is_str or (\n            default_value is not None and (self.show_default or ctx.show_default)\n        ):\n            # Typer override:\n            # Extracted to _get_default_string() to allow re-using it in rich_utils\n            default_string = self._get_default_string(\n                ctx=ctx,\n                show_default_is_str=show_default_is_str,\n                default_value=default_value,\n            )\n            # Typer override end\n            if default_string:\n                extra.append(_(\"default: {default}\").format(default=default_string))\n        if self.required:\n            extra.append(_(\"required\"))\n        if extra:\n            extra_str = \"; \".join(extra)\n            extra_str = f\"[{extra_str}]\"\n            rich_markup_mode = None\n            if hasattr(ctx, \"obj\") and isinstance(ctx.obj, dict):\n                rich_markup_mode = ctx.obj.get(MARKUP_MODE_KEY, None)\n            if HAS_RICH and rich_markup_mode == \"rich\":\n                # This is needed for when we want to export to HTML\n                from . import rich_utils\n\n                extra_str = rich_utils.escape_before_html_export(extra_str)\n\n            help = f\"{help}  {extra_str}\" if help else f\"{extra_str}\"\n        return name, help\n\n    def make_metavar(self, ctx: click.Context | None = None) -> str:\n        # Modified version of click.core.Argument.make_metavar()\n        # to include Argument name\n        if self.metavar is not None:\n            var = self.metavar\n            if not self.required and not var.startswith(\"[\"):\n                var = f\"[{var}]\"\n            return var\n        var = (self.name or \"\").upper()\n        if not self.required:\n            var = f\"[{var}]\"\n        type_var = self.type.get_metavar(self, ctx=ctx)  # type: ignore[arg-type]\n        # type_var = self.type.get_metavar(self, ctx=ctx)\n        if type_var:\n            var += f\":{type_var}\"\n        if self.nargs != 1:\n            var += \"...\"\n        return var\n\n    def value_is_missing(self, value: Any) -> bool:\n        return _value_is_missing(self, value)\n\n\nclass TyperOption(click.core.Option):\n    def __init__(\n        self,\n        *,\n        # Parameter\n        param_decls: list[str],\n        type: click.types.ParamType | Any | None = None,\n        required: bool | None = None,\n        default: Any | None = None,\n        callback: Callable[..., Any] | None = None,\n        nargs: int | None = None,\n        metavar: str | None = None,\n        expose_value: bool = True,\n        is_eager: bool = False,\n        envvar: str | list[str] | None = None,\n        # Note that shell_complete is not fully supported and will be removed in future versions\n        # TODO: Remove shell_complete in a future version (after 0.16.0)\n        shell_complete: Callable[\n            [click.Context, click.Parameter, str],\n            list[\"click.shell_completion.CompletionItem\"] | list[str],\n        ]\n        | None = None,\n        autocompletion: Callable[..., Any] | None = None,\n        # Option\n        show_default: bool | str = False,\n        prompt: bool | str = False,\n        confirmation_prompt: bool | str = False,\n        prompt_required: bool = True,\n        hide_input: bool = False,\n        is_flag: bool | None = None,\n        multiple: bool = False,\n        count: bool = False,\n        allow_from_autoenv: bool = True,\n        help: str | None = None,\n        hidden: bool = False,\n        show_choices: bool = True,\n        show_envvar: bool = False,\n        # Rich settings\n        rich_help_panel: str | None = None,\n    ):\n        super().__init__(\n            param_decls=param_decls,\n            type=type,\n            required=required,\n            default=default,\n            callback=callback,\n            nargs=nargs,\n            metavar=metavar,\n            expose_value=expose_value,\n            is_eager=is_eager,\n            envvar=envvar,\n            show_default=show_default,\n            prompt=prompt,\n            confirmation_prompt=confirmation_prompt,\n            hide_input=hide_input,\n            is_flag=is_flag,\n            multiple=multiple,\n            count=count,\n            allow_from_autoenv=allow_from_autoenv,\n            help=help,\n            hidden=hidden,\n            show_choices=show_choices,\n            show_envvar=show_envvar,\n            prompt_required=prompt_required,\n            shell_complete=shell_complete,\n        )\n        _typer_param_setup_autocompletion_compat(self, autocompletion=autocompletion)\n        self.rich_help_panel = rich_help_panel\n\n    def _get_default_string(\n        self,\n        *,\n        ctx: click.Context,\n        show_default_is_str: bool,\n        default_value: list[Any] | tuple[Any, ...] | str | Callable[..., Any] | Any,\n    ) -> str:\n        return _get_default_string(\n            self,\n            ctx=ctx,\n            show_default_is_str=show_default_is_str,\n            default_value=default_value,\n        )\n\n    def _extract_default_help_str(\n        self, *, ctx: click.Context\n    ) -> Any | Callable[[], Any] | None:\n        return _extract_default_help_str(self, ctx=ctx)\n\n    def make_metavar(self, ctx: click.Context | None = None) -> str:\n        return super().make_metavar(ctx=ctx)  # type: ignore[arg-type]\n\n    def get_help_record(self, ctx: click.Context) -> tuple[str, str] | None:\n        # Duplicate all of Click's logic only to modify a single line, to allow boolean\n        # flags with only names for False values as it's currently supported by Typer\n        # Ref: https://typer.tiangolo.com/tutorial/parameter-types/bool/#only-names-for-false\n        if self.hidden:\n            return None\n\n        any_prefix_is_slash = False\n\n        def _write_opts(opts: Sequence[str]) -> str:\n            nonlocal any_prefix_is_slash\n\n            rv, any_slashes = click.formatting.join_options(opts)\n\n            if any_slashes:\n                any_prefix_is_slash = True\n\n            if not self.is_flag and not self.count:\n                rv += f\" {self.make_metavar(ctx=ctx)}\"\n\n            return rv\n\n        rv = [_write_opts(self.opts)]\n\n        if self.secondary_opts:\n            rv.append(_write_opts(self.secondary_opts))\n\n        help = self.help or \"\"\n        extra = []\n\n        if self.show_envvar:\n            envvar = self.envvar\n\n            if envvar is None:\n                if (\n                    self.allow_from_autoenv\n                    and ctx.auto_envvar_prefix is not None\n                    and self.name is not None\n                ):\n                    envvar = f\"{ctx.auto_envvar_prefix}_{self.name.upper()}\"\n\n            if envvar is not None:\n                var_str = (\n                    envvar\n                    if isinstance(envvar, str)\n                    else \", \".join(str(d) for d in envvar)\n                )\n                extra.append(_(\"env var: {var}\").format(var=var_str))\n\n        # Typer override:\n        # Extracted to _extract_default() to allow re-using it in rich_utils\n        default_value = self._extract_default_help_str(ctx=ctx)\n        # Typer override end\n\n        show_default_is_str = isinstance(self.show_default, str)\n\n        if show_default_is_str or (\n            default_value is not None and (self.show_default or ctx.show_default)\n        ):\n            # Typer override:\n            # Extracted to _get_default_string() to allow re-using it in rich_utils\n            default_string = self._get_default_string(\n                ctx=ctx,\n                show_default_is_str=show_default_is_str,\n                default_value=default_value,\n            )\n            # Typer override end\n            if default_string:\n                extra.append(_(\"default: {default}\").format(default=default_string))\n\n        if isinstance(self.type, click.types._NumberRangeBase):\n            range_str = self.type._describe_range()\n\n            if range_str:\n                extra.append(range_str)\n\n        if self.required:\n            extra.append(_(\"required\"))\n\n        if extra:\n            extra_str = \"; \".join(extra)\n            extra_str = f\"[{extra_str}]\"\n            rich_markup_mode = None\n            if hasattr(ctx, \"obj\") and isinstance(ctx.obj, dict):\n                rich_markup_mode = ctx.obj.get(MARKUP_MODE_KEY, None)\n            if HAS_RICH and rich_markup_mode == \"rich\":\n                # This is needed for when we want to export to HTML\n                from . import rich_utils\n\n                extra_str = rich_utils.escape_before_html_export(extra_str)\n\n            help = f\"{help}  {extra_str}\" if help else f\"{extra_str}\"\n\n        return (\"; \" if any_prefix_is_slash else \" / \").join(rv), help\n\n    def value_is_missing(self, value: Any) -> bool:\n        return _value_is_missing(self, value)\n\n\ndef _value_is_missing(param: click.Parameter, value: Any) -> bool:\n    if value is None:\n        return True\n\n    # Click 8.3 and beyond\n    # if value is UNSET:\n    #     return True\n\n    if (param.nargs != 1 or param.multiple) and value == ():\n        return True  # pragma: no cover\n\n    return False\n\n\ndef _typer_format_options(\n    self: click.core.Command, *, ctx: click.Context, formatter: click.HelpFormatter\n) -> None:\n    args = []\n    opts = []\n    for param in self.get_params(ctx):\n        rv = param.get_help_record(ctx)\n        if rv is not None:\n            if param.param_type_name == \"argument\":\n                args.append(rv)\n            elif param.param_type_name == \"option\":\n                opts.append(rv)\n\n    if args:\n        with formatter.section(_(\"Arguments\")):\n            formatter.write_dl(args)\n    if opts:\n        with formatter.section(_(\"Options\")):\n            formatter.write_dl(opts)\n\n\ndef _typer_main_shell_completion(\n    self: click.core.Command,\n    *,\n    ctx_args: MutableMapping[str, Any],\n    prog_name: str,\n    complete_var: str | None = None,\n) -> None:\n    if complete_var is None:\n        complete_var = f\"_{prog_name}_COMPLETE\".replace(\"-\", \"_\").upper()\n\n    instruction = os.environ.get(complete_var)\n\n    if not instruction:\n        return\n\n    from .completion import shell_complete\n\n    rv = shell_complete(self, ctx_args, prog_name, complete_var, instruction)\n    sys.exit(rv)\n\n\nclass TyperCommand(click.core.Command):\n    def __init__(\n        self,\n        name: str | None,\n        *,\n        context_settings: dict[str, Any] | None = None,\n        callback: Callable[..., Any] | None = None,\n        params: list[click.Parameter] | None = None,\n        help: str | None = None,\n        epilog: str | None = None,\n        short_help: str | None = None,\n        options_metavar: str | None = \"[OPTIONS]\",\n        add_help_option: bool = True,\n        no_args_is_help: bool = False,\n        hidden: bool = False,\n        deprecated: bool = False,\n        # Rich settings\n        rich_markup_mode: MarkupMode = DEFAULT_MARKUP_MODE,\n        rich_help_panel: str | None = None,\n    ) -> None:\n        super().__init__(\n            name=name,\n            context_settings=context_settings,\n            callback=callback,\n            params=params,\n            help=help,\n            epilog=epilog,\n            short_help=short_help,\n            options_metavar=options_metavar,\n            add_help_option=add_help_option,\n            no_args_is_help=no_args_is_help,\n            hidden=hidden,\n            deprecated=deprecated,\n        )\n        self.rich_markup_mode: MarkupMode = rich_markup_mode\n        self.rich_help_panel = rich_help_panel\n\n    def format_options(\n        self, ctx: click.Context, formatter: click.HelpFormatter\n    ) -> None:\n        _typer_format_options(self, ctx=ctx, formatter=formatter)\n\n    def _main_shell_completion(\n        self,\n        ctx_args: MutableMapping[str, Any],\n        prog_name: str,\n        complete_var: str | None = None,\n    ) -> None:\n        _typer_main_shell_completion(\n            self, ctx_args=ctx_args, prog_name=prog_name, complete_var=complete_var\n        )\n\n    def main(\n        self,\n        args: Sequence[str] | None = None,\n        prog_name: str | None = None,\n        complete_var: str | None = None,\n        standalone_mode: bool = True,\n        windows_expand_args: bool = True,\n        **extra: Any,\n    ) -> Any:\n        return _main(\n            self,\n            args=args,\n            prog_name=prog_name,\n            complete_var=complete_var,\n            standalone_mode=standalone_mode,\n            windows_expand_args=windows_expand_args,\n            rich_markup_mode=self.rich_markup_mode,\n            **extra,\n        )\n\n    def format_help(self, ctx: click.Context, formatter: click.HelpFormatter) -> None:\n        if not HAS_RICH or self.rich_markup_mode is None:\n            if not hasattr(ctx, \"obj\") or ctx.obj is None:\n                ctx.ensure_object(dict)\n            if isinstance(ctx.obj, dict):\n                ctx.obj[MARKUP_MODE_KEY] = self.rich_markup_mode\n            return super().format_help(ctx, formatter)\n        from . import rich_utils\n\n        return rich_utils.rich_format_help(\n            obj=self,\n            ctx=ctx,\n            markup_mode=self.rich_markup_mode,\n        )\n\n\nclass TyperGroup(click.core.Group):\n    def __init__(\n        self,\n        *,\n        name: str | None = None,\n        commands: dict[str, click.Command] | Sequence[click.Command] | None = None,\n        # Rich settings\n        rich_markup_mode: MarkupMode = DEFAULT_MARKUP_MODE,\n        rich_help_panel: str | None = None,\n        suggest_commands: bool = True,\n        **attrs: Any,\n    ) -> None:\n        super().__init__(name=name, commands=commands, **attrs)\n        self.rich_markup_mode: MarkupMode = rich_markup_mode\n        self.rich_help_panel = rich_help_panel\n        self.suggest_commands = suggest_commands\n\n    def format_options(\n        self, ctx: click.Context, formatter: click.HelpFormatter\n    ) -> None:\n        _typer_format_options(self, ctx=ctx, formatter=formatter)\n        self.format_commands(ctx, formatter)\n\n    def _main_shell_completion(\n        self,\n        ctx_args: MutableMapping[str, Any],\n        prog_name: str,\n        complete_var: str | None = None,\n    ) -> None:\n        _typer_main_shell_completion(\n            self, ctx_args=ctx_args, prog_name=prog_name, complete_var=complete_var\n        )\n\n    def resolve_command(\n        self, ctx: click.Context, args: list[str]\n    ) -> tuple[str | None, click.Command | None, list[str]]:\n        try:\n            return super().resolve_command(ctx, args)\n        except click.UsageError as e:\n            if self.suggest_commands:\n                available_commands = list(self.commands.keys())\n                if available_commands and args:\n                    typo = args[0]\n                    matches = get_close_matches(typo, available_commands)\n                    if matches:\n                        suggestions = \", \".join(f\"{m!r}\" for m in matches)\n                        message = e.message.rstrip(\".\")\n                        e.message = f\"{message}. Did you mean {suggestions}?\"\n            raise\n\n    def main(\n        self,\n        args: Sequence[str] | None = None,\n        prog_name: str | None = None,\n        complete_var: str | None = None,\n        standalone_mode: bool = True,\n        windows_expand_args: bool = True,\n        **extra: Any,\n    ) -> Any:\n        return _main(\n            self,\n            args=args,\n            prog_name=prog_name,\n            complete_var=complete_var,\n            standalone_mode=standalone_mode,\n            windows_expand_args=windows_expand_args,\n            rich_markup_mode=self.rich_markup_mode,\n            **extra,\n        )\n\n    def format_help(self, ctx: click.Context, formatter: click.HelpFormatter) -> None:\n        if not HAS_RICH or self.rich_markup_mode is None:\n            return super().format_help(ctx, formatter)\n        from . import rich_utils\n\n        return rich_utils.rich_format_help(\n            obj=self,\n            ctx=ctx,\n            markup_mode=self.rich_markup_mode,\n        )\n\n    def list_commands(self, ctx: click.Context) -> list[str]:\n        \"\"\"Returns a list of subcommand names.\n        Note that in Click's Group class, these are sorted.\n        In Typer, we wish to maintain the original order of creation (cf Issue #933)\"\"\"\n        return [n for n, c in self.commands.items()]\n"
  },
  {
    "path": "typer/main.py",
    "content": "import inspect\nimport os\nimport platform\nimport shutil\nimport subprocess\nimport sys\nimport traceback\nfrom collections.abc import Callable, Sequence\nfrom datetime import datetime\nfrom enum import Enum\nfrom functools import update_wrapper\nfrom pathlib import Path\nfrom traceback import FrameSummary, StackSummary\nfrom types import TracebackType\nfrom typing import Annotated, Any\nfrom uuid import UUID\n\nimport click\nfrom annotated_doc import Doc\nfrom typer._types import TyperChoice\n\nfrom ._typing import get_args, get_origin, is_literal_type, is_union, literal_values\nfrom .completion import get_completion_inspect_parameters\nfrom .core import (\n    DEFAULT_MARKUP_MODE,\n    HAS_RICH,\n    MarkupMode,\n    TyperArgument,\n    TyperCommand,\n    TyperGroup,\n    TyperOption,\n)\nfrom .models import (\n    AnyType,\n    ArgumentInfo,\n    CommandFunctionType,\n    CommandInfo,\n    Default,\n    DefaultPlaceholder,\n    DeveloperExceptionConfig,\n    FileBinaryRead,\n    FileBinaryWrite,\n    FileText,\n    FileTextWrite,\n    NoneType,\n    OptionInfo,\n    ParameterInfo,\n    ParamMeta,\n    Required,\n    TyperInfo,\n    TyperPath,\n)\nfrom .utils import get_params_from_function\n\n_original_except_hook = sys.excepthook\n_typer_developer_exception_attr_name = \"__typer_developer_exception__\"\n\n\ndef except_hook(\n    exc_type: type[BaseException], exc_value: BaseException, tb: TracebackType | None\n) -> None:\n    exception_config: DeveloperExceptionConfig | None = getattr(\n        exc_value, _typer_developer_exception_attr_name, None\n    )\n    standard_traceback = os.getenv(\n        \"TYPER_STANDARD_TRACEBACK\", os.getenv(\"_TYPER_STANDARD_TRACEBACK\")\n    )\n    if (\n        standard_traceback\n        or not exception_config\n        or not exception_config.pretty_exceptions_enable\n    ):\n        _original_except_hook(exc_type, exc_value, tb)\n        return\n    typer_path = os.path.dirname(__file__)\n    click_path = os.path.dirname(click.__file__)  # ty: ignore\n    internal_dir_names = [typer_path, click_path]\n    exc = exc_value\n    if HAS_RICH:\n        from . import rich_utils\n\n        rich_tb = rich_utils.get_traceback(exc, exception_config, internal_dir_names)\n        console_stderr = rich_utils._get_rich_console(stderr=True)\n        console_stderr.print(rich_tb)\n        return\n    tb_exc = traceback.TracebackException.from_exception(exc)\n    stack: list[FrameSummary] = []\n    for frame in tb_exc.stack:\n        if any(frame.filename.startswith(path) for path in internal_dir_names):\n            if not exception_config.pretty_exceptions_short:\n                # Hide the line for internal libraries, Typer and Click\n                stack.append(\n                    traceback.FrameSummary(\n                        filename=frame.filename,\n                        lineno=frame.lineno,\n                        name=frame.name,\n                        line=\"\",\n                    )\n                )\n        else:\n            stack.append(frame)\n    # Type ignore ref: https://github.com/python/typeshed/pull/8244\n    final_stack_summary = StackSummary.from_list(stack)\n    tb_exc.stack = final_stack_summary\n    for line in tb_exc.format():\n        print(line, file=sys.stderr)\n    return\n\n\ndef get_install_completion_arguments() -> tuple[click.Parameter, click.Parameter]:\n    install_param, show_param = get_completion_inspect_parameters()\n    click_install_param, _ = get_click_param(install_param)\n    click_show_param, _ = get_click_param(show_param)\n    return click_install_param, click_show_param\n\n\nclass Typer:\n    \"\"\"\n    `Typer` main class, the main entrypoint to use Typer.\n\n    Read more in the\n    [Typer docs for First Steps](https://typer.tiangolo.com/tutorial/typer-app/).\n\n    ## Example\n\n    ```python\n    import typer\n\n    app = typer.Typer()\n    ```\n    \"\"\"\n\n    def __init__(\n        self,\n        *,\n        name: Annotated[\n            str | None,\n            Doc(\n                \"\"\"\n                The name of this application.\n                Mostly used to set the name for [subcommands](https://typer.tiangolo.com/tutorial/subcommands/), in which case it can be overridden by `add_typer(name=...)`.\n\n                **Example**\n\n                ```python\n                import typer\n\n                app = typer.Typer(name=\"users\")\n                ```\n                \"\"\"\n            ),\n        ] = Default(None),\n        cls: Annotated[\n            type[TyperGroup] | None,\n            Doc(\n                \"\"\"\n                The class of this app. Mainly used when [using the Click library underneath](https://typer.tiangolo.com/tutorial/using-click/). Can usually be left at the default value `None`.\n                Otherwise, should be a subtype of `TyperGroup`.\n\n                **Example**\n\n                ```python\n                import typer\n\n                app = typer.Typer(cls=TyperGroup)\n                ```\n                \"\"\"\n            ),\n        ] = Default(None),\n        invoke_without_command: Annotated[\n            bool,\n            Doc(\n                \"\"\"\n                By setting this to `True`, you can make sure a callback is executed even when no subcommand is provided.\n\n                **Example**\n\n                ```python\n                import typer\n\n                app = typer.Typer(invoke_without_command=True)\n                ```\n                \"\"\"\n            ),\n        ] = Default(False),\n        no_args_is_help: Annotated[\n            bool,\n            Doc(\n                \"\"\"\n                If this is set to `True`, running a command without any arguments will automatically show the help page.\n\n                **Example**\n\n                ```python\n                import typer\n\n                app = typer.Typer(no_args_is_help=True)\n                ```\n                \"\"\"\n            ),\n        ] = Default(False),\n        subcommand_metavar: Annotated[\n            str | None,\n            Doc(\n                \"\"\"\n                **Note**: you probably shouldn't use this parameter, it is inherited\n                from Click and supported for compatibility.\n\n                ---\n\n                How to represent the subcommand argument in help.\n                \"\"\"\n            ),\n        ] = Default(None),\n        chain: Annotated[\n            bool,\n            Doc(\n                \"\"\"\n                **Note**: you probably shouldn't use this parameter, it is inherited\n                from Click and supported for compatibility.\n\n                ---\n\n                Allow passing more than one subcommand argument.\n                \"\"\"\n            ),\n        ] = Default(False),\n        result_callback: Annotated[\n            Callable[..., Any] | None,\n            Doc(\n                \"\"\"\n                **Note**: you probably shouldn't use this parameter, it is inherited\n                from Click and supported for compatibility.\n\n                ---\n\n                A function to call after the group's and subcommand's callbacks.\n                \"\"\"\n            ),\n        ] = Default(None),\n        # Command\n        context_settings: Annotated[\n            dict[Any, Any] | None,\n            Doc(\n                \"\"\"\n                Pass configurations for the [context](https://typer.tiangolo.com/tutorial/commands/context/).\n                Available configurations can be found in the docs for Click's `Context` [here](https://click.palletsprojects.com/en/stable/api/#context).\n\n                **Example**\n\n                ```python\n                import typer\n\n                app = typer.Typer(context_settings={\"help_option_names\": [\"-h\", \"--help\"]})\n                ```\n                \"\"\"\n            ),\n        ] = Default(None),\n        callback: Annotated[\n            Callable[..., Any] | None,\n            Doc(\n                \"\"\"\n                Add a callback to the main Typer app. Can be overridden with `@app.callback()`.\n                See [the tutorial about callbacks](https://typer.tiangolo.com/tutorial/commands/callback/) for more details.\n\n                **Example**\n\n                ```python\n                import typer\n\n                def callback():\n                    print(\"Running a command\")\n\n                app = typer.Typer(callback=callback)\n                ```\n                \"\"\"\n            ),\n        ] = Default(None),\n        help: Annotated[\n            str | None,\n            Doc(\n                \"\"\"\n                Help text for the main Typer app.\n                See [the tutorial about name and help](https://typer.tiangolo.com/tutorial/subcommands/name-and-help) for different ways of setting a command's help,\n                and which one takes priority.\n\n                **Example**\n\n                ```python\n                import typer\n\n                app = typer.Typer(help=\"Some help.\")\n                ```\n                \"\"\"\n            ),\n        ] = Default(None),\n        epilog: Annotated[\n            str | None,\n            Doc(\n                \"\"\"\n                Text that will be printed right after the help text.\n\n                **Example**\n\n                ```python\n                import typer\n\n                app = typer.Typer(epilog=\"May the force be with you\")\n                ```\n                \"\"\"\n            ),\n        ] = Default(None),\n        short_help: Annotated[\n            str | None,\n            Doc(\n                \"\"\"\n                A shortened version of the help text that can be used e.g. in the help table listing subcommands.\n                When not defined, the normal `help` text will be used instead.\n\n                **Example**\n\n                ```python\n                import typer\n\n                app = typer.Typer(help=\"A lot of explanation about user management\", short_help=\"user management\")\n                ```\n                \"\"\"\n            ),\n        ] = Default(None),\n        options_metavar: Annotated[\n            str,\n            Doc(\n                \"\"\"\n                In the example usage string of the help text for a command, the default placeholder for various arguments is `[OPTIONS]`.\n                Set `options_metavar` to change this into a different string.\n\n                **Example**\n\n                ```python\n                import typer\n\n                app = typer.Typer(options_metavar=\"[OPTS]\")\n                ```\n                \"\"\"\n            ),\n        ] = Default(\"[OPTIONS]\"),\n        add_help_option: Annotated[\n            bool,\n            Doc(\n                \"\"\"\n                **Note**: you probably shouldn't use this parameter, it is inherited\n                from Click and supported for compatibility.\n\n                ---\n\n                By default each command registers a `--help` option. This can be disabled by this parameter.\n                \"\"\"\n            ),\n        ] = Default(True),\n        hidden: Annotated[\n            bool,\n            Doc(\n                \"\"\"\n                Hide this command from help outputs. `False` by default.\n\n                **Example**\n\n                ```python\n                import typer\n\n                app = typer.Typer(hidden=True)\n                ```\n                \"\"\"\n            ),\n        ] = Default(False),\n        deprecated: Annotated[\n            bool,\n            Doc(\n                \"\"\"\n                Mark this command as being deprecated in the help text. `False` by default.\n\n                **Example**\n\n                ```python\n                import typer\n\n                app = typer.Typer(deprecated=True)\n                ```\n                \"\"\"\n            ),\n        ] = Default(False),\n        add_completion: Annotated[\n            bool,\n            Doc(\n                \"\"\"\n                Toggle whether or not to add the `--install-completion` and `--show-completion` options to the app.\n                Set to `True` by default.\n\n                **Example**\n\n                ```python\n                import typer\n\n                app = typer.Typer(add_completion=False)\n                ```\n                \"\"\"\n            ),\n        ] = True,\n        # Rich settings\n        rich_markup_mode: Annotated[\n            MarkupMode,\n            Doc(\n                \"\"\"\n                Enable markup text if you have Rich installed. This can be set to `\"markdown\"`, `\"rich\"`, or `None`.\n                By default, `rich_markup_mode` is `None` if Rich is not installed, and `\"rich\"` if it is installed.\n                See [the tutorial on help formatting](https://typer.tiangolo.com/tutorial/commands/help/#rich-markdown-and-markup) for more information.\n\n                **Example**\n\n                ```python\n                import typer\n\n                app = typer.Typer(rich_markup_mode=\"rich\")\n                ```\n                \"\"\"\n            ),\n        ] = DEFAULT_MARKUP_MODE,\n        rich_help_panel: Annotated[\n            str | None,\n            Doc(\n                \"\"\"\n                Set the panel name of the command when the help is printed with Rich.\n\n                **Example**\n\n                ```python\n                import typer\n\n                app = typer.Typer(rich_help_panel=\"Utils and Configs\")\n                ```\n                \"\"\"\n            ),\n        ] = Default(None),\n        suggest_commands: Annotated[\n            bool,\n            Doc(\n                \"\"\"\n                As of version 0.20.0, Typer provides [support for mistyped command names](https://typer.tiangolo.com/tutorial/commands/help/#suggest-commands) by printing helpful suggestions.\n                You can turn this setting off with `suggest_commands`:\n\n                **Example**\n\n                ```python\n                import typer\n\n                app = typer.Typer(suggest_commands=False)\n                ```\n                \"\"\"\n            ),\n        ] = True,\n        pretty_exceptions_enable: Annotated[\n            bool,\n            Doc(\n                \"\"\"\n                If you want to disable [pretty exceptions with Rich](https://typer.tiangolo.com/tutorial/exceptions/#exceptions-with-rich),\n                you can set `pretty_exceptions_enable` to `False`. When doing so, you will see the usual standard exception trace.\n\n                **Example**\n\n                ```python\n                import typer\n\n                app = typer.Typer(pretty_exceptions_enable=False)\n                ```\n                \"\"\"\n            ),\n        ] = True,\n        pretty_exceptions_show_locals: Annotated[\n            bool,\n            Doc(\n                \"\"\"\n                If Rich is installed, [error messages](https://typer.tiangolo.com/tutorial/exceptions/#exceptions-and-errors)\n                will be nicely printed.\n\n                If you set `pretty_exceptions_show_locals=True` it will also include the values of local variables for easy debugging.\n\n                However, if such a variable contains delicate information, you should consider leaving `pretty_exceptions_show_locals=False`\n                (the default) to `False` to enhance security.\n\n                **Example**\n\n                ```python\n                import typer\n\n                app = typer.Typer(pretty_exceptions_show_locals=True)\n                ```\n                \"\"\"\n            ),\n        ] = False,\n        pretty_exceptions_short: Annotated[\n            bool,\n            Doc(\n                \"\"\"\n                By default, [pretty exceptions formatted with Rich](https://typer.tiangolo.com/tutorial/exceptions/#exceptions-with-rich) hide the long stack trace.\n                If you want to show the full trace instead, you can set the parameter `pretty_exceptions_short` to `False`:\n\n                **Example**\n\n                ```python\n                import typer\n\n                app = typer.Typer(pretty_exceptions_short=False)\n                ```\n                \"\"\"\n            ),\n        ] = True,\n    ):\n        self._add_completion = add_completion\n        self.rich_markup_mode: MarkupMode = rich_markup_mode\n        self.rich_help_panel = rich_help_panel\n        self.suggest_commands = suggest_commands\n        self.pretty_exceptions_enable = pretty_exceptions_enable\n        self.pretty_exceptions_show_locals = pretty_exceptions_show_locals\n        self.pretty_exceptions_short = pretty_exceptions_short\n        self.info = TyperInfo(\n            name=name,\n            cls=cls,\n            invoke_without_command=invoke_without_command,\n            no_args_is_help=no_args_is_help,\n            subcommand_metavar=subcommand_metavar,\n            chain=chain,\n            result_callback=result_callback,\n            context_settings=context_settings,\n            callback=callback,\n            help=help,\n            epilog=epilog,\n            short_help=short_help,\n            options_metavar=options_metavar,\n            add_help_option=add_help_option,\n            hidden=hidden,\n            deprecated=deprecated,\n        )\n        self.registered_groups: list[TyperInfo] = []\n        self.registered_commands: list[CommandInfo] = []\n        self.registered_callback: TyperInfo | None = None\n\n    def callback(\n        self,\n        *,\n        cls: Annotated[\n            type[TyperGroup] | None,\n            Doc(\n                \"\"\"\n                The class of this app. Mainly used when [using the Click library underneath](https://typer.tiangolo.com/tutorial/using-click/). Can usually be left at the default value `None`.\n                Otherwise, should be a subtype of `TyperGroup`.\n                \"\"\"\n            ),\n        ] = Default(None),\n        invoke_without_command: Annotated[\n            bool,\n            Doc(\n                \"\"\"\n                By setting this to `True`, you can make sure a callback is executed even when no subcommand is provided.\n                \"\"\"\n            ),\n        ] = Default(False),\n        no_args_is_help: Annotated[\n            bool,\n            Doc(\n                \"\"\"\n                If this is set to `True`, running a command without any arguments will automatically show the help page.\n                \"\"\"\n            ),\n        ] = Default(False),\n        subcommand_metavar: Annotated[\n            str | None,\n            Doc(\n                \"\"\"\n                **Note**: you probably shouldn't use this parameter, it is inherited\n                from Click and supported for compatibility.\n\n                ---\n\n                How to represent the subcommand argument in help.\n                \"\"\"\n            ),\n        ] = Default(None),\n        chain: Annotated[\n            bool,\n            Doc(\n                \"\"\"\n                **Note**: you probably shouldn't use this parameter, it is inherited\n                from Click and supported for compatibility.\n\n                ---\n\n                Allow passing more than one subcommand argument.\n                \"\"\"\n            ),\n        ] = Default(False),\n        result_callback: Annotated[\n            Callable[..., Any] | None,\n            Doc(\n                \"\"\"\n                **Note**: you probably shouldn't use this parameter, it is inherited\n                from Click and supported for compatibility.\n\n                ---\n\n                A function to call after the group's and subcommand's callbacks.\n                \"\"\"\n            ),\n        ] = Default(None),\n        # Command\n        context_settings: Annotated[\n            dict[Any, Any] | None,\n            Doc(\n                \"\"\"\n                Pass configurations for the [context](https://typer.tiangolo.com/tutorial/commands/context/).\n                Available configurations can be found in the docs for Click's `Context` [here](https://click.palletsprojects.com/en/stable/api/#context).\n                \"\"\"\n            ),\n        ] = Default(None),\n        help: Annotated[\n            str | None,\n            Doc(\n                \"\"\"\n                Help text for the command.\n                See [the tutorial about name and help](https://typer.tiangolo.com/tutorial/subcommands/name-and-help) for different ways of setting a command's help,\n                and which one takes priority.\n                \"\"\"\n            ),\n        ] = Default(None),\n        epilog: Annotated[\n            str | None,\n            Doc(\n                \"\"\"\n                Text that will be printed right after the help text.\n                \"\"\"\n            ),\n        ] = Default(None),\n        short_help: Annotated[\n            str | None,\n            Doc(\n                \"\"\"\n                A shortened version of the help text that can be used e.g. in the help table listing subcommands.\n                When not defined, the normal `help` text will be used instead.\n                \"\"\"\n            ),\n        ] = Default(None),\n        options_metavar: Annotated[\n            str | None,\n            Doc(\n                \"\"\"\n                In the example usage string of the help text for a command, the default placeholder for various arguments is `[OPTIONS]`.\n                Set `options_metavar` to change this into a different string. When `None`, the default value will be used.\n                \"\"\"\n            ),\n        ] = Default(None),\n        add_help_option: Annotated[\n            bool,\n            Doc(\n                \"\"\"\n                **Note**: you probably shouldn't use this parameter, it is inherited\n                from Click and supported for compatibility.\n\n                ---\n\n                By default each command registers a `--help` option. This can be disabled by this parameter.\n                \"\"\"\n            ),\n        ] = Default(True),\n        hidden: Annotated[\n            bool,\n            Doc(\n                \"\"\"\n                Hide this command from help outputs. `False` by default.\n                \"\"\"\n            ),\n        ] = Default(False),\n        deprecated: Annotated[\n            bool,\n            Doc(\n                \"\"\"\n                Mark this command as deprecated in the help text. `False` by default.\n                \"\"\"\n            ),\n        ] = Default(False),\n        # Rich settings\n        rich_help_panel: Annotated[\n            str | None,\n            Doc(\n                \"\"\"\n                Set the panel name of the command when the help is printed with Rich.\n                \"\"\"\n            ),\n        ] = Default(None),\n    ) -> Callable[[CommandFunctionType], CommandFunctionType]:\n        \"\"\"\n        Using the decorator `@app.callback`, you can declare the CLI parameters for the main CLI application.\n\n        Read more in the\n        [Typer docs for Callbacks](https://typer.tiangolo.com/tutorial/commands/callback/).\n\n        ## Example\n\n        ```python\n        import typer\n\n        app = typer.Typer()\n        state = {\"verbose\": False}\n\n        @app.callback()\n        def main(verbose: bool = False):\n            if verbose:\n                print(\"Will write verbose output\")\n                state[\"verbose\"] = True\n\n        @app.command()\n        def delete(username: str):\n            # define subcommand\n            ...\n        ```\n        \"\"\"\n\n        def decorator(f: CommandFunctionType) -> CommandFunctionType:\n            self.registered_callback = TyperInfo(\n                cls=cls,\n                invoke_without_command=invoke_without_command,\n                no_args_is_help=no_args_is_help,\n                subcommand_metavar=subcommand_metavar,\n                chain=chain,\n                result_callback=result_callback,\n                context_settings=context_settings,\n                callback=f,\n                help=help,\n                epilog=epilog,\n                short_help=short_help,\n                options_metavar=(\n                    options_metavar or self._info_val_str(\"options_metavar\")\n                ),\n                add_help_option=add_help_option,\n                hidden=hidden,\n                deprecated=deprecated,\n                rich_help_panel=rich_help_panel,\n            )\n            return f\n\n        return decorator\n\n    def command(\n        self,\n        name: Annotated[\n            str | None,\n            Doc(\n                \"\"\"\n                The name of this command.\n                \"\"\"\n            ),\n        ] = None,\n        *,\n        cls: Annotated[\n            type[TyperCommand] | None,\n            Doc(\n                \"\"\"\n                The class of this command. Mainly used when [using the Click library underneath](https://typer.tiangolo.com/tutorial/using-click/). Can usually be left at the default value `None`.\n                Otherwise, should be a subtype of `TyperCommand`.\n                \"\"\"\n            ),\n        ] = None,\n        context_settings: Annotated[\n            dict[Any, Any] | None,\n            Doc(\n                \"\"\"\n                Pass configurations for the [context](https://typer.tiangolo.com/tutorial/commands/context/).\n                Available configurations can be found in the docs for Click's `Context` [here](https://click.palletsprojects.com/en/stable/api/#context).\n                \"\"\"\n            ),\n        ] = None,\n        help: Annotated[\n            str | None,\n            Doc(\n                \"\"\"\n                Help text for the command.\n                See [the tutorial about name and help](https://typer.tiangolo.com/tutorial/subcommands/name-and-help) for different ways of setting a command's help,\n                and which one takes priority.\n                \"\"\"\n            ),\n        ] = None,\n        epilog: Annotated[\n            str | None,\n            Doc(\n                \"\"\"\n                Text that will be printed right after the help text.\n                \"\"\"\n            ),\n        ] = None,\n        short_help: Annotated[\n            str | None,\n            Doc(\n                \"\"\"\n                A shortened version of the help text that can be used e.g. in the help table listing subcommands.\n                When not defined, the normal `help` text will be used instead.\n                \"\"\"\n            ),\n        ] = None,\n        options_metavar: Annotated[\n            str | None,\n            Doc(\n                \"\"\"\n                In the example usage string of the help text for a command, the default placeholder for various arguments is `[OPTIONS]`.\n                Set `options_metavar` to change this into a different string. When `None`, the default value will be used.\n                \"\"\"\n            ),\n        ] = Default(None),\n        add_help_option: Annotated[\n            bool,\n            Doc(\n                \"\"\"\n                **Note**: you probably shouldn't use this parameter, it is inherited\n                from Click and supported for compatibility.\n\n                ---\n\n                By default each command registers a `--help` option. This can be disabled by this parameter.\n                \"\"\"\n            ),\n        ] = True,\n        no_args_is_help: Annotated[\n            bool,\n            Doc(\n                \"\"\"\n                If this is set to `True`, running a command without any arguments will automatically show the help page.\n                \"\"\"\n            ),\n        ] = False,\n        hidden: Annotated[\n            bool,\n            Doc(\n                \"\"\"\n                Hide this command from help outputs. `False` by default.\n                \"\"\"\n            ),\n        ] = False,\n        deprecated: Annotated[\n            bool,\n            Doc(\n                \"\"\"\n                Mark this command as deprecated in the help outputs. `False` by default.\n                \"\"\"\n            ),\n        ] = False,\n        # Rich settings\n        rich_help_panel: Annotated[\n            str | None,\n            Doc(\n                \"\"\"\n                Set the panel name of the command when the help is printed with Rich.\n                \"\"\"\n            ),\n        ] = Default(None),\n    ) -> Callable[[CommandFunctionType], CommandFunctionType]:\n        \"\"\"\n        Using the decorator `@app.command`, you can define a subcommand of the previously defined Typer app.\n\n        Read more in the\n        [Typer docs for Commands](https://typer.tiangolo.com/tutorial/commands/).\n\n        ## Example\n\n        ```python\n        import typer\n\n        app = typer.Typer()\n\n        @app.command()\n        def create():\n            print(\"Creating user: Hiro Hamada\")\n\n        @app.command()\n        def delete():\n            print(\"Deleting user: Hiro Hamada\")\n        ```\n        \"\"\"\n        if cls is None:\n            cls = TyperCommand\n\n        def decorator(f: CommandFunctionType) -> CommandFunctionType:\n            self.registered_commands.append(\n                CommandInfo(\n                    name=name,\n                    cls=cls,\n                    context_settings=context_settings,\n                    callback=f,\n                    help=help,\n                    epilog=epilog,\n                    short_help=short_help,\n                    options_metavar=(\n                        options_metavar or self._info_val_str(\"options_metavar\")\n                    ),\n                    add_help_option=add_help_option,\n                    no_args_is_help=no_args_is_help,\n                    hidden=hidden,\n                    deprecated=deprecated,\n                    # Rich settings\n                    rich_help_panel=rich_help_panel,\n                )\n            )\n            return f\n\n        return decorator\n\n    def add_typer(\n        self,\n        typer_instance: \"Typer\",\n        *,\n        name: Annotated[\n            str | None,\n            Doc(\n                \"\"\"\n                The name of this subcommand.\n                See [the tutorial about name and help](https://typer.tiangolo.com/tutorial/subcommands/name-and-help) for different ways of setting a command's name,\n                and which one takes priority.\n                \"\"\"\n            ),\n        ] = Default(None),\n        cls: Annotated[\n            type[TyperGroup] | None,\n            Doc(\n                \"\"\"\n                The class of this subcommand. Mainly used when [using the Click library underneath](https://typer.tiangolo.com/tutorial/using-click/). Can usually be left at the default value `None`.\n                Otherwise, should be a subtype of `TyperGroup`.\n                \"\"\"\n            ),\n        ] = Default(None),\n        invoke_without_command: Annotated[\n            bool,\n            Doc(\n                \"\"\"\n                By setting this to `True`, you can make sure a callback is executed even when no subcommand is provided.\n                \"\"\"\n            ),\n        ] = Default(False),\n        no_args_is_help: Annotated[\n            bool,\n            Doc(\n                \"\"\"\n                If this is set to `True`, running a command without any arguments will automatically show the help page.\n                \"\"\"\n            ),\n        ] = Default(False),\n        subcommand_metavar: Annotated[\n            str | None,\n            Doc(\n                \"\"\"\n                **Note**: you probably shouldn't use this parameter, it is inherited\n                from Click and supported for compatibility.\n\n                ---\n\n                How to represent the subcommand argument in help.\n                \"\"\"\n            ),\n        ] = Default(None),\n        chain: Annotated[\n            bool,\n            Doc(\n                \"\"\"\n                **Note**: you probably shouldn't use this parameter, it is inherited\n                from Click and supported for compatibility.\n\n                ---\n\n                Allow passing more than one subcommand argument.\n                \"\"\"\n            ),\n        ] = Default(False),\n        result_callback: Annotated[\n            Callable[..., Any] | None,\n            Doc(\n                \"\"\"\n                **Note**: you probably shouldn't use this parameter, it is inherited\n                from Click and supported for compatibility.\n\n                ---\n\n                A function to call after the group's and subcommand's callbacks.\n                \"\"\"\n            ),\n        ] = Default(None),\n        # Command\n        context_settings: Annotated[\n            dict[Any, Any] | None,\n            Doc(\n                \"\"\"\n                Pass configurations for the [context](https://typer.tiangolo.com/tutorial/commands/context/).\n                Available configurations can be found in the docs for Click's `Context` [here](https://click.palletsprojects.com/en/stable/api/#context).\n                \"\"\"\n            ),\n        ] = Default(None),\n        callback: Annotated[\n            Callable[..., Any] | None,\n            Doc(\n                \"\"\"\n                Add a callback to this app.\n                See [the tutorial about callbacks](https://typer.tiangolo.com/tutorial/commands/callback/) for more details.\n                \"\"\"\n            ),\n        ] = Default(None),\n        help: Annotated[\n            str | None,\n            Doc(\n                \"\"\"\n                Help text for the subcommand.\n                See [the tutorial about name and help](https://typer.tiangolo.com/tutorial/subcommands/name-and-help) for different ways of setting a command's help,\n                and which one takes priority.\n                \"\"\"\n            ),\n        ] = Default(None),\n        epilog: Annotated[\n            str | None,\n            Doc(\n                \"\"\"\n                Text that will be printed right after the help text.\n                \"\"\"\n            ),\n        ] = Default(None),\n        short_help: Annotated[\n            str | None,\n            Doc(\n                \"\"\"\n                A shortened version of the help text that can be used e.g. in the help table listing subcommands.\n                When not defined, the normal `help` text will be used instead.\n                \"\"\"\n            ),\n        ] = Default(None),\n        options_metavar: Annotated[\n            str | None,\n            Doc(\n                \"\"\"\n                In the example usage string of the help text for a command, the default placeholder for various arguments is `[OPTIONS]`.\n                Set `options_metavar` to change this into a different string. When `None`, the default value will be used.\n                \"\"\"\n            ),\n        ] = Default(None),\n        add_help_option: Annotated[\n            bool,\n            Doc(\n                \"\"\"\n                **Note**: you probably shouldn't use this parameter, it is inherited\n                from Click and supported for compatibility.\n\n                ---\n\n                By default each command registers a `--help` option. This can be disabled by this parameter.\n                \"\"\"\n            ),\n        ] = Default(True),\n        hidden: Annotated[\n            bool,\n            Doc(\n                \"\"\"\n                Hide this command from help outputs. `False` by default.\n                \"\"\"\n            ),\n        ] = Default(False),\n        deprecated: Annotated[\n            bool,\n            Doc(\n                \"\"\"\n                Mark this command as deprecated in the help outputs. `False` by default.\n                \"\"\"\n            ),\n        ] = False,\n        # Rich settings\n        rich_help_panel: Annotated[\n            str | None,\n            Doc(\n                \"\"\"\n                Set the panel name of the command when the help is printed with Rich.\n                \"\"\"\n            ),\n        ] = Default(None),\n    ) -> None:\n        \"\"\"\n        Add subcommands to the main app using `app.add_typer()`.\n        Subcommands may be defined in separate modules, ensuring clean separation of code by functionality.\n\n        Read more in the\n        [Typer docs for SubCommands](https://typer.tiangolo.com/tutorial/subcommands/add-typer/).\n\n        ## Example\n\n        ```python\n        import typer\n\n        from .add import app as add_app\n        from .delete import app as delete_app\n\n        app = typer.Typer()\n\n        app.add_typer(add_app)\n        app.add_typer(delete_app)\n        ```\n        \"\"\"\n        self.registered_groups.append(\n            TyperInfo(\n                typer_instance,\n                name=name,\n                cls=cls,\n                invoke_without_command=invoke_without_command,\n                no_args_is_help=no_args_is_help,\n                subcommand_metavar=subcommand_metavar,\n                chain=chain,\n                result_callback=result_callback,\n                context_settings=context_settings,\n                callback=callback,\n                help=help,\n                epilog=epilog,\n                short_help=short_help,\n                options_metavar=(\n                    options_metavar or self._info_val_str(\"options_metavar\")\n                ),\n                add_help_option=add_help_option,\n                hidden=hidden,\n                deprecated=deprecated,\n                rich_help_panel=rich_help_panel,\n            )\n        )\n\n    def __call__(self, *args: Any, **kwargs: Any) -> Any:\n        if sys.excepthook != except_hook:\n            sys.excepthook = except_hook\n        try:\n            return get_command(self)(*args, **kwargs)\n        except Exception as e:\n            # Set a custom attribute to tell the hook to show nice exceptions for user\n            # code. An alternative/first implementation was a custom exception with\n            # raise custom_exc from e\n            # but that means the last error shown is the custom exception, not the\n            # actual error. This trick improves developer experience by showing the\n            # actual error last.\n            setattr(\n                e,\n                _typer_developer_exception_attr_name,\n                DeveloperExceptionConfig(\n                    pretty_exceptions_enable=self.pretty_exceptions_enable,\n                    pretty_exceptions_show_locals=self.pretty_exceptions_show_locals,\n                    pretty_exceptions_short=self.pretty_exceptions_short,\n                ),\n            )\n            raise e\n\n    def _info_val_str(self, name: str) -> str:\n        val = getattr(self.info, name)\n        val_str = val.value if isinstance(val, DefaultPlaceholder) else val\n        assert isinstance(val_str, str)\n        return val_str\n\n\ndef get_group(typer_instance: Typer) -> TyperGroup:\n    group = get_group_from_info(\n        TyperInfo(typer_instance),\n        pretty_exceptions_short=typer_instance.pretty_exceptions_short,\n        rich_markup_mode=typer_instance.rich_markup_mode,\n        suggest_commands=typer_instance.suggest_commands,\n    )\n    return group\n\n\ndef get_command(typer_instance: Typer) -> click.Command:\n    if typer_instance._add_completion:\n        click_install_param, click_show_param = get_install_completion_arguments()\n    if (\n        typer_instance.registered_callback\n        or typer_instance.info.callback\n        or typer_instance.registered_groups\n        or len(typer_instance.registered_commands) > 1\n    ):\n        # Create a Group\n        click_command: click.Command = get_group(typer_instance)\n        if typer_instance._add_completion:\n            click_command.params.append(click_install_param)\n            click_command.params.append(click_show_param)\n        return click_command\n    elif len(typer_instance.registered_commands) == 1:\n        # Create a single Command\n        single_command = typer_instance.registered_commands[0]\n\n        if not single_command.context_settings and not isinstance(\n            typer_instance.info.context_settings, DefaultPlaceholder\n        ):\n            single_command.context_settings = typer_instance.info.context_settings\n\n        click_command = get_command_from_info(\n            single_command,\n            pretty_exceptions_short=typer_instance.pretty_exceptions_short,\n            rich_markup_mode=typer_instance.rich_markup_mode,\n        )\n        if typer_instance._add_completion:\n            click_command.params.append(click_install_param)\n            click_command.params.append(click_show_param)\n        return click_command\n    raise RuntimeError(\n        \"Could not get a command for this Typer instance\"\n    )  # pragma: no cover\n\n\ndef solve_typer_info_help(typer_info: TyperInfo) -> str:\n    # Priority 1: Explicit value was set in app.add_typer()\n    if not isinstance(typer_info.help, DefaultPlaceholder):\n        return inspect.cleandoc(typer_info.help or \"\")\n    # Priority 2: Explicit value was set in sub_app.callback()\n    if typer_info.typer_instance and typer_info.typer_instance.registered_callback:\n        callback_help = typer_info.typer_instance.registered_callback.help\n        if not isinstance(callback_help, DefaultPlaceholder):\n            return inspect.cleandoc(callback_help or \"\")\n    # Priority 3: Explicit value was set in sub_app = typer.Typer()\n    if typer_info.typer_instance and typer_info.typer_instance.info:\n        instance_help = typer_info.typer_instance.info.help\n        if not isinstance(instance_help, DefaultPlaceholder):\n            return inspect.cleandoc(instance_help or \"\")\n    # Priority 4: Implicit inference from callback docstring in app.add_typer()\n    if typer_info.callback:\n        doc = inspect.getdoc(typer_info.callback)\n        if doc:\n            return doc\n    # Priority 5: Implicit inference from callback docstring in @app.callback()\n    if typer_info.typer_instance and typer_info.typer_instance.registered_callback:\n        callback = typer_info.typer_instance.registered_callback.callback\n        if not isinstance(callback, DefaultPlaceholder):\n            doc = inspect.getdoc(callback or \"\")\n            if doc:\n                return doc\n    # Priority 6: Implicit inference from callback docstring in typer.Typer()\n    if typer_info.typer_instance and typer_info.typer_instance.info:\n        instance_callback = typer_info.typer_instance.info.callback\n        if not isinstance(instance_callback, DefaultPlaceholder):\n            doc = inspect.getdoc(instance_callback)\n            if doc:\n                return doc\n    # Value not set, use the default\n    return typer_info.help.value\n\n\ndef solve_typer_info_defaults(typer_info: TyperInfo) -> TyperInfo:\n    values: dict[str, Any] = {}\n    for name, value in typer_info.__dict__.items():\n        # Priority 1: Value was set in app.add_typer()\n        if not isinstance(value, DefaultPlaceholder):\n            values[name] = value\n            continue\n        # Priority 2: Value was set in @subapp.callback()\n        try:\n            callback_value = getattr(\n                typer_info.typer_instance.registered_callback,  # type: ignore\n                name,\n            )\n            if not isinstance(callback_value, DefaultPlaceholder):\n                values[name] = callback_value\n                continue\n        except AttributeError:\n            pass\n        # Priority 3: Value set in subapp = typer.Typer()\n        try:\n            instance_value = getattr(\n                typer_info.typer_instance.info,  # type: ignore\n                name,\n            )\n            if not isinstance(instance_value, DefaultPlaceholder):\n                values[name] = instance_value\n                continue\n        except AttributeError:\n            pass\n        # Value not set, use the default\n        values[name] = value.value\n    values[\"help\"] = solve_typer_info_help(typer_info)\n    return TyperInfo(**values)\n\n\ndef get_group_from_info(\n    group_info: TyperInfo,\n    *,\n    pretty_exceptions_short: bool,\n    suggest_commands: bool,\n    rich_markup_mode: MarkupMode,\n) -> TyperGroup:\n    assert group_info.typer_instance, (\n        \"A Typer instance is needed to generate a Click Group\"\n    )\n    commands: dict[str, click.Command] = {}\n    for command_info in group_info.typer_instance.registered_commands:\n        command = get_command_from_info(\n            command_info=command_info,\n            pretty_exceptions_short=pretty_exceptions_short,\n            rich_markup_mode=rich_markup_mode,\n        )\n        if command.name:\n            commands[command.name] = command\n    for sub_group_info in group_info.typer_instance.registered_groups:\n        sub_group = get_group_from_info(\n            sub_group_info,\n            pretty_exceptions_short=pretty_exceptions_short,\n            rich_markup_mode=rich_markup_mode,\n            suggest_commands=suggest_commands,\n        )\n        if sub_group.name:\n            commands[sub_group.name] = sub_group\n        else:\n            if sub_group.callback:\n                import warnings\n\n                warnings.warn(\n                    \"The 'callback' parameter is not supported by Typer when using `add_typer` without a name\",\n                    stacklevel=5,\n                )\n            for sub_command_name, sub_command in sub_group.commands.items():\n                commands[sub_command_name] = sub_command\n    solved_info = solve_typer_info_defaults(group_info)\n    (\n        params,\n        convertors,\n        context_param_name,\n    ) = get_params_convertors_ctx_param_name_from_function(solved_info.callback)\n    cls = solved_info.cls or TyperGroup\n    assert issubclass(cls, TyperGroup), f\"{cls} should be a subclass of {TyperGroup}\"\n    group = cls(\n        name=solved_info.name or \"\",\n        commands=commands,\n        invoke_without_command=solved_info.invoke_without_command,\n        no_args_is_help=solved_info.no_args_is_help,\n        subcommand_metavar=solved_info.subcommand_metavar,\n        chain=solved_info.chain,\n        result_callback=solved_info.result_callback,\n        context_settings=solved_info.context_settings,\n        callback=get_callback(\n            callback=solved_info.callback,\n            params=params,\n            convertors=convertors,\n            context_param_name=context_param_name,\n            pretty_exceptions_short=pretty_exceptions_short,\n        ),\n        params=params,\n        help=solved_info.help,\n        epilog=solved_info.epilog,\n        short_help=solved_info.short_help,\n        options_metavar=solved_info.options_metavar,\n        add_help_option=solved_info.add_help_option,\n        hidden=solved_info.hidden,\n        deprecated=solved_info.deprecated,\n        rich_markup_mode=rich_markup_mode,\n        # Rich settings\n        rich_help_panel=solved_info.rich_help_panel,\n        suggest_commands=suggest_commands,\n    )\n    return group\n\n\ndef get_command_name(name: str) -> str:\n    return name.lower().replace(\"_\", \"-\")\n\n\ndef get_params_convertors_ctx_param_name_from_function(\n    callback: Callable[..., Any] | None,\n) -> tuple[list[click.Argument | click.Option], dict[str, Any], str | None]:\n    params = []\n    convertors = {}\n    context_param_name = None\n    if callback:\n        parameters = get_params_from_function(callback)\n        for param_name, param in parameters.items():\n            if lenient_issubclass(param.annotation, click.Context):\n                context_param_name = param_name\n                continue\n            click_param, convertor = get_click_param(param)\n            if convertor:\n                convertors[param_name] = convertor\n            params.append(click_param)\n    return params, convertors, context_param_name\n\n\ndef get_command_from_info(\n    command_info: CommandInfo,\n    *,\n    pretty_exceptions_short: bool,\n    rich_markup_mode: MarkupMode,\n) -> click.Command:\n    assert command_info.callback, \"A command must have a callback function\"\n    name = command_info.name or get_command_name(command_info.callback.__name__)  # ty: ignore\n    use_help = command_info.help\n    if use_help is None:\n        use_help = inspect.getdoc(command_info.callback)\n    else:\n        use_help = inspect.cleandoc(use_help)\n    (\n        params,\n        convertors,\n        context_param_name,\n    ) = get_params_convertors_ctx_param_name_from_function(command_info.callback)\n    cls = command_info.cls or TyperCommand\n    command = cls(\n        name=name,\n        context_settings=command_info.context_settings,\n        callback=get_callback(\n            callback=command_info.callback,\n            params=params,\n            convertors=convertors,\n            context_param_name=context_param_name,\n            pretty_exceptions_short=pretty_exceptions_short,\n        ),\n        params=params,  # type: ignore\n        help=use_help,\n        epilog=command_info.epilog,\n        short_help=command_info.short_help,\n        options_metavar=command_info.options_metavar,\n        add_help_option=command_info.add_help_option,\n        no_args_is_help=command_info.no_args_is_help,\n        hidden=command_info.hidden,\n        deprecated=command_info.deprecated,\n        rich_markup_mode=rich_markup_mode,\n        # Rich settings\n        rich_help_panel=command_info.rich_help_panel,\n    )\n    return command\n\n\ndef determine_type_convertor(type_: Any) -> Callable[[Any], Any] | None:\n    convertor: Callable[[Any], Any] | None = None\n    if lenient_issubclass(type_, Path):\n        convertor = param_path_convertor\n    if lenient_issubclass(type_, Enum):\n        convertor = generate_enum_convertor(type_)\n    return convertor\n\n\ndef param_path_convertor(value: str | None = None) -> Path | None:\n    if value is not None:\n        # allow returning any subclass of Path created by an annotated parser without converting\n        # it back to a Path\n        return value if isinstance(value, Path) else Path(value)\n    return None\n\n\ndef generate_enum_convertor(enum: type[Enum]) -> Callable[[Any], Any]:\n    val_map = {str(val.value): val for val in enum}\n\n    def convertor(value: Any) -> Any:\n        if value is not None:\n            val = str(value)\n            if val in val_map:\n                key = val_map[val]\n                return enum(key)\n\n    return convertor\n\n\ndef generate_list_convertor(\n    convertor: Callable[[Any], Any] | None, default_value: Any | None\n) -> Callable[[Sequence[Any] | None], list[Any] | None]:\n    def internal_convertor(value: Sequence[Any] | None) -> list[Any] | None:\n        if (value is None) or (default_value is None and len(value) == 0):\n            return None\n        return [convertor(v) if convertor else v for v in value]\n\n    return internal_convertor\n\n\ndef generate_tuple_convertor(\n    types: Sequence[Any],\n) -> Callable[[tuple[Any, ...] | None], tuple[Any, ...] | None]:\n    convertors = [determine_type_convertor(type_) for type_ in types]\n\n    def internal_convertor(\n        param_args: tuple[Any, ...] | None,\n    ) -> tuple[Any, ...] | None:\n        if param_args is None:\n            return None\n        return tuple(\n            convertor(arg) if convertor else arg\n            for (convertor, arg) in zip(convertors, param_args, strict=False)\n        )\n\n    return internal_convertor\n\n\ndef get_callback(\n    *,\n    callback: Callable[..., Any] | None = None,\n    params: Sequence[click.Parameter] = [],\n    convertors: dict[str, Callable[[str], Any]] | None = None,\n    context_param_name: str | None = None,\n    pretty_exceptions_short: bool,\n) -> Callable[..., Any] | None:\n    use_convertors = convertors or {}\n    if not callback:\n        return None\n    parameters = get_params_from_function(callback)\n    use_params: dict[str, Any] = {}\n    for param_name in parameters:\n        use_params[param_name] = None\n    for param in params:\n        if param.name:\n            use_params[param.name] = param.default\n\n    def wrapper(**kwargs: Any) -> Any:\n        _rich_traceback_guard = pretty_exceptions_short  # noqa: F841\n        for k, v in kwargs.items():\n            if k in use_convertors:\n                use_params[k] = use_convertors[k](v)\n            else:\n                use_params[k] = v\n        if context_param_name:\n            use_params[context_param_name] = click.get_current_context()\n        return callback(**use_params)\n\n    update_wrapper(wrapper, callback)\n    return wrapper\n\n\ndef get_click_type(\n    *, annotation: Any, parameter_info: ParameterInfo\n) -> click.ParamType:\n    if parameter_info.click_type is not None:\n        return parameter_info.click_type\n\n    elif parameter_info.parser is not None:\n        return click.types.FuncParamType(parameter_info.parser)\n\n    elif annotation is str:\n        return click.STRING\n    elif annotation is int:\n        if parameter_info.min is not None or parameter_info.max is not None:\n            min_ = None\n            max_ = None\n            if parameter_info.min is not None:\n                min_ = int(parameter_info.min)\n            if parameter_info.max is not None:\n                max_ = int(parameter_info.max)\n            return click.IntRange(min=min_, max=max_, clamp=parameter_info.clamp)\n        else:\n            return click.INT\n    elif annotation is float:\n        if parameter_info.min is not None or parameter_info.max is not None:\n            return click.FloatRange(\n                min=parameter_info.min,\n                max=parameter_info.max,\n                clamp=parameter_info.clamp,\n            )\n        else:\n            return click.FLOAT\n    elif annotation is bool:\n        return click.BOOL\n    elif annotation == UUID:\n        return click.UUID\n    elif annotation == datetime:\n        return click.DateTime(formats=parameter_info.formats)\n    elif (\n        annotation == Path\n        or parameter_info.allow_dash\n        or parameter_info.path_type\n        or parameter_info.resolve_path\n    ):\n        return TyperPath(\n            exists=parameter_info.exists,\n            file_okay=parameter_info.file_okay,\n            dir_okay=parameter_info.dir_okay,\n            writable=parameter_info.writable,\n            readable=parameter_info.readable,\n            resolve_path=parameter_info.resolve_path,\n            allow_dash=parameter_info.allow_dash,\n            path_type=parameter_info.path_type,\n        )\n    elif lenient_issubclass(annotation, FileTextWrite):\n        return click.File(\n            mode=parameter_info.mode or \"w\",\n            encoding=parameter_info.encoding,\n            errors=parameter_info.errors,\n            lazy=parameter_info.lazy,\n            atomic=parameter_info.atomic,\n        )\n    elif lenient_issubclass(annotation, FileText):\n        return click.File(\n            mode=parameter_info.mode or \"r\",\n            encoding=parameter_info.encoding,\n            errors=parameter_info.errors,\n            lazy=parameter_info.lazy,\n            atomic=parameter_info.atomic,\n        )\n    elif lenient_issubclass(annotation, FileBinaryRead):\n        return click.File(\n            mode=parameter_info.mode or \"rb\",\n            encoding=parameter_info.encoding,\n            errors=parameter_info.errors,\n            lazy=parameter_info.lazy,\n            atomic=parameter_info.atomic,\n        )\n    elif lenient_issubclass(annotation, FileBinaryWrite):\n        return click.File(\n            mode=parameter_info.mode or \"wb\",\n            encoding=parameter_info.encoding,\n            errors=parameter_info.errors,\n            lazy=parameter_info.lazy,\n            atomic=parameter_info.atomic,\n        )\n    elif lenient_issubclass(annotation, Enum):\n        # The custom TyperChoice is only needed for Click < 8.2.0, to parse the\n        # command line values matching them to the enum values. Click 8.2.0 added\n        # support for enum values but reading enum names.\n        # Passing here the list of enum values (instead of just the enum) accounts for\n        # Click < 8.2.0.\n        return TyperChoice(\n            [item.value for item in annotation],\n            case_sensitive=parameter_info.case_sensitive,\n        )\n    elif is_literal_type(annotation):\n        return click.Choice(\n            literal_values(annotation),\n            case_sensitive=parameter_info.case_sensitive,\n        )\n    raise RuntimeError(f\"Type not yet supported: {annotation}\")  # pragma: no cover\n\n\ndef lenient_issubclass(cls: Any, class_or_tuple: AnyType | tuple[AnyType, ...]) -> bool:\n    return isinstance(cls, type) and issubclass(cls, class_or_tuple)\n\n\ndef get_click_param(\n    param: ParamMeta,\n) -> tuple[click.Argument | click.Option, Any]:\n    # First, find out what will be:\n    # * ParamInfo (ArgumentInfo or OptionInfo)\n    # * default_value\n    # * required\n    default_value = None\n    required = False\n    if isinstance(param.default, ParameterInfo):\n        parameter_info = param.default\n        if parameter_info.default == Required:\n            required = True\n        else:\n            default_value = parameter_info.default\n    elif param.default == Required or param.default is param.empty:\n        required = True\n        parameter_info = ArgumentInfo()\n    else:\n        default_value = param.default\n        parameter_info = OptionInfo()\n    annotation: Any\n    if param.annotation is not param.empty:\n        annotation = param.annotation\n    else:\n        annotation = str\n    main_type = annotation\n    is_list = False\n    is_tuple = False\n    parameter_type: Any = None\n    is_flag = None\n    origin = get_origin(main_type)\n\n    if origin is not None:\n        # Handle SomeType | None and Optional[SomeType]\n        if is_union(origin):\n            types = []\n            for type_ in get_args(main_type):\n                if type_ is NoneType:\n                    continue\n                types.append(type_)\n            assert len(types) == 1, \"Typer Currently doesn't support Union types\"\n            main_type = types[0]\n            origin = get_origin(main_type)\n        # Handle Tuples and Lists\n        if lenient_issubclass(origin, list):\n            main_type = get_args(main_type)[0]\n            assert not get_origin(main_type), (\n                \"List types with complex sub-types are not currently supported\"\n            )\n            is_list = True\n        elif lenient_issubclass(origin, tuple):\n            types = []\n            for type_ in get_args(main_type):\n                assert not get_origin(type_), (\n                    \"Tuple types with complex sub-types are not currently supported\"\n                )\n                types.append(\n                    get_click_type(annotation=type_, parameter_info=parameter_info)\n                )\n            parameter_type = tuple(types)\n            is_tuple = True\n    if parameter_type is None:\n        parameter_type = get_click_type(\n            annotation=main_type, parameter_info=parameter_info\n        )\n    convertor = determine_type_convertor(main_type)\n    if is_list:\n        convertor = generate_list_convertor(\n            convertor=convertor, default_value=default_value\n        )\n    if is_tuple:\n        convertor = generate_tuple_convertor(get_args(main_type))\n    if isinstance(parameter_info, OptionInfo):\n        if main_type is bool:\n            is_flag = True\n            # Click doesn't accept a flag of type bool, only None, and then it sets it\n            # to bool internally\n            parameter_type = None\n        default_option_name = get_command_name(param.name)\n        if is_flag:\n            default_option_declaration = (\n                f\"--{default_option_name}/--no-{default_option_name}\"\n            )\n        else:\n            default_option_declaration = f\"--{default_option_name}\"\n        param_decls = [param.name]\n        if parameter_info.param_decls:\n            param_decls.extend(parameter_info.param_decls)\n        else:\n            param_decls.append(default_option_declaration)\n        return (\n            TyperOption(\n                # Option\n                param_decls=param_decls,\n                show_default=parameter_info.show_default,\n                prompt=parameter_info.prompt,\n                confirmation_prompt=parameter_info.confirmation_prompt,\n                prompt_required=parameter_info.prompt_required,\n                hide_input=parameter_info.hide_input,\n                is_flag=is_flag,\n                multiple=is_list,\n                count=parameter_info.count,\n                allow_from_autoenv=parameter_info.allow_from_autoenv,\n                type=parameter_type,\n                help=parameter_info.help,\n                hidden=parameter_info.hidden,\n                show_choices=parameter_info.show_choices,\n                show_envvar=parameter_info.show_envvar,\n                # Parameter\n                required=required,\n                default=default_value,\n                callback=get_param_callback(\n                    callback=parameter_info.callback, convertor=convertor\n                ),\n                metavar=parameter_info.metavar,\n                expose_value=parameter_info.expose_value,\n                is_eager=parameter_info.is_eager,\n                envvar=parameter_info.envvar,\n                shell_complete=parameter_info.shell_complete,\n                autocompletion=get_param_completion(parameter_info.autocompletion),\n                # Rich settings\n                rich_help_panel=parameter_info.rich_help_panel,\n            ),\n            convertor,\n        )\n    elif isinstance(parameter_info, ArgumentInfo):\n        param_decls = [param.name]\n        nargs = None\n        if is_list:\n            nargs = -1\n        return (\n            TyperArgument(\n                # Argument\n                param_decls=param_decls,\n                type=parameter_type,\n                required=required,\n                nargs=nargs,\n                # TyperArgument\n                show_default=parameter_info.show_default,\n                show_choices=parameter_info.show_choices,\n                show_envvar=parameter_info.show_envvar,\n                help=parameter_info.help,\n                hidden=parameter_info.hidden,\n                # Parameter\n                default=default_value,\n                callback=get_param_callback(\n                    callback=parameter_info.callback, convertor=convertor\n                ),\n                metavar=parameter_info.metavar,\n                expose_value=parameter_info.expose_value,\n                is_eager=parameter_info.is_eager,\n                envvar=parameter_info.envvar,\n                shell_complete=parameter_info.shell_complete,\n                autocompletion=get_param_completion(parameter_info.autocompletion),\n                # Rich settings\n                rich_help_panel=parameter_info.rich_help_panel,\n            ),\n            convertor,\n        )\n    raise AssertionError(\"A click.Parameter should be returned\")  # pragma: no cover\n\n\ndef get_param_callback(\n    *,\n    callback: Callable[..., Any] | None = None,\n    convertor: Callable[..., Any] | None = None,\n) -> Callable[..., Any] | None:\n    if not callback:\n        return None\n    parameters = get_params_from_function(callback)\n    ctx_name = None\n    click_param_name = None\n    value_name = None\n    untyped_names: list[str] = []\n    for param_name, param_sig in parameters.items():\n        if lenient_issubclass(param_sig.annotation, click.Context):\n            ctx_name = param_name\n        elif lenient_issubclass(param_sig.annotation, click.Parameter):\n            click_param_name = param_name\n        else:\n            untyped_names.append(param_name)\n    # Extract value param name first\n    if untyped_names:\n        value_name = untyped_names.pop()\n    # If context and Click param were not typed (old/Click callback style) extract them\n    if untyped_names:\n        if ctx_name is None:\n            ctx_name = untyped_names.pop(0)\n        if click_param_name is None:\n            if untyped_names:\n                click_param_name = untyped_names.pop(0)\n        if untyped_names:\n            raise click.ClickException(\n                \"Too many CLI parameter callback function parameters\"\n            )\n\n    def wrapper(ctx: click.Context, param: click.Parameter, value: Any) -> Any:\n        use_params: dict[str, Any] = {}\n        if ctx_name:\n            use_params[ctx_name] = ctx\n        if click_param_name:\n            use_params[click_param_name] = param\n        if value_name:\n            if convertor:\n                use_value = convertor(value)\n            else:\n                use_value = value\n            use_params[value_name] = use_value\n        return callback(**use_params)\n\n    update_wrapper(wrapper, callback)\n    return wrapper\n\n\ndef get_param_completion(\n    callback: Callable[..., Any] | None = None,\n) -> Callable[..., Any] | None:\n    if not callback:\n        return None\n    parameters = get_params_from_function(callback)\n    ctx_name = None\n    args_name = None\n    incomplete_name = None\n    unassigned_params = list(parameters.values())\n    for param_sig in unassigned_params[:]:\n        origin = get_origin(param_sig.annotation)\n        if lenient_issubclass(param_sig.annotation, click.Context):\n            ctx_name = param_sig.name\n            unassigned_params.remove(param_sig)\n        elif lenient_issubclass(origin, list):\n            args_name = param_sig.name\n            unassigned_params.remove(param_sig)\n        elif lenient_issubclass(param_sig.annotation, str):\n            incomplete_name = param_sig.name\n            unassigned_params.remove(param_sig)\n    # If there are still unassigned parameters (not typed), extract by name\n    for param_sig in unassigned_params[:]:\n        if ctx_name is None and param_sig.name == \"ctx\":\n            ctx_name = param_sig.name\n            unassigned_params.remove(param_sig)\n        elif args_name is None and param_sig.name == \"args\":\n            args_name = param_sig.name\n            unassigned_params.remove(param_sig)\n        elif incomplete_name is None and param_sig.name == \"incomplete\":\n            incomplete_name = param_sig.name\n            unassigned_params.remove(param_sig)\n    # Extract value param name first\n    if unassigned_params:\n        show_params = \" \".join([param.name for param in unassigned_params])\n        raise click.ClickException(\n            f\"Invalid autocompletion callback parameters: {show_params}\"\n        )\n\n    def wrapper(ctx: click.Context, args: list[str], incomplete: str | None) -> Any:\n        use_params: dict[str, Any] = {}\n        if ctx_name:\n            use_params[ctx_name] = ctx\n        if args_name:\n            use_params[args_name] = args\n        if incomplete_name:\n            use_params[incomplete_name] = incomplete\n        return callback(**use_params)\n\n    update_wrapper(wrapper, callback)\n    return wrapper\n\n\ndef run(\n    function: Annotated[\n        Callable[..., Any],\n        Doc(\n            \"\"\"\n            The function that should power this CLI application.\n            \"\"\"\n        ),\n    ],\n) -> None:\n    \"\"\"\n    This function converts a given function to a CLI application with `Typer()` and executes it.\n\n    ## Example\n\n    ```python\n    import typer\n\n    def main(name: str):\n        print(f\"Hello {name}\")\n\n    if __name__ == \"__main__\":\n        typer.run(main)\n    ```\n    \"\"\"\n    app = Typer(add_completion=False)\n    app.command()(function)\n    app()\n\n\ndef _is_macos() -> bool:\n    return platform.system() == \"Darwin\"\n\n\ndef _is_linux_or_bsd() -> bool:\n    if platform.system() == \"Linux\":\n        return True\n\n    return \"BSD\" in platform.system()\n\n\ndef launch(\n    url: Annotated[\n        str,\n        Doc(\n            \"\"\"\n            URL or filename of the thing to launch.\n            \"\"\"\n        ),\n    ],\n    wait: Annotated[\n        bool,\n        Doc(\n            \"\"\"\n            Wait for the program to exit before returning. This only works if the launched program blocks.\n            In particular, `xdg-open` on Linux does not block.\n            \"\"\"\n        ),\n    ] = False,\n    locate: Annotated[\n        bool,\n        Doc(\n            \"\"\"\n            If this is set to `True`, then instead of launching the application associated with the URL, it will attempt to\n            launch a file manager with the file located. This might have weird effects if the URL does not point to the filesystem.\n            \"\"\"\n        ),\n    ] = False,\n) -> int:\n    \"\"\"\n    This function launches the given URL (or filename) in the default\n    viewer application for this file type.  If this is an executable, it\n    might launch the executable in a new session.  The return value is\n    the exit code of the launched application.  Usually, `0` indicates\n    success.\n\n    This function handles url in different operating systems separately:\n     - On macOS (Darwin), it uses the `open` command.\n     - On Linux and BSD, it uses `xdg-open` if available.\n     - On Windows (and other OSes), it uses the standard webbrowser module.\n\n    The function avoids, when possible, using the webbrowser module on Linux and macOS\n    to prevent spammy terminal messages from some browsers (e.g., Chrome).\n\n    ## Examples\n    ```python\n        import typer\n\n        typer.launch(\"https://typer.tiangolo.com/\")\n    ```\n\n    ```python\n        import typer\n\n        typer.launch(\"/my/downloaded/file\", locate=True)\n    ```\n    \"\"\"\n\n    if url.startswith(\"http://\") or url.startswith(\"https://\"):\n        if _is_macos():\n            return subprocess.Popen(\n                [\"open\", url], stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT\n            ).wait()\n\n        has_xdg_open = _is_linux_or_bsd() and shutil.which(\"xdg-open\") is not None\n\n        if has_xdg_open:\n            return subprocess.Popen(\n                [\"xdg-open\", url], stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT\n            ).wait()\n\n        import webbrowser\n\n        webbrowser.open(url)\n\n        return 0\n\n    else:\n        return click.launch(url)\n"
  },
  {
    "path": "typer/models.py",
    "content": "import inspect\nimport io\nfrom collections.abc import Callable, Sequence\nfrom typing import (\n    TYPE_CHECKING,\n    Any,\n    Optional,\n    TypeVar,\n)\n\nimport click\nimport click.shell_completion\n\nif TYPE_CHECKING:  # pragma: no cover\n    from .core import TyperCommand, TyperGroup\n    from .main import Typer\n\n\nNoneType = type(None)\n\nAnyType = type[Any]\n\nRequired = ...\n\n\nclass Context(click.Context):\n    \"\"\"\n    The [`Context`](https://click.palletsprojects.com/en/stable/api/#click.Context) has some additional data about the current execution of your program.\n    When declaring it in a [callback](https://typer.tiangolo.com/tutorial/options/callback-and-context/) function,\n    you can access this additional information.\n    \"\"\"\n\n    pass\n\n\nclass FileText(io.TextIOWrapper):\n    \"\"\"\n    Gives you a file-like object for reading text, and you will get a `str` data from it.\n    The default mode of this class is `mode=\"r\"`.\n\n    **Example**\n\n    ```python\n    from typing import Annotated\n\n    import typer\n\n    app = typer.Typer()\n\n    @app.command()\n    def main(config: Annotated[typer.FileText, typer.Option()]):\n        for line in config:\n            print(f\"Config line: {line}\")\n\n    if __name__ == \"__main__\":\n        app()\n    ```\n    \"\"\"\n\n    pass\n\n\nclass FileTextWrite(FileText):\n    \"\"\"\n    You can use this class for writing text. Alternatively, you can use `FileText` with `mode=\"w\"`.\n    The default mode of this class is `mode=\"w\"`.\n\n    **Example**\n\n    ```python\n    from typing import Annotated\n\n    import typer\n\n    app = typer.Typer()\n\n    @app.command()\n    def main(config: Annotated[typer.FileTextWrite, typer.Option()]):\n        config.write(\"Some config written by the app\")\n        print(\"Config written\")\n\n    if __name__ == \"__main__\":\n        app()\n    ```\n    \"\"\"\n\n    pass\n\n\nclass FileBinaryRead(io.BufferedReader):\n    \"\"\"\n    You can use this class to read binary data, receiving `bytes`.\n    The default mode of this class is `mode=\"rb\"`.\n    It is useful for reading binary files like images:\n\n    **Example**\n\n    ```python\n    from typing import Annotated\n\n    import typer\n\n    app = typer.Typer()\n\n    @app.command()\n    def main(file: Annotated[typer.FileBinaryRead, typer.Option()]):\n        processed_total = 0\n        for bytes_chunk in file:\n            # Process the bytes in bytes_chunk\n            processed_total += len(bytes_chunk)\n            print(f\"Processed bytes total: {processed_total}\")\n\n    if __name__ == \"__main__\":\n        app()\n    ```\n    \"\"\"\n\n    pass\n\n\nclass FileBinaryWrite(io.BufferedWriter):\n    \"\"\"\n    You can use this class to write binary data: you pass `bytes` to it instead of strings.\n    The default mode of this class is `mode=\"wb\"`.\n    It is useful for writing binary files like images:\n\n    **Example**\n\n    ```python\n    from typing import Annotated\n\n    import typer\n\n    app = typer.Typer()\n\n    @app.command()\n    def main(file: Annotated[typer.FileBinaryWrite, typer.Option()]):\n        first_line_str = \"some settings\\\\n\"\n        # You cannot write str directly to a binary file; encode it first\n        first_line_bytes = first_line_str.encode(\"utf-8\")\n        # Then you can write the bytes\n        file.write(first_line_bytes)\n        # This is already bytes, it starts with b\"\n        second_line = b\"la cig\\xc3\\xbce\\xc3\\xb1a trae al ni\\xc3\\xb1o\"\n        file.write(second_line)\n        print(\"Binary file written\")\n\n    if __name__ == \"__main__\":\n        app()\n    ```\n    \"\"\"\n\n    pass\n\n\nclass CallbackParam(click.Parameter):\n    \"\"\"\n    In a callback function, you can declare a function parameter with type `CallbackParam`\n    to access the specific Click [`Parameter`](https://click.palletsprojects.com/en/stable/api/#click.Parameter) object.\n    \"\"\"\n\n    pass\n\n\nclass DefaultPlaceholder:\n    \"\"\"\n    You shouldn't use this class directly.\n\n    It's used internally to recognize when a default value has been overwritten, even\n    if the new value is `None`.\n    \"\"\"\n\n    def __init__(self, value: Any):\n        self.value = value\n\n    def __bool__(self) -> bool:\n        return bool(self.value)\n\n\nDefaultType = TypeVar(\"DefaultType\")\n\nCommandFunctionType = TypeVar(\"CommandFunctionType\", bound=Callable[..., Any])\n\n\ndef Default(value: DefaultType) -> DefaultType:\n    \"\"\"\n    You shouldn't use this function directly.\n\n    It's used internally to recognize when a default value has been overwritten, even\n    if the new value is `None`.\n    \"\"\"\n    return DefaultPlaceholder(value)  # type: ignore\n\n\nclass CommandInfo:\n    def __init__(\n        self,\n        name: str | None = None,\n        *,\n        cls: type[\"TyperCommand\"] | None = None,\n        context_settings: dict[Any, Any] | None = None,\n        callback: Callable[..., Any] | None = None,\n        help: str | None = None,\n        epilog: str | None = None,\n        short_help: str | None = None,\n        options_metavar: str = \"[OPTIONS]\",\n        add_help_option: bool = True,\n        no_args_is_help: bool = False,\n        hidden: bool = False,\n        deprecated: bool = False,\n        # Rich settings\n        rich_help_panel: str | None = None,\n    ):\n        self.name = name\n        self.cls = cls\n        self.context_settings = context_settings\n        self.callback = callback\n        self.help = help\n        self.epilog = epilog\n        self.short_help = short_help\n        self.options_metavar = options_metavar\n        self.add_help_option = add_help_option\n        self.no_args_is_help = no_args_is_help\n        self.hidden = hidden\n        self.deprecated = deprecated\n        # Rich settings\n        self.rich_help_panel = rich_help_panel\n\n\nclass TyperInfo:\n    def __init__(\n        self,\n        typer_instance: Optional[\"Typer\"] = Default(None),\n        *,\n        name: str | None = Default(None),\n        cls: type[\"TyperGroup\"] | None = Default(None),\n        invoke_without_command: bool = Default(False),\n        no_args_is_help: bool = Default(False),\n        subcommand_metavar: str | None = Default(None),\n        chain: bool = Default(False),\n        result_callback: Callable[..., Any] | None = Default(None),\n        # Command\n        context_settings: dict[Any, Any] | None = Default(None),\n        callback: Callable[..., Any] | None = Default(None),\n        help: str | None = Default(None),\n        epilog: str | None = Default(None),\n        short_help: str | None = Default(None),\n        options_metavar: str = Default(\"[OPTIONS]\"),\n        add_help_option: bool = Default(True),\n        hidden: bool = Default(False),\n        deprecated: bool = Default(False),\n        # Rich settings\n        rich_help_panel: str | None = Default(None),\n    ):\n        self.typer_instance = typer_instance\n        self.name = name\n        self.cls = cls\n        self.invoke_without_command = invoke_without_command\n        self.no_args_is_help = no_args_is_help\n        self.subcommand_metavar = subcommand_metavar\n        self.chain = chain\n        self.result_callback = result_callback\n        self.context_settings = context_settings\n        self.callback = callback\n        self.help = help\n        self.epilog = epilog\n        self.short_help = short_help\n        self.options_metavar = options_metavar\n        self.add_help_option = add_help_option\n        self.hidden = hidden\n        self.deprecated = deprecated\n        self.rich_help_panel = rich_help_panel\n\n\nclass ParameterInfo:\n    def __init__(\n        self,\n        *,\n        default: Any | None = None,\n        param_decls: Sequence[str] | None = None,\n        callback: Callable[..., Any] | None = None,\n        metavar: str | None = None,\n        expose_value: bool = True,\n        is_eager: bool = False,\n        envvar: str | list[str] | None = None,\n        # Note that shell_complete is not fully supported and will be removed in future versions\n        # TODO: Remove shell_complete in a future version (after 0.16.0)\n        shell_complete: Callable[\n            [click.Context, click.Parameter, str],\n            list[\"click.shell_completion.CompletionItem\"] | list[str],\n        ]\n        | None = None,\n        autocompletion: Callable[..., Any] | None = None,\n        default_factory: Callable[[], Any] | None = None,\n        # Custom type\n        parser: Callable[[str], Any] | None = None,\n        click_type: click.ParamType | None = None,\n        # TyperArgument\n        show_default: bool | str = True,\n        show_choices: bool = True,\n        show_envvar: bool = True,\n        help: str | None = None,\n        hidden: bool = False,\n        # Choice\n        case_sensitive: bool = True,\n        # Numbers\n        min: int | float | None = None,\n        max: int | float | None = None,\n        clamp: bool = False,\n        # DateTime\n        formats: list[str] | None = None,\n        # File\n        mode: str | None = None,\n        encoding: str | None = None,\n        errors: str | None = \"strict\",\n        lazy: bool | None = None,\n        atomic: bool = False,\n        # Path\n        exists: bool = False,\n        file_okay: bool = True,\n        dir_okay: bool = True,\n        writable: bool = False,\n        readable: bool = True,\n        resolve_path: bool = False,\n        allow_dash: bool = False,\n        path_type: None | type[str] | type[bytes] = None,\n        # Rich settings\n        rich_help_panel: str | None = None,\n    ):\n        # Check if user has provided multiple custom parsers\n        if parser and click_type:\n            raise ValueError(\n                \"Multiple custom type parsers provided. \"\n                \"`parser` and `click_type` may not both be provided.\"\n            )\n\n        self.default = default\n        self.param_decls = param_decls\n        self.callback = callback\n        self.metavar = metavar\n        self.expose_value = expose_value\n        self.is_eager = is_eager\n        self.envvar = envvar\n        self.shell_complete = shell_complete\n        self.autocompletion = autocompletion\n        self.default_factory = default_factory\n        # Custom type\n        self.parser = parser\n        self.click_type = click_type\n        # TyperArgument\n        self.show_default = show_default\n        self.show_choices = show_choices\n        self.show_envvar = show_envvar\n        self.help = help\n        self.hidden = hidden\n        # Choice\n        self.case_sensitive = case_sensitive\n        # Numbers\n        self.min = min\n        self.max = max\n        self.clamp = clamp\n        # DateTime\n        self.formats = formats\n        # File\n        self.mode = mode\n        self.encoding = encoding\n        self.errors = errors\n        self.lazy = lazy\n        self.atomic = atomic\n        # Path\n        self.exists = exists\n        self.file_okay = file_okay\n        self.dir_okay = dir_okay\n        self.writable = writable\n        self.readable = readable\n        self.resolve_path = resolve_path\n        self.allow_dash = allow_dash\n        self.path_type = path_type\n        # Rich settings\n        self.rich_help_panel = rich_help_panel\n\n\nclass OptionInfo(ParameterInfo):\n    def __init__(\n        self,\n        *,\n        # ParameterInfo\n        default: Any | None = None,\n        param_decls: Sequence[str] | None = None,\n        callback: Callable[..., Any] | None = None,\n        metavar: str | None = None,\n        expose_value: bool = True,\n        is_eager: bool = False,\n        envvar: str | list[str] | None = None,\n        # Note that shell_complete is not fully supported and will be removed in future versions\n        # TODO: Remove shell_complete in a future version (after 0.16.0)\n        shell_complete: Callable[\n            [click.Context, click.Parameter, str],\n            list[\"click.shell_completion.CompletionItem\"] | list[str],\n        ]\n        | None = None,\n        autocompletion: Callable[..., Any] | None = None,\n        default_factory: Callable[[], Any] | None = None,\n        # Custom type\n        parser: Callable[[str], Any] | None = None,\n        click_type: click.ParamType | None = None,\n        # Option\n        show_default: bool | str = True,\n        prompt: bool | str = False,\n        confirmation_prompt: bool = False,\n        prompt_required: bool = True,\n        hide_input: bool = False,\n        # TODO: remove is_flag and flag_value in a future release\n        is_flag: bool | None = None,\n        flag_value: Any | None = None,\n        count: bool = False,\n        allow_from_autoenv: bool = True,\n        help: str | None = None,\n        hidden: bool = False,\n        show_choices: bool = True,\n        show_envvar: bool = True,\n        # Choice\n        case_sensitive: bool = True,\n        # Numbers\n        min: int | float | None = None,\n        max: int | float | None = None,\n        clamp: bool = False,\n        # DateTime\n        formats: list[str] | None = None,\n        # File\n        mode: str | None = None,\n        encoding: str | None = None,\n        errors: str | None = \"strict\",\n        lazy: bool | None = None,\n        atomic: bool = False,\n        # Path\n        exists: bool = False,\n        file_okay: bool = True,\n        dir_okay: bool = True,\n        writable: bool = False,\n        readable: bool = True,\n        resolve_path: bool = False,\n        allow_dash: bool = False,\n        path_type: None | type[str] | type[bytes] = None,\n        # Rich settings\n        rich_help_panel: str | None = None,\n    ):\n        super().__init__(\n            default=default,\n            param_decls=param_decls,\n            callback=callback,\n            metavar=metavar,\n            expose_value=expose_value,\n            is_eager=is_eager,\n            envvar=envvar,\n            shell_complete=shell_complete,\n            autocompletion=autocompletion,\n            default_factory=default_factory,\n            # Custom type\n            parser=parser,\n            click_type=click_type,\n            # TyperArgument\n            show_default=show_default,\n            show_choices=show_choices,\n            show_envvar=show_envvar,\n            help=help,\n            hidden=hidden,\n            # Choice\n            case_sensitive=case_sensitive,\n            # Numbers\n            min=min,\n            max=max,\n            clamp=clamp,\n            # DateTime\n            formats=formats,\n            # File\n            mode=mode,\n            encoding=encoding,\n            errors=errors,\n            lazy=lazy,\n            atomic=atomic,\n            # Path\n            exists=exists,\n            file_okay=file_okay,\n            dir_okay=dir_okay,\n            writable=writable,\n            readable=readable,\n            resolve_path=resolve_path,\n            allow_dash=allow_dash,\n            path_type=path_type,\n            # Rich settings\n            rich_help_panel=rich_help_panel,\n        )\n        if is_flag is not None or flag_value is not None:\n            import warnings\n\n            warnings.warn(\n                \"The 'is_flag' and 'flag_value' parameters are not supported by Typer \"\n                \"and will be removed entirely in a future release.\",\n                DeprecationWarning,\n                stacklevel=2,\n            )\n        self.prompt = prompt\n        self.confirmation_prompt = confirmation_prompt\n        self.prompt_required = prompt_required\n        self.hide_input = hide_input\n        self.count = count\n        self.allow_from_autoenv = allow_from_autoenv\n\n\nclass ArgumentInfo(ParameterInfo):\n    def __init__(\n        self,\n        *,\n        # ParameterInfo\n        default: Any | None = None,\n        param_decls: Sequence[str] | None = None,\n        callback: Callable[..., Any] | None = None,\n        metavar: str | None = None,\n        expose_value: bool = True,\n        is_eager: bool = False,\n        envvar: str | list[str] | None = None,\n        # Note that shell_complete is not fully supported and will be removed in future versions\n        # TODO: Remove shell_complete in a future version (after 0.16.0)\n        shell_complete: Callable[\n            [click.Context, click.Parameter, str],\n            list[\"click.shell_completion.CompletionItem\"] | list[str],\n        ]\n        | None = None,\n        autocompletion: Callable[..., Any] | None = None,\n        default_factory: Callable[[], Any] | None = None,\n        # Custom type\n        parser: Callable[[str], Any] | None = None,\n        click_type: click.ParamType | None = None,\n        # TyperArgument\n        show_default: bool | str = True,\n        show_choices: bool = True,\n        show_envvar: bool = True,\n        help: str | None = None,\n        hidden: bool = False,\n        # Choice\n        case_sensitive: bool = True,\n        # Numbers\n        min: int | float | None = None,\n        max: int | float | None = None,\n        clamp: bool = False,\n        # DateTime\n        formats: list[str] | None = None,\n        # File\n        mode: str | None = None,\n        encoding: str | None = None,\n        errors: str | None = \"strict\",\n        lazy: bool | None = None,\n        atomic: bool = False,\n        # Path\n        exists: bool = False,\n        file_okay: bool = True,\n        dir_okay: bool = True,\n        writable: bool = False,\n        readable: bool = True,\n        resolve_path: bool = False,\n        allow_dash: bool = False,\n        path_type: None | type[str] | type[bytes] = None,\n        # Rich settings\n        rich_help_panel: str | None = None,\n    ):\n        super().__init__(\n            default=default,\n            param_decls=param_decls,\n            callback=callback,\n            metavar=metavar,\n            expose_value=expose_value,\n            is_eager=is_eager,\n            envvar=envvar,\n            shell_complete=shell_complete,\n            autocompletion=autocompletion,\n            default_factory=default_factory,\n            # Custom type\n            parser=parser,\n            click_type=click_type,\n            # TyperArgument\n            show_default=show_default,\n            show_choices=show_choices,\n            show_envvar=show_envvar,\n            help=help,\n            hidden=hidden,\n            # Choice\n            case_sensitive=case_sensitive,\n            # Numbers\n            min=min,\n            max=max,\n            clamp=clamp,\n            # DateTime\n            formats=formats,\n            # File\n            mode=mode,\n            encoding=encoding,\n            errors=errors,\n            lazy=lazy,\n            atomic=atomic,\n            # Path\n            exists=exists,\n            file_okay=file_okay,\n            dir_okay=dir_okay,\n            writable=writable,\n            readable=readable,\n            resolve_path=resolve_path,\n            allow_dash=allow_dash,\n            path_type=path_type,\n            # Rich settings\n            rich_help_panel=rich_help_panel,\n        )\n\n\nclass ParamMeta:\n    empty = inspect.Parameter.empty\n\n    def __init__(\n        self,\n        *,\n        name: str,\n        default: Any = inspect.Parameter.empty,\n        annotation: Any = inspect.Parameter.empty,\n    ) -> None:\n        self.name = name\n        self.default = default\n        self.annotation = annotation\n\n\nclass DeveloperExceptionConfig:\n    def __init__(\n        self,\n        *,\n        pretty_exceptions_enable: bool = True,\n        pretty_exceptions_show_locals: bool = True,\n        pretty_exceptions_short: bool = True,\n    ) -> None:\n        self.pretty_exceptions_enable = pretty_exceptions_enable\n        self.pretty_exceptions_show_locals = pretty_exceptions_show_locals\n        self.pretty_exceptions_short = pretty_exceptions_short\n\n\nclass TyperPath(click.Path):\n    # Overwrite Click's behaviour to be compatible with Typer's autocompletion system\n    def shell_complete(\n        self, ctx: click.Context, param: click.Parameter, incomplete: str\n    ) -> list[click.shell_completion.CompletionItem]:\n        \"\"\"Return an empty list so that the autocompletion functionality\n        will work properly from the commandline.\n        \"\"\"\n        return []\n"
  },
  {
    "path": "typer/params.py",
    "content": "from collections.abc import Callable\nfrom typing import TYPE_CHECKING, Annotated, Any, overload\n\nimport click\nfrom annotated_doc import Doc\n\nfrom .models import ArgumentInfo, OptionInfo\n\nif TYPE_CHECKING:  # pragma: no cover\n    import click.shell_completion\n\n\n# Overload for Option created with custom type 'parser'\n@overload\ndef Option(\n    # Parameter\n    default: Any | None = ...,\n    *param_decls: str,\n    callback: Callable[..., Any] | None = None,\n    metavar: str | None = None,\n    expose_value: bool = True,\n    is_eager: bool = False,\n    envvar: str | list[str] | None = None,\n    # Note that shell_complete is not fully supported and will be removed in future versions\n    # TODO: Remove shell_complete in a future version (after 0.16.0)\n    shell_complete: Callable[\n        [click.Context, click.Parameter, str],\n        list[\"click.shell_completion.CompletionItem\"] | list[str],\n    ]\n    | None = None,\n    autocompletion: Callable[..., Any] | None = None,\n    default_factory: Callable[[], Any] | None = None,\n    # Custom type\n    parser: Callable[[str], Any] | None = None,\n    # Option\n    show_default: bool | str = True,\n    prompt: bool | str = False,\n    confirmation_prompt: bool = False,\n    prompt_required: bool = True,\n    hide_input: bool = False,\n    # TODO: remove is_flag and flag_value in a future release\n    is_flag: bool | None = None,\n    flag_value: Any | None = None,\n    count: bool = False,\n    allow_from_autoenv: bool = True,\n    help: str | None = None,\n    hidden: bool = False,\n    show_choices: bool = True,\n    show_envvar: bool = True,\n    # Choice\n    case_sensitive: bool = True,\n    # Numbers\n    min: int | float | None = None,\n    max: int | float | None = None,\n    clamp: bool = False,\n    # DateTime\n    formats: list[str] | None = None,\n    # File\n    mode: str | None = None,\n    encoding: str | None = None,\n    errors: str | None = \"strict\",\n    lazy: bool | None = None,\n    atomic: bool = False,\n    # Path\n    exists: bool = False,\n    file_okay: bool = True,\n    dir_okay: bool = True,\n    writable: bool = False,\n    readable: bool = True,\n    resolve_path: bool = False,\n    allow_dash: bool = False,\n    path_type: None | type[str] | type[bytes] = None,\n    # Rich settings\n    rich_help_panel: str | None = None,\n) -> Any: ...\n\n\n# Overload for Option created with custom type 'click_type'\n@overload\ndef Option(\n    # Parameter\n    default: Any | None = ...,\n    *param_decls: str,\n    callback: Callable[..., Any] | None = None,\n    metavar: str | None = None,\n    expose_value: bool = True,\n    is_eager: bool = False,\n    envvar: str | list[str] | None = None,\n    # Note that shell_complete is not fully supported and will be removed in future versions\n    # TODO: Remove shell_complete in a future version (after 0.16.0)\n    shell_complete: Callable[\n        [click.Context, click.Parameter, str],\n        list[\"click.shell_completion.CompletionItem\"] | list[str],\n    ]\n    | None = None,\n    autocompletion: Callable[..., Any] | None = None,\n    default_factory: Callable[[], Any] | None = None,\n    # Custom type\n    click_type: click.ParamType | None = None,\n    # Option\n    show_default: bool | str = True,\n    prompt: bool | str = False,\n    confirmation_prompt: bool = False,\n    prompt_required: bool = True,\n    hide_input: bool = False,\n    # TODO: remove is_flag and flag_value in a future release\n    is_flag: bool | None = None,\n    flag_value: Any | None = None,\n    count: bool = False,\n    allow_from_autoenv: bool = True,\n    help: str | None = None,\n    hidden: bool = False,\n    show_choices: bool = True,\n    show_envvar: bool = True,\n    # Choice\n    case_sensitive: bool = True,\n    # Numbers\n    min: int | float | None = None,\n    max: int | float | None = None,\n    clamp: bool = False,\n    # DateTime\n    formats: list[str] | None = None,\n    # File\n    mode: str | None = None,\n    encoding: str | None = None,\n    errors: str | None = \"strict\",\n    lazy: bool | None = None,\n    atomic: bool = False,\n    # Path\n    exists: bool = False,\n    file_okay: bool = True,\n    dir_okay: bool = True,\n    writable: bool = False,\n    readable: bool = True,\n    resolve_path: bool = False,\n    allow_dash: bool = False,\n    path_type: None | type[str] | type[bytes] = None,\n    # Rich settings\n    rich_help_panel: str | None = None,\n) -> Any: ...\n\n\ndef Option(\n    # Parameter\n    default: Annotated[\n        Any | None,\n        Doc(\n            \"\"\"\n            Usually, [CLI options](https://typer.tiangolo.com/tutorial/options/) are optional and have a default value, passed on like this:\n\n            **Example**\n\n            ```python\n            @app.command()\n            def main(network: str = typer.Option(\"CNN\")):\n                print(f\"Training neural network of type: {network}\")\n            ```\n\n            Note that this usage is deprecated, and we recommend to use `Annotated` instead:\n            ```\n            @app.command()\n            def main(network: Annotated[str, typer.Option()] = \"CNN\"):\n                print(f\"Hello {name}!\")\n            ```\n\n            You can also use `...` ([Ellipsis](https://docs.python.org/3/library/constants.html#Ellipsis)) as the \"default\" value to clarify that this is a required CLI option.\n            \"\"\"\n        ),\n    ] = ...,\n    *param_decls: Annotated[\n        str,\n        Doc(\n            \"\"\"\n            Positional argument that defines how users can call this option on the command line. This may be one or multiple aliases, all strings.\n            If not defined, Typer will automatically use the function parameter as default name.\n            See [the tutorial about CLI Option Names](https://typer.tiangolo.com/tutorial/options/name/) for more details.\n\n            **Example**\n\n            ```python\n            @app.command()\n            def main(user_name: Annotated[str, typer.Option(\"--user\", \"-u\", \"-x\")]):\n                print(f\"Hello {user_name}\")\n            ```\n            \"\"\"\n        ),\n    ],\n    callback: Annotated[\n        Callable[..., Any] | None,\n        Doc(\n            \"\"\"\n            Add a callback to this CLI Option, to execute additional logic after its value was received from the terminal.\n            See [the tutorial about callbacks](https://typer.tiangolo.com/tutorial/options/callback-and-context/) for more details.\n\n            **Example**\n\n            ```python\n            def name_callback(value: str):\n                if value != \"Deadpool\":\n                    raise typer.BadParameter(\"Only Deadpool is allowed\")\n                return value\n\n            @app.command()\n            def main(name: Annotated[str, typer.Option(callback=name_callback)]):\n                print(f\"Hello {name}\")\n            ```\n            \"\"\"\n        ),\n    ] = None,\n    metavar: Annotated[\n        str | None,\n        Doc(\n            \"\"\"\n            Customize the name displayed in the [help text](https://typer.tiangolo.com/tutorial/options/help/) to represent this CLI option.\n            Note that this doesn't influence the way the option must be called.\n\n            **Example**\n\n            ```python\n            @app.command()\n            def main(user: Annotated[str, typer.Option(metavar=\"User name\")]):\n                print(f\"Hello {user}\")\n            ```\n            \"\"\"\n        ),\n    ] = None,\n    expose_value: Annotated[\n        bool,\n        Doc(\n            \"\"\"\n            **Note**: you probably shouldn't use this parameter, it is inherited from Click and supported for compatibility.\n\n            ---\n\n            If this is `True` then the value is passed onwards to the command callback and stored on the context, otherwise it’s skipped.\n            \"\"\"\n        ),\n    ] = True,\n    is_eager: Annotated[\n        bool,\n        Doc(\n            \"\"\"\n            Mark a CLI Option to be \"eager\", ensuring it gets processed before other CLI parameters. This could be relevant when there are other parameters with callbacks that could exit the program early.\n            For more information and an extended example, see the documentation [here](https://typer.tiangolo.com/tutorial/options/version/#fix-with-is_eager).\n            \"\"\"\n        ),\n    ] = False,\n    envvar: Annotated[\n        str | list[str] | None,\n        Doc(\n            \"\"\"\n            Configure a CLI Option to read its value from an environment variable if it is not provided in the command line.\n            For more information, see the [documentation on Environment Variables](https://typer.tiangolo.com/tutorial/arguments/envvar/).\n\n            **Example**\n\n            ```python\n            @app.command()\n            def main(user: Annotated[str, typer.Option(envvar=\"ME\")]):\n                print(f\"Hello {user}\")\n            ```\n            \"\"\"\n        ),\n    ] = None,\n    # TODO: Remove shell_complete in a future version (after 0.16.0)\n    shell_complete: Annotated[\n        Callable[\n            [click.Context, click.Parameter, str],\n            list[\"click.shell_completion.CompletionItem\"] | list[str],\n        ]\n        | None,\n        Doc(\n            \"\"\"\n            **Note**: you probably shouldn't use this parameter, it is inherited from Click and supported for compatibility.\n            It is however not fully functional, and will likely be removed in future versions.\n            \"\"\"\n        ),\n    ] = None,\n    autocompletion: Annotated[\n        Callable[..., Any] | None,\n        Doc(\n            \"\"\"\n            Provide a custom function that helps to autocomplete the values of this CLI Option.\n            See [the tutorial on parameter autocompletion](https://typer.tiangolo.com/tutorial/options-autocompletion) for more details.\n\n            **Example**\n\n            ```python\n            def complete():\n                return [\"Me\", \"Myself\", \"I\"]\n\n            @app.command()\n            def main(name: Annotated[str, typer.Option(autocompletion=complete)]):\n                print(f\"Hello {name}\")\n            ```\n            \"\"\"\n        ),\n    ] = None,\n    default_factory: Annotated[\n        Callable[[], Any] | None,\n        Doc(\n            \"\"\"\n            Provide a custom function that dynamically generates a [default](https://typer.tiangolo.com/tutorial/arguments/default) for this CLI Option.\n\n            **Example**\n\n            ```python\n            def get_name():\n                return random.choice([\"Me\", \"Myself\", \"I\"])\n\n            @app.command()\n            def main(name: Annotated[str, typer.Option(default_factory=get_name)]):\n                print(f\"Hello {name}\")\n            ```\n            \"\"\"\n        ),\n    ] = None,\n    # Custom type\n    parser: Annotated[\n        Callable[[str], Any] | None,\n        Doc(\n            \"\"\"\n            Use your own custom types in Typer applications by defining a `parser` function that parses input into your own types:\n\n            **Example**\n\n            ```python\n            class CustomClass:\n                def __init__(self, value: str):\n                    self.value = value\n\n                def __str__(self):\n                    return f\"<CustomClass: value={self.value}>\"\n\n            def my_parser(value: str):\n                return CustomClass(value * 2)\n\n            @app.command()\n            def main(opt: Annotated[CustomClass, typer.Option(parser=my_parser)] = \"Foo\"):\n                print(f\"--opt is {opt}\")\n            ```\n            \"\"\"\n        ),\n    ] = None,\n    click_type: Annotated[\n        click.ParamType | None,\n        Doc(\n            \"\"\"\n            Define this parameter to use a [custom Click type](https://click.palletsprojects.com/en/stable/parameters/#implementing-custom-types) in your Typer applications.\n\n            **Example**\n\n            ```python\n            class MyClass:\n                def __init__(self, value: str):\n                    self.value = value\n\n                def __str__(self):\n                    return f\"<MyClass: value={self.value}>\"\n\n            class MyParser(click.ParamType):\n                name = \"MyClass\"\n\n                def convert(self, value, param, ctx):\n                    return MyClass(value * 3)\n\n            @app.command()\n            def main(opt: Annotated[MyClass, typer.Option(click_type=MyParser())] = \"Foo\"):\n                print(f\"--opt is {opt}\")\n            ```\n            \"\"\"\n        ),\n    ] = None,\n    # Option\n    show_default: Annotated[\n        bool | str,\n        Doc(\n            \"\"\"\n            When set to `False`, don't show the default value of this CLI Option in the [help text](https://typer.tiangolo.com/tutorial/options/help/).\n\n            **Example**\n\n            ```python\n            @app.command()\n            def main(name: Annotated[str, typer.Option(show_default=False)] = \"Rick\"):\n                print(f\"Hello {name}\")\n            ```\n            \"\"\"\n        ),\n    ] = True,\n    prompt: Annotated[\n        bool | str,\n        Doc(\n            \"\"\"\n            When set to `True`, a prompt will appear to ask for the value of this CLI Option if it was not provided:\n\n            **Example**\n\n            ```python\n            @app.command()\n            def main(name: str, lastname: Annotated[str, typer.Option(prompt=True)]):\n                print(f\"Hello {name} {lastname}\")\n            ```\n            \"\"\"\n        ),\n    ] = False,\n    confirmation_prompt: Annotated[\n        bool,\n        Doc(\n            \"\"\"\n            When set to `True`, a user will need to type a prompted value twice (may be useful for passwords etc.).\n\n            **Example**\n\n            ```python\n            @app.command()\n            def main(project: Annotated[str, typer.Option(prompt=True, confirmation_prompt=True)]):\n                print(f\"Deleting project {project}\")\n            ```\n            \"\"\"\n        ),\n    ] = False,\n    prompt_required: Annotated[\n        bool,\n        Doc(\n            \"\"\"\n            **Note**: you probably shouldn't use this parameter, it is inherited from Click and supported for compatibility.\n\n            ---\n\n            If this is `False` then a prompt is only shown if the option's flag is given without a value.\n            \"\"\"\n        ),\n    ] = True,\n    hide_input: Annotated[\n        bool,\n        Doc(\n            \"\"\"\n            When you've configured a prompt, for instance for [querying a password](https://typer.tiangolo.com/tutorial/options/password/),\n            don't show anything on the screen while the user is typing the value.\n\n            **Example**\n\n            ```python\n            @app.command()\n            def login(\n                name: str,\n                password: Annotated[str, typer.Option(prompt=True, hide_input=True)],\n            ):\n                print(f\"Hello {name}. Doing something very secure with password.\")\n            ```\n            \"\"\"\n        ),\n    ] = False,\n    # TODO: remove is_flag and flag_value in a future release\n    is_flag: Annotated[\n        bool | None,\n        Doc(\n            \"\"\"\n            **Note**: you probably shouldn't use this parameter, it is inherited from Click and supported for compatibility.\n            It is however not fully functional, and will likely be removed in future versions.\n            \"\"\"\n        ),\n    ] = None,\n    flag_value: Annotated[\n        Any | None,\n        Doc(\n            \"\"\"\n            **Note**: you probably shouldn't use this parameter, it is inherited from Click and supported for compatibility.\n            It is however not fully functional, and will likely be removed in future versions.\n            \"\"\"\n        ),\n    ] = None,\n    count: Annotated[\n        bool,\n        Doc(\n            \"\"\"\n            Make a CLI Option work as a [counter](https://typer.tiangolo.com/tutorial/parameter-types/number/#counter-cli-options).\n            The CLI option will have the `int` value representing the number of times the option was used on the command line.\n\n            **Example**\n\n            ```python\n            @app.command()\n            def main(verbose: Annotated[int, typer.Option(\"--verbose\", \"-v\", count=True)] = 0):\n                print(f\"Verbose level is {verbose}\")\n            ```\n            \"\"\"\n        ),\n    ] = False,\n    allow_from_autoenv: Annotated[\n        bool,\n        Doc(\n            \"\"\"\n            **Note**: you probably shouldn't use this parameter, it is inherited from Click and supported for compatibility.\n\n            ---\n\n            If this is enabled then the value of this parameter will be pulled from an environment variable in case a prefix is defined on the context.\n            \"\"\"\n        ),\n    ] = True,\n    help: Annotated[\n        str | None,\n        Doc(\n            \"\"\"\n            Help text for this CLI Option.\n            See [the tutorial about CLI Options with help](https://typer.tiangolo.com/tutorial/options/help/) for more dedails.\n\n            **Example**\n\n            ```python\n            @app.command()\n            def greet(name: Annotated[str, typer.Option(help=\"Person to greet\")] = \"Deadpool\"):\n                print(f\"Hello {name}\")\n            ```\n            \"\"\"\n        ),\n    ] = None,\n    hidden: Annotated[\n        bool,\n        Doc(\n            \"\"\"\n            Hide this CLI Option from [help outputs](https://typer.tiangolo.com/tutorial/options/help). `False` by default.\n\n            **Example**\n\n            ```python\n            @app.command()\n            def greet(name: Annotated[str, typer.Option(hidden=True)] = \"Deadpool\"):\n                print(f\"Hello {name}\")\n            ```\n            \"\"\"\n        ),\n    ] = False,\n    show_choices: Annotated[\n        bool,\n        Doc(\n            \"\"\"\n            **Note**: you probably shouldn't use this parameter, it is inherited from Click and supported for compatibility.\n\n            ---\n\n            When set to `False`, this suppresses choices from being displayed inline when `prompt` is used.\n            \"\"\"\n        ),\n    ] = True,\n    show_envvar: Annotated[\n        bool,\n        Doc(\n            \"\"\"\n            When an [\"envvar\"](https://typer.tiangolo.com/tutorial/arguments/envvar) is defined, prevent it from showing up in the help text:\n\n            **Example**\n\n            ```python\n            @app.command()\n            def main(user: Annotated[str, typer.Option(envvar=\"ME\", show_envvar=False)]):\n                print(f\"Hello {user}\")\n            ```\n            \"\"\"\n        ),\n    ] = True,\n    # Choice\n    case_sensitive: Annotated[\n        bool,\n        Doc(\n            \"\"\"\n            For a CLI Option representing an [Enum (choice)](https://typer.tiangolo.com/tutorial/parameter-types/enum),\n            you can allow case-insensitive matching with this parameter:\n\n            **Example**\n\n            ```python\n            from enum import Enum\n\n            class NeuralNetwork(str, Enum):\n                simple = \"simple\"\n                conv = \"conv\"\n                lstm = \"lstm\"\n\n            @app.command()\n            def main(\n                network: Annotated[NeuralNetwork, typer.Option(case_sensitive=False)]):\n                print(f\"Training neural network of type: {network.value}\")\n            ```\n\n            With this setting, \"LSTM\" or \"lstm\" will both be valid values that will be resolved to `NeuralNetwork.lstm`.\n            \"\"\"\n        ),\n    ] = True,\n    # Numbers\n    min: Annotated[\n        int | float | None,\n        Doc(\n            \"\"\"\n            For a CLI Option representing a [number](https://typer.tiangolo.com/tutorial/parameter-types/number/) (`int` or `float`),\n            you can define numeric validations with `min` and `max` values:\n\n            **Example**\n\n            ```python\n            @app.command()\n            def main(\n                user: Annotated[str, typer.Argument()],\n                user_id: Annotated[int, typer.Option(min=1, max=1000)],\n            ):\n                print(f\"ID for {user} is {user_id}\")\n            ```\n\n            If the user attempts to input an invalid number, an error will be shown, explaining why the value is invalid.\n            \"\"\"\n        ),\n    ] = None,\n    max: Annotated[\n        int | float | None,\n        Doc(\n            \"\"\"\n            For a CLI Option representing a [number](https://typer.tiangolo.com/tutorial/parameter-types/number/) (`int` or `float`),\n            you can define numeric validations with `min` and `max` values:\n\n            **Example**\n\n            ```python\n            @app.command()\n            def main(\n                user: Annotated[str, typer.Argument()],\n                user_id: Annotated[int, typer.Option(min=1, max=1000)],\n            ):\n                print(f\"ID for {user} is {user_id}\")\n            ```\n\n            If the user attempts to input an invalid number, an error will be shown, explaining why the value is invalid.\n            \"\"\"\n        ),\n    ] = None,\n    clamp: Annotated[\n        bool,\n        Doc(\n            \"\"\"\n            For a CLI Option representing a [number](https://typer.tiangolo.com/tutorial/parameter-types/number/) and that is bounded by using `min` and/or `max`,\n            you can opt to use the closest minimum or maximum value instead of raising an error when the value is out of bounds. This is done by setting `clamp` to `True`.\n\n            **Example**\n\n            ```python\n            @app.command()\n            def main(\n                user: Annotated[str, typer.Argument()],\n                user_id: Annotated[int, typer.Option(min=1, max=1000, clamp=True)],\n            ):\n                print(f\"ID for {user} is {user_id}\")\n            ```\n\n            If the user attempts to input 3420 for `user_id`, this will internally be converted to `1000`.\n            \"\"\"\n        ),\n    ] = False,\n    # DateTime\n    formats: Annotated[\n        list[str] | None,\n        Doc(\n            \"\"\"\n            For a CLI Option representing a [DateTime object](https://typer.tiangolo.com/tutorial/parameter-types/datetime),\n            you can customize the formats that can be parsed automatically:\n\n            **Example**\n\n            ```python\n            from datetime import datetime\n\n            @app.command()\n            def main(\n                birthday: Annotated[\n                    datetime,\n                    typer.Option(\n                        formats=[\"%Y-%m-%d\", \"%Y-%m-%d %H:%M:%S\", \"%m/%d/%Y\"]\n                    ),\n                ],\n            ):\n                print(f\"Birthday defined at: {birthday}\")\n            ```\n            \"\"\"\n        ),\n    ] = None,\n    # File\n    mode: Annotated[\n        str | None,\n        Doc(\n            \"\"\"\n            For a CLI Option representing a [File object](https://typer.tiangolo.com/tutorial/parameter-types/file/),\n            you can customize the mode to open the file with. If unset, Typer will set a [sensible value by default](https://typer.tiangolo.com/tutorial/parameter-types/file/#advanced-mode).\n\n            **Example**\n\n            ```python\n            @app.command()\n            def main(config: Annotated[typer.FileText, typer.Option(mode=\"a\")]):\n                config.write(\"This is a single line\\\\n\")\n                print(\"Config line written\")\n            ```\n            \"\"\"\n        ),\n    ] = None,\n    encoding: Annotated[\n        str | None,\n        Doc(\n            \"\"\"\n            Customize the encoding of this CLI Option represented by a [File object](https://typer.tiangolo.com/tutorial/parameter-types/file/).\n\n            **Example**\n\n            ```python\n            @app.command()\n            def main(config: Annotated[typer.FileText, typer.Option(encoding=\"utf-8\")]):\n                config.write(\"All the text gets written\\\\n\")\n            ```\n            \"\"\"\n        ),\n    ] = None,\n    errors: Annotated[\n        str | None,\n        Doc(\n            \"\"\"\n            **Note**: you probably shouldn't use this parameter, it is inherited from Click and supported for compatibility.\n\n            ---\n\n            The error handling mode.\n            \"\"\"\n        ),\n    ] = \"strict\",\n    lazy: Annotated[\n        bool | None,\n        Doc(\n            \"\"\"\n            For a CLI Option representing a [File object](https://typer.tiangolo.com/tutorial/parameter-types/file/),\n            by default the file will not be created until you actually start writing to it.\n            You can change this behaviour by setting this parameter.\n            By default, it's set to `True` for writing and to `False` for reading.\n\n            **Example**\n\n            ```python\n            @app.command()\n            def main(config: Annotated[typer.FileText, typer.Option(mode=\"a\", lazy=False)]):\n                config.write(\"This is a single line\\\\n\")\n                print(\"Config line written\")\n            ```\n            \"\"\"\n        ),\n    ] = None,\n    atomic: Annotated[\n        bool,\n        Doc(\n            \"\"\"\n            For a CLI Option representing a [File object](https://typer.tiangolo.com/tutorial/parameter-types/file/),\n            you can ensure that all write instructions first go into a temporal file, and are only moved to the final destination after completing\n            by setting `atomic` to `True`. This can be useful for files with potential concurrent access.\n\n            **Example**\n\n            ```python\n            @app.command()\n            def main(config: Annotated[typer.FileText, typer.Option(mode=\"a\", atomic=True)]):\n                config.write(\"All the text\")\n            ```\n            \"\"\"\n        ),\n    ] = False,\n    # Path\n    exists: Annotated[\n        bool,\n        Doc(\n            \"\"\"\n            When set to `True` for a [`Path` CLI Option](https://typer.tiangolo.com/tutorial/parameter-types/path/),\n            additional validation is performed to check that the file or directory exists. If not, the value will be invalid.\n\n            **Example**\n\n            ```python\n            from pathlib import Path\n\n            @app.command()\n            def main(config: Annotated[Path, typer.Option(exists=True)]):\n                text = config.read_text()\n                print(f\"Config file contents: {text}\")\n            ```\n            \"\"\"\n        ),\n    ] = False,\n    file_okay: Annotated[\n        bool,\n        Doc(\n            \"\"\"\n            Determine whether or not a [`Path` CLI Option](https://typer.tiangolo.com/tutorial/parameter-types/path/)\n            is allowed to refer to a file. When this is set to `False`, the application will raise a validation error when a path to a file is given.\n\n            **Example**\n\n            ```python\n            from pathlib import Path\n\n            @app.command()\n            def main(config: Annotated[Path, typer.Option(exists=True, file_okay=False)]):\n                print(f\"Directory listing: {[x.name for x in config.iterdir()]}\")\n            ```\n            \"\"\"\n        ),\n    ] = True,\n    dir_okay: Annotated[\n        bool,\n        Doc(\n            \"\"\"\n            Determine whether or not a [`Path` CLI Option](https://typer.tiangolo.com/tutorial/parameter-types/path/)\n            is allowed to refer to a directory. When this is set to `False`, the application will raise a validation error when a path to a directory is given.\n\n            **Example**\n\n            ```python\n            from pathlib import Path\n\n            @app.command()\n            def main(config: Annotated[Path, typer.Argument(exists=True, dir_okay=False)]):\n                text = config.read_text()\n                print(f\"Config file contents: {text}\")\n            ```\n            \"\"\"\n        ),\n    ] = True,\n    writable: Annotated[\n        bool,\n        Doc(\n            \"\"\"\n            Whether or not to perform a writable check for this [`Path` CLI Option](https://typer.tiangolo.com/tutorial/parameter-types/path/).\n\n            **Example**\n\n            ```python\n            from pathlib import Path\n\n            @app.command()\n            def main(config: Annotated[Path, typer.Option(writable=True)]):\n                config.write_text(\"All the text\")\n            ```\n            \"\"\"\n        ),\n    ] = False,\n    readable: Annotated[\n        bool,\n        Doc(\n            \"\"\"\n            Whether or not to perform a readable check for this [`Path` CLI Option](https://typer.tiangolo.com/tutorial/parameter-types/path/).\n\n            **Example**\n\n            ```python\n            from pathlib import Path\n\n            @app.command()\n            def main(config: Annotated[Path, typer.Option(readable=True)]):\n                config.read_text(\"All the text\")\n            ```\n            \"\"\"\n        ),\n    ] = True,\n    resolve_path: Annotated[\n        bool,\n        Doc(\n            \"\"\"\n            Whether or not to fully resolve the path of this [`Path` CLI Option](https://typer.tiangolo.com/tutorial/parameter-types/path/),\n            meaning that the path becomes absolute and symlinks are resolved.\n\n            **Example**\n\n            ```python\n            from pathlib import Path\n\n            @app.command()\n            def main(config: Annotated[Path, typer.Option(resolve_path=True)]):\n                config.read_text(\"All the text\")\n            ```\n            \"\"\"\n        ),\n    ] = False,\n    allow_dash: Annotated[\n        bool,\n        Doc(\n            \"\"\"\n            When set to `True`, a single dash for this [`Path` CLI Option](https://typer.tiangolo.com/tutorial/parameter-types/path/)\n            would be a valid value, indicating standard streams. This is a more advanced use-case.\n            \"\"\"\n        ),\n    ] = False,\n    path_type: Annotated[\n        None | type[str] | type[bytes],\n        Doc(\n            \"\"\"\n             A string type that will be used to represent this [`Path` argument](https://typer.tiangolo.com/tutorial/parameter-types/path/).\n             The default is `None` which means the return value will be either bytes or unicode, depending on what makes most sense given the input data.\n             This is a more advanced use-case.\n            \"\"\"\n        ),\n    ] = None,\n    # Rich settings\n    rich_help_panel: Annotated[\n        str | None,\n        Doc(\n            \"\"\"\n            Set the panel name where you want this CLI Option to be shown in the [help text](https://typer.tiangolo.com/tutorial/arguments/help).\n\n            **Example**\n\n            ```python\n            @app.command()\n            def main(\n                name: Annotated[str, typer.Argument(help=\"Who to greet\")],\n                age: Annotated[str, typer.Option(help=\"Their age\", rich_help_panel=\"Data\")],\n            ):\n                print(f\"Hello {name} of age {age}\")\n            ```\n            \"\"\"\n        ),\n    ] = None,\n) -> Any:\n    \"\"\"\n    A [CLI Option](https://typer.tiangolo.com/tutorial/options) is a parameter to your command line application that is called with a single or double dash, something like `--verbose` or `-v`.\n\n    Often, CLI Options are optional, meaning that users can omit them from the command. However, you can set them to be required by using `Annotated`\n    and omitting a default value.\n\n    ## Example\n\n    ```python\n    @app.command()\n    def register(\n        user: Annotated[str, typer.Argument()],\n        age: Annotated[int, typer.Option(min=18)],\n    ):\n        print(f\"User is {user}\")\n        print(f\"--age is {age}\")\n    ```\n\n    Note how in this example, `--age` is a required CLI Option.\n    \"\"\"\n    return OptionInfo(\n        # Parameter\n        default=default,\n        param_decls=param_decls,\n        callback=callback,\n        metavar=metavar,\n        expose_value=expose_value,\n        is_eager=is_eager,\n        envvar=envvar,\n        shell_complete=shell_complete,\n        autocompletion=autocompletion,\n        default_factory=default_factory,\n        # Custom type\n        parser=parser,\n        click_type=click_type,\n        # Option\n        show_default=show_default,\n        prompt=prompt,\n        confirmation_prompt=confirmation_prompt,\n        prompt_required=prompt_required,\n        hide_input=hide_input,\n        is_flag=is_flag,\n        flag_value=flag_value,\n        count=count,\n        allow_from_autoenv=allow_from_autoenv,\n        help=help,\n        hidden=hidden,\n        show_choices=show_choices,\n        show_envvar=show_envvar,\n        # Choice\n        case_sensitive=case_sensitive,\n        # Numbers\n        min=min,\n        max=max,\n        clamp=clamp,\n        # DateTime\n        formats=formats,\n        # File\n        mode=mode,\n        encoding=encoding,\n        errors=errors,\n        lazy=lazy,\n        atomic=atomic,\n        # Path\n        exists=exists,\n        file_okay=file_okay,\n        dir_okay=dir_okay,\n        writable=writable,\n        readable=readable,\n        resolve_path=resolve_path,\n        allow_dash=allow_dash,\n        path_type=path_type,\n        # Rich settings\n        rich_help_panel=rich_help_panel,\n    )\n\n\n# Overload for Argument created with custom type 'parser'\n@overload\ndef Argument(\n    # Parameter\n    default: Any | None = ...,\n    *,\n    callback: Callable[..., Any] | None = None,\n    metavar: str | None = None,\n    expose_value: bool = True,\n    is_eager: bool = False,\n    envvar: str | list[str] | None = None,\n    # Note that shell_complete is not fully supported and will be removed in future versions\n    # TODO: Remove shell_complete in a future version (after 0.16.0)\n    shell_complete: Callable[\n        [click.Context, click.Parameter, str],\n        list[\"click.shell_completion.CompletionItem\"] | list[str],\n    ]\n    | None = None,\n    autocompletion: Callable[..., Any] | None = None,\n    default_factory: Callable[[], Any] | None = None,\n    # Custom type\n    parser: Callable[[str], Any] | None = None,\n    # TyperArgument\n    show_default: bool | str = True,\n    show_choices: bool = True,\n    show_envvar: bool = True,\n    help: str | None = None,\n    hidden: bool = False,\n    # Choice\n    case_sensitive: bool = True,\n    # Numbers\n    min: int | float | None = None,\n    max: int | float | None = None,\n    clamp: bool = False,\n    # DateTime\n    formats: list[str] | None = None,\n    # File\n    mode: str | None = None,\n    encoding: str | None = None,\n    errors: str | None = \"strict\",\n    lazy: bool | None = None,\n    atomic: bool = False,\n    # Path\n    exists: bool = False,\n    file_okay: bool = True,\n    dir_okay: bool = True,\n    writable: bool = False,\n    readable: bool = True,\n    resolve_path: bool = False,\n    allow_dash: bool = False,\n    path_type: None | type[str] | type[bytes] = None,\n    # Rich settings\n    rich_help_panel: str | None = None,\n) -> Any: ...\n\n\n# Overload for Argument created with custom type 'click_type'\n@overload\ndef Argument(\n    # Parameter\n    default: Any | None = ...,\n    *,\n    callback: Callable[..., Any] | None = None,\n    metavar: str | None = None,\n    expose_value: bool = True,\n    is_eager: bool = False,\n    envvar: str | list[str] | None = None,\n    # Note that shell_complete is not fully supported and will be removed in future versions\n    # TODO: Remove shell_complete in a future version (after 0.16.0)\n    shell_complete: Callable[\n        [click.Context, click.Parameter, str],\n        list[\"click.shell_completion.CompletionItem\"] | list[str],\n    ]\n    | None = None,\n    autocompletion: Callable[..., Any] | None = None,\n    default_factory: Callable[[], Any] | None = None,\n    # Custom type\n    click_type: click.ParamType | None = None,\n    # TyperArgument\n    show_default: bool | str = True,\n    show_choices: bool = True,\n    show_envvar: bool = True,\n    help: str | None = None,\n    hidden: bool = False,\n    # Choice\n    case_sensitive: bool = True,\n    # Numbers\n    min: int | float | None = None,\n    max: int | float | None = None,\n    clamp: bool = False,\n    # DateTime\n    formats: list[str] | None = None,\n    # File\n    mode: str | None = None,\n    encoding: str | None = None,\n    errors: str | None = \"strict\",\n    lazy: bool | None = None,\n    atomic: bool = False,\n    # Path\n    exists: bool = False,\n    file_okay: bool = True,\n    dir_okay: bool = True,\n    writable: bool = False,\n    readable: bool = True,\n    resolve_path: bool = False,\n    allow_dash: bool = False,\n    path_type: None | type[str] | type[bytes] = None,\n    # Rich settings\n    rich_help_panel: str | None = None,\n) -> Any: ...\n\n\ndef Argument(\n    # Parameter\n    default: Annotated[\n        Any | None,\n        Doc(\n            \"\"\"\n            By default, CLI arguments are required. However, by giving them a default value they become [optional](https://typer.tiangolo.com/tutorial/arguments/optional):\n\n            **Example**\n\n            ```python\n            @app.command()\n            def main(name: str = typer.Argument(\"World\")):\n                print(f\"Hello {name}!\")\n            ```\n\n            Note that this usage is deprecated, and we recommend to use `Annotated` instead:\n            ```python\n            @app.command()\n            def main(name: Annotated[str, typer.Argument()] = \"World\"):\n                print(f\"Hello {name}!\")\n            ```\n            \"\"\"\n        ),\n    ] = ...,\n    *,\n    callback: Annotated[\n        Callable[..., Any] | None,\n        Doc(\n            \"\"\"\n            Add a callback to this CLI Argument, to execute additional logic with the value received from the terminal.\n            See [the tutorial about callbacks](https://typer.tiangolo.com/tutorial/options/callback-and-context/) for more details.\n\n            **Example**\n\n            ```python\n            def name_callback(value: str):\n                if value != \"Deadpool\":\n                    raise typer.BadParameter(\"Only Deadpool is allowed\")\n                return value\n\n            @app.command()\n            def main(name: Annotated[str, typer.Argument(callback=name_callback)]):\n                print(f\"Hello {name}\")\n            ```\n            \"\"\"\n        ),\n    ] = None,\n    metavar: Annotated[\n        str | None,\n        Doc(\n            \"\"\"\n            Customize the name displayed in the help text to represent this CLI Argument.\n            By default, it will be the same name you declared, in uppercase.\n            See [the tutorial about CLI Arguments with Help](https://typer.tiangolo.com/tutorial/arguments/help/#custom-help-name-metavar) for more details.\n\n            **Example**\n\n            ```python\n            @app.command()\n            def main(name: Annotated[str, typer.Argument(metavar=\"✨username✨\")]):\n                print(f\"Hello {name}\")\n            ```\n            \"\"\"\n        ),\n    ] = None,\n    expose_value: Annotated[\n        bool,\n        Doc(\n            \"\"\"\n            **Note**: you probably shouldn't use this parameter, it is inherited from Click and supported for compatibility.\n\n            ---\n\n            If this is `True` then the value is passed onwards to the command callback and stored on the context, otherwise it’s skipped.\n            \"\"\"\n        ),\n    ] = True,\n    is_eager: Annotated[\n        bool,\n        Doc(\n            \"\"\"\n            Set an argument to \"eager\" to ensure it gets processed before other CLI parameters. This could be relevant when there are other parameters with callbacks that could exit the program early.\n            For more information and an extended example, see the documentation [here](https://typer.tiangolo.com/tutorial/options/version/#fix-with-is_eager).\n            \"\"\"\n        ),\n    ] = False,\n    envvar: Annotated[\n        str | list[str] | None,\n        Doc(\n            \"\"\"\n            Configure an argument to read a value from an environment variable if it is not provided in the command line as a CLI argument.\n            For more information, see the [documentation on Environment Variables](https://typer.tiangolo.com/tutorial/arguments/envvar/).\n\n            **Example**\n\n            ```python\n            @app.command()\n            def main(name: Annotated[str, typer.Argument(envvar=\"ME\")]):\n                print(f\"Hello Mr. {name}\")\n            ```\n            \"\"\"\n        ),\n    ] = None,\n    # TODO: Remove shell_complete in a future version (after 0.16.0)\n    shell_complete: Annotated[\n        Callable[\n            [click.Context, click.Parameter, str],\n            list[\"click.shell_completion.CompletionItem\"] | list[str],\n        ]\n        | None,\n        Doc(\n            \"\"\"\n            **Note**: you probably shouldn't use this parameter, it is inherited from Click and supported for compatibility.\n            It is however not fully functional, and will likely be removed in future versions.\n            \"\"\"\n        ),\n    ] = None,\n    autocompletion: Annotated[\n        Callable[..., Any] | None,\n        Doc(\n            \"\"\"\n            Provide a custom function that helps to autocomplete the values of this CLI Argument.\n            See [the tutorial on parameter autocompletion](https://typer.tiangolo.com/tutorial/options-autocompletion) for more details.\n\n            **Example**\n\n            ```python\n            def complete():\n                return [\"Me\", \"Myself\", \"I\"]\n\n            @app.command()\n            def main(name: Annotated[str, typer.Argument(autocompletion=complete)]):\n                print(f\"Hello {name}\")\n            ```\n            \"\"\"\n        ),\n    ] = None,\n    default_factory: Annotated[\n        Callable[[], Any] | None,\n        Doc(\n            \"\"\"\n            Provide a custom function that dynamically generates a [default](https://typer.tiangolo.com/tutorial/arguments/default) for this CLI Argument.\n\n            **Example**\n\n            ```python\n            def get_name():\n                return random.choice([\"Me\", \"Myself\", \"I\"])\n\n            @app.command()\n            def main(name: Annotated[str, typer.Argument(default_factory=get_name)]):\n                print(f\"Hello {name}\")\n            ```\n            \"\"\"\n        ),\n    ] = None,\n    # Custom type\n    parser: Annotated[\n        Callable[[str], Any] | None,\n        Doc(\n            \"\"\"\n            Use your own custom types in Typer applications by defining a `parser` function that parses input into your own types:\n\n            **Example**\n\n            ```python\n            class CustomClass:\n                def __init__(self, value: str):\n                    self.value = value\n\n                def __str__(self):\n                    return f\"<CustomClass: value={self.value}>\"\n\n            def my_parser(value: str):\n                return CustomClass(value * 2)\n\n            @app.command()\n            def main(arg: Annotated[CustomClass, typer.Argument(parser=my_parser):\n                print(f\"arg is {arg}\")\n            ```\n            \"\"\"\n        ),\n    ] = None,\n    click_type: Annotated[\n        click.ParamType | None,\n        Doc(\n            \"\"\"\n            Define this parameter to use a [custom Click type](https://click.palletsprojects.com/en/stable/parameters/#implementing-custom-types) in your Typer applications.\n\n            **Example**\n\n            ```python\n            class MyClass:\n                def __init__(self, value: str):\n                    self.value = value\n\n                def __str__(self):\n                    return f\"<MyClass: value={self.value}>\"\n\n            class MyParser(click.ParamType):\n                name = \"MyClass\"\n\n                def convert(self, value, param, ctx):\n                    return MyClass(value * 3)\n\n            @app.command()\n            def main(arg: Annotated[MyClass, typer.Argument(click_type=MyParser())]):\n                print(f\"arg is {arg}\")\n            ```\n            \"\"\"\n        ),\n    ] = None,\n    # TyperArgument\n    show_default: Annotated[\n        bool | str,\n        Doc(\n            \"\"\"\n            When set to `False`, don't show the default value of this CLI Argument in the [help text](https://typer.tiangolo.com/tutorial/arguments/help/).\n\n            **Example**\n\n            ```python\n            @app.command()\n            def main(name: Annotated[str, typer.Argument(show_default=False)] = \"Rick\"):\n                print(f\"Hello {name}\")\n            ```\n            \"\"\"\n        ),\n    ] = True,\n    show_choices: Annotated[\n        bool,\n        Doc(\n            \"\"\"\n            **Note**: you probably shouldn't use this parameter, it is inherited from Click and supported for compatibility.\n\n            ---\n\n            When set to `False`, this suppresses choices from being displayed inline when `prompt` is used.\n            \"\"\"\n        ),\n    ] = True,\n    show_envvar: Annotated[\n        bool,\n        Doc(\n            \"\"\"\n            When an [\"envvar\"](https://typer.tiangolo.com/tutorial/arguments/envvar) is defined, prevent it from showing up in the help text:\n\n            **Example**\n\n            ```python\n            @app.command()\n            def main(name: Annotated[str, typer.Argument(envvar=\"ME\", show_envvar=False)]):\n                print(f\"Hello Mr. {name}\")\n            ```\n            \"\"\"\n        ),\n    ] = True,\n    help: Annotated[\n        str | None,\n        Doc(\n            \"\"\"\n            Help text for this CLI Argument.\n            See [the tutorial about CLI Arguments with help](https://typer.tiangolo.com/tutorial/arguments/help/) for more dedails.\n\n            **Example**\n\n            ```python\n            @app.command()\n            def greet(name: Annotated[str, typer.Argument(help=\"Person to greet\")]):\n                print(f\"Hello {name}\")\n            ```\n            \"\"\"\n        ),\n    ] = None,\n    hidden: Annotated[\n        bool,\n        Doc(\n            \"\"\"\n            Hide this CLI Argument from [help outputs](https://typer.tiangolo.com/tutorial/arguments/help). `False` by default.\n\n            **Example**\n\n            ```python\n            @app.command()\n            def main(name: Annotated[str, typer.Argument(hidden=True)] = \"World\"):\n                print(f\"Hello {name}\")\n            ```\n            \"\"\"\n        ),\n    ] = False,\n    # Choice\n    case_sensitive: Annotated[\n        bool,\n        Doc(\n            \"\"\"\n            For a CLI Argument representing an [Enum (choice)](https://typer.tiangolo.com/tutorial/parameter-types/enum),\n            you can allow case-insensitive matching with this parameter:\n\n            **Example**\n\n            ```python\n            from enum import Enum\n\n            class NeuralNetwork(str, Enum):\n                simple = \"simple\"\n                conv = \"conv\"\n                lstm = \"lstm\"\n\n            @app.command()\n            def main(\n                network: Annotated[NeuralNetwork, typer.Argument(case_sensitive=False)]):\n                print(f\"Training neural network of type: {network.value}\")\n            ```\n\n            With this setting, \"LSTM\" or \"lstm\" will both be valid values that will be resolved to `NeuralNetwork.lstm`.\n            \"\"\"\n        ),\n    ] = True,\n    # Numbers\n    min: Annotated[\n        int | float | None,\n        Doc(\n            \"\"\"\n            For a CLI Argument representing a [number](https://typer.tiangolo.com/tutorial/parameter-types/number/) (`int` or `float`),\n            you can define numeric validations with `min` and `max` values:\n\n            **Example**\n\n            ```python\n            @app.command()\n            def main(\n                user: Annotated[str, typer.Argument()],\n                user_id: Annotated[int, typer.Argument(min=1, max=1000)],\n            ):\n                print(f\"ID for {user} is {user_id}\")\n            ```\n\n            If the user attempts to input an invalid number, an error will be shown, explaining why the value is invalid.\n            \"\"\"\n        ),\n    ] = None,\n    max: Annotated[\n        int | float | None,\n        Doc(\n            \"\"\"\n            For a CLI Argument representing a [number](https://typer.tiangolo.com/tutorial/parameter-types/number/) (`int` or `float`),\n            you can define numeric validations with `min` and `max` values:\n\n            **Example**\n\n            ```python\n            @app.command()\n            def main(\n                user: Annotated[str, typer.Argument()],\n                user_id: Annotated[int, typer.Argument(min=1, max=1000)],\n            ):\n                print(f\"ID for {user} is {user_id}\")\n            ```\n\n            If the user attempts to input an invalid number, an error will be shown, explaining why the value is invalid.\n            \"\"\"\n        ),\n    ] = None,\n    clamp: Annotated[\n        bool,\n        Doc(\n            \"\"\"\n            For a CLI Argument representing a [number](https://typer.tiangolo.com/tutorial/parameter-types/number/) and that is bounded by using `min` and/or `max`,\n            you can opt to use the closest minimum or maximum value instead of raising an error. This is done by setting `clamp` to `True`.\n\n            **Example**\n\n            ```python\n            @app.command()\n            def main(\n                user: Annotated[str, typer.Argument()],\n                user_id: Annotated[int, typer.Argument(min=1, max=1000, clamp=True)],\n            ):\n                print(f\"ID for {user} is {user_id}\")\n            ```\n\n            If the user attempts to input 3420 for `user_id`, this will internally be converted to `1000`.\n            \"\"\"\n        ),\n    ] = False,\n    # DateTime\n    formats: Annotated[\n        list[str] | None,\n        Doc(\n            \"\"\"\n            For a CLI Argument representing a [DateTime object](https://typer.tiangolo.com/tutorial/parameter-types/datetime),\n            you can customize the formats that can be parsed automatically:\n\n            **Example**\n\n            ```python\n            from datetime import datetime\n\n            @app.command()\n            def main(\n                birthday: Annotated[\n                    datetime,\n                    typer.Argument(\n                        formats=[\"%Y-%m-%d\", \"%Y-%m-%d %H:%M:%S\", \"%m/%d/%Y\"]\n                    ),\n                ],\n            ):\n                print(f\"Birthday defined at: {birthday}\")\n            ```\n            \"\"\"\n        ),\n    ] = None,\n    # File\n    mode: Annotated[\n        str | None,\n        Doc(\n            \"\"\"\n            For a CLI Argument representing a [File object](https://typer.tiangolo.com/tutorial/parameter-types/file/),\n            you can customize the mode to open the file with. If unset, Typer will set a [sensible value by default](https://typer.tiangolo.com/tutorial/parameter-types/file/#advanced-mode).\n\n            **Example**\n\n            ```python\n            @app.command()\n            def main(config: Annotated[typer.FileText, typer.Argument(mode=\"a\")]):\n                config.write(\"This is a single line\\\\n\")\n                print(\"Config line written\")\n            ```\n            \"\"\"\n        ),\n    ] = None,\n    encoding: Annotated[\n        str | None,\n        Doc(\n            \"\"\"\n            Customize the encoding of this CLI Argument represented by a [File object](https://typer.tiangolo.com/tutorial/parameter-types/file/).\n\n            **Example**\n\n            ```python\n            @app.command()\n            def main(config: Annotated[typer.FileText, typer.Argument(encoding=\"utf-8\")]):\n                config.write(\"All the text gets written\\\\n\")\n            ```\n            \"\"\"\n        ),\n    ] = None,\n    errors: Annotated[\n        str | None,\n        Doc(\n            \"\"\"\n            **Note**: you probably shouldn't use this parameter, it is inherited from Click and supported for compatibility.\n\n            ---\n\n            The error handling mode.\n            \"\"\"\n        ),\n    ] = \"strict\",\n    lazy: Annotated[\n        bool | None,\n        Doc(\n            \"\"\"\n            For a CLI Argument representing a [File object](https://typer.tiangolo.com/tutorial/parameter-types/file/),\n            by default the file will not be created until you actually start writing to it.\n            You can change this behaviour by setting this parameter.\n            By default, it's set to `True` for writing and to `False` for reading.\n\n            **Example**\n\n            ```python\n            @app.command()\n            def main(config: Annotated[typer.FileText, typer.Argument(mode=\"a\", lazy=False)]):\n                config.write(\"This is a single line\\\\n\")\n                print(\"Config line written\")\n            ```\n            \"\"\"\n        ),\n    ] = None,\n    atomic: Annotated[\n        bool,\n        Doc(\n            \"\"\"\n            For a CLI Argument representing a [File object](https://typer.tiangolo.com/tutorial/parameter-types/file/),\n            you can ensure that all write instructions first go into a temporal file, and are only moved to the final destination after completing\n            by setting `atomic` to `True`. This can be useful for files with potential concurrent access.\n\n            **Example**\n\n            ```python\n            @app.command()\n            def main(config: Annotated[typer.FileText, typer.Argument(mode=\"a\", atomic=True)]):\n                config.write(\"All the text\")\n            ```\n            \"\"\"\n        ),\n    ] = False,\n    # Path\n    exists: Annotated[\n        bool,\n        Doc(\n            \"\"\"\n            When set to `True` for a [`Path` argument](https://typer.tiangolo.com/tutorial/parameter-types/path/),\n            additional validation is performed to check that the file or directory exists. If not, the value will be invalid.\n\n            **Example**\n\n            ```python\n            from pathlib import Path\n\n            @app.command()\n            def main(config: Annotated[Path, typer.Argument(exists=True)]):\n                text = config.read_text()\n                print(f\"Config file contents: {text}\")\n            ```\n            \"\"\"\n        ),\n    ] = False,\n    file_okay: Annotated[\n        bool,\n        Doc(\n            \"\"\"\n            Determine whether or not a [`Path` argument](https://typer.tiangolo.com/tutorial/parameter-types/path/)\n            is allowed to refer to a file. When this is set to `False`, the application will raise a validation error when a path to a file is given.\n\n            **Example**\n\n            ```python\n            from pathlib import Path\n\n            @app.command()\n            def main(config: Annotated[Path, typer.Argument(exists=True, file_okay=False)]):\n                print(f\"Directory listing: {[x.name for x in config.iterdir()]}\")\n            ```\n            \"\"\"\n        ),\n    ] = True,\n    dir_okay: Annotated[\n        bool,\n        Doc(\n            \"\"\"\n            Determine whether or not a [`Path` argument](https://typer.tiangolo.com/tutorial/parameter-types/path/)\n            is allowed to refer to a directory. When this is set to `False`, the application will raise a validation error when a path to a directory is given.\n\n            **Example**\n\n            ```python\n            from pathlib import Path\n\n            @app.command()\n            def main(config: Annotated[Path, typer.Argument(exists=True, dir_okay=False)]):\n                text = config.read_text()\n                print(f\"Config file contents: {text}\")\n            ```\n            \"\"\"\n        ),\n    ] = True,\n    writable: Annotated[\n        bool,\n        Doc(\n            \"\"\"\n            Whether or not to perform a writable check for this [`Path` argument](https://typer.tiangolo.com/tutorial/parameter-types/path/).\n\n            **Example**\n\n            ```python\n            from pathlib import Path\n\n            @app.command()\n            def main(config: Annotated[Path, typer.Argument(writable=True)]):\n                config.write_text(\"All the text\")\n            ```\n            \"\"\"\n        ),\n    ] = False,\n    readable: Annotated[\n        bool,\n        Doc(\n            \"\"\"\n            Whether or not to perform a readable check for this [`Path` argument](https://typer.tiangolo.com/tutorial/parameter-types/path/).\n\n            **Example**\n\n            ```python\n            from pathlib import Path\n\n            @app.command()\n            def main(config: Annotated[Path, typer.Argument(readable=True)]):\n                config.read_text(\"All the text\")\n            ```\n            \"\"\"\n        ),\n    ] = True,\n    resolve_path: Annotated[\n        bool,\n        Doc(\n            \"\"\"\n            Whether or not to fully resolve the path of this [`Path` argument](https://typer.tiangolo.com/tutorial/parameter-types/path/),\n            meaning that the path becomes absolute and symlinks are resolved.\n\n            **Example**\n\n            ```python\n            from pathlib import Path\n\n            @app.command()\n            def main(config: Annotated[Path, typer.Argument(resolve_path=True)]):\n                config.read_text(\"All the text\")\n            ```\n            \"\"\"\n        ),\n    ] = False,\n    allow_dash: Annotated[\n        bool,\n        Doc(\n            \"\"\"\n            When set to `True`, a single dash for this [`Path` argument](https://typer.tiangolo.com/tutorial/parameter-types/path/)\n            would be a valid value, indicating standard streams. This is a more advanced use-case.\n            \"\"\"\n        ),\n    ] = False,\n    path_type: Annotated[\n        None | type[str] | type[bytes],\n        Doc(\n            \"\"\"\n            A string type that will be used to represent this [`Path` argument](https://typer.tiangolo.com/tutorial/parameter-types/path/).\n            The default is `None` which means the return value will be either bytes or unicode, depending on what makes most sense given the input data.\n            This is a more advanced use-case.\n            \"\"\"\n        ),\n    ] = None,\n    # Rich settings\n    rich_help_panel: Annotated[\n        str | None,\n        Doc(\n            \"\"\"\n            Set the panel name where you want this CLI Argument to be shown in the [help text](https://typer.tiangolo.com/tutorial/arguments/help).\n\n            **Example**\n\n            ```python\n            @app.command()\n            def main(\n                name: Annotated[str, typer.Argument(help=\"Who to greet\")],\n                age: Annotated[str, typer.Option(help=\"Their age\", rich_help_panel=\"Data\")],\n            ):\n                print(f\"Hello {name} of age {age}\")\n            ```\n            \"\"\"\n        ),\n    ] = None,\n) -> Any:\n    \"\"\"\n    A [CLI Argument](https://typer.tiangolo.com/tutorial/arguments) is a positional parameter to your command line application.\n\n    Often, CLI Arguments are required, meaning that users have to specify them. However, you can set them to be optional by defining a default value:\n\n    ## Example\n\n    ```python\n    @app.command()\n    def main(name: Annotated[str, typer.Argument()] = \"World\"):\n        print(f\"Hello {name}!\")\n    ```\n\n    Note how in this example, if `name` is not specified on the command line, the application will still execute normally and print \"Hello World!\".\n    \"\"\"\n    return ArgumentInfo(\n        # Parameter\n        default=default,\n        # Arguments can only have one param declaration\n        # it will be generated from the param name\n        param_decls=None,\n        callback=callback,\n        metavar=metavar,\n        expose_value=expose_value,\n        is_eager=is_eager,\n        envvar=envvar,\n        shell_complete=shell_complete,\n        autocompletion=autocompletion,\n        default_factory=default_factory,\n        # Custom type\n        parser=parser,\n        click_type=click_type,\n        # TyperArgument\n        show_default=show_default,\n        show_choices=show_choices,\n        show_envvar=show_envvar,\n        help=help,\n        hidden=hidden,\n        # Choice\n        case_sensitive=case_sensitive,\n        # Numbers\n        min=min,\n        max=max,\n        clamp=clamp,\n        # DateTime\n        formats=formats,\n        # File\n        mode=mode,\n        encoding=encoding,\n        errors=errors,\n        lazy=lazy,\n        atomic=atomic,\n        # Path\n        exists=exists,\n        file_okay=file_okay,\n        dir_okay=dir_okay,\n        writable=writable,\n        readable=readable,\n        resolve_path=resolve_path,\n        allow_dash=allow_dash,\n        path_type=path_type,\n        # Rich settings\n        rich_help_panel=rich_help_panel,\n    )\n"
  },
  {
    "path": "typer/py.typed",
    "content": ""
  },
  {
    "path": "typer/rich_utils.py",
    "content": "# Extracted and modified from https://github.com/ewels/rich-click\n\nimport inspect\nimport io\nfrom collections import defaultdict\nfrom collections.abc import Iterable\nfrom gettext import gettext as _\nfrom os import getenv\nfrom typing import Any, Literal\n\nimport click\nfrom rich import box\nfrom rich.align import Align\nfrom rich.columns import Columns\nfrom rich.console import Console, RenderableType, group\nfrom rich.emoji import Emoji\nfrom rich.highlighter import RegexHighlighter\nfrom rich.markdown import Markdown\nfrom rich.markup import escape\nfrom rich.padding import Padding\nfrom rich.panel import Panel\nfrom rich.table import Table\nfrom rich.text import Text\nfrom rich.theme import Theme\nfrom rich.traceback import Traceback\nfrom typer.models import DeveloperExceptionConfig\n\n# Default styles\nSTYLE_OPTION = \"bold cyan\"\nSTYLE_SWITCH = \"bold green\"\nSTYLE_NEGATIVE_OPTION = \"bold magenta\"\nSTYLE_NEGATIVE_SWITCH = \"bold red\"\nSTYLE_METAVAR = \"bold yellow\"\nSTYLE_METAVAR_SEPARATOR = \"dim\"\nSTYLE_USAGE = \"yellow\"\nSTYLE_USAGE_COMMAND = \"bold\"\nSTYLE_DEPRECATED = \"red\"\nSTYLE_DEPRECATED_COMMAND = \"dim\"\nSTYLE_HELPTEXT_FIRST_LINE = \"\"\nSTYLE_HELPTEXT = \"dim\"\nSTYLE_OPTION_HELP = \"\"\nSTYLE_OPTION_DEFAULT = \"dim\"\nSTYLE_OPTION_ENVVAR = \"dim yellow\"\nSTYLE_REQUIRED_SHORT = \"red\"\nSTYLE_REQUIRED_LONG = \"dim red\"\nSTYLE_OPTIONS_PANEL_BORDER = \"dim\"\nALIGN_OPTIONS_PANEL: Literal[\"left\", \"center\", \"right\"] = \"left\"\nSTYLE_OPTIONS_TABLE_SHOW_LINES = False\nSTYLE_OPTIONS_TABLE_LEADING = 0\nSTYLE_OPTIONS_TABLE_PAD_EDGE = False\nSTYLE_OPTIONS_TABLE_PADDING = (0, 1)\nSTYLE_OPTIONS_TABLE_BOX = \"\"\nSTYLE_OPTIONS_TABLE_ROW_STYLES = None\nSTYLE_OPTIONS_TABLE_BORDER_STYLE = None\nSTYLE_COMMANDS_PANEL_BORDER = \"dim\"\nALIGN_COMMANDS_PANEL: Literal[\"left\", \"center\", \"right\"] = \"left\"\nSTYLE_COMMANDS_TABLE_SHOW_LINES = False\nSTYLE_COMMANDS_TABLE_LEADING = 0\nSTYLE_COMMANDS_TABLE_PAD_EDGE = False\nSTYLE_COMMANDS_TABLE_PADDING = (0, 1)\nSTYLE_COMMANDS_TABLE_BOX = \"\"\nSTYLE_COMMANDS_TABLE_ROW_STYLES = None\nSTYLE_COMMANDS_TABLE_BORDER_STYLE = None\nSTYLE_COMMANDS_TABLE_FIRST_COLUMN = \"bold cyan\"\nSTYLE_ERRORS_PANEL_BORDER = \"red\"\nALIGN_ERRORS_PANEL: Literal[\"left\", \"center\", \"right\"] = \"left\"\nSTYLE_ERRORS_SUGGESTION = \"dim\"\nSTYLE_ABORTED = \"red\"\n_TERMINAL_WIDTH = getenv(\"TERMINAL_WIDTH\")\nMAX_WIDTH = int(_TERMINAL_WIDTH) if _TERMINAL_WIDTH else None\nCOLOR_SYSTEM: Literal[\"auto\", \"standard\", \"256\", \"truecolor\", \"windows\"] | None = (\n    \"auto\"  # Set to None to disable colors\n)\n_TYPER_FORCE_DISABLE_TERMINAL = getenv(\"_TYPER_FORCE_DISABLE_TERMINAL\")\nFORCE_TERMINAL = (\n    True\n    if getenv(\"GITHUB_ACTIONS\") or getenv(\"FORCE_COLOR\") or getenv(\"PY_COLORS\")\n    else None\n)\nif _TYPER_FORCE_DISABLE_TERMINAL:\n    FORCE_TERMINAL = False\n\n# Fixed strings\nDEPRECATED_STRING = _(\"(deprecated) \")\nDEFAULT_STRING = _(\"[default: {}]\")\nENVVAR_STRING = _(\"[env var: {}]\")\nREQUIRED_SHORT_STRING = \"*\"\nREQUIRED_LONG_STRING = _(\"[required]\")\nRANGE_STRING = \" [{}]\"\nARGUMENTS_PANEL_TITLE = _(\"Arguments\")\nOPTIONS_PANEL_TITLE = _(\"Options\")\nCOMMANDS_PANEL_TITLE = _(\"Commands\")\nERRORS_PANEL_TITLE = _(\"Error\")\nABORTED_TEXT = _(\"Aborted.\")\nRICH_HELP = _(\"Try [blue]'{command_path} {help_option}'[/] for help.\")\n\nMARKUP_MODE_MARKDOWN = \"markdown\"\nMARKUP_MODE_RICH = \"rich\"\n_RICH_HELP_PANEL_NAME = \"rich_help_panel\"\nANSI_PREFIX = \"\\033[\"\n\nMarkupModeStrict = Literal[\"markdown\", \"rich\"]\n\n\n# Rich regex highlighter\nclass OptionHighlighter(RegexHighlighter):\n    \"\"\"Highlights our special options.\"\"\"\n\n    highlights = [\n        r\"(^|\\W)(?P<switch>\\-\\w+)(?![a-zA-Z0-9])\",\n        r\"(^|\\W)(?P<option>\\-\\-[\\w\\-]+)(?![a-zA-Z0-9])\",\n        r\"(?P<metavar>\\<[^\\>]+\\>)\",\n        r\"(?P<usage>Usage: )\",\n    ]\n\n\nclass NegativeOptionHighlighter(RegexHighlighter):\n    highlights = [\n        r\"(^|\\W)(?P<negative_switch>\\-\\w+)(?![a-zA-Z0-9])\",\n        r\"(^|\\W)(?P<negative_option>\\-\\-[\\w\\-]+)(?![a-zA-Z0-9])\",\n    ]\n\n\n# Highlighter to make [ | ] and <> dim\nclass MetavarHighlighter(RegexHighlighter):\n    highlights = [\n        r\"^(?P<metavar_sep>(\\[|<))\",\n        r\"(?P<metavar_sep>\\|)\",\n        r\"(?P<metavar_sep>(\\]|>))(\\.\\.\\.)?$\",\n    ]\n\n\nhighlighter = OptionHighlighter()\nnegative_highlighter = NegativeOptionHighlighter()\nmetavar_highlighter = MetavarHighlighter()\n\n\ndef _has_ansi_character(text: str) -> bool:\n    return ANSI_PREFIX in text\n\n\ndef _get_rich_console(stderr: bool = False) -> Console:\n    return Console(\n        theme=Theme(\n            {\n                \"option\": STYLE_OPTION,\n                \"switch\": STYLE_SWITCH,\n                \"negative_option\": STYLE_NEGATIVE_OPTION,\n                \"negative_switch\": STYLE_NEGATIVE_SWITCH,\n                \"metavar\": STYLE_METAVAR,\n                \"metavar_sep\": STYLE_METAVAR_SEPARATOR,\n                \"usage\": STYLE_USAGE,\n            },\n        ),\n        highlighter=highlighter,\n        color_system=COLOR_SYSTEM,\n        force_terminal=FORCE_TERMINAL,\n        width=MAX_WIDTH,\n        stderr=stderr,\n    )\n\n\ndef _make_rich_text(\n    *, text: str, style: str = \"\", markup_mode: MarkupModeStrict\n) -> Markdown | Text:\n    \"\"\"Take a string, remove indentations, and return styled text.\n\n    If `markup_mode` is `\"rich\"`, the text is parsed for Rich markup strings.\n    If `markup_mode` is `\"markdown\"`, parse as Markdown.\n    \"\"\"\n    # Remove indentations from input text\n    text = inspect.cleandoc(text)\n    if markup_mode == MARKUP_MODE_MARKDOWN:\n        text = Emoji.replace(text)\n        return Markdown(text, style=style)\n    else:\n        assert markup_mode == MARKUP_MODE_RICH\n        if _has_ansi_character(text):\n            return highlighter(Text.from_ansi(text, style=style))\n        else:\n            return highlighter(Text.from_markup(text, style=style))\n\n\n@group()\ndef _get_help_text(\n    *,\n    obj: click.Command | click.Group,\n    markup_mode: MarkupModeStrict,\n) -> Iterable[Markdown | Text]:\n    \"\"\"Build primary help text for a click command or group.\n\n    Returns the prose help text for a command or group, rendered either as a\n    Rich Text object or as Markdown.\n    If the command is marked as deprecated, the deprecated string will be prepended.\n    \"\"\"\n    # Prepend deprecated status\n    if obj.deprecated:\n        yield Text(DEPRECATED_STRING, style=STYLE_DEPRECATED)\n\n    # Fetch and dedent the help text\n    help_text = inspect.cleandoc(obj.help or \"\")\n\n    # Trim off anything that comes after \\f on its own line\n    help_text = help_text.partition(\"\\f\")[0]\n\n    # Get the first paragraph\n    first_line, *remaining_paragraphs = help_text.split(\"\\n\\n\")\n\n    # Remove single linebreaks\n    if markup_mode != MARKUP_MODE_MARKDOWN and not first_line.startswith(\"\\b\"):\n        first_line = first_line.replace(\"\\n\", \" \")\n    yield _make_rich_text(\n        text=first_line.strip(),\n        style=STYLE_HELPTEXT_FIRST_LINE,\n        markup_mode=markup_mode,\n    )\n\n    # Get remaining lines, remove single line breaks and format as dim\n    if remaining_paragraphs:\n        # Add a newline inbetween the header and the remaining paragraphs\n        yield Text(\"\")\n        # Join with double linebreaks for markdown and Rich markup\n        remaining_lines = \"\\n\\n\".join(remaining_paragraphs)\n\n        yield _make_rich_text(\n            text=remaining_lines,\n            style=STYLE_HELPTEXT,\n            markup_mode=markup_mode,\n        )\n\n\ndef _get_parameter_help(\n    *,\n    param: click.Option | click.Argument | click.Parameter,\n    ctx: click.Context,\n    markup_mode: MarkupModeStrict,\n) -> Columns:\n    \"\"\"Build primary help text for a click option or argument.\n\n    Returns the prose help text for an option or argument, rendered either\n    as a Rich Text object or as Markdown.\n    Additional elements are appended to show the default and required status if\n    applicable.\n    \"\"\"\n    # import here to avoid cyclic imports\n    from .core import TyperArgument, TyperOption\n\n    items: list[Text | Markdown] = []\n\n    # Get the environment variable first\n\n    envvar = getattr(param, \"envvar\", None)\n    var_str = \"\"\n    # https://github.com/pallets/click/blob/0aec1168ac591e159baf6f61026d6ae322c53aaf/src/click/core.py#L2720-L2726\n    if envvar is None:\n        if (\n            getattr(param, \"allow_from_autoenv\", None)\n            and getattr(ctx, \"auto_envvar_prefix\", None) is not None\n            and param.name is not None\n        ):\n            envvar = f\"{ctx.auto_envvar_prefix}_{param.name.upper()}\"\n    if envvar is not None:\n        var_str = (\n            envvar if isinstance(envvar, str) else \", \".join(str(d) for d in envvar)\n        )\n\n    # Main help text\n    help_value: str | None = getattr(param, \"help\", None)\n    if help_value:\n        paragraphs = help_value.split(\"\\n\\n\")\n        # Remove single linebreaks\n        if markup_mode != MARKUP_MODE_MARKDOWN:\n            paragraphs = [\n                x.replace(\"\\n\", \" \").strip()\n                if not x.startswith(\"\\b\")\n                else \"{}\\n\".format(x.strip(\"\\b\\n\"))\n                for x in paragraphs\n            ]\n        items.append(\n            _make_rich_text(\n                text=\"\\n\".join(paragraphs).strip(),\n                style=STYLE_OPTION_HELP,\n                markup_mode=markup_mode,\n            )\n        )\n\n    # Environment variable AFTER help text\n    if envvar and getattr(param, \"show_envvar\", None):\n        items.append(Text(ENVVAR_STRING.format(var_str), style=STYLE_OPTION_ENVVAR))\n\n    # Default value\n    # This uses Typer's specific param._get_default_string\n    if isinstance(param, (TyperOption, TyperArgument)):\n        default_value = param._extract_default_help_str(ctx=ctx)\n        show_default_is_str = isinstance(param.show_default, str)\n        if show_default_is_str or (\n            default_value is not None and (param.show_default or ctx.show_default)\n        ):\n            default_str = param._get_default_string(\n                ctx=ctx,\n                show_default_is_str=show_default_is_str,\n                default_value=default_value,\n            )\n            if default_str:\n                items.append(\n                    Text(\n                        DEFAULT_STRING.format(default_str),\n                        style=STYLE_OPTION_DEFAULT,\n                    )\n                )\n\n    # Required?\n    if param.required:\n        items.append(Text(REQUIRED_LONG_STRING, style=STYLE_REQUIRED_LONG))\n\n    # Use Columns - this allows us to group different renderable types\n    # (Text, Markdown) onto a single line.\n    return Columns(items)\n\n\ndef _make_command_help(\n    *,\n    help_text: str,\n    markup_mode: MarkupModeStrict,\n) -> Text | Markdown:\n    \"\"\"Build cli help text for a click group command.\n\n    That is, when calling help on groups with multiple subcommands\n    (not the main help text when calling the subcommand help).\n\n    Returns the first paragraph of help text for a command, rendered either as a\n    Rich Text object or as Markdown.\n    Ignores single newlines as paragraph markers, looks for double only.\n    \"\"\"\n    paragraphs = inspect.cleandoc(help_text).split(\"\\n\\n\")\n    # Remove single linebreaks\n    if markup_mode != MARKUP_MODE_RICH and not paragraphs[0].startswith(\"\\b\"):\n        paragraphs[0] = paragraphs[0].replace(\"\\n\", \" \")\n    elif paragraphs[0].startswith(\"\\b\"):\n        paragraphs[0] = paragraphs[0].replace(\"\\b\\n\", \"\")\n    return _make_rich_text(\n        text=paragraphs[0].strip(),\n        style=STYLE_OPTION_HELP,\n        markup_mode=markup_mode,\n    )\n\n\ndef _print_options_panel(\n    *,\n    name: str,\n    params: list[click.Option] | list[click.Argument],\n    ctx: click.Context,\n    markup_mode: MarkupModeStrict,\n    console: Console,\n) -> None:\n    options_rows: list[list[RenderableType]] = []\n    required_rows: list[str | Text] = []\n    for param in params:\n        # Short and long form\n        opt_long_strs = []\n        opt_short_strs = []\n        secondary_opt_long_strs = []\n        secondary_opt_short_strs = []\n        for opt_str in param.opts:\n            if \"--\" in opt_str:\n                opt_long_strs.append(opt_str)\n            else:\n                opt_short_strs.append(opt_str)\n        for opt_str in param.secondary_opts:\n            if \"--\" in opt_str:\n                secondary_opt_long_strs.append(opt_str)\n            else:\n                secondary_opt_short_strs.append(opt_str)\n\n        # Column for a metavar, if we have one\n        metavar = Text(style=STYLE_METAVAR, overflow=\"fold\")\n        metavar_str = param.make_metavar(ctx=ctx)\n        # Do it ourselves if this is a positional argument\n        if (\n            isinstance(param, click.Argument)\n            and param.name\n            and metavar_str == param.name.upper()\n        ):\n            metavar_str = param.type.name.upper()\n\n        # Skip booleans and choices (handled above)\n        if metavar_str != \"BOOLEAN\":\n            metavar.append(metavar_str)\n\n        # Range - from\n        # https://github.com/pallets/click/blob/c63c70dabd3f86ca68678b4f00951f78f52d0270/src/click/core.py#L2698-L2706  # noqa: E501\n        # skip count with default range type\n        if (\n            isinstance(param.type, click.types._NumberRangeBase)\n            and isinstance(param, click.Option)\n            and not (param.count and param.type.min == 0 and param.type.max is None)\n        ):\n            range_str = param.type._describe_range()\n            if range_str:\n                metavar.append(RANGE_STRING.format(range_str))\n\n        # Required asterisk\n        required: str | Text = \"\"\n        if param.required:\n            required = Text(REQUIRED_SHORT_STRING, style=STYLE_REQUIRED_SHORT)\n\n        required_rows.append(required)\n        options_rows.append(\n            [\n                highlighter(\",\".join(opt_long_strs)),\n                highlighter(\",\".join(opt_short_strs)),\n                negative_highlighter(\",\".join(secondary_opt_long_strs)),\n                negative_highlighter(\",\".join(secondary_opt_short_strs)),\n                metavar_highlighter(metavar),\n                _get_parameter_help(\n                    param=param,\n                    ctx=ctx,\n                    markup_mode=markup_mode,\n                ),\n            ]\n        )\n    rows_with_required: list[list[RenderableType]] = []\n    if any(required_rows):\n        for required, row in zip(required_rows, options_rows, strict=True):\n            rows_with_required.append([required, *row])\n    else:\n        rows_with_required = options_rows\n    if options_rows:\n        t_styles: dict[str, Any] = {\n            \"show_lines\": STYLE_OPTIONS_TABLE_SHOW_LINES,\n            \"leading\": STYLE_OPTIONS_TABLE_LEADING,\n            \"box\": STYLE_OPTIONS_TABLE_BOX,\n            \"border_style\": STYLE_OPTIONS_TABLE_BORDER_STYLE,\n            \"row_styles\": STYLE_OPTIONS_TABLE_ROW_STYLES,\n            \"pad_edge\": STYLE_OPTIONS_TABLE_PAD_EDGE,\n            \"padding\": STYLE_OPTIONS_TABLE_PADDING,\n        }\n        box_style = getattr(box, t_styles.pop(\"box\"), None)\n\n        options_table = Table(\n            highlight=True,\n            show_header=False,\n            expand=True,\n            box=box_style,\n            **t_styles,\n        )\n        for row in rows_with_required:\n            options_table.add_row(*row)\n        console.print(\n            Panel(\n                options_table,\n                border_style=STYLE_OPTIONS_PANEL_BORDER,\n                title=name,\n                title_align=ALIGN_OPTIONS_PANEL,\n            )\n        )\n\n\ndef _print_commands_panel(\n    *,\n    name: str,\n    commands: list[click.Command],\n    markup_mode: MarkupModeStrict,\n    console: Console,\n    cmd_len: int,\n) -> None:\n    t_styles: dict[str, Any] = {\n        \"show_lines\": STYLE_COMMANDS_TABLE_SHOW_LINES,\n        \"leading\": STYLE_COMMANDS_TABLE_LEADING,\n        \"box\": STYLE_COMMANDS_TABLE_BOX,\n        \"border_style\": STYLE_COMMANDS_TABLE_BORDER_STYLE,\n        \"row_styles\": STYLE_COMMANDS_TABLE_ROW_STYLES,\n        \"pad_edge\": STYLE_COMMANDS_TABLE_PAD_EDGE,\n        \"padding\": STYLE_COMMANDS_TABLE_PADDING,\n    }\n    box_style = getattr(box, t_styles.pop(\"box\"), None)\n\n    commands_table = Table(\n        highlight=False,\n        show_header=False,\n        expand=True,\n        box=box_style,\n        **t_styles,\n    )\n    # Define formatting in first column, as commands don't match highlighter\n    # regex\n    commands_table.add_column(\n        style=STYLE_COMMANDS_TABLE_FIRST_COLUMN,\n        no_wrap=True,\n        width=cmd_len,\n    )\n\n    # A big ratio makes the description column be greedy and take all the space\n    # available instead of allowing the command column to grow and misalign with\n    # other panels.\n    commands_table.add_column(\"Description\", justify=\"left\", no_wrap=False, ratio=10)\n    rows: list[list[RenderableType | None]] = []\n    deprecated_rows: list[RenderableType | None] = []\n    for command in commands:\n        helptext = command.short_help or command.help or \"\"\n        command_name = command.name or \"\"\n        if command.deprecated:\n            command_name_text = Text(f\"{command_name}\", style=STYLE_DEPRECATED_COMMAND)\n            deprecated_rows.append(Text(DEPRECATED_STRING, style=STYLE_DEPRECATED))\n        else:\n            command_name_text = Text(command_name)\n            deprecated_rows.append(None)\n        rows.append(\n            [\n                command_name_text,\n                _make_command_help(\n                    help_text=helptext,\n                    markup_mode=markup_mode,\n                ),\n            ]\n        )\n    rows_with_deprecated = rows\n    if any(deprecated_rows):\n        rows_with_deprecated = []\n        for row, deprecated_text in zip(rows, deprecated_rows, strict=True):\n            rows_with_deprecated.append([*row, deprecated_text])\n    for row in rows_with_deprecated:\n        commands_table.add_row(*row)\n    if commands_table.row_count:\n        console.print(\n            Panel(\n                commands_table,\n                border_style=STYLE_COMMANDS_PANEL_BORDER,\n                title=name,\n                title_align=ALIGN_COMMANDS_PANEL,\n            )\n        )\n\n\ndef rich_format_help(\n    *,\n    obj: click.Command | click.Group,\n    ctx: click.Context,\n    markup_mode: MarkupModeStrict,\n) -> None:\n    \"\"\"Print nicely formatted help text using rich.\n\n    Based on original code from rich-cli, by @willmcgugan.\n    https://github.com/Textualize/rich-cli/blob/8a2767c7a340715fc6fbf4930ace717b9b2fc5e5/src/rich_cli/__main__.py#L162-L236\n\n    Replacement for the click function format_help().\n    Takes a command or group and builds the help text output.\n    \"\"\"\n    console = _get_rich_console()\n\n    # Print usage\n    console.print(\n        Padding(highlighter(obj.get_usage(ctx)), 1), style=STYLE_USAGE_COMMAND\n    )\n\n    # Print command / group help if we have some\n    if obj.help:\n        # Print with some padding\n        console.print(\n            Padding(\n                Align(\n                    _get_help_text(\n                        obj=obj,\n                        markup_mode=markup_mode,\n                    ),\n                    pad=False,\n                ),\n                (0, 1, 1, 1),\n            )\n        )\n    panel_to_arguments: defaultdict[str, list[click.Argument]] = defaultdict(list)\n    panel_to_options: defaultdict[str, list[click.Option]] = defaultdict(list)\n    for param in obj.get_params(ctx):\n        # Skip if option is hidden\n        if getattr(param, \"hidden\", False):\n            continue\n        if isinstance(param, click.Argument):\n            panel_name = (\n                getattr(param, _RICH_HELP_PANEL_NAME, None) or ARGUMENTS_PANEL_TITLE\n            )\n            panel_to_arguments[panel_name].append(param)\n        elif isinstance(param, click.Option):\n            panel_name = (\n                getattr(param, _RICH_HELP_PANEL_NAME, None) or OPTIONS_PANEL_TITLE\n            )\n            panel_to_options[panel_name].append(param)\n    default_arguments = panel_to_arguments.get(ARGUMENTS_PANEL_TITLE, [])\n    _print_options_panel(\n        name=ARGUMENTS_PANEL_TITLE,\n        params=default_arguments,\n        ctx=ctx,\n        markup_mode=markup_mode,\n        console=console,\n    )\n    for panel_name, arguments in panel_to_arguments.items():\n        if panel_name == ARGUMENTS_PANEL_TITLE:\n            # Already printed above\n            continue\n        _print_options_panel(\n            name=panel_name,\n            params=arguments,\n            ctx=ctx,\n            markup_mode=markup_mode,\n            console=console,\n        )\n    default_options = panel_to_options.get(OPTIONS_PANEL_TITLE, [])\n    _print_options_panel(\n        name=OPTIONS_PANEL_TITLE,\n        params=default_options,\n        ctx=ctx,\n        markup_mode=markup_mode,\n        console=console,\n    )\n    for panel_name, options in panel_to_options.items():\n        if panel_name == OPTIONS_PANEL_TITLE:\n            # Already printed above\n            continue\n        _print_options_panel(\n            name=panel_name,\n            params=options,\n            ctx=ctx,\n            markup_mode=markup_mode,\n            console=console,\n        )\n\n    if isinstance(obj, click.Group):\n        panel_to_commands: defaultdict[str, list[click.Command]] = defaultdict(list)\n        for command_name in obj.list_commands(ctx):\n            command = obj.get_command(ctx, command_name)\n            if command and not command.hidden:\n                panel_name = (\n                    getattr(command, _RICH_HELP_PANEL_NAME, None)\n                    or COMMANDS_PANEL_TITLE\n                )\n                panel_to_commands[panel_name].append(command)\n\n        # Identify the longest command name in all panels\n        max_cmd_len = max(\n            [\n                len(command.name or \"\")\n                for commands in panel_to_commands.values()\n                for command in commands\n            ],\n            default=0,\n        )\n\n        # Print each command group panel\n        default_commands = panel_to_commands.get(COMMANDS_PANEL_TITLE, [])\n        _print_commands_panel(\n            name=COMMANDS_PANEL_TITLE,\n            commands=default_commands,\n            markup_mode=markup_mode,\n            console=console,\n            cmd_len=max_cmd_len,\n        )\n        for panel_name, commands in panel_to_commands.items():\n            if panel_name == COMMANDS_PANEL_TITLE:\n                # Already printed above\n                continue\n            _print_commands_panel(\n                name=panel_name,\n                commands=commands,\n                markup_mode=markup_mode,\n                console=console,\n                cmd_len=max_cmd_len,\n            )\n\n    # Epilogue if we have it\n    if obj.epilog:\n        # Remove single linebreaks, replace double with single\n        lines = obj.epilog.split(\"\\n\\n\")\n        epilogue = \"\\n\".join([x.replace(\"\\n\", \" \").strip() for x in lines])\n        epilogue_text = _make_rich_text(text=epilogue, markup_mode=markup_mode)\n        console.print(Padding(Align(epilogue_text, pad=False), 1))\n\n\ndef rich_format_error(self: click.ClickException) -> None:\n    \"\"\"Print richly formatted click errors.\n\n    Called by custom exception handler to print richly formatted click errors.\n    Mimics original click.ClickException.echo() function but with rich formatting.\n    \"\"\"\n    # Don't do anything when it's a NoArgsIsHelpError (without importing it, cf. #1278)\n    if self.__class__.__name__ == \"NoArgsIsHelpError\":\n        return\n\n    console = _get_rich_console(stderr=True)\n    ctx: click.Context | None = getattr(self, \"ctx\", None)\n    if ctx is not None:\n        console.print(ctx.get_usage())\n\n    if ctx is not None and ctx.command.get_help_option(ctx) is not None:\n        console.print(\n            RICH_HELP.format(\n                command_path=ctx.command_path, help_option=ctx.help_option_names[0]\n            ),\n            style=STYLE_ERRORS_SUGGESTION,\n        )\n\n    console.print(\n        Panel(\n            highlighter(self.format_message()),\n            border_style=STYLE_ERRORS_PANEL_BORDER,\n            title=ERRORS_PANEL_TITLE,\n            title_align=ALIGN_ERRORS_PANEL,\n        )\n    )\n\n\ndef rich_abort_error() -> None:\n    \"\"\"Print richly formatted abort error.\"\"\"\n    console = _get_rich_console(stderr=True)\n    console.print(ABORTED_TEXT, style=STYLE_ABORTED)\n\n\ndef escape_before_html_export(input_text: str) -> str:\n    \"\"\"Ensure that the input string can be used for HTML export.\"\"\"\n    return escape(input_text).strip()\n\n\ndef rich_to_html(input_text: str) -> str:\n    \"\"\"Print the HTML version of a rich-formatted input string.\n\n    This function does not provide a full HTML page, but can be used to insert\n    HTML-formatted text spans into a markdown file.\n    \"\"\"\n    console = Console(record=True, highlight=False, file=io.StringIO())\n\n    console.print(input_text, overflow=\"ignore\", crop=False)\n\n    return console.export_html(inline_styles=True, code_format=\"{code}\").strip()\n\n\ndef rich_render_text(text: str) -> str:\n    \"\"\"Remove rich tags and render a pure text representation\"\"\"\n    console = _get_rich_console()\n    return \"\".join(segment.text for segment in console.render(text)).rstrip(\"\\n\")\n\n\ndef get_traceback(\n    exc: BaseException,\n    exception_config: DeveloperExceptionConfig,\n    internal_dir_names: list[str],\n) -> Traceback:\n    rich_tb = Traceback.from_exception(\n        type(exc),\n        exc,\n        exc.__traceback__,\n        show_locals=exception_config.pretty_exceptions_show_locals,\n        suppress=internal_dir_names,\n        width=MAX_WIDTH,\n    )\n    return rich_tb\n"
  },
  {
    "path": "typer/testing.py",
    "content": "from collections.abc import Mapping, Sequence\nfrom typing import IO, Any\n\nfrom click.testing import CliRunner as ClickCliRunner  # noqa\nfrom click.testing import Result\nfrom typer.main import Typer\nfrom typer.main import get_command as _get_command\n\n\nclass CliRunner(ClickCliRunner):\n    def invoke(  # type: ignore\n        self,\n        app: Typer,\n        args: str | Sequence[str] | None = None,\n        input: bytes | str | IO[Any] | None = None,\n        env: Mapping[str, str | None] | None = None,\n        catch_exceptions: bool = True,\n        color: bool = False,\n        **extra: Any,\n    ) -> Result:\n        use_cli = _get_command(app)\n        return super().invoke(\n            use_cli,\n            args=args,\n            input=input,\n            env=env,\n            catch_exceptions=catch_exceptions,\n            color=color,\n            **extra,\n        )\n"
  },
  {
    "path": "typer/utils.py",
    "content": "import inspect\nfrom collections.abc import Callable\nfrom copy import copy\nfrom typing import Any, cast\n\nfrom ._typing import Annotated, get_args, get_origin, get_type_hints\nfrom .models import ArgumentInfo, OptionInfo, ParameterInfo, ParamMeta\n\n\ndef _param_type_to_user_string(param_type: type[ParameterInfo]) -> str:\n    # Render a `ParameterInfo` subclass for use in error messages.\n    # User code doesn't call `*Info` directly, so errors should present the classes how\n    # they were (probably) defined in the user code.\n    if param_type is OptionInfo:\n        return \"`Option`\"\n    elif param_type is ArgumentInfo:\n        return \"`Argument`\"\n    # This line shouldn't be reachable during normal use.\n    return f\"`{param_type.__name__}`\"  # pragma: no cover\n\n\nclass AnnotatedParamWithDefaultValueError(Exception):\n    argument_name: str\n    param_type: type[ParameterInfo]\n\n    def __init__(self, argument_name: str, param_type: type[ParameterInfo]):\n        self.argument_name = argument_name\n        self.param_type = param_type\n\n    def __str__(self) -> str:\n        param_type_str = _param_type_to_user_string(self.param_type)\n        return (\n            f\"{param_type_str} default value cannot be set in `Annotated`\"\n            f\" for {self.argument_name!r}. Set the default value with `=` instead.\"\n        )\n\n\nclass MixedAnnotatedAndDefaultStyleError(Exception):\n    argument_name: str\n    annotated_param_type: type[ParameterInfo]\n    default_param_type: type[ParameterInfo]\n\n    def __init__(\n        self,\n        argument_name: str,\n        annotated_param_type: type[ParameterInfo],\n        default_param_type: type[ParameterInfo],\n    ):\n        self.argument_name = argument_name\n        self.annotated_param_type = annotated_param_type\n        self.default_param_type = default_param_type\n\n    def __str__(self) -> str:\n        annotated_param_type_str = _param_type_to_user_string(self.annotated_param_type)\n        default_param_type_str = _param_type_to_user_string(self.default_param_type)\n        msg = f\"Cannot specify {annotated_param_type_str} in `Annotated` and\"\n        if self.annotated_param_type is self.default_param_type:\n            msg += \" default value\"\n        else:\n            msg += f\" {default_param_type_str} as a default value\"\n        msg += f\" together for {self.argument_name!r}\"\n        return msg\n\n\nclass MultipleTyperAnnotationsError(Exception):\n    argument_name: str\n\n    def __init__(self, argument_name: str):\n        self.argument_name = argument_name\n\n    def __str__(self) -> str:\n        return (\n            \"Cannot specify multiple `Annotated` Typer arguments\"\n            f\" for {self.argument_name!r}\"\n        )\n\n\nclass DefaultFactoryAndDefaultValueError(Exception):\n    argument_name: str\n    param_type: type[ParameterInfo]\n\n    def __init__(self, argument_name: str, param_type: type[ParameterInfo]):\n        self.argument_name = argument_name\n        self.param_type = param_type\n\n    def __str__(self) -> str:\n        param_type_str = _param_type_to_user_string(self.param_type)\n        return (\n            \"Cannot specify `default_factory` and a default value together\"\n            f\" for {param_type_str}\"\n        )\n\n\ndef _split_annotation_from_typer_annotations(\n    base_annotation: type[Any],\n) -> tuple[type[Any], list[ParameterInfo]]:\n    if get_origin(base_annotation) is not Annotated:\n        return base_annotation, []\n    base_annotation, *maybe_typer_annotations = get_args(base_annotation)\n    return base_annotation, [\n        annotation\n        for annotation in maybe_typer_annotations\n        if isinstance(annotation, ParameterInfo)\n    ]\n\n\ndef get_params_from_function(func: Callable[..., Any]) -> dict[str, ParamMeta]:\n    signature = inspect.signature(func, eval_str=True)\n    type_hints = get_type_hints(func)\n    params = {}\n    for param in signature.parameters.values():\n        annotation, typer_annotations = _split_annotation_from_typer_annotations(\n            param.annotation,\n        )\n        if len(typer_annotations) > 1:\n            raise MultipleTyperAnnotationsError(param.name)\n\n        default = param.default\n        if typer_annotations:\n            # It's something like `my_param: Annotated[str, Argument()]`\n            [parameter_info] = typer_annotations\n\n            # Forbid `my_param: Annotated[str, Argument()] = Argument(\"...\")`\n            if isinstance(param.default, ParameterInfo):\n                raise MixedAnnotatedAndDefaultStyleError(\n                    argument_name=param.name,\n                    annotated_param_type=type(parameter_info),\n                    default_param_type=type(param.default),\n                )\n\n            parameter_info = copy(parameter_info)\n\n            # When used as a default, `Option` takes a default value and option names\n            # as positional arguments:\n            #   `Option(some_value, \"--some-argument\", \"-s\")`\n            # When used in `Annotated` (ie, what this is handling), `Option` just takes\n            # option names as positional arguments:\n            #   `Option(\"--some-argument\", \"-s\")`\n            # In this case, the `default` attribute of `parameter_info` is actually\n            # meant to be the first item of `param_decls`.\n            if (\n                isinstance(parameter_info, OptionInfo)\n                and parameter_info.default is not ...\n            ):\n                parameter_info.param_decls = (\n                    cast(str, parameter_info.default),\n                    *(parameter_info.param_decls or ()),\n                )\n                parameter_info.default = ...\n\n            # Forbid `my_param: Annotated[str, Argument('some-default')]`\n            if parameter_info.default is not ...:\n                raise AnnotatedParamWithDefaultValueError(\n                    param_type=type(parameter_info),\n                    argument_name=param.name,\n                )\n            if param.default is not param.empty:\n                # Put the parameter's default (set by `=`) into `parameter_info`, where\n                # typer can find it.\n                parameter_info.default = param.default\n\n            default = parameter_info\n        elif param.name in type_hints:\n            # Resolve forward references.\n            annotation = type_hints[param.name]\n\n        if isinstance(default, ParameterInfo):\n            parameter_info = copy(default)\n            # Click supports `default` as either\n            # - an actual value; or\n            # - a factory function (returning a default value.)\n            # The two are not interchangeable for static typing, so typer allows\n            # specifying `default_factory`. Move the `default_factory` into `default`\n            # so click can find it.\n            if parameter_info.default is ... and parameter_info.default_factory:\n                parameter_info.default = parameter_info.default_factory\n            elif parameter_info.default_factory:\n                raise DefaultFactoryAndDefaultValueError(\n                    argument_name=param.name, param_type=type(parameter_info)\n                )\n            default = parameter_info\n\n        params[param.name] = ParamMeta(\n            name=param.name, default=default, annotation=annotation\n        )\n    return params\n\n\ndef parse_boolean_env_var(env_var_value: str | None, default: bool) -> bool:\n    if env_var_value is None:\n        return default\n    value = env_var_value.lower()\n    if value in (\"y\", \"yes\", \"t\", \"true\", \"on\", \"1\"):\n        return True\n    if value in (\"n\", \"no\", \"f\", \"false\", \"off\", \"0\"):\n        return False\n    return default\n"
  },
  {
    "path": "typer-cli/README.md",
    "content": "<p align=\"center\">\n  <a href=\"https://typer.tiangolo.com\"><img src=\"https://typer.tiangolo.com/img/logo-margin/logo-margin-vector.svg\" alt=\"Typer\"></a>\n</p>\n<p align=\"center\">\n    <em>Typer, build great CLIs. Easy to code. Based on Python type hints.</em>\n</p>\n<p align=\"center\">\n<a href=\"https://github.com/fastapi/typer/actions?query=workflow%3ATest+event%3Apush+branch%3Amaster\" target=\"_blank\">\n    <img src=\"https://github.com/fastapi/typer/actions/workflows/test.yml/badge.svg?event=push&branch=master\" alt=\"Test\">\n</a>\n<a href=\"https://github.com/fastapi/typer/actions?query=workflow%3APublish\" target=\"_blank\">\n    <img src=\"https://github.com/fastapi/typer/workflows/Publish/badge.svg\" alt=\"Publish\">\n</a>\n<a href=\"https://coverage-badge.samuelcolvin.workers.dev/redirect/fastapi/typer\" target=\"_blank\">\n    <img src=\"https://coverage-badge.samuelcolvin.workers.dev/fastapi/typer.svg\" alt=\"Coverage\">\n<a href=\"https://pypi.org/project/typer\" target=\"_blank\">\n    <img src=\"https://img.shields.io/pypi/v/typer?color=%2334D058&label=pypi%20package\" alt=\"Package version\">\n</a>\n</p>\n\n---\n\n**Documentation**: <a href=\"https://typer.tiangolo.com\" target=\"_blank\">https://typer.tiangolo.com/tutorial/typer-command/</a>\n\n**Source Code**: <a href=\"https://github.com/fastapi/typer\" target=\"_blank\">https://github.com/fastapi/typer</a>\n\n---\n\nTyper is a library for building <abbr title=\"command line interface, programs executed from a terminal\">CLI</abbr> applications that users will **love using** and developers will **love creating**. Based on Python type hints.\n\nIt's also a command line tool to run scripts, automatically converting them to CLI applications.\n\n## Typer CLI\n\n⚠️ Do not install this package. ⚠️\n\nThis package, `typer-cli`, does nothing other than depend on `typer`.\n\nAll the functionality has been integrated into `typer`.\n\nThe only reason this package exists is as a migration path for old projects that used to depend on `typer-cli`, so that they can get the latest version of `typer`.\n\nYou **should not** install this package.\n\nInstall instead:\n\n```bash\npip install typer\n```\n\nThat includes the `typer` command.\n\nThis package is deprecated and will stop receiving any updates and published versions.\n\n## License\n\nThis project is licensed under the terms of the MIT license.\n"
  },
  {
    "path": "typer-slim/README.md",
    "content": "<p align=\"center\">\n  <a href=\"https://typer.tiangolo.com\"><img src=\"https://typer.tiangolo.com/img/logo-margin/logo-margin-vector.svg\" alt=\"Typer\"></a>\n</p>\n<p align=\"center\">\n    <em>Typer, build great CLIs. Easy to code. Based on Python type hints.</em>\n</p>\n<p align=\"center\">\n<a href=\"https://github.com/fastapi/typer/actions?query=workflow%3ATest+event%3Apush+branch%3Amaster\" target=\"_blank\">\n    <img src=\"https://github.com/fastapi/typer/actions/workflows/test.yml/badge.svg?event=push&branch=master\" alt=\"Test\">\n</a>\n<a href=\"https://github.com/fastapi/typer/actions?query=workflow%3APublish\" target=\"_blank\">\n    <img src=\"https://github.com/fastapi/typer/workflows/Publish/badge.svg\" alt=\"Publish\">\n</a>\n<a href=\"https://coverage-badge.samuelcolvin.workers.dev/redirect/fastapi/typer\" target=\"_blank\">\n    <img src=\"https://coverage-badge.samuelcolvin.workers.dev/fastapi/typer.svg\" alt=\"Coverage\">\n<a href=\"https://pypi.org/project/typer\" target=\"_blank\">\n    <img src=\"https://img.shields.io/pypi/v/typer?color=%2334D058&label=pypi%20package\" alt=\"Package version\">\n</a>\n</p>\n\n---\n\n**Documentation**: <a href=\"https://typer.tiangolo.com\" target=\"_blank\">https://typer.tiangolo.com/tutorial/typer-command/</a>\n\n**Source Code**: <a href=\"https://github.com/fastapi/typer\" target=\"_blank\">https://github.com/fastapi/typer</a>\n\n---\n\nTyper is a library for building <abbr title=\"command line interface, programs executed from a terminal\">CLI</abbr> applications that users will **love using** and developers will **love creating**. Based on Python type hints.\n\nIt's also a command line tool to run scripts, automatically converting them to CLI applications.\n\n## `typer-slim`\n\n⚠️ Do not install this package. ⚠️\n\nThis package, `typer-slim`, does nothing other than depend on `typer`.\n\nThere used to be a slimmed-down version of Typer called `typer-slim`, which didn't include the dependencies `rich` and `shellingham`, nor the `typer` command.\n\nHowever, since version 0.22.0, we have stopped supporting this, and `typer-slim` now simply installs (all of) Typer.\n\nIf you want to disable Rich globally, you can set an environmental variable `TYPER_USE_RICH` to `False` or `0`.\n\nThe only reason this package exists is as a migration path for old projects that used to depend on `typer-slim`, so that they can get the latest version of `typer`.\n\nYou **should not** install this package.\n\nInstall instead:\n\n```bash\npip install typer\n```\n\nThis package is deprecated and will stop receiving any updates and published versions.\n\n## License\n\nThis project is licensed under the terms of the MIT license.\n"
  }
]