Skip to content

Commit cdab152

Browse files
committed
Add classes for better dialect control
1 parent 40b4eec commit cdab152

File tree

2 files changed

+128
-0
lines changed

2 files changed

+128
-0
lines changed

mypy/syntax/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# This page intentionally left blank

mypy/syntax/dialect.py

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
"""Python dialect control.
2+
3+
This may be incomplet or inkorrekt.
4+
5+
Versions < 2.7 is not a priority.
6+
Versions >= 3.0 and < 3.2 are not a priority.
7+
Versions < 2.4 have many fundamental differences.
8+
Versions < 2.2 have even more fundamental differences.
9+
Versions 1.6 and 2.0 are not distinguished.
10+
Versions < 1.6 are not even mentioned.
11+
"""
12+
13+
14+
from typing import (
15+
List,
16+
Sequence,
17+
Set,
18+
Tuple,
19+
)
20+
21+
import os
22+
import subprocess
23+
import sys
24+
25+
26+
# Maintain our own copy instead of using __future__ just in case we need
27+
# to parse a newer version of python than we're running.
28+
# Also we can add artificial ones.
29+
30+
available_futures = {
31+
# Internal pseudo-futures.
32+
'mypy-codec': '0.0',
33+
'mypy-stub': '0.0',
34+
'mypy-instring': '0.0',
35+
36+
# Pseudo-futures from platform.python_implementation().
37+
# Note that only PyPy and CPython are tested.
38+
'variant-CPython': '0.0',
39+
'variant-PyPy': '0.0',
40+
'variant-Jython': '0.0',
41+
'variant-IronPython': '0.0',
42+
43+
# Real features from the __future__ module.
44+
'nested_scopes': '2.1',
45+
'generators': '2.2',
46+
'division': '2.2',
47+
'absolute_import': '2.5',
48+
'with_statement': '2.5',
49+
'print_function': '2.6',
50+
'unicode_literals': '2.6',
51+
'barry_as_FLUFL': '3.1',
52+
}
53+
54+
55+
def check_futures(version: str, futures: Sequence[str]) -> Set[str]:
56+
for fut in futures:
57+
assert version >= available_futures[fut]
58+
return set(futures)
59+
60+
61+
class Dialect:
62+
63+
def __init__(self, version: str, future_list: Sequence[str] = []) -> None:
64+
"""Construct a dialect for the given Python version and future set.
65+
66+
`version` is like `'2.7.0'`, e.g. `platform.python_version()`.
67+
`future_list` is like `['X', 'Y', 'Z']` in `from __future__ import X, Y, Z`.
68+
"""
69+
self.major, self.minor, self.patchlevel = [int(x) for x in version.split('.')]
70+
future_set = check_futures(version, future_list)
71+
self.base_version = version
72+
self.base_future_list = future_list
73+
self.base_future_set = future_set
74+
75+
self.possible_futures = {k for (k, v) in available_futures.items() if v <= version}
76+
77+
# Additional members will be set as needed by the lexer, parser, etc.
78+
79+
def __repr__(self) -> str:
80+
return 'Dialect(%r, %r)' % (self.base_version, self.base_future_list)
81+
82+
def add_future(self, future: str) -> 'Dialect':
83+
if future in self.base_future_set:
84+
return self
85+
return Dialect(self.base_version, self.base_future_list + [future])
86+
87+
88+
class Implementation:
89+
90+
def __init__(self, executable: str) -> None:
91+
command = '''if True:
92+
import platform, sys
93+
print((platform.python_implementation(), platform.python_version(), sys.path))
94+
'''
95+
output = subprocess.check_output([executable, '-c', command])
96+
impl, version, path = eval(output) # type: Tuple[str, str, List[str]]
97+
98+
self.executable = executable
99+
self.base_dialect = Dialect(version, ['variant-' + impl])
100+
self.stub_dialect = self.base_dialect.add_future('mypy-codec')
101+
self.python_path = path
102+
# TODO self.stub_path = []
103+
104+
105+
def default_implementation(*, force_py2: bool = False) -> Implementation:
106+
"""Return the preferred python implementation for the inferior.
107+
108+
This looks at the MYPY_PYTHON environment variable, or else uses
109+
the current python version.
110+
111+
The `force_py2` argument should possibly be deprecated.
112+
"""
113+
if force_py2:
114+
try_pythons = [os.getenv('MYPY_PYTHON'), 'python2', 'python2.7', 'python']
115+
else:
116+
try_pythons = [os.getenv('MYPY_PYTHON'), sys.executable]
117+
for python in try_pythons:
118+
if python is None:
119+
continue
120+
try:
121+
impl = Implementation(python)
122+
except (OSError, subprocess.CalledProcessError):
123+
pass
124+
if force_py2 and impl.base_dialect.major != 2:
125+
continue
126+
return impl
127+
sys.exit('No suitable python executable found')

0 commit comments

Comments
 (0)