Skip to content

Commit 2d9f5e2

Browse files
committed
Merge remote-tracking branch 'upstream/master' into pytest-complete
2 parents 2f65bc0 + 9ab6936 commit 2d9f5e2

23 files changed

+334
-51
lines changed

docs/source/installed_packages.rst

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ If you would like to publish a library package to a package repository (e.g.
4343
PyPI) for either internal or external use in type checking, packages that
4444
supply type information via type comments or annotations in the code should put
4545
a ``py.typed`` in their package directory. For example, with a directory
46-
structure as follows:
46+
structure as follows
4747

4848
.. code-block:: text
4949
@@ -53,7 +53,7 @@ structure as follows:
5353
lib.py
5454
py.typed
5555
56-
the setup.py might look like:
56+
the setup.py might look like
5757

5858
.. code-block:: python
5959
@@ -67,8 +67,13 @@ the setup.py might look like:
6767
packages=["package_a"]
6868
)
6969
70+
.. note::
71+
72+
If you use setuptools, you must pass the option ``zip_safe=False`` to
73+
``setup()``, or mypy will not be able to find the installed package.
74+
7075
Some packages have a mix of stub files and runtime files. These packages also
71-
require a ``py.typed`` file. An example can be seen below:
76+
require a ``py.typed`` file. An example can be seen below
7277

7378
.. code-block:: text
7479

extensions/mypy_extensions.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,3 +135,7 @@ def KwArg(type=Any):
135135

136136
# Return type that indicates a function does not return
137137
class NoReturn: pass
138+
139+
140+
def trait(cls):
141+
return cls

mypy/build.py

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -199,26 +199,43 @@ def default_flush_errors(new_messages: List[str], is_serious: bool) -> None:
199199

200200

201201
@functools.lru_cache(maxsize=None)
202-
def _get_site_packages_dirs(python_executable: Optional[str]) -> List[str]:
202+
def _get_site_packages_dirs(python_executable: Optional[str],
203+
fscache: FileSystemCache) -> List[str]:
203204
"""Find package directories for given python.
204205
205206
This runs a subprocess call, which generates a list of the site package directories.
206207
To avoid repeatedly calling a subprocess (which can be slow!) we lru_cache the results."""
208+
def make_abspath(path: str, root: str) -> str:
209+
"""Take a path and make it absolute relative to root if not already absolute."""
210+
if os.path.isabs(path):
211+
return os.path.normpath(path)
212+
else:
213+
return os.path.join(root, os.path.normpath(path))
214+
207215
if python_executable is None:
208216
return []
209217
if python_executable == sys.executable:
210218
# Use running Python's package dirs
211-
return sitepkgs.getsitepackages()
219+
site_packages = sitepkgs.getsitepackages()
212220
else:
213221
# Use subprocess to get the package directory of given Python
214222
# executable
215-
return ast.literal_eval(subprocess.check_output([python_executable, sitepkgs.__file__],
216-
stderr=subprocess.PIPE).decode())
223+
site_packages = ast.literal_eval(
224+
subprocess.check_output([python_executable, sitepkgs.__file__],
225+
stderr=subprocess.PIPE).decode())
226+
egg_dirs = []
227+
for dir in site_packages:
228+
pth = os.path.join(dir, 'easy-install.pth')
229+
if fscache.isfile(pth):
230+
with open(pth) as f:
231+
egg_dirs.extend([make_abspath(d.rstrip(), dir) for d in f.readlines()])
232+
return egg_dirs + site_packages
217233

218234

219235
def compute_search_paths(sources: List[BuildSource],
220236
options: Options,
221237
data_dir: str,
238+
fscache: FileSystemCache,
222239
alt_lib_path: Optional[str] = None) -> SearchPaths:
223240
"""Compute the search paths as specified in PEP 561.
224241
@@ -275,9 +292,21 @@ def compute_search_paths(sources: List[BuildSource],
275292
if alt_lib_path:
276293
mypypath.insert(0, alt_lib_path)
277294

295+
package_path = tuple(_get_site_packages_dirs(options.python_executable, fscache))
296+
for site_dir in package_path:
297+
assert site_dir not in lib_path
298+
if site_dir in mypypath:
299+
print("{} is in the MYPYPATH. Please remove it.".format(site_dir), file=sys.stderr)
300+
sys.exit(1)
301+
elif site_dir in python_path:
302+
print("{} is in the PYTHONPATH. Please change directory"
303+
" so it is not.".format(site_dir),
304+
file=sys.stderr)
305+
sys.exit(1)
306+
278307
return SearchPaths(tuple(reversed(python_path)),
279308
tuple(mypypath),
280-
tuple(_get_site_packages_dirs(options.python_executable)),
309+
package_path,
281310
tuple(lib_path))
282311

283312

@@ -294,7 +323,7 @@ def _build(sources: List[BuildSource],
294323
data_dir = default_data_dir(bin_dir)
295324
fscache = fscache or FileSystemCache()
296325

297-
search_paths = compute_search_paths(sources, options, data_dir, alt_lib_path)
326+
search_paths = compute_search_paths(sources, options, data_dir, fscache, alt_lib_path)
298327

299328
reports = Reports(data_dir, options.report_dirs)
300329
source_set = BuildSourceSet(sources)

mypy/checkstrformat.py

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import re
44

5-
from typing import cast, List, Tuple, Dict, Callable, Union, Optional
5+
from typing import cast, List, Tuple, Dict, Callable, Union, Optional, Pattern
66

77
from mypy.types import (
88
Type, AnyType, TupleType, Instance, UnionType, TypeOfAny
@@ -21,6 +21,20 @@
2121
Checkers = Tuple[Callable[[Expression], None], Callable[[Type], None]]
2222

2323

24+
def compile_format_re() -> Pattern[str]:
25+
key_re = r'(\(([^()]*)\))?' # (optional) parenthesised sequence of characters.
26+
flags_re = r'([#0\-+ ]*)' # (optional) sequence of flags.
27+
width_re = r'(\*|[1-9][0-9]*)?' # (optional) minimum field width (* or numbers).
28+
precision_re = r'(?:\.(\*|[0-9]+)?)?' # (optional) . followed by * of numbers.
29+
length_mod_re = r'[hlL]?' # (optional) length modifier (unused).
30+
type_re = r'(.)?' # conversion type.
31+
format_re = '%' + key_re + flags_re + width_re + precision_re + length_mod_re + type_re
32+
return re.compile(format_re)
33+
34+
35+
FORMAT_RE = compile_format_re()
36+
37+
2438
class ConversionSpecifier:
2539
def __init__(self, key: str, flags: str, width: str, precision: str, type: str) -> None:
2640
self.key = key
@@ -90,16 +104,8 @@ def check_str_interpolation(self,
90104
assert False
91105

92106
def parse_conversion_specifiers(self, format: str) -> List[ConversionSpecifier]:
93-
key_regex = r'(\(([^()]*)\))?' # (optional) parenthesised sequence of characters
94-
flags_regex = r'([#0\-+ ]*)' # (optional) sequence of flags
95-
width_regex = r'(\*|[1-9][0-9]*)?' # (optional) minimum field width (* or numbers)
96-
precision_regex = r'(?:\.(\*|[0-9]+)?)?' # (optional) . followed by * of numbers
97-
length_mod_regex = r'[hlL]?' # (optional) length modifier (unused)
98-
type_regex = r'(.)?' # conversion type
99-
regex = ('%' + key_regex + flags_regex + width_regex +
100-
precision_regex + length_mod_regex + type_regex)
101107
specifiers = [] # type: List[ConversionSpecifier]
102-
for parens_key, key, flags, width, precision, type in re.findall(regex, format):
108+
for parens_key, key, flags, width, precision, type in FORMAT_RE.findall(format):
103109
if parens_key == '':
104110
key = None
105111
specifiers.append(ConversionSpecifier(key, flags, width, precision, type))

mypy/constraints.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,8 @@ def visit_instance(self, template: Instance) -> List[Constraint]:
312312
actual = actual.as_anonymous().fallback
313313
if isinstance(actual, Instance):
314314
instance = actual
315+
erased = erase_typevars(template)
316+
assert isinstance(erased, Instance)
315317
# We always try nominal inference if possible,
316318
# it is much faster than the structural one.
317319
if (self.direction == SUBTYPE_OF and
@@ -343,8 +345,11 @@ def visit_instance(self, template: Instance) -> List[Constraint]:
343345
# This is a conservative way break the inference cycles.
344346
# It never produces any "false" constraints but gives up soon
345347
# on purely structural inference cycles, see #3829.
348+
# Note that we use is_protocol_implementation instead of is_subtype
349+
# because some type may be considered a subtype of a protocol
350+
# due to _promote, but still not implement the protocol.
346351
not any(is_same_type(template, t) for t in template.type.inferring) and
347-
mypy.subtypes.is_subtype(instance, erase_typevars(template))):
352+
mypy.subtypes.is_protocol_implementation(instance, erased)):
348353
template.type.inferring.append(template)
349354
self.infer_constraints_from_protocol_members(res, instance, template,
350355
original_actual, template)
@@ -353,7 +358,7 @@ def visit_instance(self, template: Instance) -> List[Constraint]:
353358
elif (instance.type.is_protocol and self.direction == SUBTYPE_OF and
354359
# We avoid infinite recursion for structural subtypes also here.
355360
not any(is_same_type(instance, i) for i in instance.type.inferring) and
356-
mypy.subtypes.is_subtype(erase_typevars(template), instance)):
361+
mypy.subtypes.is_protocol_implementation(erased, instance)):
357362
instance.type.inferring.append(instance)
358363
self.infer_constraints_from_protocol_members(res, instance, template,
359364
template, instance)

mypy/dmypy_server.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,7 @@ def fine_grained_increment(self, sources: List[mypy.build.BuildSource]) -> Dict[
327327
self.update_sources(sources)
328328
changed, removed = self.find_changed(sources)
329329
manager.search_paths = mypy.build.compute_search_paths(
330-
sources, manager.options, manager.data_dir)
330+
sources, manager.options, manager.data_dir, mypy.build.FileSystemCache())
331331
t1 = time.time()
332332
messages = self.fine_grained_manager.update(changed, removed)
333333
t2 = time.time()

mypy/fixup.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -279,11 +279,11 @@ def lookup_qualified_stnode(modules: Dict[str, MypyFile], name: str,
279279
return stnode
280280
node = stnode.node
281281
# In fine-grained mode, could be a cross-reference to a deleted module
282-
if node is None:
282+
# or a Var made up for a missing module.
283+
if not isinstance(node, TypeInfo):
283284
if not quick_and_dirty:
284285
assert node, "Cannot find %s" % (name,)
285286
return None
286-
assert isinstance(node, TypeInfo)
287287
names = node.names
288288

289289

mypy/semanal.py

Lines changed: 51 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -855,6 +855,7 @@ def calculate_abstract_status(self, typ: TypeInfo) -> None:
855855
"""
856856
concrete = set() # type: Set[str]
857857
abstract = [] # type: List[str]
858+
abstract_in_this_class = [] # type: List[str]
858859
for base in typ.mro:
859860
for name, symnode in base.names.items():
860861
node = symnode.node
@@ -871,12 +872,30 @@ def calculate_abstract_status(self, typ: TypeInfo) -> None:
871872
if fdef.is_abstract and name not in concrete:
872873
typ.is_abstract = True
873874
abstract.append(name)
875+
if base is typ:
876+
abstract_in_this_class.append(name)
874877
elif isinstance(node, Var):
875878
if node.is_abstract_var and name not in concrete:
876879
typ.is_abstract = True
877880
abstract.append(name)
881+
if base is typ:
882+
abstract_in_this_class.append(name)
878883
concrete.add(name)
884+
# In stubs, abstract classes need to be explicitly marked because it is too
885+
# easy to accidentally leave a concrete class abstract by forgetting to
886+
# implement some methods.
879887
typ.abstract_attributes = sorted(abstract)
888+
if not self.is_stub_file:
889+
return
890+
if (typ.declared_metaclass and typ.declared_metaclass.type.fullname() == 'abc.ABCMeta'):
891+
return
892+
if typ.is_protocol:
893+
return
894+
if abstract and not abstract_in_this_class:
895+
attrs = ", ".join('"{}"'.format(attr) for attr in sorted(abstract))
896+
self.fail("Class {} has abstract attributes {}".format(typ.fullname(), attrs), typ)
897+
self.note("If it is meant to be abstract, add 'abc.ABCMeta' as an explicit metaclass",
898+
typ)
880899

881900
def setup_type_promotion(self, defn: ClassDef) -> None:
882901
"""Setup extra, ad-hoc subtyping relationships between classes (promotion).
@@ -1386,19 +1405,14 @@ def visit_import_from(self, imp: ImportFrom) -> None:
13861405
# If it is still not resolved, check for a module level __getattr__
13871406
if (module and not node and (module.is_stub or self.options.python_version >= (3, 7))
13881407
and '__getattr__' in module.names):
1389-
getattr_defn = module.names['__getattr__']
1390-
if isinstance(getattr_defn.node, (FuncDef, Var)):
1391-
if isinstance(getattr_defn.node.type, CallableType):
1392-
typ = getattr_defn.node.type.ret_type
1393-
else:
1394-
typ = AnyType(TypeOfAny.from_error)
1395-
if as_id:
1396-
name = as_id
1397-
else:
1398-
name = id
1399-
ast_node = Var(name, type=typ)
1400-
symbol = SymbolTableNode(GDEF, ast_node)
1401-
self.add_symbol(name, symbol, imp)
1408+
name = as_id if as_id else id
1409+
if self.type:
1410+
fullname = self.type.fullname() + "." + name
1411+
else:
1412+
fullname = self.qualified_name(name)
1413+
gvar = self.create_getattr_var(module.names['__getattr__'], name, fullname)
1414+
if gvar:
1415+
self.add_symbol(name, gvar, imp)
14021416
continue
14031417
if node and node.kind != UNBOUND_IMPORTED and not node.module_hidden:
14041418
if not node:
@@ -3064,7 +3078,15 @@ def lookup_qualified(self, name: str, ctx: Context,
30643078
n = names.get(parts[i], None)
30653079
if n and isinstance(n.node, ImportedName):
30663080
n = self.dereference_module_cross_ref(n)
3081+
elif '__getattr__' in names:
3082+
gvar = self.create_getattr_var(names['__getattr__'],
3083+
parts[i], parts[i])
3084+
if gvar:
3085+
names[name] = gvar
3086+
n = gvar
30673087
# TODO: What if node is Var or FuncDef?
3088+
# Currently, missing these cases results in controversial behavior, when
3089+
# lookup_qualified(x.y.z) returns Var(x).
30683090
if not n:
30693091
if not suppress_errors:
30703092
self.name_not_defined(name, ctx)
@@ -3077,6 +3099,22 @@ def lookup_qualified(self, name: str, ctx: Context,
30773099
return n
30783100
return None
30793101

3102+
def create_getattr_var(self, getattr_defn: SymbolTableNode,
3103+
name: str, fullname: str) -> Optional[SymbolTableNode]:
3104+
"""Create a dummy global symbol using __getattr__ return type.
3105+
3106+
If not possible, return None.
3107+
"""
3108+
if isinstance(getattr_defn.node, (FuncDef, Var)):
3109+
if isinstance(getattr_defn.node.type, CallableType):
3110+
typ = getattr_defn.node.type.ret_type
3111+
else:
3112+
typ = AnyType(TypeOfAny.from_error)
3113+
v = Var(name, type=typ)
3114+
v._fullname = fullname
3115+
return SymbolTableNode(GDEF, v)
3116+
return None
3117+
30803118
def rebind_symbol_table_node(self, n: SymbolTableNode) -> Optional[SymbolTableNode]:
30813119
"""If node refers to old version of module, return reference to new version.
30823120

mypy/stubutil.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import re
22
import sys
3+
import os
34

45
from typing import Optional, Tuple, Sequence, MutableSequence, List, MutableMapping, IO
56
from types import ModuleType
@@ -87,7 +88,8 @@ def find_unique_signatures(sigs: Sequence[Sig]) -> List[Sig]:
8788

8889

8990
def is_c_module(module: ModuleType) -> bool:
90-
return '__file__' not in module.__dict__ or module.__dict__['__file__'].endswith('.so')
91+
return ('__file__' not in module.__dict__ or
92+
os.path.splitext(module.__dict__['__file__'])[-1] in ['.so', '.pyd'])
9193

9294

9395
def write_header(file: IO[str], module_name: Optional[str] = None,

mypy/subtypes.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -524,7 +524,7 @@ def get_member_flags(name: str, info: TypeInfo) -> Set[int]:
524524
if v.var.is_staticmethod or v.var.is_classmethod:
525525
return {IS_CLASS_OR_STATIC}
526526
# just a variable
527-
if isinstance(v, Var):
527+
if isinstance(v, Var) and not v.is_property:
528528
flags = {IS_SETTABLE}
529529
if v.is_classvar:
530530
flags.add(IS_CLASSVAR)

0 commit comments

Comments
 (0)