diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 7bec79c..b9d5c42 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -22,6 +22,7 @@ jobs: - Windows - macOS py: + - 3.10.0-rc.2 - 3.9 - 3.8 - 3.7 @@ -30,7 +31,7 @@ jobs: - name: Setup python for tox uses: actions/setup-python@v2 with: - python-version: 3.9 + python-version: 3.10.0-rc.2 - name: Install tox run: python -m pip install tox - uses: actions/checkout@v2 @@ -81,10 +82,10 @@ jobs: - dev steps: - uses: actions/checkout@v2 - - name: Setup Python 3.9 + - name: Setup Python 3.10.0-rc.2 uses: actions/setup-python@v2 with: - python-version: 3.9 + python-version: 3.10.0-rc.2 - name: Install tox run: python -m pip install tox - name: Run check for ${{ matrix.tox_env }} @@ -100,7 +101,7 @@ jobs: - name: Setup python to build package uses: actions/setup-python@v2 with: - python-version: 3.9 + python-version: 3.10.0-rc.2 - name: Install build run: python -m pip install build - uses: actions/checkout@v2 diff --git a/.gitignore b/.gitignore index 0579b4b..af111cb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,69 +1,13 @@ -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -.Python -env/ -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ +*.pyc +*.pyo +*.swp +__pycache__ +.eggs +/src/retype/version.py +build +dist *.egg-info -.installed.cfg -*.egg - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ .tox -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*,cover -.hypothesis/ - -# Translations -*.mo -*.pot - -# Django stuff: -*.log - -# Sphinx documentation -docs/_build/ - -# PyBuilder -target/ - -#Ipython Notebook -.ipynb_checkpoints - -typed-src/ - +/.*_cache +.dmypy.json pip-wheel-metadata -.mypy_cache - -src/retype/version.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a60c9cd..6fd3dc1 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.0.0 + rev: v4.0.1 hooks: - id: trailing-whitespace - id: end-of-file-fixer @@ -12,15 +12,15 @@ repos: - id: flake8 additional_dependencies: [ "flake8-bugbear", "flake8-pyi" ] - repo: https://github.com/asottile/pyupgrade - rev: v2.15.0 + rev: v2.29.0 hooks: - id: pyupgrade - repo: https://github.com/PyCQA/isort - rev: 5.8.0 + rev: 5.9.3 hooks: - id: isort - repo: https://github.com/python/black - rev: 21.5b1 + rev: 21.9b0 hooks: - id: black args: [ --safe ] @@ -28,7 +28,8 @@ repos: rev: v1.17.0 hooks: - id: setup-cfg-fmt + args: ['--max-py-version', '3.10'] - repo: https://github.com/tox-dev/tox-ini-fmt - rev: "0.5.0" + rev: "0.5.1" hooks: - id: tox-ini-fmt diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 0000000..5f91842 --- /dev/null +++ b/codecov.yml @@ -0,0 +1,6 @@ +coverage: + status: + patch: + default: + informational: true +comment: false diff --git a/setup.cfg b/setup.cfg index 209aede..c8fe924 100644 --- a/setup.cfg +++ b/setup.cfg @@ -21,6 +21,7 @@ classifiers = Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 + Programming Language :: Python :: 3.10 Topic :: Software Development :: Libraries :: Python Modules Topic :: Software Development :: Quality Assurance keywords = mypy typing typehints type hints pep484 pyi stubs @@ -31,9 +32,9 @@ project_urls = [options] packages = find: install_requires = - click + click>=7 pathspec>=0.5.9,<1 - typed-ast + typed-ast>=1.3 python_requires = >=3.6 package_dir = =src @@ -48,8 +49,8 @@ console_scripts = [options.extras_require] testing = - pytest>=3.0.0,<6 - pytest-cov>=2.5.1,<3 + pytest>=6 + pytest-cov>=2.5.1 [flake8] ignore = diff --git a/src/retype/__init__.py b/src/retype/__init__.py index faef8ea..558d45c 100644 --- a/src/retype/__init__.py +++ b/src/retype/__init__.py @@ -74,7 +74,7 @@ def retype_file(src, pyi_dir, targets, *, quiet=False, hg=False, flags=None): with tokenize.open(src) as src_buffer: src_contents = src_buffer.read() if src_contents == "": - return + return None src_encoding = src_buffer.encoding src_node = lib2to3_parse(src_contents) try: @@ -284,7 +284,7 @@ def _r_functiondef(fun, node, flags): raise ValueError( f"Annotation problem in function {name.value!r}: " + f"{lineno}:{column}: {ve}" - ) + ) from ve break else: raise ValueError(f"Function {name.value!r} not found in source.") @@ -1005,7 +1005,7 @@ def get_function_signature(fun, *, is_method=False): raise ValueError( f"Annotation problem in function {fun.name!r}: " + f"{fun.lineno}:{fun.col_offset + 1}: {exc}" - ) + ) from exc copy_type_comments_to_annotations(args) return args, returns @@ -1025,8 +1025,10 @@ def parse_signature_type_comment(type_comment): """ try: result = ast3.parse(type_comment, "", "func_type") - except SyntaxError: - raise ValueError(f"invalid function signature type comment: {type_comment!r}") + except SyntaxError as exc: + raise ValueError( + f"invalid function signature type comment: {type_comment!r}" + ) from exc assert isinstance(result, ast3.FunctionType) if len(result.argtypes) == 1: @@ -1147,11 +1149,10 @@ def copy_type_comment_to_annotation(arg): def normalize_strings_to_repr(node): """Normalize string leaf nodes to a repr since that is what we generate.""" - if node.type == token.STRING: + if isinstance(node, Leaf) and node.type == token.STRING: node.value = repr(ast3.literal_eval(node.value)) elif isinstance(node, Node): node.children = [normalize_strings_to_repr(i) for i in node.children] - return node @@ -1518,8 +1519,9 @@ def walk_not_git_ignored(path, keep, extra_ignore): ) -__all__ = ( +__all__ = [ "__version__", "retype_path", "retype_file", -) + "ReApplyFlags", +] diff --git a/tests/test_discovery.py b/tests/test_discovery.py index a2a7a70..e34aa33 100644 --- a/tests/test_discovery.py +++ b/tests/test_discovery.py @@ -6,7 +6,7 @@ from retype import walk_not_git_ignored -@pytest.fixture() # type: ignore +@pytest.fixture() def build(tmp_path): def _build(files): for file, content in files.items(): @@ -44,9 +44,7 @@ def _build(files): } -@pytest.mark.parametrize( # type: ignore - "case", WALK_TESTS.values(), ids=list(WALK_TESTS.keys()) -) +@pytest.mark.parametrize("case", WALK_TESTS.values(), ids=list(WALK_TESTS.keys())) def test_walk(case: Case, build, monkeypatch): path = build(case.files) dest = path / case.cwd diff --git a/tests/test_retype.py b/tests/test_retype.py index 0bbbf73..af732b4 100644 --- a/tests/test_retype.py +++ b/tests/test_retype.py @@ -4,7 +4,7 @@ from contextlib import contextmanager from pathlib import Path from textwrap import dedent -from typing import Optional +from typing import Iterator, Optional from unittest import TestCase, main from typed_ast import ast3 @@ -22,7 +22,7 @@ @contextmanager -def as_cwd(path): +def as_cwd(path: Path) -> Iterator[None]: old = Path.cwd() try: os.chdir(path) diff --git a/tox.ini b/tox.ini index b4557fa..6485bc3 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,6 @@ [tox] envlist = + py310 py39 py38 py37 @@ -31,6 +32,8 @@ commands = --cov-report xml:{toxworkdir}/coverage.{envname}.xml \ --junitxml {toxworkdir}/junit.{envname}.xml \ tests {posargs} +package = wheel +wheel_build_env = .pkg [testenv:coverage] description = [run locally after tests]: combine coverage data and create report; @@ -52,6 +55,7 @@ commands = coverage html -d {toxworkdir}/htmlcov diff-cover --compare-branch {env:DIFF_AGAINST:origin/master} {toxworkdir}/coverage.xml depends = + py310 py39 py38 py37 @@ -70,7 +74,8 @@ commands = description = run the type checker skip_install = true deps = - mypy==0.782 + mypy==0.910 + types-typed-ast commands = mypy types --strict @@ -92,7 +97,7 @@ commands = description = check that the long description is valid (need for PyPi) skip_install = true deps = - build>=0.0.4 + build>=0.5 twine>=3 extras = commands = diff --git a/types/src/retype/__init__.pyi b/types/src/retype/__init__.pyi index 51f0d2f..633cc33 100644 --- a/types/src/retype/__init__.pyi +++ b/types/src/retype/__init__.pyi @@ -8,7 +8,6 @@ from typing import ( Iterator, List, Optional, - Sequence, Tuple, Type, Union, @@ -17,7 +16,7 @@ from typing import ( from pathspec import PathSpec from typed_ast import ast3 -from retype import ReApplyFlags +from retype import ReApplyFlags # type: ignore # implicit reexport _LN = Union[Node, Leaf] Callbacks = List[Callable[[], None]] @@ -109,6 +108,8 @@ def names_already_imported( ) -> bool: ... def _convert_annotation(ann: ast3.AST) -> _LN: ... def convert_annotation(ann: ast3.AST) -> _LN: ... +def normalize_node(node: _LN) -> _LN: ... +def normalize_strings_to_repr(node: _LN) -> _LN: ... def serialize_attribute(attr: ast3.AST) -> str: ... def reapply( ast_node: ast3.AST, lib2to3_node: Node, flags: ReApplyFlags @@ -126,7 +127,7 @@ def retype_file( quiet: bool = ..., hg: bool = ..., flags: Optional[ReApplyFlags] = ..., -) -> Path: ... +) -> Optional[Path]: ... def retype_path( src: Path, pyi_dir: Path, @@ -165,7 +166,7 @@ def _c_str(s: ast3.Str) -> Leaf: ... def _c_num(n: ast3.Num) -> Leaf: ... def _c_index(index: ast3.Index) -> _LN: ... def _c_tuple(tup: ast3.Tuple) -> Node: ... -def _c_attribute(attr: ast3.Attribute) -> Leaf: ... +def _c_attribute(attr: ast3.Attribute) -> Node: ... def _c_call(call: ast3.Call) -> Node: ... def _c_keyword(kwarg: ast3.keyword) -> Node: ... def _c_list(l: ast3.List) -> Node: ... diff --git a/types/tests/test_discovery.pyi b/types/tests/test_discovery.pyi index b09fcb1..fd4de87 100644 --- a/types/tests/test_discovery.pyi +++ b/types/tests/test_discovery.pyi @@ -5,11 +5,14 @@ from _pytest.monkeypatch import MonkeyPatch Case: NamedTuple = ... -WALK_TESTS: Dict[str, Case] +WALK_TESTS: Dict[str, Case] # type: ignore # retype does not support merging class variant namedtuple with inline one def build(tmp_path: Path) -> Callable[[Dict[str, str]], Path]: def _build(files: Dict[str, str]) -> Path: ... + return _build def test_walk( - case: Case, build: Callable[[Dict[str, str]], Path], monkeypatch: MonkeyPatch + case: Case, # type: ignore # retype does not support merging class variant namedtuple with inline one + build: Callable[[Dict[str, str]], Path], + monkeypatch: MonkeyPatch, ) -> None: ... diff --git a/types/tests/test_retype.pyi b/types/tests/test_retype.pyi index 3dd30a9..09cb59d 100644 --- a/types/tests/test_retype.pyi +++ b/types/tests/test_retype.pyi @@ -1,7 +1,10 @@ from lib2to3.pytree import Leaf, Node +from pathlib import Path from typing import Tuple, Type, TypeVar, Union from unittest import TestCase +from typed_ast import ast3 + _E = TypeVar("_E", bound=Exception) _LN = Union[Node, Leaf] @@ -13,7 +16,7 @@ class RetypeTestCase(TestCase): *, incremental: bool = ..., replace_any: bool = ..., - ) -> Tuple[_LN, _LN]: ... + ) -> Tuple[ast3.Module, Node]: ... def assertReapply( self, pyi_txt: str, @@ -41,3 +44,6 @@ class RetypeTestCase(TestCase): incremental: bool = ..., replace_any: bool = ..., ) -> _E: ... + +def test_can_run_against_current_directory(tmp_path: Path) -> None: ... +def test_does_not_error_on_empty_file(tmp_path: Path) -> None: ...