Skip to content

Commit ae3671d

Browse files
authored
Adds python3.9 and proper MYPYPATH handling (#44)
* Adds python3.9 and proper MYPYPATH handling * Now using Github Actions * Now using Github Actions * Fixes mypy
1 parent 7fa83ef commit ae3671d

File tree

9 files changed

+187
-68
lines changed

9 files changed

+187
-68
lines changed

.github/workflows/misspell.yml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
name: misspell
2+
3+
on:
4+
schedule:
5+
- cron: '0 0 * * *'
6+
7+
jobs:
8+
build:
9+
10+
runs-on: ubuntu-latest
11+
12+
steps:
13+
- uses: actions/checkout@v2
14+
- uses: sobolevn/[email protected]
15+
- uses: peter-evans/[email protected]
16+
with:
17+
token: ${{ secrets.GITHUB_TOKEN }}
18+
commit-message: 'Fixes by misspell-fixer'
19+
title: 'Typos fix by misspell-fixer'

.github/workflows/test.yml

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
name: test
2+
3+
on: [push, pull_request, workflow_dispatch]
4+
5+
jobs:
6+
test:
7+
runs-on: ubuntu-latest
8+
strategy:
9+
matrix:
10+
python-version: [3.6, 3.7, 3.8, 3.9]
11+
12+
steps:
13+
- uses: actions/checkout@v2
14+
- name: Set up Python ${{ matrix.python-version }}
15+
uses: actions/setup-python@v2
16+
with:
17+
python-version: ${{ matrix.python-version }}
18+
- name: Install dependencies
19+
run: |
20+
pip install -U pip setuptools wheel
21+
pip install -r dev-requirements.txt
22+
- name: Run tests
23+
run: pytest
24+
25+
lint:
26+
runs-on: ubuntu-latest
27+
28+
steps:
29+
- uses: actions/checkout@v2
30+
- name: Set up Python 3.7
31+
uses: actions/setup-python@v1
32+
with:
33+
python-version: 3.7
34+
- name: Install dependencies
35+
run: |
36+
pip install -U pip setuptools wheel
37+
pip install -r dev-requirements.txt
38+
- name: Run linters
39+
run: |
40+
mypy .
41+
black --check pytest_mypy_plugins setup.py
42+
isort --check --diff .

.travis.yml

Lines changed: 0 additions & 44 deletions
This file was deleted.

CHANGELOG.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Version history
2+
3+
## Version 1.6.0
4+
5+
### Features
6+
7+
- Adds `python3.9` support
8+
- Bumps required version of `pytest` to `>=6.0`
9+
- Bumps required version of `mypy` to `>=0.790`
10+
11+
### Misc
12+
13+
- Moves from Travis to Github Actions
14+
15+
16+
## Version 1.5.0
17+
18+
### Features
19+
20+
- Adds `PYTHONPATH` and `MYPYPATH` special handling

README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,19 @@ where relative paths such as `PYTHONPATH=./my_plugin` do not reference the direc
3333
If you encounter this, consider invoking `pytest` with `--mypy-same-process` or make your paths absolute,
3434
e.g. `PYTHONPATH=$(pwd)/my_plugin pytest`.
3535

36+
You can also specify `PYTHONPATH`, `MYPYPATH`, or any other environment variable in `env:` section of `yml` spec:
37+
38+
```yml
39+
- case: mypy_path_from_env
40+
main: |
41+
from pair import Pair
42+
43+
instance: Pair
44+
reveal_type(instance) # N: Revealed type is 'pair.Pair'
45+
env:
46+
- MYPYPATH=./pytest_mypy_plugins/tests/fixtures
47+
```
48+
3649
3750
### What is a test case?
3851

pytest_mypy_plugins/item.py

Lines changed: 70 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,29 @@
55
import tempfile
66
from configparser import ConfigParser
77
from pathlib import Path
8-
from typing import Any, Callable, Dict, List, Optional, Tuple
8+
from typing import (
9+
TYPE_CHECKING,
10+
Any,
11+
Callable,
12+
Dict,
13+
List,
14+
Optional,
15+
Tuple,
16+
Union,
17+
)
918

19+
import py
1020
import pytest
1121
from _pytest._code import ExceptionInfo
12-
from _pytest._code.code import ReprEntry, ReprFileLocation
22+
from _pytest._code.code import ReprEntry, ReprFileLocation, TerminalRepr
23+
from _pytest._io import TerminalWriter
1324
from _pytest.config import Config
1425
from mypy import build
1526
from mypy.fscache import FileSystemCache
1627
from mypy.main import process_options
17-
from py._io.terminalwriter import TerminalWriter
28+
29+
if TYPE_CHECKING:
30+
from _pytest._code.code import _TracebackStyle
1831

1932
from pytest_mypy_plugins import utils
2033
from pytest_mypy_plugins.collect import File, YamlTestFile
@@ -28,6 +41,9 @@
2841

2942
class TraceLastReprEntry(ReprEntry):
3043
def toterminal(self, tw: TerminalWriter) -> None:
44+
if not self.reprfileloc:
45+
return
46+
3147
self.reprfileloc.toterminal(tw)
3248
for line in self.lines:
3349
red = line.startswith("E ")
@@ -56,6 +72,12 @@ def replace_fpath_with_module_name(line: str, rootdir: Path) -> str:
5672
return line.strip().replace(".py:", ":")
5773

5874

75+
def maybe_to_abspath(rel_or_abs: str, rootdir: Optional[Path]) -> str:
76+
if rootdir is None or os.path.isabs(rel_or_abs):
77+
return rel_or_abs
78+
return str(rootdir / rel_or_abs)
79+
80+
5981
class ReturnCodes:
6082
SUCCESS = 0
6183
FAIL = 1
@@ -174,22 +196,11 @@ def typecheck_in_new_subprocess(
174196
mypy_executable = distutils.spawn.find_executable("mypy")
175197
assert mypy_executable is not None, "mypy executable is not found"
176198

199+
rootdir = getattr(getattr(self.parent, "config", None), "rootdir", None)
177200
# add current directory to path
178-
self.environment_variables["PYTHONPATH"] = ":".join(
179-
[
180-
os.environ.get("PYTHONPATH", ""),
181-
str(execution_path),
182-
]
183-
)
184-
201+
self._collect_python_path(rootdir, execution_path)
185202
# adding proper MYPYPATH variable
186-
mypy_path_parts = []
187-
existing_mypy_path = os.environ.get("MYPYPATH", "")
188-
if existing_mypy_path:
189-
mypy_path_parts.append(existing_mypy_path)
190-
if self.base_ini_fpath:
191-
mypy_path_parts.append(os.path.dirname(self.base_ini_fpath))
192-
self.environment_variables["MYPYPATH"] = ":".join(mypy_path_parts)
203+
self._collect_mypy_path(rootdir)
193204

194205
# Windows requires this to be set, otherwise the interpreter crashes
195206
if "SYSTEMROOT" in os.environ:
@@ -313,7 +324,9 @@ def prepare_mypy_cmd_options(self, execution_path: Path) -> List[str]:
313324

314325
return mypy_cmd_options
315326

316-
def repr_failure(self, excinfo: ExceptionInfo) -> str:
327+
def repr_failure(
328+
self, excinfo: ExceptionInfo[BaseException], style: Optional["_TracebackStyle"] = None
329+
) -> Union[str, TerminalRepr]:
317330
if excinfo.errisinstance(SystemExit):
318331
# We assume that before doing exit() (which raises SystemExit) we've printed
319332
# enough context about what happened so that a stack trace is not useful.
@@ -323,9 +336,9 @@ def repr_failure(self, excinfo: ExceptionInfo) -> str:
323336
elif excinfo.errisinstance(TypecheckAssertionError):
324337
# with traceback removed
325338
exception_repr = excinfo.getrepr(style="short")
326-
exception_repr.reprcrash.message = ""
339+
exception_repr.reprcrash.message = "" # type: ignore
327340
repr_file_location = ReprFileLocation(
328-
path=self.fspath, lineno=self.starting_lineno + excinfo.value.lineno, message=""
341+
path=self.fspath, lineno=self.starting_lineno + excinfo.value.lineno, message="" # type: ignore
329342
)
330343
repr_tb_entry = TraceLastReprEntry(
331344
exception_repr.reprtraceback.reprentries[-1].lines[1:], None, None, repr_file_location, "short"
@@ -335,5 +348,41 @@ def repr_failure(self, excinfo: ExceptionInfo) -> str:
335348
else:
336349
return super().repr_failure(excinfo, style="native")
337350

338-
def reportinfo(self) -> Tuple[str, Optional[str], str]:
351+
def reportinfo(self) -> Tuple[Union[py.path.local, str], Optional[int], str]:
339352
return self.fspath, None, self.name
353+
354+
def _collect_python_path(
355+
self,
356+
rootdir: Optional[Path],
357+
execution_path: Path,
358+
) -> None:
359+
python_path_parts = []
360+
361+
existing_python_path = os.environ.get("PYTHONPATH")
362+
if existing_python_path:
363+
python_path_parts.append(existing_python_path)
364+
if execution_path:
365+
python_path_parts.append(str(execution_path))
366+
python_path_key = self.environment_variables.get("PYTHONPATH")
367+
if python_path_key:
368+
python_path_parts.append(
369+
maybe_to_abspath(python_path_key, rootdir),
370+
)
371+
372+
self.environment_variables["PYTHONPATH"] = ":".join(python_path_parts)
373+
374+
def _collect_mypy_path(self, rootdir: Optional[Path]) -> None:
375+
mypy_path_parts = []
376+
377+
existing_mypy_path = os.environ.get("MYPYPATH")
378+
if existing_mypy_path:
379+
mypy_path_parts.append(existing_mypy_path)
380+
if self.base_ini_fpath:
381+
mypy_path_parts.append(os.path.dirname(self.base_ini_fpath))
382+
mypy_path_key = self.environment_variables.get("MYPYPATH")
383+
if mypy_path_key:
384+
mypy_path_parts.append(
385+
maybe_to_abspath(mypy_path_key, rootdir),
386+
)
387+
388+
self.environment_variables["MYPYPATH"] = ":".join(mypy_path_parts)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
class Pair(object):
2+
"""We only have this type to make sure that MYPYPATH works."""
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
- case: mypy_path_from_env
2+
main: |
3+
from pair import Pair
4+
5+
a: Pair
6+
reveal_type(a) # N: Revealed type is 'pair.Pair'
7+
env:
8+
- MYPYPATH=./pytest_mypy_plugins/tests/fixtures
9+
10+
11+
- case: mypy_path_from_env_with_error
12+
main: |
13+
from pair import Missing
14+
out: |
15+
main:1: error: Module 'pair' has no attribute 'Missing'
16+
env:
17+
- MYPYPATH=./pytest_mypy_plugins/tests/fixtures

setup.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,16 @@
44
readme = f.read()
55

66
dependencies = [
7-
"pytest>=5.4.0",
8-
"mypy>=0.730",
7+
"pytest>=6.0.0",
8+
"mypy>=0.790",
99
"decorator",
1010
"pyyaml",
1111
"pystache>=0.5.4",
1212
]
1313

1414
setup(
1515
name="pytest-mypy-plugins",
16-
version="1.5.0",
16+
version="1.6.0",
1717
description="pytest plugin for writing tests for mypy plugins",
1818
long_description=readme,
1919
long_description_content_type="text/markdown",
@@ -32,5 +32,6 @@
3232
"Programming Language :: Python :: 3.6",
3333
"Programming Language :: Python :: 3.7",
3434
"Programming Language :: Python :: 3.8",
35+
"Programming Language :: Python :: 3.9",
3536
],
3637
)

0 commit comments

Comments
 (0)