[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