diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 875263fe73..cb207b60e2 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-20.04 strategy: matrix: - python-version: ["3.6", "3.7", "3.8", "3.9", "3.10", "3.11"] + python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"] click-7: [true, false] fail-fast: false @@ -25,17 +25,12 @@ jobs: - name: Install Flit run: pip install flit - name: Install Dependencies - if: ${{ matrix.python-version != '3.6' }} run: python -m flit install --symlink - - name: Install Dependencies - if: ${{ matrix.python-version == '3.6' }} - # This doesn't install the editable install, so coverage doesn't get subprocesses - run: python -m pip install ".[test]" - name: Install Click 7 if: matrix.click-7 run: pip install "click<8.0.0" - name: Lint - if: ${{ matrix.python-version != '3.6' && matrix.click-7 == false }} + if: ${{ matrix.click-7 == false }} run: bash scripts/lint.sh - run: mkdir coverage - name: Test diff --git a/.gitignore b/.gitignore index 8ab16b46cb..1509614ce0 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,6 @@ *.pyc __pycache__ env3.7 -env3.6 env dist .mypy_cache diff --git a/README.md b/README.md index de9420074c..98c6aaad2b 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ --- -Typer is a library for building CLI applications that users will **love using** and developers will **love creating**. Based on Python 3.6+ type hints. +Typer is a library for building CLI applications that users will **love using** and developers will **love creating**. Based on Python 3.7+ type hints. The key features are: @@ -46,7 +46,7 @@ And it's intended to be the FastAPI of CLIs. ## Requirements -Python 3.6+ +Python 3.7+ **Typer** stands on the shoulders of a giant. Its only internal dependency is Click. @@ -286,7 +286,7 @@ You do that with standard modern Python types. You don't have to learn a new syntax, the methods or classes of a specific library, etc. -Just standard **Python 3.6+**. +Just standard **Python 3.7+**. For example, for an `int`: diff --git a/docs/features.md b/docs/features.md index 6b2bbb2b51..e218f25d76 100644 --- a/docs/features.md +++ b/docs/features.md @@ -8,7 +8,7 @@ It follows the same design and ideas. If you know **FastAPI**, you already know ## Just Modern Python -It's all based on standard **Python 3.6 type** declarations. No new syntax to learn. Just standard modern Python. +It's all based on standard **Python 3.7 type** declarations. No new syntax to learn. Just standard modern Python. If 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: Python types intro. diff --git a/docs/index.md b/docs/index.md index de9420074c..98c6aaad2b 100644 --- a/docs/index.md +++ b/docs/index.md @@ -26,7 +26,7 @@ --- -Typer is a library for building CLI applications that users will **love using** and developers will **love creating**. Based on Python 3.6+ type hints. +Typer is a library for building CLI applications that users will **love using** and developers will **love creating**. Based on Python 3.7+ type hints. The key features are: @@ -46,7 +46,7 @@ And it's intended to be the FastAPI of CLIs. ## Requirements -Python 3.6+ +Python 3.7+ **Typer** stands on the shoulders of a giant. Its only internal dependency is Click. @@ -286,7 +286,7 @@ You do that with standard modern Python types. You don't have to learn a new syntax, the methods or classes of a specific library, etc. -Just standard **Python 3.6+**. +Just standard **Python 3.7+**. For example, for an `int`: diff --git a/docs/tutorial/package.md b/docs/tutorial/package.md index 8ad35e60ab..7178b4ed81 100644 --- a/docs/tutorial/package.md +++ b/docs/tutorial/package.md @@ -52,7 +52,7 @@ Add `typer[all]` to your dependencies: $ poetry add "typer[all]" // It creates a virtual environment for your project -Creating virtualenv rick-portal-gun-w31dJa0b-py3.6 in /home/rick/.cache/pypoetry/virtualenvs +Creating virtualenv rick-portal-gun-w31dJa0b-py3.7 in /home/rick/.cache/pypoetry/virtualenvs Using version ^0.1.0 for typer Updating dependencies @@ -83,7 +83,7 @@ Package operations: 15 installs, 0 updates, 0 removals // Activate that new virtual environment $ poetry shell -Spawning shell within /home/rick/.cache/pypoetry/virtualenvs/rick-portal-gun-w31dJa0b-py3.6 +Spawning shell within /home/rick/.cache/pypoetry/virtualenvs/rick-portal-gun-w31dJa0b-py3.7 // Open an editor using this new environment, for example VS Code $ code ./ @@ -174,7 +174,7 @@ description = "" authors = ["Rick Sanchez "] [tool.poetry.dependencies] -python = "^3.6" +python = "^3.7" typer = {extras = ["all"], version = "^0.1.0"} [tool.poetry.dev-dependencies] @@ -198,7 +198,7 @@ authors = ["Rick Sanchez "] readme = "README.md" [tool.poetry.dependencies] -python = "^3.6" +python = "^3.7" typer = {extras = ["all"], version = "^0.1.0"} [tool.poetry.dev-dependencies] @@ -229,7 +229,7 @@ readme = "README.md" rick-portal-gun = "rick_portal_gun.main:app" [tool.poetry.dependencies] -python = "^3.6" +python = "^3.7" typer = {extras = ["all"], version = "^0.1.0"} [tool.poetry.dev-dependencies] @@ -302,7 +302,7 @@ Your package is installed in the environment created by Poetry, but you can alre $ which rick-portal-gun // You get the one from your environment -/home/rick/.cache/pypoetry/virtualenvs/rick-portal-gun-w31dJa0b-py3.6/bin/rick-portal-gun +/home/rick/.cache/pypoetry/virtualenvs/rick-portal-gun-w31dJa0b-py3.7/bin/rick-portal-gun // Try it $ rick-portal-gun @@ -659,8 +659,8 @@ Found existing installation: rick-portal-gun 0.1.0 Uninstalling rick-portal-gun-0.1.0: Would remove: /home/user/.local/bin/rick-portal-gun - /home/user/.local/lib/python3.6/site-packages/rick_portal_gun-0.1.0.dist-info/* - /home/user/.local/lib/python3.6/site-packages/rick_portal_gun/* + /home/user/.local/lib/python3.7/site-packages/rick_portal_gun-0.1.0.dist-info/* + /home/user/.local/lib/python3.7/site-packages/rick_portal_gun/* # Proceed (y/n)? $ y Successfully uninstalled rick-portal-gun-0.1.0 ``` @@ -677,10 +677,10 @@ $ pip install --user rick-portal-gun // Notice that it says "Downloading" 🚀 Collecting rick-portal-gun Downloading rick_portal_gun-0.1.0-py3-none-any.whl (1.8 kB) -Requirement already satisfied: typer[all]<0.0.12,>=0.0.11 in ./.local/lib/python3.6/site-packages (from rick-portal-gun) (0.0.11) -Requirement already satisfied: click<7.2.0,>=7.1.1 in ./anaconda3/lib/python3.6/site-packages (from typer[all]<0.0.12,>=0.0.11->rick-portal-gun) (7.1.1) -Requirement already satisfied: colorama; extra == "all" in ./anaconda3/lib/python3.6/site-packages (from typer[all]<0.0.12,>=0.0.11->rick-portal-gun) (0.4.3) -Requirement already satisfied: shellingham; extra == "all" in ./anaconda3/lib/python3.6/site-packages (from typer[all]<0.0.12,>=0.0.11->rick-portal-gun) (1.3.1) +Requirement already satisfied: typer[all]<0.0.12,>=0.0.11 in ./.local/lib/python3.7/site-packages (from rick-portal-gun) (0.0.11) +Requirement already satisfied: click<7.2.0,>=7.1.1 in ./anaconda3/lib/python3.7/site-packages (from typer[all]<0.0.12,>=0.0.11->rick-portal-gun) (7.1.1) +Requirement already satisfied: colorama; extra == "all" in ./anaconda3/lib/python3.7/site-packages (from typer[all]<0.0.12,>=0.0.11->rick-portal-gun) (0.4.3) +Requirement already satisfied: shellingham; extra == "all" in ./anaconda3/lib/python3.7/site-packages (from typer[all]<0.0.12,>=0.0.11->rick-portal-gun) (1.3.1) Installing collected packages: rick-portal-gun Successfully installed rick-portal-gun-0.1.0 ``` @@ -738,7 +738,7 @@ readme = "README.md" rick-portal-gun = "rick_portal_gun.main:app" [tool.poetry.dependencies] -python = "^3.6" +python = "^3.7" typer = {extras = ["all"], version = "^0.1.0"} [tool.poetry.dev-dependencies] diff --git a/docs/tutorial/testing.md b/docs/tutorial/testing.md index c8b2b76121..31ca867367 100644 --- a/docs/tutorial/testing.md +++ b/docs/tutorial/testing.md @@ -85,7 +85,7 @@ Then you can call `pytest` in your directory and it will run your tests: $ pytest ================ test session starts ================ -platform linux -- Python 3.6.9, pytest-5.3.5, py-1.8.1, pluggy-0.13.1 +platform linux -- Python 3.7.9, pytest-5.3.5, py-1.8.1, pluggy-0.13.1 rootdir: /home/user/code/superawesome-cli/app plugins: forked-1.1.3, xdist-1.31.0, cov-2.8.1 collected 1 item diff --git a/pyproject.toml b/pyproject.toml index acaf8f895d..e10b2da8da 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,7 +21,6 @@ classifiers = [ "Development Status :: 4 - Beta", "Intended Audience :: Developers", "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", @@ -33,7 +32,7 @@ requires = [ "click >= 7.1.1, <9.0.0" ] description-file = "README.md" -requires-python = ">=3.6" +requires-python = ">=3.7" [tool.flit.metadata.urls] Documentation = "https://typer.tiangolo.com/" @@ -46,7 +45,7 @@ test = [ "coverage >=6.2,<7.0", "pytest-xdist >=1.32.0,<4.0.0", "pytest-sugar >=0.9.4,<0.10.0", - "mypy ==0.910", + "mypy ==1.0.1", "black >=22.3.0,<23.0.0", "isort >=5.0.6,<6.0.0", "rich >=10.11.0,<13.0.0", diff --git a/scripts/netlify-docs.sh b/scripts/netlify-docs.sh index e77fbc39ff..1f48dc5b4e 100755 --- a/scripts/netlify-docs.sh +++ b/scripts/netlify-docs.sh @@ -4,11 +4,11 @@ set -e # Install pip cd /tmp curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py -python3.6 get-pip.py --user +python3.7 get-pip.py --user cd - # Install Flit to be able to install all -python3.6 -m pip install --user flit +python3.7 -m pip install --user flit # Install with Flit -python3.6 -m flit install --user --deps develop +python3.7 -m flit install --user --deps develop # Finally, run mkdocs -python3.6 -m mkdocs build +python3.7 -m mkdocs build diff --git a/tests/test_others.py b/tests/test_others.py index fb41bdc4f3..a0e1e544b2 100644 --- a/tests/test_others.py +++ b/tests/test_others.py @@ -99,8 +99,7 @@ def test_completion_untyped_parameters(): file_path = Path(__file__).parent / "assets/completion_no_types.py" result = subprocess.run( [sys.executable, "-m", "coverage", "run", str(file_path)], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, + capture_output=True, encoding="utf-8", env={ **os.environ, @@ -121,8 +120,7 @@ def test_completion_untyped_parameters(): result = subprocess.run( [sys.executable, "-m", "coverage", "run", str(file_path)], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, + capture_output=True, encoding="utf-8", ) assert "Hello World" in result.stdout @@ -132,8 +130,7 @@ def test_completion_untyped_parameters_different_order_correct_names(): file_path = Path(__file__).parent / "assets/completion_no_types_order.py" result = subprocess.run( [sys.executable, "-m", "coverage", "run", str(file_path)], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, + capture_output=True, encoding="utf-8", env={ **os.environ, @@ -154,8 +151,7 @@ def test_completion_untyped_parameters_different_order_correct_names(): result = subprocess.run( [sys.executable, "-m", "coverage", "run", str(file_path)], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, + capture_output=True, encoding="utf-8", ) assert "Hello World" in result.stdout diff --git a/tests/test_prog_name.py b/tests/test_prog_name.py index 58626c7ac2..cfb5a3464f 100644 --- a/tests/test_prog_name.py +++ b/tests/test_prog_name.py @@ -7,8 +7,7 @@ def test_custom_prog_name(): file_path = Path(__file__).parent / "assets/prog_name.py" result = subprocess.run( [sys.executable, "-m", "coverage", "run", str(file_path), "--help"], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, + capture_output=True, encoding="utf-8", ) assert "Usage: custom-name [OPTIONS] I" in result.stdout diff --git a/tests/test_tracebacks.py b/tests/test_tracebacks.py index 0289e9c942..8c8ab0295d 100644 --- a/tests/test_tracebacks.py +++ b/tests/test_tracebacks.py @@ -8,8 +8,7 @@ def test_traceback_no_rich(): file_path = Path(__file__).parent / "assets/type_error_no_rich.py" result = subprocess.run( [sys.executable, "-m", "coverage", "run", str(file_path)], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, + capture_output=True, encoding="utf-8", env={**os.environ, "_TYPER_STANDARD_TRACEBACK": ""}, ) @@ -17,19 +16,14 @@ def test_traceback_no_rich(): assert "typer.run(main)" in result.stderr assert "print(name + 3)" in result.stderr - # TODO: when deprecating Python 3.6, remove second option - assert ( - 'TypeError: can only concatenate str (not "int") to str' in result.stderr - or "TypeError: must be str, not int" in result.stderr - ) + assert 'TypeError: can only concatenate str (not "int") to str' in result.stderr def test_traceback_no_rich_short_disable(): file_path = Path(__file__).parent / "assets/type_error_no_rich_short_disable.py" result = subprocess.run( [sys.executable, "-m", "coverage", "run", str(file_path)], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, + capture_output=True, encoding="utf-8", env={**os.environ, "_TYPER_STANDARD_TRACEBACK": ""}, ) @@ -37,19 +31,14 @@ def test_traceback_no_rich_short_disable(): assert "app()" in result.stderr assert "print(name + 3)" in result.stderr - # TODO: when deprecating Python 3.6, remove second option - assert ( - 'TypeError: can only concatenate str (not "int") to str' in result.stderr - or "TypeError: must be str, not int" in result.stderr - ) + assert 'TypeError: can only concatenate str (not "int") to str' in result.stderr def test_unmodified_traceback(): file_path = Path(__file__).parent / "assets/type_error_normal_traceback.py" result = subprocess.run( [sys.executable, "-m", "coverage", "run", str(file_path)], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, + capture_output=True, encoding="utf-8", env={**os.environ, "_TYPER_STANDARD_TRACEBACK": ""}, ) @@ -60,8 +49,4 @@ def test_unmodified_traceback(): ) assert "typer.main.get_command(broken_app)()" in result.stderr assert "print(name + 3)" in result.stderr - # TODO: when deprecating Python 3.6, remove second option - assert ( - 'TypeError: can only concatenate str (not "int") to str' in result.stderr - or "TypeError: must be str, not int" in result.stderr - ) + assert 'TypeError: can only concatenate str (not "int") to str' in result.stderr diff --git a/tests/test_tutorial/test_exceptions/test_tutorial001.py b/tests/test_tutorial/test_exceptions/test_tutorial001.py index 0a4590bbf5..d58384bcaa 100644 --- a/tests/test_tutorial/test_exceptions/test_tutorial001.py +++ b/tests/test_tutorial/test_exceptions/test_tutorial001.py @@ -24,11 +24,7 @@ def test_traceback_rich(): assert "typer.run(main)" not in result.stderr assert "print(name + 3)" in result.stderr - # TODO: when deprecating Python 3.6, remove second option - assert ( - 'TypeError: can only concatenate str (not "int") to str' in result.stderr - or "TypeError: must be str, not int" in result.stderr - ) + assert 'TypeError: can only concatenate str (not "int") to str' in result.stderr assert "name = 'morty'" in result.stderr @@ -46,11 +42,7 @@ def test_standard_traceback_env_var(): assert "typer.run(main)" in result.stderr assert "print(name + 3)" in result.stderr - # TODO: when deprecating Python 3.6, remove second option - assert ( - 'TypeError: can only concatenate str (not "int") to str' in result.stderr - or "TypeError: must be str, not int" in result.stderr - ) + assert 'TypeError: can only concatenate str (not "int") to str' in result.stderr assert "name = 'morty'" not in result.stderr diff --git a/tests/test_tutorial/test_exceptions/test_tutorial002.py b/tests/test_tutorial/test_exceptions/test_tutorial002.py index 66286e064d..87390c821e 100644 --- a/tests/test_tutorial/test_exceptions/test_tutorial002.py +++ b/tests/test_tutorial/test_exceptions/test_tutorial002.py @@ -24,11 +24,7 @@ def test_traceback_rich(): assert "app()" not in result.stderr assert "print(password + 3)" in result.stderr - # TODO: when deprecating Python 3.6, remove second option - assert ( - 'TypeError: can only concatenate str (not "int") to str' in result.stderr - or "TypeError: must be str, not int" in result.stderr - ) + assert 'TypeError: can only concatenate str (not "int") to str' in result.stderr assert "name = 'morty'" not in result.stderr @@ -46,11 +42,7 @@ def test_standard_traceback_env_var(): assert "app()" in result.stderr assert "print(password + 3)" in result.stderr - # TODO: when deprecating Python 3.6, remove second option - assert ( - 'TypeError: can only concatenate str (not "int") to str' in result.stderr - or "TypeError: must be str, not int" in result.stderr - ) + assert 'TypeError: can only concatenate str (not "int") to str' in result.stderr assert "name = 'morty'" not in result.stderr diff --git a/tests/test_tutorial/test_exceptions/test_tutorial003.py b/tests/test_tutorial/test_exceptions/test_tutorial003.py index 51cb170eba..86429d4670 100644 --- a/tests/test_tutorial/test_exceptions/test_tutorial003.py +++ b/tests/test_tutorial/test_exceptions/test_tutorial003.py @@ -24,11 +24,7 @@ def test_traceback_rich_pretty_short_disable(): assert "app()" in result.stderr assert "print(name + 3)" in result.stderr - # TODO: when deprecating Python 3.6, remove second option - assert ( - 'TypeError: can only concatenate str (not "int") to str' in result.stderr - or "TypeError: must be str, not int" in result.stderr - ) + assert 'TypeError: can only concatenate str (not "int") to str' in result.stderr assert "name = 'morty'" in result.stderr diff --git a/tests/test_tutorial/test_exceptions/test_tutorial004.py b/tests/test_tutorial/test_exceptions/test_tutorial004.py index bb575e4eb2..417e0d4608 100644 --- a/tests/test_tutorial/test_exceptions/test_tutorial004.py +++ b/tests/test_tutorial/test_exceptions/test_tutorial004.py @@ -23,11 +23,7 @@ def test_rich_pretty_exceptions_disable(): assert "app()" in result.stderr assert "print(name + 3)" in result.stderr - # TODO: when deprecating Python 3.6, remove second option - assert ( - 'TypeError: can only concatenate str (not "int") to str' in result.stderr - or "TypeError: must be str, not int" in result.stderr - ) + assert 'TypeError: can only concatenate str (not "int") to str' in result.stderr def test_script(): diff --git a/typer/_completion_shared.py b/typer/_completion_shared.py index 7cbaf98d75..2e98daa134 100644 --- a/typer/_completion_shared.py +++ b/typer/_completion_shared.py @@ -92,7 +92,7 @@ def get_completion_script(*, prog_name: str, complete_var: str, shell: str) -> s return ( script % dict( - complete_func="_{}_completion".format(cf_name), + complete_func=f"_{cf_name}_completion", prog_name=prog_name, autocomplete_var=complete_var, ) diff --git a/typer/core.py b/typer/core.py index c0776368a6..19d8e2b319 100644 --- a/typer/core.py +++ b/typer/core.py @@ -400,7 +400,7 @@ def make_metavar(self) -> str: return self.metavar var = (self.name or "").upper() if not self.required: - var = "[{}]".format(var) + var = f"[{var}]" type_var = self.type.get_metavar(self) if type_var: var += f":{type_var}" diff --git a/typer/main.py b/typer/main.py index f493e544e2..f10db0b484 100644 --- a/typer/main.py +++ b/typer/main.py @@ -51,7 +51,7 @@ def except_hook( - exc_type: Type[BaseException], exc_value: BaseException, tb: TracebackType + exc_type: Type[BaseException], exc_value: BaseException, tb: Optional[TracebackType] ) -> None: exception_config: Union[DeveloperExceptionConfig, None] = getattr( exc_value, _typer_developer_exception_attr_name, None @@ -96,8 +96,8 @@ def except_hook( ) else: stack.append(frame) - # Type ignore ref: https://github.com/python/typeshed/pull/8244 - final_stack_summary = StackSummary.from_list(stack) # type: ignore + + final_stack_summary = StackSummary.from_list(stack) tb_exc.stack = final_stack_summary for line in tb_exc.format(): print(line, file=sys.stderr)