Skip to content

Commit a8d7eaa

Browse files
committed
Use new Dialect objects to store version everywhere
1 parent 58bc742 commit a8d7eaa

16 files changed

+149
-137
lines changed

mypy/build.py

Lines changed: 19 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
from mypy import parse
2828
from mypy import stats
2929
from mypy.report import Reports
30-
from mypy import defaults
30+
from mypy.syntax.dialect import Dialect, Implementation, default_implementation
3131

3232

3333
# We need to know the location of this file to load data, but
@@ -95,7 +95,7 @@ def build(program_path: str,
9595
argument: str = None,
9696
program_text: Union[str, bytes] = None,
9797
alt_lib_path: str = None,
98-
pyversion: Tuple[int, int] = defaults.PYTHON3_VERSION,
98+
implementation: Implementation = None,
9999
custom_typing_module: str = None,
100100
report_dirs: Dict[str, str] = {},
101101
flags: List[str] = None,
@@ -115,17 +115,18 @@ def build(program_path: str,
115115
program_text: the main source file contents; if omitted, read from file
116116
alt_lib_dir: an additional directory for looking up library modules
117117
(takes precedence over other directories)
118-
pyversion: Python version (major, minor)
118+
implementation: Python version
119119
custom_typing_module: if not None, use this module id as an alias for typing
120120
flags: list of build options (e.g. COMPILE_ONLY)
121121
"""
122122
flags = flags or []
123123
module = module or '__main__'
124+
implementation = implementation or default_implementation()
124125

125126
data_dir = default_data_dir()
126127

127128
# Determine the default module search path.
128-
lib_path = default_lib_path(data_dir, target, pyversion, python_path)
129+
lib_path = default_lib_path(data_dir, target, implementation, python_path)
129130

130131
if TEST_BUILTINS in flags:
131132
# Use stub builtins (to speed up test cases and to make them easier to
@@ -157,7 +158,7 @@ def build(program_path: str,
157158
#
158159
# Ignore current directory prefix in error messages.
159160
manager = BuildManager(data_dir, lib_path, target,
160-
pyversion=pyversion, flags=flags,
161+
dialect=implementation.base_dialect, flags=flags,
161162
ignore_prefix=os.getcwd(),
162163
custom_typing_module=custom_typing_module,
163164
reports=reports)
@@ -181,7 +182,7 @@ def default_data_dir() -> str:
181182
return os.path.join(os.path.dirname(__file__), 'data')
182183

183184

184-
def default_lib_path(data_dir: str, target: int, pyversion: Tuple[int, int],
185+
def default_lib_path(data_dir: str, target: int, implementation: Implementation,
185186
python_path: bool) -> List[str]:
186187
"""Return default standard library search paths."""
187188
# IDEA: Make this more portable.
@@ -196,14 +197,12 @@ def default_lib_path(data_dir: str, target: int, pyversion: Tuple[int, int],
196197
# stubs/x.y directory of the mypy installation. Additionally, stubs
197198
# for earlier versions in the same major version will be added, and
198199
# as a last resort, third-party stubs will be added.
199-
if pyversion == 2:
200-
major, minor = 2, 7
200+
major = implementation.base_dialect.major
201+
minor = implementation.base_dialect.minor
202+
if major == 3:
203+
version_dir = '3.2'
204+
third_party_dir = 'third-party-3.2'
201205
else:
202-
# See bug #886
203-
major, minor = sys.version_info[0], sys.version_info[1]
204-
version_dir = '3.2'
205-
third_party_dir = 'third-party-3.2'
206-
if pyversion[0] < 3:
207206
version_dir = '2.7'
208207
third_party_dir = 'third-party-2.7'
209208
path.append(os.path.join(data_dir, 'stubs', version_dir))
@@ -224,13 +223,9 @@ def default_lib_path(data_dir: str, target: int, pyversion: Tuple[int, int],
224223
if os.path.isdir(third_party_stubdir):
225224
path.append(third_party_stubdir)
226225

227-
# Add fallback path that can be used if we have a broken installation.
228-
if sys.platform != 'win32':
229-
path.append('/usr/local/lib/mypy')
230-
231226
# Contents of Python's sys.path go last, to prefer the stubs
232227
if python_path:
233-
path.extend(sys.path)
228+
path.extend(implementation.python_path)
234229

235230
return path
236231

@@ -272,7 +267,7 @@ class BuildManager:
272267
Semantic analyzer, pass 3
273268
type_checker: Type checker
274269
errors: Used for reporting all errors
275-
pyversion: Python version (major, minor)
270+
dialect: Python version
276271
flags: Build options
277272
states: States of all individual files that are being
278273
processed. Each file in a build is always represented
@@ -290,7 +285,7 @@ class BuildManager:
290285
def __init__(self, data_dir: str,
291286
lib_path: List[str],
292287
target: int,
293-
pyversion: Tuple[int, int],
288+
dialect: Dialect,
294289
flags: List[str],
295290
ignore_prefix: str,
296291
custom_typing_module: str,
@@ -300,16 +295,16 @@ def __init__(self, data_dir: str,
300295
self.errors.set_ignore_prefix(ignore_prefix)
301296
self.lib_path = lib_path
302297
self.target = target
303-
self.pyversion = pyversion
298+
self.dialect = dialect
304299
self.flags = flags
305300
self.custom_typing_module = custom_typing_module
306301
self.reports = reports
307302
self.semantic_analyzer = SemanticAnalyzer(lib_path, self.errors,
308-
pyversion=pyversion)
303+
dialect=dialect)
309304
self.semantic_analyzer_pass3 = ThirdPass(self.errors)
310305
self.type_checker = TypeChecker(self.errors,
311306
self.semantic_analyzer.modules,
312-
self.pyversion)
307+
dialect)
313308
self.states = [] # type: List[State]
314309
self.module_files = {} # type: Dict[str, str]
315310
self.module_deps = {} # type: Dict[Tuple[str, str], bool]
@@ -720,7 +715,7 @@ def parse(self, source_text: Union[str, bytes], fnam: str) -> MypyFile:
720715
"""
721716
num_errs = self.errors().num_messages()
722717
tree = parse.parse(source_text, fnam, self.errors(),
723-
pyversion=self.manager.pyversion,
718+
dialect=self.manager.dialect,
724719
custom_typing_module=self.manager.custom_typing_module)
725720
tree._fullname = self.id
726721
if self.errors().num_messages() != num_errs:

mypy/checker.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
from mypy.sametypes import is_same_type
3434
from mypy.messages import MessageBuilder
3535
import mypy.checkexpr
36-
from mypy import defaults
36+
from mypy.syntax.dialect import Dialect, default_dialect
3737
from mypy import messages
3838
from mypy.subtypes import (
3939
is_subtype, is_equivalent, is_proper_subtype,
@@ -302,7 +302,7 @@ class TypeChecker(NodeVisitor[Type]):
302302
"""
303303

304304
# Target Python version
305-
pyversion = defaults.PYTHON3_VERSION
305+
dialect = None # type: Dialect
306306
# Are we type checking a stub?
307307
is_stub = False
308308
# Error message reporter
@@ -337,7 +337,7 @@ class TypeChecker(NodeVisitor[Type]):
337337
modules = None # type: Dict[str, MypyFile]
338338

339339
def __init__(self, errors: Errors, modules: Dict[str, MypyFile],
340-
pyversion: Tuple[int, int] = defaults.PYTHON3_VERSION) -> None:
340+
dialect: Dialect = default_dialect()) -> None:
341341
"""Construct a type checker.
342342
343343
Use errors to report type check errors. Assume symtable has been
@@ -346,7 +346,7 @@ def __init__(self, errors: Errors, modules: Dict[str, MypyFile],
346346
self.expr_checker
347347
self.errors = errors
348348
self.modules = modules
349-
self.pyversion = pyversion
349+
self.dialect = dialect
350350
self.msg = MessageBuilder(errors)
351351
self.type_map = {}
352352
self.binder = ConditionalTypeBinder()
@@ -1548,7 +1548,7 @@ def type_check_raise(self, e: Node, s: RaiseStmt) -> None:
15481548
# Good!
15491549
return None
15501550
# Else fall back to the checks below (which will fail).
1551-
if isinstance(typ, TupleType) and self.pyversion[0] == 2:
1551+
if isinstance(typ, TupleType) and self.dialect.major == 2:
15521552
# allow `raise type, value, traceback`
15531553
# https://docs.python.org/2/reference/simple_stmts.html#the-raise-statement
15541554
# TODO: Also check tuple item types.
@@ -1660,7 +1660,7 @@ def analyze_iterable_item_type(self, expr: Node) -> Type:
16601660
method = echk.analyze_external_member_access('__iter__', iterable,
16611661
expr)
16621662
iterator = echk.check_call(method, [], [], expr)[0]
1663-
if self.pyversion[0] >= 3:
1663+
if self.dialect.major >= 3:
16641664
nextmethod = '__next__'
16651665
else:
16661666
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[0] >= 3:
730+
if self.chk.dialect.major >= 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[0] == 2:
824+
if op == '/' and self.chk.dialect.major == 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[0] == 2:
896+
if method == '__div__' and self.chk.dialect.major == 2:
897897
return '__rdiv__'
898898
else:
899899
return nodes.reverse_op_methods[method]

mypy/defaults.py

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

mypy/lex.py

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88

99
import re
1010

11+
from mypy.syntax.dialect import Dialect, default_dialect
1112
from mypy.util import short_type
12-
from mypy import defaults
1313
from typing import List, Callable, Dict, Any, Match, Pattern, Set, Union, Tuple
1414

1515

@@ -159,14 +159,13 @@ def __str__(self):
159159

160160

161161
def lex(string: Union[str, bytes], first_line: int = 1,
162-
pyversion: Tuple[int, int] = defaults.PYTHON3_VERSION,
163-
is_stub_file: bool = False) -> Tuple[List[Token], Set[int]]:
162+
dialect: Dialect = default_dialect()) -> Tuple[List[Token], Set[int]]:
164163
"""Analyze string, and return an array of token objects and the lines to ignore.
165164
166165
The last token is always Eof. The intention is to ignore any
167166
semantic and type check errors on the ignored lines.
168167
"""
169-
l = Lexer(pyversion, is_stub_file=is_stub_file)
168+
l = Lexer(dialect)
170169
l.lex(string, first_line)
171170
return l.tok, l.ignored_lines
172171

@@ -293,19 +292,17 @@ class Lexer:
293292
# newlines within parentheses/brackets.
294293
open_brackets = None # type: List[str]
295294

296-
pyversion = defaults.PYTHON3_VERSION
295+
dialect = None # type: Dialect
297296

298297
# Ignore errors on these lines (defined using '# type: ignore').
299298
ignored_lines = None # type: Set[int]
300299

301-
def __init__(self, pyversion: Tuple[int, int] = defaults.PYTHON3_VERSION,
302-
is_stub_file: bool = False) -> None:
300+
def __init__(self, dialect: Dialect = default_dialect()) -> None:
303301
self.map = [self.unknown_character] * 256
304302
self.tok = []
305303
self.indents = [0]
306304
self.open_brackets = []
307-
self.pyversion = pyversion
308-
self.is_stub_file = is_stub_file
305+
self.dialect = dialect
309306
self.ignored_lines = set()
310307
# Fill in the map from valid character codes to relevant lexer methods.
311308
for seq, method in [('ABCDEFGHIJKLMNOPQRSTUVWXYZ', self.lex_name),
@@ -325,12 +322,12 @@ def __init__(self, pyversion: Tuple[int, int] = defaults.PYTHON3_VERSION,
325322
('-+*/<>%&|^~=!,@', self.lex_misc)]:
326323
for c in seq:
327324
self.map[ord(c)] = method
328-
if pyversion[0] == 2:
325+
if dialect.major == 2:
329326
self.keywords = keywords_common | keywords2
330327
# Decimal/hex/octal/binary literal or integer complex literal
331328
self.number_exp1 = re.compile('(0[xXoObB][0-9a-fA-F]+|[0-9]+)[lL]?')
332329

333-
if pyversion[0] == 3:
330+
if dialect.major == 3:
334331
self.keywords = keywords_common | keywords3
335332
self.number_exp1 = re.compile('0[xXoObB][0-9a-fA-F]+|[0-9]+')
336333

@@ -397,7 +394,7 @@ def find_encoding(self, text: bytes) -> Tuple[str, int]:
397394
line = 2 if result.group(1) else 1
398395
return result.group(3).decode('ascii'), line
399396
else:
400-
default_encoding = 'utf8' if self.pyversion[0] >= 3 else 'ascii'
397+
default_encoding = 'utf8' if self.dialect.major >= 3 else 'ascii'
401398
return default_encoding, -1
402399

403400
def report_unicode_decode_error(self, exc: UnicodeDecodeError, text: bytes) -> None:
@@ -487,7 +484,7 @@ def lex_number(self) -> None:
487484
self.add_token(LexError(' ' * maxlen, NUMERIC_LITERAL_ERROR))
488485
elif len(s1) == maxlen:
489486
# Integer literal.
490-
if self.pyversion[0] >= 3 and self.octal_int.match(s1):
487+
if self.dialect.major >= 3 and self.octal_int.match(s1):
491488
# Python 2 style octal literal such as 0377 not supported in Python 3.
492489
self.add_token(LexError(s1, NUMERIC_LITERAL_ERROR))
493490
else:

mypy/main.py

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

1313
from mypy import build
14-
from mypy import defaults
14+
from mypy.syntax.dialect import Implementation, default_implementation
1515
from mypy.errors import CompileError
1616

1717
from mypy.version import __version__
@@ -22,7 +22,7 @@ def __init__(self) -> None:
2222
# Set default options.
2323
self.target = build.TYPE_CHECK
2424
self.build_flags = [] # type: List[str]
25-
self.pyversion = defaults.PYTHON3_VERSION
25+
self.implementation = None # type: Implementation
2626
self.custom_typing_module = None # type: str
2727
self.report_dirs = {} # type: Dict[str, str]
2828
self.python_path = False
@@ -58,7 +58,7 @@ def type_check_only(path: str, module: str, program_text: str,
5858
module=module,
5959
program_text=program_text,
6060
target=build.TYPE_CHECK,
61-
pyversion=options.pyversion,
61+
implementation=options.implementation,
6262
custom_typing_module=options.custom_typing_module,
6363
report_dirs=options.report_dirs,
6464
flags=options.build_flags,
@@ -75,23 +75,21 @@ def process_options(args: List[str]) -> Tuple[str, str, str, Options]:
7575
options = Options()
7676
help = False
7777
ver = False
78+
python_executable = None # type: str
79+
force_py2 = False
80+
7881
while args and args[0].startswith('-'):
7982
if args[0] == '--verbose':
8083
options.build_flags.append(build.VERBOSE)
8184
args = args[1:]
8285
elif args[0] == '--py2':
8386
# Use Python 2 mode.
84-
options.pyversion = defaults.PYTHON2_VERSION
87+
force_py2 = True
8588
args = args[1:]
86-
elif args[0] == '--python-version':
87-
version_components = args[1].split(".")[0:2]
88-
if len(version_components) != 2:
89-
fail("Invalid python version {} (expected format: 'x.y')".format(
90-
repr(args[1])))
91-
if not all(item.isdigit() for item in version_components):
92-
fail("Found non-digit in python version: {}".format(
93-
args[1]))
94-
options.pyversion = (int(version_components[0]), int(version_components[1]))
89+
elif args[0] == '--python-executable':
90+
if len(args) < 2:
91+
fail('argument required')
92+
python_executable = args[1]
9593
args = args[2:]
9694
elif args[0] == '-m' and args[1:]:
9795
options.build_flags.append(build.MODULE)
@@ -137,9 +135,12 @@ def process_options(args: List[str]) -> Tuple[str, str, str, Options]:
137135
if args[1:]:
138136
usage('Extra argument: {}'.format(args[1]))
139137

140-
if options.python_path and options.pyversion[0] == 2:
141-
usage('Python version 2 (or --py2) specified, '
142-
'but --use-python-path will search in sys.path of Python 3')
138+
if python_executable is not None:
139+
options.implementation = Implementation(python_executable)
140+
if force_py2 and options.implementation.base_dialect.major != 2:
141+
usage('given --python-executable is not --py2')
142+
else:
143+
options.implementation = default_implementation(force_py2=force_py2)
143144

144145
return args[0], None, None, options
145146

@@ -182,9 +183,12 @@ def usage(msg: str = None) -> None:
182183
--verbose more verbose messages
183184
--use-python-path search for modules in sys.path of running Python
184185
--version show the current version information
186+
--python-executable emulate this python interpreter
187+
--py2 deprecated, automatically find a python2 interpreter
185188
186189
Environment variables:
187190
MYPYPATH additional module search path
191+
MYPY_PYTHON interpreter to emulate
188192
""" % ', '.join(REPORTS))
189193
sys.exit(2)
190194

0 commit comments

Comments
 (0)