Skip to content

Commit 6d9351a

Browse files
committed
Merge branch 'matthiaskramm-master'
2 parents 6f84117 + b7b6f42 commit 6d9351a

12 files changed

+89
-62
lines changed

mypy/build.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
from mypy import parse
2828
from mypy import stats
2929
from mypy.report import Reports
30+
from mypy import defaults
3031

3132

3233
# We need to know the location of this file to load data, but
@@ -95,7 +96,7 @@ def build(program_path: str,
9596
program_text: Union[str, bytes] = None,
9697
alt_lib_path: str = None,
9798
bin_dir: str = None,
98-
pyversion: int = 3,
99+
pyversion: Tuple[int, int] = defaults.PYTHON3_VERSION,
99100
custom_typing_module: str = None,
100101
report_dirs: Dict[str, str] = {},
101102
flags: List[str] = None,
@@ -117,7 +118,7 @@ def build(program_path: str,
117118
(takes precedence over other directories)
118119
bin_dir: directory containing the mypy script, used for finding data
119120
directories; if omitted, use '.' as the data directory
120-
pyversion: Python version (2 for 2.x or 3 for 3.x)
121+
pyversion: Python version (major, minor)
121122
custom_typing_module: if not None, use this module id as an alias for typing
122123
flags: list of build options (e.g. COMPILE_ONLY)
123124
"""
@@ -200,7 +201,7 @@ def default_data_dir(bin_dir: str) -> str:
200201
raise RuntimeError("Broken installation: can't determine base dir")
201202

202203

203-
def default_lib_path(data_dir: str, target: int, pyversion: int,
204+
def default_lib_path(data_dir: str, target: int, pyversion: Tuple[int, int],
204205
python_path: bool) -> List[str]:
205206
"""Return default standard library search paths."""
206207
# IDEA: Make this more portable.
@@ -222,7 +223,7 @@ def default_lib_path(data_dir: str, target: int, pyversion: int,
222223
major, minor = sys.version_info[0], sys.version_info[1]
223224
version_dir = '3.2'
224225
third_party_dir = 'third-party-3.2'
225-
if pyversion < 3:
226+
if pyversion[0] < 3:
226227
version_dir = '2.7'
227228
third_party_dir = 'third-party-2.7'
228229
path.append(os.path.join(data_dir, 'stubs', version_dir))
@@ -291,7 +292,7 @@ class BuildManager:
291292
Semantic analyzer, pass 3
292293
type_checker: Type checker
293294
errors: Used for reporting all errors
294-
pyversion: Python version (2 or 3)
295+
pyversion: Python version (major, minor)
295296
flags: Build options
296297
states: States of all individual files that are being
297298
processed. Each file in a build is always represented
@@ -309,7 +310,7 @@ class BuildManager:
309310
def __init__(self, data_dir: str,
310311
lib_path: List[str],
311312
target: int,
312-
pyversion: int,
313+
pyversion: Tuple[int, int],
313314
flags: List[str],
314315
ignore_prefix: str,
315316
custom_typing_module: str,

mypy/checker.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
from mypy.sametypes import is_same_type
3131
from mypy.messages import MessageBuilder
3232
import mypy.checkexpr
33+
from mypy import defaults
3334
from mypy import messages
3435
from mypy.subtypes import (
3536
is_subtype, is_equivalent, is_proper_subtype,
@@ -302,8 +303,8 @@ class TypeChecker(NodeVisitor[Type]):
302303
Type check mypy source files that have been semantically analyzed.
303304
"""
304305

305-
# Target Python major version
306-
pyversion = 3
306+
# Target Python version
307+
pyversion = defaults.PYTHON3_VERSION
307308
# Are we type checking a stub?
308309
is_stub = False
309310
# Error message reporter
@@ -338,7 +339,7 @@ class TypeChecker(NodeVisitor[Type]):
338339
modules = None # type: Dict[str, MypyFile]
339340

340341
def __init__(self, errors: Errors, modules: Dict[str, MypyFile],
341-
pyversion: int = 3) -> None:
342+
pyversion: Tuple[int, int] = defaults.PYTHON3_VERSION) -> None:
342343
"""Construct a type checker.
343344
344345
Use errors to report type check errors. Assume symtable has been
@@ -1530,7 +1531,7 @@ def type_check_raise(self, e: Node, s: RaiseStmt) -> None:
15301531
# Good!
15311532
return None
15321533
# Else fall back to the checks below (which will fail).
1533-
if isinstance(typ, TupleType) and self.pyversion == 2:
1534+
if isinstance(typ, TupleType) and self.pyversion[0] == 2:
15341535
# allow `raise type, value, traceback`
15351536
# https://docs.python.org/2/reference/simple_stmts.html#the-raise-statement
15361537
# TODO: Also check tuple item types.
@@ -1642,7 +1643,7 @@ def analyze_iterable_item_type(self, expr: Node) -> Type:
16421643
method = echk.analyze_external_member_access('__iter__', iterable,
16431644
expr)
16441645
iterator = echk.check_call(method, [], [], expr)[0]
1645-
if self.pyversion >= 3:
1646+
if self.pyversion[0] >= 3:
16461647
nextmethod = '__next__'
16471648
else:
16481649
nextmethod = 'next'

mypy/checkexpr.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -727,7 +727,7 @@ def visit_complex_expr(self, e: ComplexExpr) -> Type:
727727

728728
def visit_ellipsis(self, e: EllipsisExpr) -> Type:
729729
"""Type check '...'."""
730-
if self.chk.pyversion >= 3:
730+
if self.chk.pyversion[0] >= 3:
731731
return self.named_type('builtins.ellipsis')
732732
else:
733733
# '...' is not valid in normal Python 2 code, but it can
@@ -821,7 +821,7 @@ def visit_comparison_expr(self, e: ComparisonExpr) -> Type:
821821
return result
822822

823823
def get_operator_method(self, op: str) -> str:
824-
if op == '/' and self.chk.pyversion == 2:
824+
if op == '/' and self.chk.pyversion[0] == 2:
825825
# TODO also check for "from __future__ import division"
826826
return '__div__'
827827
else:
@@ -893,7 +893,7 @@ def check_op(self, method: str, base_type: Type, arg: Node,
893893
self.msg)
894894

895895
def get_reverse_op_method(self, method: str) -> str:
896-
if method == '__div__' and self.chk.pyversion == 2:
896+
if method == '__div__' and self.chk.pyversion[0] == 2:
897897
return '__rdiv__'
898898
else:
899899
return nodes.reverse_op_methods[method]

mypy/defaults.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
PYTHON2_VERSION = (2, 7)
2+
PYTHON3_VERSION = (3, 5)

mypy/lex.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import re
1010

1111
from mypy.util import short_type
12+
from mypy import defaults
1213
from typing import List, Callable, Dict, Any, Match, Pattern, Set, Union, Tuple
1314

1415

@@ -158,7 +159,8 @@ def __str__(self):
158159

159160

160161
def lex(string: Union[str, bytes], first_line: int = 1,
161-
pyversion: int = 3, is_stub_file: bool = False) -> Tuple[List[Token], Set[int]]:
162+
pyversion: Tuple[int, int] = defaults.PYTHON3_VERSION,
163+
is_stub_file: bool = False) -> Tuple[List[Token], Set[int]]:
162164
"""Analyze string, and return an array of token objects and the lines to ignore.
163165
164166
The last token is always Eof. The intention is to ignore any
@@ -291,12 +293,13 @@ class Lexer:
291293
# newlines within parentheses/brackets.
292294
open_brackets = None # type: List[str]
293295

294-
pyversion = 3
296+
pyversion = defaults.PYTHON3_VERSION
295297

296298
# Ignore errors on these lines (defined using '# type: ignore').
297299
ignored_lines = None # type: Set[int]
298300

299-
def __init__(self, pyversion: int = 3, is_stub_file: bool = False) -> None:
301+
def __init__(self, pyversion: Tuple[int, int] = defaults.PYTHON3_VERSION,
302+
is_stub_file: bool = False) -> None:
300303
self.map = [self.unknown_character] * 256
301304
self.tok = []
302305
self.indents = [0]
@@ -322,12 +325,12 @@ def __init__(self, pyversion: int = 3, is_stub_file: bool = False) -> None:
322325
('-+*/<>%&|^~=!,@', self.lex_misc)]:
323326
for c in seq:
324327
self.map[ord(c)] = method
325-
if pyversion == 2:
328+
if pyversion[0] == 2:
326329
self.keywords = keywords_common | keywords2
327330
# Decimal/hex/octal/binary literal or integer complex literal
328331
self.number_exp1 = re.compile('(0[xXoObB][0-9a-fA-F]+|[0-9]+)[lL]?')
329332

330-
if pyversion == 3:
333+
if pyversion[0] == 3:
331334
self.keywords = keywords_common | keywords3
332335
self.number_exp1 = re.compile('0[xXoObB][0-9a-fA-F]+|[0-9]+')
333336

@@ -394,7 +397,7 @@ def find_encoding(self, text: bytes) -> Tuple[str, int]:
394397
line = 2 if result.group(1) else 1
395398
return result.group(3).decode('ascii'), line
396399
else:
397-
default_encoding = 'utf8' if self.pyversion >= 3 else 'ascii'
400+
default_encoding = 'utf8' if self.pyversion[0] >= 3 else 'ascii'
398401
return default_encoding, -1
399402

400403
def report_unicode_decode_error(self, exc: UnicodeDecodeError, text: bytes) -> None:
@@ -484,7 +487,7 @@ def lex_number(self) -> None:
484487
self.add_token(LexError(' ' * maxlen, NUMERIC_LITERAL_ERROR))
485488
elif len(s1) == maxlen:
486489
# Integer literal.
487-
if self.pyversion >= 3 and self.octal_int.match(s1):
490+
if self.pyversion[0] >= 3 and self.octal_int.match(s1):
488491
# Python 2 style octal literal such as 0377 not supported in Python 3.
489492
self.add_token(LexError(s1, NUMERIC_LITERAL_ERROR))
490493
else:

mypy/main.py

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from typing import Dict, List, Tuple
1212

1313
from mypy import build
14+
from mypy import defaults
1415
from mypy.errors import CompileError
1516

1617
from mypy.version import __version__
@@ -21,7 +22,7 @@ def __init__(self) -> None:
2122
# Set default options.
2223
self.target = build.TYPE_CHECK
2324
self.build_flags = [] # type: List[str]
24-
self.pyversion = 3
25+
self.pyversion = defaults.PYTHON3_VERSION
2526
self.custom_typing_module = None # type: str
2627
self.report_dirs = {} # type: Dict[str, str]
2728
self.python_path = False
@@ -97,8 +98,18 @@ def process_options(args: List[str]) -> Tuple[str, str, str, Options]:
9798
args = args[1:]
9899
elif args[0] == '--py2':
99100
# Use Python 2 mode.
100-
options.pyversion = 2
101+
options.pyversion = defaults.PYTHON2_VERSION
101102
args = args[1:]
103+
elif args[0] == '--python-version':
104+
version_components = args[1].split(".")[0:2]
105+
if len(version_components) != 2:
106+
fail("Invalid python version {} (expected format: 'x.y')".format(
107+
repr(args[1])))
108+
if not all(item.isdigit() for item in version_components):
109+
fail("Found non-digit in python version: {}".format(
110+
args[1]))
111+
options.pyversion = (int(version_components[0]), int(version_components[1]))
112+
args = args[2:]
102113
elif args[0] == '-m' and args[1:]:
103114
options.build_flags.append(build.MODULE)
104115
return None, args[1], None, options
@@ -143,8 +154,8 @@ def process_options(args: List[str]) -> Tuple[str, str, str, Options]:
143154
if args[1:]:
144155
usage('Extra argument: {}'.format(args[1]))
145156

146-
if options.python_path and options.pyversion == 2:
147-
usage('--py2 specified, '
157+
if options.python_path and options.pyversion[0] == 2:
158+
usage('Python version 2 (or --py2) specified, '
148159
'but --use-python-path will search in sys.path of Python 3')
149160

150161
return args[0], None, None, options
@@ -172,7 +183,7 @@ def usage(msg: str = None) -> None:
172183
if msg:
173184
sys.stderr.write('%s\n' % msg)
174185
sys.stderr.write("""\
175-
usage: mypy [option ...] [-m mod | file]
186+
usage: mypy [option ...] [-c cmd | -m mod | file]
176187
Try 'mypy -h' for more information.
177188
""")
178189
else:
@@ -184,7 +195,7 @@ def usage(msg: str = None) -> None:
184195
--<fmt>-report dir generate a <fmt> report of type precision under dir/
185196
<fmt> may be one of: %s
186197
-m mod type check module
187-
-c string type check string
198+
-c string type check program passed in as string
188199
--verbose more verbose messages
189200
--use-python-path search for modules in sys.path of running Python
190201
--version show the current version information

mypy/parse.py

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
StarExpr, YieldFromStmt, YieldFromExpr, NonlocalDecl, DictionaryComprehension,
2929
SetComprehension, ComplexExpr, EllipsisExpr, YieldExpr, ExecStmt
3030
)
31+
from mypy import defaults
3132
from mypy import nodes
3233
from mypy.errors import Errors, CompileError
3334
from mypy.types import Void, Type, CallableType, AnyType, UnboundType
@@ -67,14 +68,14 @@
6768

6869

6970
def parse(source: Union[str, bytes], fnam: str = None, errors: Errors = None,
70-
pyversion: int = 3, custom_typing_module: str = None) -> MypyFile:
71+
pyversion: Tuple[int, int] = defaults.PYTHON3_VERSION,
72+
custom_typing_module: str = None) -> MypyFile:
7173
"""Parse a source file, without doing any semantic analysis.
7274
7375
Return the parse tree. If errors is not provided, raise ParseError
7476
on failure. Otherwise, use the errors object to report parse errors.
7577
76-
The pyversion argument determines the Python syntax variant (2 for 2.x and
77-
3 for 3.x).
78+
The pyversion (major, minor) argument determines the Python syntax variant.
7879
"""
7980
is_stub_file = bool(fnam) and fnam.endswith('.pyi')
8081
parser = Parser(fnam, errors, pyversion, custom_typing_module, is_stub_file=is_stub_file)
@@ -108,7 +109,7 @@ class Parser:
108109
# Lines to ignore (using # type: ignore).
109110
ignored_lines = None # type: Set[int]
110111

111-
def __init__(self, fnam: str, errors: Errors, pyversion: int,
112+
def __init__(self, fnam: str, errors: Errors, pyversion: Tuple[int, int],
112113
custom_typing_module: str = None, is_stub_file: bool = False) -> None:
113114
self.raise_on_error = errors is None
114115
self.pyversion = pyversion
@@ -761,7 +762,7 @@ def parse_statement(self) -> Tuple[Node, bool]:
761762
is_simple = False
762763
elif ts == 'global':
763764
stmt = self.parse_global_decl()
764-
elif ts == 'nonlocal' and self.pyversion >= 3:
765+
elif ts == 'nonlocal' and self.pyversion[0] >= 3:
765766
stmt = self.parse_nonlocal_decl()
766767
elif ts == 'assert':
767768
stmt = self.parse_assert_stmt()
@@ -775,10 +776,10 @@ def parse_statement(self) -> Tuple[Node, bool]:
775776
elif ts == '@':
776777
stmt = self.parse_decorated_function_or_class()
777778
is_simple = False
778-
elif ts == 'print' and (self.pyversion == 2 and
779+
elif ts == 'print' and (self.pyversion[0] == 2 and
779780
'print_function' not in self.future_options):
780781
stmt = self.parse_print_stmt()
781-
elif ts == 'exec' and self.pyversion == 2:
782+
elif ts == 'exec' and self.pyversion[0] == 2:
782783
stmt = self.parse_exec_stmt()
783784
else:
784785
stmt = self.parse_expression_or_assignment()
@@ -1033,7 +1034,7 @@ def parse_try_stmt(self) -> Node:
10331034
self.expect('as')
10341035
vars.append(self.parse_name_expr())
10351036
else:
1036-
if (self.pyversion == 2 and
1037+
if (self.pyversion[0] == 2 and
10371038
isinstance(types[-1], TupleExpr) and
10381039
len(cast(TupleExpr, types[-1]).items) == 2 and
10391040
isinstance(cast(TupleExpr, types[-1]).items[1], NameExpr)):
@@ -1166,7 +1167,8 @@ def parse_expression(self, prec: int = 0, star_expr_allowed: bool = False) -> No
11661167
elif isinstance(current, Keyword) and s == "yield":
11671168
# The expression yield from and yield to assign
11681169
expr = self.parse_yield_or_yield_from_expr()
1169-
elif isinstance(current, EllipsisToken) and (self.pyversion >= 3 or self.is_stub_file):
1170+
elif isinstance(current, EllipsisToken) and (self.pyversion[0] >= 3
1171+
or self.is_stub_file):
11701172
expr = self.parse_ellipsis()
11711173
else:
11721174
# Invalid expression.
@@ -1417,7 +1419,7 @@ def parse_str_expr(self) -> Node:
14171419
tok.append(t)
14181420
value += t.parsed()
14191421
node = None # type: Node
1420-
if self.pyversion == 2 and 'unicode_literals' in self.future_options:
1422+
if self.pyversion[0] == 2 and 'unicode_literals' in self.future_options:
14211423
node = UnicodeExpr(value)
14221424
else:
14231425
node = StrExpr(value)
@@ -1430,7 +1432,7 @@ def parse_bytes_literal(self) -> Node:
14301432
while isinstance(self.current(), BytesLit):
14311433
t = cast(BytesLit, self.skip())
14321434
value += t.parsed()
1433-
if self.pyversion >= 3:
1435+
if self.pyversion[0] >= 3:
14341436
node = BytesExpr(value) # type: Node
14351437
else:
14361438
node = StrExpr(value)
@@ -1443,7 +1445,7 @@ def parse_unicode_literal(self) -> Node:
14431445
while isinstance(self.current(), UnicodeLit):
14441446
t = cast(UnicodeLit, self.skip())
14451447
value += t.parsed()
1446-
if self.pyversion >= 3:
1448+
if self.pyversion[0] >= 3:
14471449
# Python 3.3 supports u'...' as an alias of '...'.
14481450
node = StrExpr(value) # type: Node
14491451
else:
@@ -1833,11 +1835,11 @@ def usage():
18331835
sys.exit(2)
18341836

18351837
args = sys.argv[1:]
1836-
pyversion = 3
1838+
pyversion = defaults.PYTHON3_VERSION
18371839
quiet = False
18381840
while args and args[0].startswith('--'):
18391841
if args[0] == '--py2':
1840-
pyversion = 2
1842+
pyversion = defaults.PYTHON2_VERSION
18411843
elif args[0] == '--quiet':
18421844
quiet = True
18431845
else:

0 commit comments

Comments
 (0)