[med-svn] [Git][python-team/packages/mypy][master] 5 commits: routine-update: New upstream version
Michael R. Crusoe (@crusoe)
gitlab at salsa.debian.org
Tue Nov 15 14:18:52 GMT 2022
Michael R. Crusoe pushed to branch master at Debian Python Team / packages / mypy
Commits:
af0c9dcf by Michael R. Crusoe at 2022-11-14T18:39:30+01:00
routine-update: New upstream version
- - - - -
b51395bf by Michael R. Crusoe at 2022-11-14T18:39:31+01:00
New upstream version 0.991
- - - - -
092e2747 by Michael R. Crusoe at 2022-11-14T18:39:45+01:00
Update upstream source from tag 'upstream/0.991'
Update to upstream version '0.991'
with Debian dir 29c2fc69dbbc274ec17ed352b373bd3b6cfa0f50
- - - - -
d5cb40dd by Michael R. Crusoe at 2022-11-14T18:40:01+01:00
Apply multi-arch hints.
+ mypy-doc: Add Multi-Arch: foreign.
Changes-By: apply-multiarch-hints
- - - - -
6fa42c6c by Michael R. Crusoe at 2022-11-14T18:40:05+01:00
routine-update: Ready to upload to unstable
- - - - -
20 changed files:
- PKG-INFO
- debian/changelog
- debian/control
- docs/source/command_line.rst
- mypy.egg-info/PKG-INFO
- mypy/build.py
- mypy/checker.py
- mypy/constraints.py
- mypy/dmypy_server.py
- mypy/modulefinder.py
- mypy/report.py
- mypy/server/update.py
- mypy/test/testcmdline.py
- mypy/types.py
- mypy/typeshed/stdlib/_ast.pyi
- mypy/typeshed/stdlib/ast.pyi
- mypy/version.py
- test-data/unit/check-functions.test
- test-data/unit/check-recursive-types.test
- test-data/unit/cmdline.test
Changes:
=====================================
PKG-INFO
=====================================
@@ -1,6 +1,6 @@
Metadata-Version: 2.1
Name: mypy
-Version: 0.990
+Version: 0.991
Summary: Optional static typing for Python
Home-page: http://www.mypy-lang.org/
Author: Jukka Lehtosalo
=====================================
debian/changelog
=====================================
@@ -1,3 +1,9 @@
+mypy (0.991-1) unstable; urgency=medium
+
+ * New upstream version
+
+ -- Michael R. Crusoe <crusoe at debian.org> Mon, 14 Nov 2022 18:40:05 +0100
+
mypy (0.990-1) unstable; urgency=medium
* New upstream version
=====================================
debian/control
=====================================
@@ -75,6 +75,7 @@ Description: documentation for mypy
.
This package provides the documentation.
Build-Profiles: <!nodoc>
+Multi-Arch: foreign
Package: python3-mypy
Architecture: any
=====================================
docs/source/command_line.rst
=====================================
@@ -448,9 +448,10 @@ potentially problematic or redundant in some way.
are when:
- The function has a ``None`` or ``Any`` return type
- - The function has an empty body or a body that is just
- ellipsis (``...``). Empty functions are often used for
- abstract methods.
+ - The function has an empty body and is marked as an abstract method,
+ is in a protocol class, or is in a stub file
+ - The execution path can never return; for example, if an exception
+ is always raised
Passing in :option:`--no-warn-no-return` will disable these error
messages in all cases.
=====================================
mypy.egg-info/PKG-INFO
=====================================
@@ -1,6 +1,6 @@
Metadata-Version: 2.1
Name: mypy
-Version: 0.990
+Version: 0.991
Summary: Optional static typing for Python
Home-page: http://www.mypy-lang.org/
Author: Jukka Lehtosalo
=====================================
mypy/build.py
=====================================
@@ -1940,7 +1940,7 @@ class State:
raise
if follow_imports == "silent":
self.ignore_all = True
- elif path and is_silent_import_module(manager, path):
+ elif path and is_silent_import_module(manager, path) and not root_source:
self.ignore_all = True
self.path = path
if path:
@@ -2629,7 +2629,7 @@ def find_module_and_diagnose(
else:
skipping_module(manager, caller_line, caller_state, id, result)
raise ModuleNotFound
- if is_silent_import_module(manager, result):
+ if is_silent_import_module(manager, result) and not root_source:
follow_imports = "silent"
return (result, follow_imports)
else:
@@ -3024,7 +3024,11 @@ def load_graph(
for bs in sources:
try:
st = State(
- id=bs.module, path=bs.path, source=bs.text, manager=manager, root_source=True
+ id=bs.module,
+ path=bs.path,
+ source=bs.text,
+ manager=manager,
+ root_source=not bs.followed,
)
except ModuleNotFound:
continue
=====================================
mypy/checker.py
=====================================
@@ -960,7 +960,10 @@ class TypeChecker(NodeVisitor[None], CheckerPluginInterface):
# Function definition overrides a variable initialized via assignment or a
# decorated function.
orig_type = defn.original_def.type
- assert orig_type is not None, f"Error checking function redefinition {defn}"
+ if orig_type is None:
+ # If other branch is unreachable, we don't type check it and so we might
+ # not have a type for the original definition
+ return
if isinstance(orig_type, PartialType):
if orig_type.type is None:
# Ah this is a partial type. Give it the type of the function.
=====================================
mypy/constraints.py
=====================================
@@ -177,8 +177,9 @@ def infer_constraints(template: Type, actual: Type, direction: int) -> list[Cons
for (t, a) in reversed(TypeState.inferring)
):
return []
- if has_recursive_types(template):
+ if has_recursive_types(template) or isinstance(get_proper_type(template), Instance):
# This case requires special care because it may cause infinite recursion.
+ # Note that we include Instances because the may be recursive as str(Sequence[str]).
if not has_type_vars(template):
# Return early on an empty branch.
return []
=====================================
mypy/dmypy_server.py
=====================================
@@ -592,7 +592,7 @@ class Server:
sources.extend(new_files)
# Process changes directly reachable from roots.
- messages = fine_grained_manager.update(changed, [])
+ messages = fine_grained_manager.update(changed, [], followed=True)
# Follow deps from changed modules (still within graph).
worklist = changed[:]
@@ -609,13 +609,13 @@ class Server:
sources2, graph, seen, changed_paths
)
self.update_sources(new_files)
- messages = fine_grained_manager.update(changed, [])
+ messages = fine_grained_manager.update(changed, [], followed=True)
worklist.extend(changed)
t2 = time.time()
def refresh_file(module: str, path: str) -> list[str]:
- return fine_grained_manager.update([(module, path)], [])
+ return fine_grained_manager.update([(module, path)], [], followed=True)
for module_id, state in list(graph.items()):
new_messages = refresh_suppressed_submodules(
@@ -632,10 +632,10 @@ class Server:
new_unsuppressed = self.find_added_suppressed(graph, seen, manager.search_paths)
if not new_unsuppressed:
break
- new_files = [BuildSource(mod[1], mod[0]) for mod in new_unsuppressed]
+ new_files = [BuildSource(mod[1], mod[0], followed=True) for mod in new_unsuppressed]
sources.extend(new_files)
self.update_sources(new_files)
- messages = fine_grained_manager.update(new_unsuppressed, [])
+ messages = fine_grained_manager.update(new_unsuppressed, [], followed=True)
for module_id, path in new_unsuppressed:
new_messages = refresh_suppressed_submodules(
@@ -717,7 +717,7 @@ class Server:
for dep in state.dependencies:
if dep not in seen:
seen.add(dep)
- worklist.append(BuildSource(graph[dep].path, graph[dep].id))
+ worklist.append(BuildSource(graph[dep].path, graph[dep].id, followed=True))
return changed, new_files
def direct_imports(
@@ -725,7 +725,7 @@ class Server:
) -> list[BuildSource]:
"""Return the direct imports of module not included in seen."""
state = graph[module[0]]
- return [BuildSource(graph[dep].path, dep) for dep in state.dependencies]
+ return [BuildSource(graph[dep].path, dep, followed=True) for dep in state.dependencies]
def find_added_suppressed(
self, graph: mypy.build.Graph, seen: set[str], search_paths: SearchPaths
=====================================
mypy/modulefinder.py
=====================================
@@ -115,15 +115,19 @@ class BuildSource:
module: str | None,
text: str | None = None,
base_dir: str | None = None,
+ followed: bool = False,
) -> None:
self.path = path # File where it's found (e.g. 'xxx/yyy/foo/bar.py')
self.module = module or "__main__" # Module name (e.g. 'foo.bar')
self.text = text # Source code, if initially supplied, else None
self.base_dir = base_dir # Directory where the package is rooted (e.g. 'xxx/yyy')
+ self.followed = followed # Was this found by following imports?
def __repr__(self) -> str:
- return "BuildSource(path={!r}, module={!r}, has_text={}, base_dir={!r})".format(
- self.path, self.module, self.text is not None, self.base_dir
+ return (
+ "BuildSource(path={!r}, module={!r}, has_text={}, base_dir={!r}, followed={})".format(
+ self.path, self.module, self.text is not None, self.base_dir, self.followed
+ )
)
=====================================
mypy/report.py
=====================================
@@ -637,51 +637,48 @@ class CoberturaXmlReporter(AbstractReporter):
etree.SubElement(class_element, "methods")
lines_element = etree.SubElement(class_element, "lines")
- with tokenize.open(path) as input_file:
- class_lines_covered = 0
- class_total_lines = 0
- for lineno, _ in enumerate(input_file, 1):
- status = visitor.line_map.get(lineno, stats.TYPE_EMPTY)
- hits = 0
- branch = False
- if status == stats.TYPE_EMPTY:
- continue
- class_total_lines += 1
- if status != stats.TYPE_ANY:
- class_lines_covered += 1
- hits = 1
- if status == stats.TYPE_IMPRECISE:
- branch = True
- file_info.counts[status] += 1
- line_element = etree.SubElement(
- lines_element,
- "line",
- branch=str(branch).lower(),
- hits=str(hits),
- number=str(lineno),
- precision=stats.precision_names[status],
- )
- if branch:
- line_element.attrib["condition-coverage"] = "50% (1/2)"
- class_element.attrib["branch-rate"] = "0"
- class_element.attrib["line-rate"] = get_line_rate(
- class_lines_covered, class_total_lines
+ class_lines_covered = 0
+ class_total_lines = 0
+ for lineno, _ in iterate_python_lines(path):
+ status = visitor.line_map.get(lineno, stats.TYPE_EMPTY)
+ hits = 0
+ branch = False
+ if status == stats.TYPE_EMPTY:
+ continue
+ class_total_lines += 1
+ if status != stats.TYPE_ANY:
+ class_lines_covered += 1
+ hits = 1
+ if status == stats.TYPE_IMPRECISE:
+ branch = True
+ file_info.counts[status] += 1
+ line_element = etree.SubElement(
+ lines_element,
+ "line",
+ branch=str(branch).lower(),
+ hits=str(hits),
+ number=str(lineno),
+ precision=stats.precision_names[status],
)
- # parent_module is set to whichever module contains this file. For most files, we want
- # to simply strip the last element off of the module. But for __init__.py files,
- # the module == the parent module.
- parent_module = file_info.module.rsplit(".", 1)[0]
- if file_info.name.endswith("__init__.py"):
- parent_module = file_info.module
-
- if parent_module not in self.root_package.packages:
- self.root_package.packages[parent_module] = CoberturaPackage(parent_module)
- current_package = self.root_package.packages[parent_module]
- packages_to_update = [self.root_package, current_package]
- for package in packages_to_update:
- package.total_lines += class_total_lines
- package.covered_lines += class_lines_covered
- current_package.classes[class_name] = class_element
+ if branch:
+ line_element.attrib["condition-coverage"] = "50% (1/2)"
+ class_element.attrib["branch-rate"] = "0"
+ class_element.attrib["line-rate"] = get_line_rate(class_lines_covered, class_total_lines)
+ # parent_module is set to whichever module contains this file. For most files, we want
+ # to simply strip the last element off of the module. But for __init__.py files,
+ # the module == the parent module.
+ parent_module = file_info.module.rsplit(".", 1)[0]
+ if file_info.name.endswith("__init__.py"):
+ parent_module = file_info.module
+
+ if parent_module not in self.root_package.packages:
+ self.root_package.packages[parent_module] = CoberturaPackage(parent_module)
+ current_package = self.root_package.packages[parent_module]
+ packages_to_update = [self.root_package, current_package]
+ for package in packages_to_update:
+ package.total_lines += class_total_lines
+ package.covered_lines += class_lines_covered
+ current_package.classes[class_name] = class_element
def on_finish(self) -> None:
self.root.attrib["line-rate"] = get_line_rate(
=====================================
mypy/server/update.py
=====================================
@@ -203,7 +203,10 @@ class FineGrainedBuildManager:
self.processed_targets: list[str] = []
def update(
- self, changed_modules: list[tuple[str, str]], removed_modules: list[tuple[str, str]]
+ self,
+ changed_modules: list[tuple[str, str]],
+ removed_modules: list[tuple[str, str]],
+ followed: bool = False,
) -> list[str]:
"""Update previous build result by processing changed modules.
@@ -219,6 +222,7 @@ class FineGrainedBuildManager:
Assume this is correct; it's not validated here.
removed_modules: Modules that have been deleted since the previous update
or removed from the build.
+ followed: If True, the modules were found through following imports
Returns:
A list of errors.
@@ -256,7 +260,9 @@ class FineGrainedBuildManager:
self.blocking_error = None
while True:
- result = self.update_one(changed_modules, initial_set, removed_set, blocking_error)
+ result = self.update_one(
+ changed_modules, initial_set, removed_set, blocking_error, followed
+ )
changed_modules, (next_id, next_path), blocker_messages = result
if blocker_messages is not None:
@@ -329,6 +335,7 @@ class FineGrainedBuildManager:
initial_set: set[str],
removed_set: set[str],
blocking_error: str | None,
+ followed: bool,
) -> tuple[list[tuple[str, str]], tuple[str, str], list[str] | None]:
"""Process a module from the list of changed modules.
@@ -355,7 +362,7 @@ class FineGrainedBuildManager:
)
return changed_modules, (next_id, next_path), None
- result = self.update_module(next_id, next_path, next_id in removed_set)
+ result = self.update_module(next_id, next_path, next_id in removed_set, followed)
remaining, (next_id, next_path), blocker_messages = result
changed_modules = [(id, path) for id, path in changed_modules if id != next_id]
changed_modules = dedupe_modules(remaining + changed_modules)
@@ -368,7 +375,7 @@ class FineGrainedBuildManager:
return changed_modules, (next_id, next_path), blocker_messages
def update_module(
- self, module: str, path: str, force_removed: bool
+ self, module: str, path: str, force_removed: bool, followed: bool
) -> tuple[list[tuple[str, str]], tuple[str, str], list[str] | None]:
"""Update a single modified module.
@@ -380,6 +387,7 @@ class FineGrainedBuildManager:
path: File system path of the module
force_removed: If True, consider module removed from the build even if path
exists (used for removing an existing file from the build)
+ followed: Was this found via import following?
Returns:
Tuple with these items:
@@ -417,7 +425,7 @@ class FineGrainedBuildManager:
manager.errors.reset()
self.processed_targets.append(module)
result = update_module_isolated(
- module, path, manager, previous_modules, graph, force_removed
+ module, path, manager, previous_modules, graph, force_removed, followed
)
if isinstance(result, BlockedUpdate):
# Blocking error -- just give up
@@ -552,6 +560,7 @@ def update_module_isolated(
previous_modules: dict[str, str],
graph: Graph,
force_removed: bool,
+ followed: bool,
) -> UpdateResult:
"""Build a new version of one changed module only.
@@ -575,7 +584,7 @@ def update_module_isolated(
delete_module(module, path, graph, manager)
return NormalUpdate(module, path, [], None)
- sources = get_sources(manager.fscache, previous_modules, [(module, path)])
+ sources = get_sources(manager.fscache, previous_modules, [(module, path)], followed)
if module in manager.missing_modules:
manager.missing_modules.remove(module)
@@ -728,12 +737,15 @@ def get_module_to_path_map(graph: Graph) -> dict[str, str]:
def get_sources(
- fscache: FileSystemCache, modules: dict[str, str], changed_modules: list[tuple[str, str]]
+ fscache: FileSystemCache,
+ modules: dict[str, str],
+ changed_modules: list[tuple[str, str]],
+ followed: bool,
) -> list[BuildSource]:
sources = []
for id, path in changed_modules:
if fscache.isfile(path):
- sources.append(BuildSource(path, id, None))
+ sources.append(BuildSource(path, id, None, followed=followed))
return sources
=====================================
mypy/test/testcmdline.py
=====================================
@@ -69,12 +69,10 @@ def test_python_cmdline(testcase: DataDrivenTestCase, step: int) -> None:
env["PYTHONPATH"] = PREFIX
if os.path.isdir(extra_path):
env["PYTHONPATH"] += os.pathsep + extra_path
+ cwd = os.path.join(test_temp_dir, custom_cwd or "")
+ args = [arg.replace("$CWD", os.path.abspath(cwd)) for arg in args]
process = subprocess.Popen(
- fixed + args,
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE,
- cwd=os.path.join(test_temp_dir, custom_cwd or ""),
- env=env,
+ fixed + args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=cwd, env=env
)
outb, errb = process.communicate()
result = process.returncode
=====================================
mypy/types.py
=====================================
@@ -3240,6 +3240,12 @@ class HasTypeVars(TypeQuery[bool]):
def visit_type_var(self, t: TypeVarType) -> bool:
return True
+ def visit_type_var_tuple(self, t: TypeVarTupleType) -> bool:
+ return True
+
+ def visit_param_spec(self, t: ParamSpecType) -> bool:
+ return True
+
def has_type_vars(typ: Type) -> bool:
"""Check if a type contains any type variables (recursively)."""
=====================================
mypy/typeshed/stdlib/_ast.pyi
=====================================
@@ -329,7 +329,7 @@ class JoinedStr(expr):
if sys.version_info < (3, 8):
class Num(expr): # Deprecated in 3.8; use Constant
- n: complex
+ n: int | float | complex
class Str(expr): # Deprecated in 3.8; use Constant
s: str
@@ -349,7 +349,7 @@ class Constant(expr):
kind: str | None
# Aliases for value, for backwards compatibility
s: Any
- n: complex
+ n: int | float | complex
if sys.version_info >= (3, 8):
class NamedExpr(expr):
=====================================
mypy/typeshed/stdlib/ast.pyi
=====================================
@@ -10,7 +10,7 @@ if sys.version_info >= (3, 8):
def __init__(cls, *args: object) -> None: ...
class Num(Constant, metaclass=_ABC):
- value: complex
+ value: int | float | complex
class Str(Constant, metaclass=_ABC):
value: str
=====================================
mypy/version.py
=====================================
@@ -8,7 +8,7 @@ from mypy import git
# - Release versions have the form "0.NNN".
# - Dev versions have the form "0.NNN+dev" (PLUS sign to conform to PEP 440).
# - For 1.0 we'll switch back to 1.2.3 form.
-__version__ = "0.990"
+__version__ = "0.991"
base_version = __version__
mypy_dir = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
=====================================
test-data/unit/check-functions.test
=====================================
@@ -1475,6 +1475,20 @@ else:
@dec
def f(): pass
+[case testConditionalFunctionDefinitionUnreachable]
+def bar() -> None:
+ if False:
+ foo = 1
+ else:
+ def foo(obj): ...
+
+def baz() -> None:
+ if False:
+ foo: int = 1
+ else:
+ def foo(obj): ... # E: Incompatible redefinition (redefinition with type "Callable[[Any], Any]", original type "int")
+[builtins fixtures/tuple.pyi]
+
[case testConditionalRedefinitionOfAnUnconditionalFunctionDefinition1]
from typing import Any
def f(x: str) -> None: pass
=====================================
test-data/unit/check-recursive-types.test
=====================================
@@ -826,3 +826,14 @@ z = z
x = y # E: Incompatible types in assignment (expression has type "L", variable has type "K")
z = x # OK
[builtins fixtures/tuple.pyi]
+
+[case testRecursiveInstanceInferenceNoCrash]
+from typing import Sequence, TypeVar, Union
+
+class C(Sequence[C]): ...
+
+T = TypeVar("T")
+def foo(x: T) -> C: ...
+
+Nested = Union[C, Sequence[Nested]]
+x: Nested = foo(42)
=====================================
test-data/unit/cmdline.test
=====================================
@@ -1505,3 +1505,68 @@ def f():
[out]
a.py:2: note: By default the bodies of untyped functions are not checked, consider using --check-untyped-defs
== Return code: 0
+
+[case testCustomTypeshedDirFilePassedExplicitly]
+# cmd: mypy --custom-typeshed-dir dir m.py dir/stdlib/foo.pyi
+[file m.py]
+1()
+[file dir/stdlib/abc.pyi]
+1() # Errors are not reported from typeshed by default
+[file dir/stdlib/builtins.pyi]
+class object: pass
+class str(object): pass
+class int(object): pass
+[file dir/stdlib/sys.pyi]
+[file dir/stdlib/types.pyi]
+[file dir/stdlib/typing.pyi]
+[file dir/stdlib/mypy_extensions.pyi]
+[file dir/stdlib/typing_extensions.pyi]
+[file dir/stdlib/foo.pyi]
+1() # Errors are reported if the file was explicitly passed on the command line
+[file dir/stdlib/VERSIONS]
+[out]
+dir/stdlib/foo.pyi:1: error: "int" not callable
+m.py:1: error: "int" not callable
+
+[case testFileInPythonPathPassedExplicitly1]
+# cmd: mypy $CWD/pypath/foo.py
+[file pypath/foo.py]
+1()
+[out]
+pypath/foo.py:1: error: "int" not callable
+
+[case testFileInPythonPathPassedExplicitly2]
+# cmd: mypy pypath/foo.py
+[file pypath/foo.py]
+1()
+[out]
+pypath/foo.py:1: error: "int" not callable
+
+[case testFileInPythonPathPassedExplicitly3]
+# cmd: mypy -p foo
+# cwd: pypath
+[file pypath/foo/__init__.py]
+1()
+[file pypath/foo/m.py]
+1()
+[out]
+foo/m.py:1: error: "int" not callable
+foo/__init__.py:1: error: "int" not callable
+
+[case testFileInPythonPathPassedExplicitly4]
+# cmd: mypy -m foo
+# cwd: pypath
+[file pypath/foo.py]
+1()
+[out]
+foo.py:1: error: "int" not callable
+
+[case testFileInPythonPathPassedExplicitly5]
+# cmd: mypy -m foo.m
+# cwd: pypath
+[file pypath/foo/__init__.py]
+1() # TODO: Maybe this should generate errors as well? But how would we decide?
+[file pypath/foo/m.py]
+1()
+[out]
+foo/m.py:1: error: "int" not callable
View it on GitLab: https://salsa.debian.org/python-team/packages/mypy/-/compare/f573a2f7e3ceb7a4046be7c2c27e5e4525cd7483...6fa42c6c8aed10dd0b8220f6bff475f6af5e1f01
--
View it on GitLab: https://salsa.debian.org/python-team/packages/mypy/-/compare/f573a2f7e3ceb7a4046be7c2c27e5e4525cd7483...6fa42c6c8aed10dd0b8220f6bff475f6af5e1f01
You're receiving this email because of your account on salsa.debian.org.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/debian-med-commit/attachments/20221115/4203fe31/attachment-0001.htm>
More information about the debian-med-commit
mailing list