Skip to content

Implement XML-based reports #713

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 27 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
a0c7b31
Rename some stubs from .py to .pyi
o11c Oct 11, 2015
bf40f0b
Miscellaneous small stub changes
o11c Oct 11, 2015
3e6ae1a
Stub for docutils should be third-party
o11c Oct 11, 2015
4fd1040
Stub for token
o11c Oct 11, 2015
8d3a11e
Fix codec-related stubs
o11c Oct 11, 2015
4cfdbaf
Fix broken stub caused by JukkaL not merging my PR yet
o11c Oct 11, 2015
95f35a2
Add stub for pipes
o11c Oct 7, 2015
f41a270
Add stubs for textwrap
o11c Jul 31, 2015
dfcd744
Stubs for bisect
o11c Jul 26, 2015
9861d51
Stub for inspect.stack() and related classes
o11c Oct 11, 2015
06b670d
Use SupportsBytes
o11c Oct 11, 2015
2e591dc
Distinguish third-party stubs from 3.2 stubs
o11c Jun 18, 2015
2c009ca
Move script to mypy.main module and just leave stubs
o11c Jul 1, 2015
e45f793
Fix bugs in test driver
o11c Jul 25, 2015
9d8c671
Allow testsuite to run in parallel
o11c Jul 25, 2015
f6b23d3
Run myunit test modules individually
o11c Aug 1, 2015
129215e
Use multiline repr when tests fail
o11c Sep 25, 2015
30a3d67
Fix test failures after rebase
o11c Oct 1, 2015
300a19c
Appease the silly linter
o11c Oct 1, 2015
b91362d
make test logging less verbose
matthiaskramm Oct 1, 2015
66da905
Move linting into travis.py
o11c Oct 1, 2015
ba47837
Add verbosity options
o11c Oct 2, 2015
7febd53
Rename test driver
o11c Oct 7, 2015
46cd254
Implement suggestions
o11c Oct 7, 2015
923760a
Implement infrastructure for reports
o11c Jun 19, 2015
d19403d
Implement XML-based reports
o11c Jun 20, 2015
bf4c7a9
Linters are silly
o11c Oct 1, 2015
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ __pycache__
/env
docs/build/
*.iml
/out/
3 changes: 2 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ install:
- pip install -r test-requirements.txt
- python setup.py install

script: bash travis.sh
script:
- python runtests.py -v

notifications:
irc:
Expand Down
40 changes: 28 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,28 +117,44 @@ Running tests and linting

First install any additional dependencies needed for testing:

$ pip install -r test-requirements.txt
$ pip3 install -r test-requirements.txt

To run tests, run the script `tests.py` in the mypy repository:
To run all tests, run the script `runtests.py` in the mypy repository:

$ python3 tests.py
$ ./runtests.py

You can also run tests without having to run `setup.py` first by
setting up the Python module search path suitably:
Note that some tests will be disabled for older python versions.

$ export PYTHONPATH=PREFIX/mypy:PREFIX/mypy/lib-typing/3.2
$ python3 tests.py
You can run a subset of test suites by passing postive or negative filters:

Replace `PREFIX` with the path where you have the repository cloned.
$ ./runtests.py lex parse -x lint -x stub

You can also run the type checker for manual testing now without
installing anything by running `scripts/mypy`:
If you want to run individual unit tests, you can run myunit directly, or
pass inferior arguments via -a:

$ python3 PREFIX/mypy/scripts/mypy PROGRAM
$ scripts/myunit -m mypy.test.testlex -v '*backslash*'
$ ./runtests.py mypy.test.testlex -a -v -a '*backslash*'

You can also run the type checker for manual testing without
installing anything by setting up the Python module search path suitably:

$ export PYTHONPATH=$PWD:$PWD/lib-typing/3.2
$ python<version> -m mypy PROGRAM.py

You can add the entry scripts to PATH for a single python3 version:

$ export PATH=$PWD/scripts
$ mypy PROGRAM.py

You can check a module or string instead of a file:

$ mypy PROGRAM.py
$ mypy -m MODULE
$ mypy -c 'import MODULE'

To run the linter:

$ ./lint.sh
$ ./runtests.py lint


Development status
Expand Down
2 changes: 2 additions & 0 deletions lib-typing/3.2/typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@
# Structural checks, a.k.a. protocols.
'Reversible',
'SupportsAbs',
'SupportsBytes',
'SupportsComplex',
'SupportsFloat',
'SupportsInt',
'SupportsRound',
Expand Down
11 changes: 0 additions & 11 deletions lint.sh

This file was deleted.

5 changes: 5 additions & 0 deletions mypy/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
"""Mypy type checker command line tool."""

from mypy.main import main

main()
71 changes: 45 additions & 26 deletions mypy/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,13 @@
from mypy.errors import Errors, CompileError
from mypy import parse
from mypy import stats
from mypy.report import Reports


# We need to know the location of this file to load data, but
# until Python 3.4, __file__ is relative.
__file__ = os.path.realpath(__file__)

debug = False


Expand All @@ -39,7 +44,10 @@
# Build flags
VERBOSE = 'verbose' # More verbose messages (for troubleshooting)
MODULE = 'module' # Build module as a script
PROGRAM_TEXT = 'program-text' # Build command-line argument as a script
TEST_BUILTINS = 'test-builtins' # Use stub builtins to speed up tests
DUMP_TYPE_STATS = 'dump-type-stats'
DUMP_INFER_STATS = 'dump-infer-stats'

# State ids. These describe the states a source file / module can be in a
# build.
Expand Down Expand Up @@ -83,12 +91,13 @@ def __init__(self, files: Dict[str, MypyFile],
def build(program_path: str,
target: int,
module: str = None,
argument: str = None,
program_text: Union[str, bytes] = None,
alt_lib_path: str = None,
bin_dir: str = None,
pyversion: int = 3,
custom_typing_module: str = None,
html_report_dir: str = None,
report_dirs: Dict[str, str] = {},
flags: List[str] = None,
python_path: bool = False) -> BuildResult:
"""Analyze a program.
Expand Down Expand Up @@ -123,7 +132,7 @@ def build(program_path: str,
if TEST_BUILTINS in flags:
# Use stub builtins (to speed up test cases and to make them easier to
# debug).
lib_path.insert(0, os.path.join('mypy', 'test', 'data', 'lib-stub'))
lib_path.insert(0, os.path.join(os.path.dirname(__file__), 'test', 'data', 'lib-stub'))
elif program_path:
# Include directory of the program file in the module search path.
lib_path.insert(
Expand All @@ -137,6 +146,14 @@ def build(program_path: str,
if alt_lib_path:
lib_path.insert(0, alt_lib_path)

if program_text is None:
program_path = program_path or lookup_program(module, lib_path)
program_text = read_program(program_path)
else:
program_path = program_path or '<string>'

reports = Reports(program_path, data_dir, report_dirs)

# Construct a build manager object that performs all the stages of the
# build in the correct order.
#
Expand All @@ -145,11 +162,7 @@ def build(program_path: str,
pyversion=pyversion, flags=flags,
ignore_prefix=os.getcwd(),
custom_typing_module=custom_typing_module,
html_report_dir=html_report_dir)

program_path = program_path or lookup_program(module, lib_path)
if program_text is None:
program_text = read_program(program_path)
reports=reports)

# Construct information that describes the initial file. __main__ is the
# implicit module id and the import context is empty initially ([]).
Expand All @@ -158,22 +171,22 @@ def build(program_path: str,
# initial state of all files) to the manager. The manager will process the
# file and all dependant modules recursively.
result = manager.process(UnprocessedFile(info, program_text))
if 'html-report' in flags:
stats.generate_html_index(html_report_dir)
reports.finish()
return result


def default_data_dir(bin_dir: str) -> str:
# TODO fix this logic
if not bin_dir:
# Default to current directory.
return ''
# Default to directory containing this file's parent.
return os.path.dirname(os.path.dirname(__file__))
base = os.path.basename(bin_dir)
dir = os.path.dirname(bin_dir)
if (sys.platform == 'win32' and base.lower() == 'scripts'
if (sys.platform == 'win32' and base.lower() == 'mypy'
and not os.path.isdir(os.path.join(dir, 'stubs'))):
# Installed, on Windows.
return os.path.join(dir, 'Lib', 'mypy')
elif base == 'scripts':
elif base == 'mypy':
# Assume that we have a repo check out or unpacked source tarball.
return os.path.dirname(bin_dir)
elif base == 'bin':
Expand All @@ -199,7 +212,14 @@ def default_lib_path(data_dir: str, target: int, pyversion: int,
path[:0] = path_env.split(os.pathsep)

# Add library stubs directory. By convention, they are stored in the
# stubs/x.y directory of the mypy installation.
# stubs/x.y directory of the mypy installation. Additionally, stubs
# for earlier versions in the same major version will be added, and
# as a last resort, third-party stubs will be added.
if pyversion == 2:
major, minor = 2, 7
else:
# See bug #886
major, minor = sys.version_info[0], sys.version_info[1]
version_dir = '3.2'
third_party_dir = 'third-party-3.2'
if pyversion < 3:
Expand All @@ -208,9 +228,12 @@ def default_lib_path(data_dir: str, target: int, pyversion: int,
path.append(os.path.join(data_dir, 'stubs', version_dir))
path.append(os.path.join(data_dir, 'stubs', third_party_dir))
path.append(os.path.join(data_dir, 'stubs-auto', version_dir))
if sys.version_info.major == 3:
if major == 3:
# Add additional stub directories.
versions = ['3.3', '3.4', '3.5', '3.6']
if False:
# Ick, we really should figure out how to use this again.
versions = ['3.%d' % i for i in range(minor, -1, -1)]
for v in versions:
stubdir = os.path.join(data_dir, 'stubs', v)
if os.path.isdir(stubdir):
Expand Down Expand Up @@ -290,7 +313,7 @@ def __init__(self, data_dir: str,
flags: List[str],
ignore_prefix: str,
custom_typing_module: str,
html_report_dir: str) -> None:
reports: Reports) -> None:
self.data_dir = data_dir
self.errors = Errors()
self.errors.set_ignore_prefix(ignore_prefix)
Expand All @@ -299,7 +322,7 @@ def __init__(self, data_dir: str,
self.pyversion = pyversion
self.flags = flags
self.custom_typing_module = custom_typing_module
self.html_report_dir = html_report_dir
self.reports = reports
self.semantic_analyzer = SemanticAnalyzer(lib_path, self.errors,
pyversion=pyversion)
self.semantic_analyzer_pass3 = ThirdPass(self.errors)
Expand Down Expand Up @@ -444,7 +467,7 @@ def lookup_state(self, module: str) -> 'State':
for state in self.states:
if state.id == module:
return state
raise RuntimeError('%s not found' % str)
raise RuntimeError('%s not found' % module)

def all_imported_modules_in_file(self,
file: MypyFile) -> List[Tuple[str, int]]:
Expand All @@ -459,7 +482,7 @@ def correct_rel_imp(imp: Union[ImportFrom, ImportAll]) -> str:
rel = imp.relative
if rel == 0:
return imp.id
if os.path.basename(file.path) == '__init__.py':
if os.path.basename(file.path).startswith('__init__.'):
rel -= 1
if rel != 0:
file_id = ".".join(file_id.split(".")[:-rel])
Expand Down Expand Up @@ -781,7 +804,7 @@ class PartiallySemanticallyAnalyzedFile(ParsedFile):
def process(self) -> None:
"""Perform final pass of semantic analysis and advance state."""
self.semantic_analyzer_pass3().visit_file(self.tree, self.tree.path)
if 'dump-type-stats' in self.manager.flags:
if DUMP_TYPE_STATS in self.manager.flags:
stats.dump_type_stats(self.tree, self.tree.path)
self.switch_state(SemanticallyAnalyzedFile(self.info(), self.tree))

Expand All @@ -794,14 +817,10 @@ def process(self) -> None:
"""Type check file and advance to the next state."""
if self.manager.target >= TYPE_CHECK:
self.type_checker().visit_file(self.tree, self.tree.path)
if 'dump-infer-stats' in self.manager.flags:
if DUMP_INFER_STATS in self.manager.flags:
stats.dump_type_stats(self.tree, self.tree.path, inferred=True,
typemap=self.manager.type_checker.type_map)
elif 'html-report' in self.manager.flags:
stats.generate_html_report(
self.tree, self.tree.path,
type_map=self.manager.type_checker.type_map,
output_dir=self.manager.html_report_dir)
self.manager.reports.file(self.tree, type_map=self.manager.type_checker.type_map)

# FIX remove from active state list to speed up processing

Expand Down
4 changes: 3 additions & 1 deletion mypy/checkstrformat.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
from mypy.nodes import (
Node, StrExpr, TupleExpr, DictExpr, Context
)
import mypy.checker
if False:
# break import cycle only needed for mypy
import mypy.checker
from mypy import messages
from mypy.messages import MessageBuilder

Expand Down
10 changes: 7 additions & 3 deletions mypy/codec/pytokenize.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@
from token import *

import token
x = None
x = None # type: str
__all__ = [x for x in dir(token) if not x.startswith("_")]
__all__ += ["COMMENT", "tokenize", "generate_tokens", "NL", "untokenize"]
del x
Expand Down Expand Up @@ -187,7 +187,7 @@ def maybe(*choices): return group(*choices) + '?'
'r': None, 'R': None, 'u': None, 'U': None,
'b': None, 'B': None}

triple_quoted = {}
triple_quoted = {} # type: Dict[str, str]
for t in ("'''", '"""',
"r'''", 'r"""', "R'''", 'R"""',
"u'''", 'u"""', "U'''", 'U"""',
Expand All @@ -197,7 +197,7 @@ def maybe(*choices): return group(*choices) + '?'
"br'''", 'br"""', "Br'''", 'Br"""',
"bR'''", 'bR"""', "BR'''", 'BR"""'):
triple_quoted[t] = t
single_quoted = {}
single_quoted = {} # type: Dict[str, str]
for t in ("'", '"',
"r'", 'r"', "R'", 'R"',
"u'", 'u"', "U'", 'U"',
Expand Down Expand Up @@ -333,6 +333,10 @@ def generate_tokens(readline):
contline = None
indents = [0]

if 0:
# type hints for mypy
strstart = (0, 0)
endprog = re.compile('')
while 1: # loop over lines in stream
try:
line = readline()
Expand Down
Loading