Repository: sympy/sympy
Pull request: quality: fix type hints and remove type: ignore
<!-- Your title above should be a short description of what
was changed. Do not include the issue number in the title. -->

#### References to other Issues or PRs
<!-- If this pull request fixes an issue, write "Fixes #NNNN" in that exact
format, e.g. "Fixes #1234" (see
https://tinyurl.com/auto-closing for more information). Also, please
write a comment on that issue linking back to this pull request once it is
open. -->

Fixes #22697

#### Brief description of what is fixed or changed

Remove a lot of `# type: ignore` that aren't needed with latest mypy. Fix some inaccurate type annotations.

#### Other comments


#### Release Notes

<!-- Write the release notes for this release below between the BEGIN and END
statements. The basic format is a bulleted list with the name of the subpackage
and the release note for this PR. For example:

* solvers
  * Added a new solver for logarithmic equations.

* functions
  * Fixed a bug with log of integers.

or if no release note(s) should be included use:

NO ENTRY

See https://github.com/sympy/sympy/wiki/Writing-Release-Notes for more
information on how to write release notes. The bot will check your release
notes automatically to see if they are formatted correctly. -->

<!-- BEGIN RELEASE NOTES -->
NO ENTRY
<!-- END RELEASE NOTES -->


Problem statement:
mypy code quality test failing
The code quality test seems to be currently failing for all pull requests. This does not seem to have anything to do with the pull requests themselves. In particular, the `mypy` run is failing with the following message:
```
sympy/tensor/array/expressions/conv_array_to_matrix.py:288: error: No overload variant of "__getitem__" of "tuple" matches argument type "List[Any]"
sympy/tensor/array/expressions/conv_array_to_matrix.py:288: note: Possible overload variants:
sympy/tensor/array/expressions/conv_array_to_matrix.py:288: note:     def __getitem__(self, int) -> Basic
sympy/tensor/array/expressions/conv_array_to_matrix.py:288: note:     def __getitem__(self, slice) -> Tuple[Basic, ...]
sympy/tensor/array/expressions/conv_array_to_matrix.py:290: error: No overload variant of "__getitem__" of "tuple" matches argument type "List[Any]"
sympy/tensor/array/expressions/conv_array_to_matrix.py:290: note: Possible overload variants:
sympy/tensor/array/expressions/conv_array_to_matrix.py:290: note:     def __getitem__(self, int) -> Basic
sympy/tensor/array/expressions/conv_array_to_matrix.py:290: note:     def __getitem__(self, slice) -> Tuple[Basic, ...]
sympy/tensor/array/expressions/conv_array_to_matrix.py:494: error: Incompatible types in assignment (expression has type "List[Tuple[Any, ...]]", variable has type "Dict[Any, Tuple[Any, ...]]")
sympy/tensor/array/expressions/conv_array_to_matrix.py:500: error: Incompatible types in assignment (expression has type "List[Any]", variable has type "Dict[Any, Tuple[Any, ...]]")
sympy/tensor/array/expressions/arrayexpr_derivatives.py:26: error: "Expr" has no attribute "shape"
sympy/tensor/array/expressions/arrayexpr_derivatives.py:66: error: "Expr" has no attribute "shape"
sympy/tensor/array/expressions/arrayexpr_derivatives.py:77: error: "Expr" has no attribute "shape"
sympy/tensor/array/expressions/arrayexpr_derivatives.py:82: error: "Expr" has no attribute "shape"
Found 8 errors in 2 files (checked 1455 source files)
Error: Process completed with exit code 1.
```
This should probably be fixed soon as it is also preventing other checks from running.

Hints from issue discussion:
Yes ,I had reported this issue on the SymPy Gitter chat channel yesterday and asked for a potential fix .Thanks for opening this as an issue !
This diff makes mypy happy about the errors in `conv_array_to_matrix.py`:
```diff
diff --git a/sympy/tensor/array/expressions/conv_array_to_matrix.py b/sympy/tensor/array/expressions/conv_array_to_matrix.py
index 72e8369..6b8cfbf 100644
--- a/sympy/tensor/array/expressions/conv_array_to_matrix.py
+++ b/sympy/tensor/array/expressions/conv_array_to_matrix.py
@@ -283,11 +283,11 @@ def _(expr: PermuteDims):
             p2 = permuted[2*i+1]
             if p1 // 2 != p2 // 2:
                 return _permute_dims(mat_mul_lines, permutation)
-            pos = p1 // 2
+            pos1 = p1 // 2
             if p1 > p2:
-                args_array[i] = _a2m_transpose(mat_mul_lines.args[pos])
+                args_array[i] = _a2m_transpose(mat_mul_lines.args[pos1])
             else:
-                args_array[i] = mat_mul_lines.args[pos]
+                args_array[i] = mat_mul_lines.args[pos1]
         return _a2m_tensor_product(*args_array)
     else:
         return expr
@@ -491,15 +491,15 @@ def _(expr: ArrayDiagonal):
     for old_diag_tuple, new_diag_tuple in new_diag_indices.items():
         if len(new_diag_tuple) == 1:
             removed = [i for i in removed if i not in old_diag_tuple]
-    new_diag_indices = [tuple(j - shifts[j] for j in i) for i in new_diag_indices.values()]
+    new_diag_indices2 = [tuple(j - shifts[j] for j in i) for i in new_diag_indices.values()]
     rank = get_rank(expr.expr)
     removed = ArrayDiagonal._push_indices_up(expr.diagonal_indices, removed, rank)
     removed = sorted({i for i in removed})
     # If there are single axes to diagonalize remaining, it means that their
     # corresponding dimension has been removed, they no longer need diagonalization:
-    new_diag_indices = [i for i in new_diag_indices if len(i) > 0]
-    if len(new_diag_indices) > 0:
-        newexpr2 = _array_diagonal(newexpr, *new_diag_indices, allow_trivial_diags=True)
+    new_diag_indices3 = [i for i in new_diag_indices2 if len(i) > 0]
+    if len(new_diag_indices3) > 0:
+        newexpr2 = _array_diagonal(newexpr, *new_diag_indices3, allow_trivial_diags=True)
     else:
         newexpr2 = newexpr
     if isinstance(newexpr2, ArrayDiagonal):
```
The problem is that mypy doesn't like the same variable e.g. `new_diag_indices` changing type within the function. The code has `new_diag_indices` change from a list to a dict etc. Probably better variable names can be used to describe what these different objects are but mypy just wants the names to be different.
The other errors are:
```
sympy/tensor/array/expressions/arrayexpr_derivatives.py:26: error: "Expr" has no attribute "shape"
sympy/tensor/array/expressions/arrayexpr_derivatives.py:66: error: "Expr" has no attribute "shape"
sympy/tensor/array/expressions/arrayexpr_derivatives.py:77: error: "Expr" has no attribute "shape"
sympy/tensor/array/expressions/arrayexpr_derivatives.py:82: error: "Expr" has no attribute "shape"
```
You can this e.g. here:
https://github.com/sympy/sympy/blob/88ed7abb488da615b007dd2ed5404312caef473c/sympy/tensor/array/expressions/arrayexpr_derivatives.py#L24-L26
The `type: ignore` was just added in #22699 to silence this error but it clearly is an error. An arbitrary `Expr` does not have a `.shape` attribute.
That definitely looks like a legitimate bug. 

By the way, it looks like singledispatch.register can read type annotations as of Python 3.7 https://docs.python.org/3/library/functools.html. So the above could instead be 

```py
 @array_derive.register
 def _(expr: Expr, x: Expr): 
     return ZeroArray(*x.shape)
```

This would not only be more concise, but it would mean that mypy actually check the same thing as the decorator (i.e., if the register type was wrong but the annotation was right, I'm not sure if mypy would have caught this).

Commit message: quality: remove most type: ignore from sets

Patch to review:
diff --git a/sympy/assumptions/handlers/calculus.py b/sympy/assumptions/handlers/calculus.py
index ec2d9dcfd0c7..263bed6da00c 100644
--- a/sympy/assumptions/handlers/calculus.py
+++ b/sympy/assumptions/handlers/calculus.py
@@ -18,7 +18,7 @@
 # FinitePredicate
 
 
-@FinitePredicate.register(Symbol) # type: ignore
+@FinitePredicate.register(Symbol)
 def _(expr, assumptions):
     """
     Handles Symbol.
@@ -29,7 +29,7 @@ def _(expr, assumptions):
         return True
     return None
 
-@FinitePredicate.register(Add) # type: ignore
+@FinitePredicate.register(Add)
 def _(expr, assumptions):
     """
     Return True if expr is bounded, False if not and None if unknown.
@@ -111,7 +111,7 @@ def _(expr, assumptions):
             result = _bounded
     return result
 
-@FinitePredicate.register(Mul) # type: ignore
+@FinitePredicate.register(Mul)
 def _(expr, assumptions):
     """
     Return True if expr is bounded, False if not and None if unknown.
@@ -166,7 +166,7 @@ def _(expr, assumptions):
             result = False
     return result
 
-@FinitePredicate.register(Pow) # type: ignore
+@FinitePredicate.register(Pow)
 def _(expr, assumptions):
     """
     * Unbounded ** NonZero -> Unbounded
@@ -198,11 +198,11 @@ def _(expr, assumptions):
         return False
     return None
 
-@FinitePredicate.register(exp) # type: ignore
+@FinitePredicate.register(exp)
 def _(expr, assumptions):
     return ask(Q.finite(expr.exp), assumptions)
 
-@FinitePredicate.register(log) # type: ignore
+@FinitePredicate.register(log)
 def _(expr, assumptions):
     # After complex -> finite fact is registered to new assumption system,
     # querying Q.infinite may be removed.
@@ -210,16 +210,16 @@ def _(expr, assumptions):
         return False
     return ask(~Q.zero(expr.args[0]), assumptions)
 
-@FinitePredicate.register_many(cos, sin, Number, Pi, Exp1, GoldenRatio, # type: ignore
+@FinitePredicate.register_many(cos, sin, Number, Pi, Exp1, GoldenRatio,
     TribonacciConstant, ImaginaryUnit, sign)
 def _(expr, assumptions):
     return True
 
-@FinitePredicate.register_many(ComplexInfinity, Infinity, NegativeInfinity) # type: ignore
+@FinitePredicate.register_many(ComplexInfinity, Infinity, NegativeInfinity)
 def _(expr, assumptions):
     return False
 
-@FinitePredicate.register(NaN) # type: ignore
+@FinitePredicate.register(NaN)
 def _(expr, assumptions):
     return None
 
@@ -227,7 +227,7 @@ def _(expr, assumptions):
 # InfinitePredicate
 
 
-@InfinitePredicate.register_many(ComplexInfinity, Infinity, NegativeInfinity) # type: ignore
+@InfinitePredicate.register_many(ComplexInfinity, Infinity, NegativeInfinity)
 def _(expr, assumptions):
     return True
 
@@ -235,12 +235,12 @@ def _(expr, assumptions):
 # PositiveInfinitePredicate
 
 
-@PositiveInfinitePredicate.register(Infinity) # type: ignore
+@PositiveInfinitePredicate.register(Infinity)
 def _(expr, assumptions):
     return True
 
 
-@PositiveInfinitePredicate.register_many(NegativeInfinity, ComplexInfinity) # type: ignore
+@PositiveInfinitePredicate.register_many(NegativeInfinity, ComplexInfinity)
 def _(expr, assumptions):
     return False
 
@@ -248,11 +248,11 @@ def _(expr, assumptions):
 # NegativeInfinitePredicate
 
 
-@NegativeInfinitePredicate.register(NegativeInfinity) # type: ignore
+@NegativeInfinitePredicate.register(NegativeInfinity)
 def _(expr, assumptions):
     return True
 
 
-@NegativeInfinitePredicate.register_many(Infinity, ComplexInfinity) # type: ignore
+@NegativeInfinitePredicate.register_many(Infinity, ComplexInfinity)
 def _(expr, assumptions):
     return False
diff --git a/sympy/assumptions/handlers/common.py b/sympy/assumptions/handlers/common.py
index 303e6e3a2596..8d2ef9859335 100644
--- a/sympy/assumptions/handlers/common.py
+++ b/sympy/assumptions/handlers/common.py
@@ -47,7 +47,7 @@ def AlwaysNone(expr, assumptions):
 
 # CommutativePredicate
 
-@CommutativePredicate.register(Symbol) # type: ignore
+@CommutativePredicate.register(Symbol)
 def _(expr, assumptions):
     """Objects are expected to be commutative unless otherwise stated"""
     assumps = conjuncts(assumptions)
@@ -59,41 +59,41 @@ def _(expr, assumptions):
         return False
     return True
 
-@CommutativePredicate.register(Basic) # type: ignore
+@CommutativePredicate.register(Basic)
 def _(expr, assumptions):
     for arg in expr.args:
         if not ask(Q.commutative(arg), assumptions):
             return False
     return True
 
-@CommutativePredicate.register(Number) # type: ignore
+@CommutativePredicate.register(Number)
 def _(expr, assumptions):
     return True
 
-@CommutativePredicate.register(NaN) # type: ignore
+@CommutativePredicate.register(NaN)
 def _(expr, assumptions):
     return True
 
 
 # IsTruePredicate
 
-@IsTruePredicate.register(bool) # type: ignore
+@IsTruePredicate.register(bool)
 def _(expr, assumptions):
     return expr
 
-@IsTruePredicate.register(BooleanTrue) # type: ignore
+@IsTruePredicate.register(BooleanTrue)
 def _(expr, assumptions):
     return True
 
-@IsTruePredicate.register(BooleanFalse) # type: ignore
+@IsTruePredicate.register(BooleanFalse)
 def _(expr, assumptions):
     return False
 
-@IsTruePredicate.register(AppliedPredicate) # type: ignore
+@IsTruePredicate.register(AppliedPredicate)
 def _(expr, assumptions):
     return ask(expr, assumptions)
 
-@IsTruePredicate.register(Not) # type: ignore
+@IsTruePredicate.register(Not)
 def _(expr, assumptions):
     arg = expr.args[0]
     if arg.is_Symbol:
@@ -105,7 +105,7 @@ def _(expr, assumptions):
     else:
         return None
 
-@IsTruePredicate.register(Or) # type: ignore
+@IsTruePredicate.register(Or)
 def _(expr, assumptions):
     result = False
     for arg in expr.args:
@@ -116,7 +116,7 @@ def _(expr, assumptions):
             result = None
     return result
 
-@IsTruePredicate.register(And) # type: ignore
+@IsTruePredicate.register(And)
 def _(expr, assumptions):
     result = True
     for arg in expr.args:
@@ -127,12 +127,12 @@ def _(expr, assumptions):
             result = None
     return result
 
-@IsTruePredicate.register(Implies) # type: ignore
+@IsTruePredicate.register(Implies)
 def _(expr, assumptions):
     p, q = expr.args
     return ask(~p | q, assumptions=assumptions)
 
-@IsTruePredicate.register(Equivalent) # type: ignore
+@IsTruePredicate.register(Equivalent)
 def _(expr, assumptions):
     p, q = expr.args
     pt = ask(p, assumptions=assumptions)
diff --git a/sympy/assumptions/handlers/matrices.py b/sympy/assumptions/handlers/matrices.py
index a220b4363dd3..73debd00c8bc 100644
--- a/sympy/assumptions/handlers/matrices.py
+++ b/sympy/assumptions/handlers/matrices.py
@@ -31,14 +31,14 @@ def _Factorization(predicate, expr, assumptions):
 
 # SquarePredicate
 
-@SquarePredicate.register(MatrixExpr) # type: ignore
+@SquarePredicate.register(MatrixExpr)
 def _(expr, assumptions):
     return expr.shape[0] == expr.shape[1]
 
 
 # SymmetricPredicate
 
-@SymmetricPredicate.register(MatMul) # type: ignore
+@SymmetricPredicate.register(MatMul)
 def _(expr, assumptions):
     factor, mmul = expr.as_coeff_mmul()
     if all(ask(Q.symmetric(arg), assumptions) for arg in mmul.args):
@@ -52,7 +52,7 @@ def _(expr, assumptions):
             return True
         return ask(Q.symmetric(MatMul(*mmul.args[1:-1])), assumptions)
 
-@SymmetricPredicate.register(MatPow) # type: ignore
+@SymmetricPredicate.register(MatPow)
 def _(expr, assumptions):
     # only for integer powers
     base, exp = expr.args
@@ -65,11 +65,11 @@ def _(expr, assumptions):
         return ask(Q.symmetric(base), assumptions)
     return None
 
-@SymmetricPredicate.register(MatAdd) # type: ignore
+@SymmetricPredicate.register(MatAdd)
 def _(expr, assumptions):
     return all(ask(Q.symmetric(arg), assumptions) for arg in expr.args)
 
-@SymmetricPredicate.register(MatrixSymbol) # type: ignore
+@SymmetricPredicate.register(MatrixSymbol)
 def _(expr, assumptions):
     if not expr.is_square:
         return False
@@ -80,15 +80,15 @@ def _(expr, assumptions):
     if Q.symmetric(expr) in conjuncts(assumptions):
         return True
 
-@SymmetricPredicate.register_many(OneMatrix, ZeroMatrix) # type: ignore
+@SymmetricPredicate.register_many(OneMatrix, ZeroMatrix)
 def _(expr, assumptions):
     return ask(Q.square(expr), assumptions)
 
-@SymmetricPredicate.register_many(Inverse, Transpose) # type: ignore
+@SymmetricPredicate.register_many(Inverse, Transpose)
 def _(expr, assumptions):
     return ask(Q.symmetric(expr.arg), assumptions)
 
-@SymmetricPredicate.register(MatrixSlice) # type: ignore
+@SymmetricPredicate.register(MatrixSlice)
 def _(expr, assumptions):
     # TODO: implement sathandlers system for the matrices.
     # Now it duplicates the general fact: Implies(Q.diagonal, Q.symmetric).
@@ -99,14 +99,14 @@ def _(expr, assumptions):
     else:
         return ask(Q.symmetric(expr.parent), assumptions)
 
-@SymmetricPredicate.register(Identity) # type: ignore
+@SymmetricPredicate.register(Identity)
 def _(expr, assumptions):
     return True
 
 
 # InvertiblePredicate
 
-@InvertiblePredicate.register(MatMul) # type: ignore
+@InvertiblePredicate.register(MatMul)
 def _(expr, assumptions):
     factor, mmul = expr.as_coeff_mmul()
     if all(ask(Q.invertible(arg), assumptions) for arg in mmul.args):
@@ -115,7 +115,7 @@ def _(expr, assumptions):
             for arg in mmul.args):
         return False
 
-@InvertiblePredicate.register(MatPow) # type: ignore
+@InvertiblePredicate.register(MatPow)
 def _(expr, assumptions):
     # only for integer powers
     base, exp = expr.args
@@ -126,53 +126,53 @@ def _(expr, assumptions):
         return ask(Q.invertible(base), assumptions)
     return None
 
-@InvertiblePredicate.register(MatAdd) # type: ignore
+@InvertiblePredicate.register(MatAdd)
 def _(expr, assumptions):
     return None
 
-@InvertiblePredicate.register(MatrixSymbol) # type: ignore
+@InvertiblePredicate.register(MatrixSymbol)
 def _(expr, assumptions):
     if not expr.is_square:
         return False
     if Q.invertible(expr) in conjuncts(assumptions):
         return True
 
-@InvertiblePredicate.register_many(Identity, Inverse) # type: ignore
+@InvertiblePredicate.register_many(Identity, Inverse)
 def _(expr, assumptions):
     return True
 
-@InvertiblePredicate.register(ZeroMatrix) # type: ignore
+@InvertiblePredicate.register(ZeroMatrix)
 def _(expr, assumptions):
     return False
 
-@InvertiblePredicate.register(OneMatrix) # type: ignore
+@InvertiblePredicate.register(OneMatrix)
 def _(expr, assumptions):
     return expr.shape[0] == 1 and expr.shape[1] == 1
 
-@InvertiblePredicate.register(Transpose) # type: ignore
+@InvertiblePredicate.register(Transpose)
 def _(expr, assumptions):
     return ask(Q.invertible(expr.arg), assumptions)
 
-@InvertiblePredicate.register(MatrixSlice) # type: ignore
+@InvertiblePredicate.register(MatrixSlice)
 def _(expr, assumptions):
     if not expr.on_diag:
         return None
     else:
         return ask(Q.invertible(expr.parent), assumptions)
 
-@InvertiblePredicate.register(MatrixBase) # type: ignore
+@InvertiblePredicate.register(MatrixBase)
 def _(expr, assumptions):
     if not expr.is_square:
         return False
     return expr.rank() == expr.rows
 
-@InvertiblePredicate.register(MatrixExpr) # type: ignore
+@InvertiblePredicate.register(MatrixExpr)
 def _(expr, assumptions):
     if not expr.is_square:
         return False
     return None
 
-@InvertiblePredicate.register(BlockMatrix) # type: ignore
+@InvertiblePredicate.register(BlockMatrix)
 def _(expr, assumptions):
     from sympy.matrices.expressions.blockmatrix import reblock_2x2
     if not expr.is_square:
@@ -200,7 +200,7 @@ def _(expr, assumptions):
                 return invertible
     return None
 
-@InvertiblePredicate.register(BlockDiagMatrix) # type: ignore
+@InvertiblePredicate.register(BlockDiagMatrix)
 def _(expr, assumptions):
     if expr.rowblocksizes != expr.colblocksizes:
         return None
@@ -209,7 +209,7 @@ def _(expr, assumptions):
 
 # OrthogonalPredicate
 
-@OrthogonalPredicate.register(MatMul) # type: ignore
+@OrthogonalPredicate.register(MatMul)
 def _(expr, assumptions):
     factor, mmul = expr.as_coeff_mmul()
     if (all(ask(Q.orthogonal(arg), assumptions) for arg in mmul.args) and
@@ -219,7 +219,7 @@ def _(expr, assumptions):
             for arg in mmul.args):
         return False
 
-@OrthogonalPredicate.register(MatPow) # type: ignore
+@OrthogonalPredicate.register(MatPow)
 def _(expr, assumptions):
     # only for integer powers
     base, exp = expr.args
@@ -228,13 +228,13 @@ def _(expr, assumptions):
         return ask(Q.orthogonal(base), assumptions)
     return None
 
-@OrthogonalPredicate.register(MatAdd) # type: ignore
+@OrthogonalPredicate.register(MatAdd)
 def _(expr, assumptions):
     if (len(expr.args) == 1 and
             ask(Q.orthogonal(expr.args[0]), assumptions)):
         return True
 
-@OrthogonalPredicate.register(MatrixSymbol) # type: ignore
+@OrthogonalPredicate.register(MatrixSymbol)
 def _(expr, assumptions):
     if (not expr.is_square or
                     ask(Q.invertible(expr), assumptions) is False):
@@ -242,33 +242,33 @@ def _(expr, assumptions):
     if Q.orthogonal(expr) in conjuncts(assumptions):
         return True
 
-@OrthogonalPredicate.register(Identity) # type: ignore
+@OrthogonalPredicate.register(Identity)
 def _(expr, assumptions):
     return True
 
-@OrthogonalPredicate.register(ZeroMatrix) # type: ignore
+@OrthogonalPredicate.register(ZeroMatrix)
 def _(expr, assumptions):
     return False
 
-@OrthogonalPredicate.register_many(Inverse, Transpose) # type: ignore
+@OrthogonalPredicate.register_many(Inverse, Transpose)
 def _(expr, assumptions):
     return ask(Q.orthogonal(expr.arg), assumptions)
 
-@OrthogonalPredicate.register(MatrixSlice) # type: ignore
+@OrthogonalPredicate.register(MatrixSlice)
 def _(expr, assumptions):
     if not expr.on_diag:
         return None
     else:
         return ask(Q.orthogonal(expr.parent), assumptions)
 
-@OrthogonalPredicate.register(Factorization) # type: ignore
+@OrthogonalPredicate.register(Factorization)
 def _(expr, assumptions):
     return _Factorization(Q.orthogonal, expr, assumptions)
 
 
 # UnitaryPredicate
 
-@UnitaryPredicate.register(MatMul) # type: ignore
+@UnitaryPredicate.register(MatMul)
 def _(expr, assumptions):
     factor, mmul = expr.as_coeff_mmul()
     if (all(ask(Q.unitary(arg), assumptions) for arg in mmul.args) and
@@ -278,7 +278,7 @@ def _(expr, assumptions):
             for arg in mmul.args):
         return False
 
-@UnitaryPredicate.register(MatPow) # type: ignore
+@UnitaryPredicate.register(MatPow)
 def _(expr, assumptions):
     # only for integer powers
     base, exp = expr.args
@@ -287,7 +287,7 @@ def _(expr, assumptions):
         return ask(Q.unitary(base), assumptions)
     return None
 
-@UnitaryPredicate.register(MatrixSymbol) # type: ignore
+@UnitaryPredicate.register(MatrixSymbol)
 def _(expr, assumptions):
     if (not expr.is_square or
                     ask(Q.invertible(expr), assumptions) is False):
@@ -295,38 +295,38 @@ def _(expr, assumptions):
     if Q.unitary(expr) in conjuncts(assumptions):
         return True
 
-@UnitaryPredicate.register_many(Inverse, Transpose) # type: ignore
+@UnitaryPredicate.register_many(Inverse, Transpose)
 def _(expr, assumptions):
     return ask(Q.unitary(expr.arg), assumptions)
 
-@UnitaryPredicate.register(MatrixSlice) # type: ignore
+@UnitaryPredicate.register(MatrixSlice)
 def _(expr, assumptions):
     if not expr.on_diag:
         return None
     else:
         return ask(Q.unitary(expr.parent), assumptions)
 
-@UnitaryPredicate.register_many(DFT, Identity) # type: ignore
+@UnitaryPredicate.register_many(DFT, Identity)
 def _(expr, assumptions):
     return True
 
-@UnitaryPredicate.register(ZeroMatrix) # type: ignore
+@UnitaryPredicate.register(ZeroMatrix)
 def _(expr, assumptions):
     return False
 
-@UnitaryPredicate.register(Factorization) # type: ignore
+@UnitaryPredicate.register(Factorization)
 def _(expr, assumptions):
     return _Factorization(Q.unitary, expr, assumptions)
 
 
 # FullRankPredicate
 
-@FullRankPredicate.register(MatMul) # type: ignore
+@FullRankPredicate.register(MatMul)
 def _(expr, assumptions):
     if all(ask(Q.fullrank(arg), assumptions) for arg in expr.args):
         return True
 
-@FullRankPredicate.register(MatPow) # type: ignore
+@FullRankPredicate.register(MatPow)
 def _(expr, assumptions):
     # only for integer powers
     base, exp = expr.args
@@ -335,23 +335,23 @@ def _(expr, assumptions):
         return ask(Q.fullrank(base), assumptions)
     return None
 
-@FullRankPredicate.register(Identity) # type: ignore
+@FullRankPredicate.register(Identity)
 def _(expr, assumptions):
     return True
 
-@FullRankPredicate.register(ZeroMatrix) # type: ignore
+@FullRankPredicate.register(ZeroMatrix)
 def _(expr, assumptions):
     return False
 
-@FullRankPredicate.register(OneMatrix) # type: ignore
+@FullRankPredicate.register(OneMatrix)
 def _(expr, assumptions):
     return expr.shape[0] == 1 and expr.shape[1] == 1
 
-@FullRankPredicate.register_many(Inverse, Transpose) # type: ignore
+@FullRankPredicate.register_many(Inverse, Transpose)
 def _(expr, assumptions):
     return ask(Q.fullrank(expr.arg), assumptions)
 
-@FullRankPredicate.register(MatrixSlice) # type: ignore
+@FullRankPredicate.register(MatrixSlice)
 def _(expr, assumptions):
     if ask(Q.orthogonal(expr.parent), assumptions):
         return True
@@ -359,7 +359,7 @@ def _(expr, assumptions):
 
 # PositiveDefinitePredicate
 
-@PositiveDefinitePredicate.register(MatMul) # type: ignore
+@PositiveDefinitePredicate.register(MatMul)
 def _(expr, assumptions):
     factor, mmul = expr.as_coeff_mmul()
     if (all(ask(Q.positive_definite(arg), assumptions)
@@ -371,42 +371,42 @@ def _(expr, assumptions):
         return ask(Q.positive_definite(
             MatMul(*mmul.args[1:-1])), assumptions)
 
-@PositiveDefinitePredicate.register(MatPow) # type: ignore
+@PositiveDefinitePredicate.register(MatPow)
 def _(expr, assumptions):
     # a power of a positive definite matrix is positive definite
     if ask(Q.positive_definite(expr.args[0]), assumptions):
         return True
 
-@PositiveDefinitePredicate.register(MatAdd) # type: ignore
+@PositiveDefinitePredicate.register(MatAdd)
 def _(expr, assumptions):
     if all(ask(Q.positive_definite(arg), assumptions)
             for arg in expr.args):
         return True
 
-@PositiveDefinitePredicate.register(MatrixSymbol) # type: ignore
+@PositiveDefinitePredicate.register(MatrixSymbol)
 def _(expr, assumptions):
     if not expr.is_square:
         return False
     if Q.positive_definite(expr) in conjuncts(assumptions):
         return True
 
-@PositiveDefinitePredicate.register(Identity) # type: ignore
+@PositiveDefinitePredicate.register(Identity)
 def _(expr, assumptions):
     return True
 
-@PositiveDefinitePredicate.register(ZeroMatrix) # type: ignore
+@PositiveDefinitePredicate.register(ZeroMatrix)
 def _(expr, assumptions):
     return False
 
-@PositiveDefinitePredicate.register(OneMatrix) # type: ignore
+@PositiveDefinitePredicate.register(OneMatrix)
 def _(expr, assumptions):
     return expr.shape[0] == 1 and expr.shape[1] == 1
 
-@PositiveDefinitePredicate.register_many(Inverse, Transpose) # type: ignore
+@PositiveDefinitePredicate.register_many(Inverse, Transpose)
 def _(expr, assumptions):
     return ask(Q.positive_definite(expr.arg), assumptions)
 
-@PositiveDefinitePredicate.register(MatrixSlice) # type: ignore
+@PositiveDefinitePredicate.register(MatrixSlice)
 def _(expr, assumptions):
     if not expr.on_diag:
         return None
@@ -416,18 +416,18 @@ def _(expr, assumptions):
 
 # UpperTriangularPredicate
 
-@UpperTriangularPredicate.register(MatMul) # type: ignore
+@UpperTriangularPredicate.register(MatMul)
 def _(expr, assumptions):
     factor, matrices = expr.as_coeff_matrices()
     if all(ask(Q.upper_triangular(m), assumptions) for m in matrices):
         return True
 
-@UpperTriangularPredicate.register(MatAdd) # type: ignore
+@UpperTriangularPredicate.register(MatAdd)
 def _(expr, assumptions):
     if all(ask(Q.upper_triangular(arg), assumptions) for arg in expr.args):
         return True
 
-@UpperTriangularPredicate.register(MatPow) # type: ignore
+@UpperTriangularPredicate.register(MatPow)
 def _(expr, assumptions):
     # only for integer powers
     base, exp = expr.args
@@ -440,52 +440,52 @@ def _(expr, assumptions):
         return ask(Q.upper_triangular(base), assumptions)
     return None
 
-@UpperTriangularPredicate.register(MatrixSymbol) # type: ignore
+@UpperTriangularPredicate.register(MatrixSymbol)
 def _(expr, assumptions):
     if Q.upper_triangular(expr) in conjuncts(assumptions):
         return True
 
-@UpperTriangularPredicate.register_many(Identity, ZeroMatrix) # type: ignore
+@UpperTriangularPredicate.register_many(Identity, ZeroMatrix)
 def _(expr, assumptions):
     return True
 
-@UpperTriangularPredicate.register(OneMatrix) # type: ignore
+@UpperTriangularPredicate.register(OneMatrix)
 def _(expr, assumptions):
     return expr.shape[0] == 1 and expr.shape[1] == 1
 
-@UpperTriangularPredicate.register(Transpose) # type: ignore
+@UpperTriangularPredicate.register(Transpose)
 def _(expr, assumptions):
     return ask(Q.lower_triangular(expr.arg), assumptions)
 
-@UpperTriangularPredicate.register(Inverse) # type: ignore
+@UpperTriangularPredicate.register(Inverse)
 def _(expr, assumptions):
     return ask(Q.upper_triangular(expr.arg), assumptions)
 
-@UpperTriangularPredicate.register(MatrixSlice) # type: ignore
+@UpperTriangularPredicate.register(MatrixSlice)
 def _(expr, assumptions):
     if not expr.on_diag:
         return None
     else:
         return ask(Q.upper_triangular(expr.parent), assumptions)
 
-@UpperTriangularPredicate.register(Factorization) # type: ignore
+@UpperTriangularPredicate.register(Factorization)
 def _(expr, assumptions):
     return _Factorization(Q.upper_triangular, expr, assumptions)
 
 # LowerTriangularPredicate
 
-@LowerTriangularPredicate.register(MatMul) # type: ignore
+@LowerTriangularPredicate.register(MatMul)
 def _(expr, assumptions):
     factor, matrices = expr.as_coeff_matrices()
     if all(ask(Q.lower_triangular(m), assumptions) for m in matrices):
         return True
 
-@LowerTriangularPredicate.register(MatAdd) # type: ignore
+@LowerTriangularPredicate.register(MatAdd)
 def _(expr, assumptions):
     if all(ask(Q.lower_triangular(arg), assumptions) for arg in expr.args):
         return True
 
-@LowerTriangularPredicate.register(MatPow) # type: ignore
+@LowerTriangularPredicate.register(MatPow)
 def _(expr, assumptions):
     # only for integer powers
     base, exp = expr.args
@@ -498,35 +498,35 @@ def _(expr, assumptions):
         return ask(Q.lower_triangular(base), assumptions)
     return None
 
-@LowerTriangularPredicate.register(MatrixSymbol) # type: ignore
+@LowerTriangularPredicate.register(MatrixSymbol)
 def _(expr, assumptions):
     if Q.lower_triangular(expr) in conjuncts(assumptions):
         return True
 
-@LowerTriangularPredicate.register_many(Identity, ZeroMatrix) # type: ignore
+@LowerTriangularPredicate.register_many(Identity, ZeroMatrix)
 def _(expr, assumptions):
     return True
 
-@LowerTriangularPredicate.register(OneMatrix) # type: ignore
+@LowerTriangularPredicate.register(OneMatrix)
 def _(expr, assumptions):
     return expr.shape[0] == 1 and expr.shape[1] == 1
 
-@LowerTriangularPredicate.register(Transpose) # type: ignore
+@LowerTriangularPredicate.register(Transpose)
 def _(expr, assumptions):
     return ask(Q.upper_triangular(expr.arg), assumptions)
 
-@LowerTriangularPredicate.register(Inverse) # type: ignore
+@LowerTriangularPredicate.register(Inverse)
 def _(expr, assumptions):
     return ask(Q.lower_triangular(expr.arg), assumptions)
 
-@LowerTriangularPredicate.register(MatrixSlice) # type: ignore
+@LowerTriangularPredicate.register(MatrixSlice)
 def _(expr, assumptions):
     if not expr.on_diag:
         return None
     else:
         return ask(Q.lower_triangular(expr.parent), assumptions)
 
-@LowerTriangularPredicate.register(Factorization) # type: ignore
+@LowerTriangularPredicate.register(Factorization)
 def _(expr, assumptions):
     return _Factorization(Q.lower_triangular, expr, assumptions)
 
@@ -536,7 +536,7 @@ def _(expr, assumptions):
 def _is_empty_or_1x1(expr):
     return expr.shape in ((0, 0), (1, 1))
 
-@DiagonalPredicate.register(MatMul) # type: ignore
+@DiagonalPredicate.register(MatMul)
 def _(expr, assumptions):
     if _is_empty_or_1x1(expr):
         return True
@@ -544,7 +544,7 @@ def _(expr, assumptions):
     if all(ask(Q.diagonal(m), assumptions) for m in matrices):
         return True
 
-@DiagonalPredicate.register(MatPow) # type: ignore
+@DiagonalPredicate.register(MatPow)
 def _(expr, assumptions):
     # only for integer powers
     base, exp = expr.args
@@ -557,27 +557,27 @@ def _(expr, assumptions):
         return ask(Q.diagonal(base), assumptions)
     return None
 
-@DiagonalPredicate.register(MatAdd) # type: ignore
+@DiagonalPredicate.register(MatAdd)
 def _(expr, assumptions):
     if all(ask(Q.diagonal(arg), assumptions) for arg in expr.args):
         return True
 
-@DiagonalPredicate.register(MatrixSymbol) # type: ignore
+@DiagonalPredicate.register(MatrixSymbol)
 def _(expr, assumptions):
     if _is_empty_or_1x1(expr):
         return True
     if Q.diagonal(expr) in conjuncts(assumptions):
         return True
 
-@DiagonalPredicate.register(OneMatrix) # type: ignore
+@DiagonalPredicate.register(OneMatrix)
 def _(expr, assumptions):
     return expr.shape[0] == 1 and expr.shape[1] == 1
 
-@DiagonalPredicate.register_many(Inverse, Transpose) # type: ignore
+@DiagonalPredicate.register_many(Inverse, Transpose)
 def _(expr, assumptions):
     return ask(Q.diagonal(expr.arg), assumptions)
 
-@DiagonalPredicate.register(MatrixSlice) # type: ignore
+@DiagonalPredicate.register(MatrixSlice)
 def _(expr, assumptions):
     if _is_empty_or_1x1(expr):
         return True
@@ -586,11 +586,11 @@ def _(expr, assumptions):
     else:
         return ask(Q.diagonal(expr.parent), assumptions)
 
-@DiagonalPredicate.register_many(DiagonalMatrix, DiagMatrix, Identity, ZeroMatrix) # type: ignore
+@DiagonalPredicate.register_many(DiagonalMatrix, DiagMatrix, Identity, ZeroMatrix)
 def _(expr, assumptions):
     return True
 
-@DiagonalPredicate.register(Factorization) # type: ignore
+@DiagonalPredicate.register(Factorization)
 def _(expr, assumptions):
     return _Factorization(Q.diagonal, expr, assumptions)
 
@@ -613,12 +613,12 @@ def MatMul_elements(matrix_predicate, scalar_predicate, expr, assumptions):
         test_closed_group(Basic(*matrices), assumptions, matrix_predicate)])
 
 
-@IntegerElementsPredicate.register_many(Determinant, HadamardProduct, MatAdd, # type: ignore
+@IntegerElementsPredicate.register_many(Determinant, HadamardProduct, MatAdd,
     Trace, Transpose)
 def _(expr, assumptions):
     return test_closed_group(expr, assumptions, Q.integer_elements)
 
-@IntegerElementsPredicate.register(MatPow) # type: ignore
+@IntegerElementsPredicate.register(MatPow)
 def _(expr, assumptions):
     # only for integer powers
     base, exp = expr.args
@@ -629,31 +629,31 @@ def _(expr, assumptions):
         return ask(Q.integer_elements(base), assumptions)
     return None
 
-@IntegerElementsPredicate.register_many(Identity, OneMatrix, ZeroMatrix) # type: ignore
+@IntegerElementsPredicate.register_many(Identity, OneMatrix, ZeroMatrix)
 def _(expr, assumptions):
     return True
 
-@IntegerElementsPredicate.register(MatMul) # type: ignore
+@IntegerElementsPredicate.register(MatMul)
 def _(expr, assumptions):
     return MatMul_elements(Q.integer_elements, Q.integer, expr, assumptions)
 
-@IntegerElementsPredicate.register(MatrixSlice) # type: ignore
+@IntegerElementsPredicate.register(MatrixSlice)
 def _(expr, assumptions):
     return MS_elements(Q.integer_elements, expr, assumptions)
 
-@IntegerElementsPredicate.register(BlockMatrix) # type: ignore
+@IntegerElementsPredicate.register(BlockMatrix)
 def _(expr, assumptions):
     return BM_elements(Q.integer_elements, expr, assumptions)
 
 
 # RealElementsPredicate
 
-@RealElementsPredicate.register_many(Determinant, Factorization, HadamardProduct, # type: ignore
+@RealElementsPredicate.register_many(Determinant, Factorization, HadamardProduct,
     MatAdd, Trace, Transpose)
 def _(expr, assumptions):
     return test_closed_group(expr, assumptions, Q.real_elements)
 
-@RealElementsPredicate.register(MatPow) # type: ignore
+@RealElementsPredicate.register(MatPow)
 def _(expr, assumptions):
     # only for integer powers
     base, exp = expr.args
@@ -666,27 +666,27 @@ def _(expr, assumptions):
         return ask(Q.real_elements(base), assumptions)
     return None
 
-@RealElementsPredicate.register(MatMul) # type: ignore
+@RealElementsPredicate.register(MatMul)
 def _(expr, assumptions):
     return MatMul_elements(Q.real_elements, Q.real, expr, assumptions)
 
-@RealElementsPredicate.register(MatrixSlice) # type: ignore
+@RealElementsPredicate.register(MatrixSlice)
 def _(expr, assumptions):
     return MS_elements(Q.real_elements, expr, assumptions)
 
-@RealElementsPredicate.register(BlockMatrix) # type: ignore
+@RealElementsPredicate.register(BlockMatrix)
 def _(expr, assumptions):
     return BM_elements(Q.real_elements, expr, assumptions)
 
 
 # ComplexElementsPredicate
 
-@ComplexElementsPredicate.register_many(Determinant, Factorization, HadamardProduct, # type: ignore
+@ComplexElementsPredicate.register_many(Determinant, Factorization, HadamardProduct,
     Inverse, MatAdd, Trace, Transpose)
 def _(expr, assumptions):
     return test_closed_group(expr, assumptions, Q.complex_elements)
 
-@ComplexElementsPredicate.register(MatPow) # type: ignore
+@ComplexElementsPredicate.register(MatPow)
 def _(expr, assumptions):
     # only for integer powers
     base, exp = expr.args
@@ -699,18 +699,18 @@ def _(expr, assumptions):
         return ask(Q.complex_elements(base), assumptions)
     return None
 
-@ComplexElementsPredicate.register(MatMul) # type: ignore
+@ComplexElementsPredicate.register(MatMul)
 def _(expr, assumptions):
     return MatMul_elements(Q.complex_elements, Q.complex, expr, assumptions)
 
-@ComplexElementsPredicate.register(MatrixSlice) # type: ignore
+@ComplexElementsPredicate.register(MatrixSlice)
 def _(expr, assumptions):
     return MS_elements(Q.complex_elements, expr, assumptions)
 
-@ComplexElementsPredicate.register(BlockMatrix) # type: ignore
+@ComplexElementsPredicate.register(BlockMatrix)
 def _(expr, assumptions):
     return BM_elements(Q.complex_elements, expr, assumptions)
 
-@ComplexElementsPredicate.register(DFT) # type: ignore
+@ComplexElementsPredicate.register(DFT)
 def _(expr, assumptions):
     return True
diff --git a/sympy/assumptions/handlers/ntheory.py b/sympy/assumptions/handlers/ntheory.py
index 48b5b09b45f8..4f1397b283ee 100644
--- a/sympy/assumptions/handlers/ntheory.py
+++ b/sympy/assumptions/handlers/ntheory.py
@@ -31,19 +31,19 @@ def _PrimePredicate_number(expr, assumptions):
     # when not exact, we won't give a True or False
     # since the number represents an approximate value
 
-@PrimePredicate.register(Expr) # type: ignore
+@PrimePredicate.register(Expr)
 def _(expr, assumptions):
     ret = expr.is_prime
     if ret is None:
         raise MDNotImplementedError
     return ret
 
-@PrimePredicate.register(Basic) # type: ignore
+@PrimePredicate.register(Basic)
 def _(expr, assumptions):
     if expr.is_number:
         return _PrimePredicate_number(expr, assumptions)
 
-@PrimePredicate.register(Mul) # type: ignore
+@PrimePredicate.register(Mul)
 def _(expr, assumptions):
     if expr.is_number:
         return _PrimePredicate_number(expr, assumptions)
@@ -54,7 +54,7 @@ def _(expr, assumptions):
         if arg.is_number and arg.is_composite:
             return False
 
-@PrimePredicate.register(Pow) # type: ignore
+@PrimePredicate.register(Pow)
 def _(expr, assumptions):
     """
     Integer**Integer     -> !Prime
@@ -65,37 +65,37 @@ def _(expr, assumptions):
             ask(Q.integer(expr.base), assumptions):
         return False
 
-@PrimePredicate.register(Integer) # type: ignore
+@PrimePredicate.register(Integer)
 def _(expr, assumptions):
     return isprime(expr)
 
-@PrimePredicate.register_many(Rational, Infinity, NegativeInfinity, ImaginaryUnit) # type: ignore
+@PrimePredicate.register_many(Rational, Infinity, NegativeInfinity, ImaginaryUnit)
 def _(expr, assumptions):
     return False
 
-@PrimePredicate.register(Float) # type: ignore
+@PrimePredicate.register(Float)
 def _(expr, assumptions):
     return _PrimePredicate_number(expr, assumptions)
 
-@PrimePredicate.register(NumberSymbol) # type: ignore
+@PrimePredicate.register(NumberSymbol)
 def _(expr, assumptions):
     return _PrimePredicate_number(expr, assumptions)
 
-@PrimePredicate.register(NaN) # type: ignore
+@PrimePredicate.register(NaN)
 def _(expr, assumptions):
     return None
 
 
 # CompositePredicate
 
-@CompositePredicate.register(Expr) # type: ignore
+@CompositePredicate.register(Expr)
 def _(expr, assumptions):
     ret = expr.is_composite
     if ret is None:
         raise MDNotImplementedError
     return ret
 
-@CompositePredicate.register(Basic) # type: ignore
+@CompositePredicate.register(Basic)
 def _(expr, assumptions):
     _positive = ask(Q.positive(expr), assumptions)
     if _positive:
@@ -129,19 +129,19 @@ def _EvenPredicate_number(expr, assumptions):
         return False
     return i % 2 == 0
 
-@EvenPredicate.register(Expr) # type: ignore
+@EvenPredicate.register(Expr)
 def _(expr, assumptions):
     ret = expr.is_even
     if ret is None:
         raise MDNotImplementedError
     return ret
 
-@EvenPredicate.register(Basic) # type: ignore
+@EvenPredicate.register(Basic)
 def _(expr, assumptions):
     if expr.is_number:
         return _EvenPredicate_number(expr, assumptions)
 
-@EvenPredicate.register(Mul) # type: ignore
+@EvenPredicate.register(Mul)
 def _(expr, assumptions):
     """
     Even * Integer    -> Even
@@ -182,7 +182,7 @@ def _(expr, assumptions):
         if odd == len(expr.args):
             return False
 
-@EvenPredicate.register(Add) # type: ignore
+@EvenPredicate.register(Add)
 def _(expr, assumptions):
     """
     Even + Odd  -> Odd
@@ -203,7 +203,7 @@ def _(expr, assumptions):
     else:
         return _result
 
-@EvenPredicate.register(Pow) # type: ignore
+@EvenPredicate.register(Pow)
 def _(expr, assumptions):
     if expr.is_number:
         return _EvenPredicate_number(expr, assumptions)
@@ -215,48 +215,48 @@ def _(expr, assumptions):
         elif expr.base is S.NegativeOne:
             return False
 
-@EvenPredicate.register(Integer) # type: ignore
+@EvenPredicate.register(Integer)
 def _(expr, assumptions):
     return not bool(expr.p & 1)
 
-@EvenPredicate.register_many(Rational, Infinity, NegativeInfinity, ImaginaryUnit) # type: ignore
+@EvenPredicate.register_many(Rational, Infinity, NegativeInfinity, ImaginaryUnit)
 def _(expr, assumptions):
     return False
 
-@EvenPredicate.register(NumberSymbol) # type: ignore
+@EvenPredicate.register(NumberSymbol)
 def _(expr, assumptions):
     return _EvenPredicate_number(expr, assumptions)
 
-@EvenPredicate.register(Abs) # type: ignore
+@EvenPredicate.register(Abs)
 def _(expr, assumptions):
     if ask(Q.real(expr.args[0]), assumptions):
         return ask(Q.even(expr.args[0]), assumptions)
 
-@EvenPredicate.register(re) # type: ignore
+@EvenPredicate.register(re)
 def _(expr, assumptions):
     if ask(Q.real(expr.args[0]), assumptions):
         return ask(Q.even(expr.args[0]), assumptions)
 
-@EvenPredicate.register(im) # type: ignore
+@EvenPredicate.register(im)
 def _(expr, assumptions):
     if ask(Q.real(expr.args[0]), assumptions):
         return True
 
-@EvenPredicate.register(NaN) # type: ignore
+@EvenPredicate.register(NaN)
 def _(expr, assumptions):
     return None
 
 
 # OddPredicate
 
-@OddPredicate.register(Expr) # type: ignore
+@OddPredicate.register(Expr)
 def _(expr, assumptions):
     ret = expr.is_odd
     if ret is None:
         raise MDNotImplementedError
     return ret
 
-@OddPredicate.register(Basic) # type: ignore
+@OddPredicate.register(Basic)
 def _(expr, assumptions):
     _integer = ask(Q.integer(expr), assumptions)
     if _integer:
diff --git a/sympy/assumptions/handlers/order.py b/sympy/assumptions/handlers/order.py
index 30d025118d2e..f4a5378c20a9 100644
--- a/sympy/assumptions/handlers/order.py
+++ b/sympy/assumptions/handlers/order.py
@@ -40,19 +40,19 @@ def _NegativePredicate_number(expr, assumptions):
             if r._prec != 1:
                 return r < 0
 
-@NegativePredicate.register(Basic) # type: ignore
+@NegativePredicate.register(Basic)
 def _(expr, assumptions):
     if expr.is_number:
         return _NegativePredicate_number(expr, assumptions)
 
-@NegativePredicate.register(Expr) # type: ignore
+@NegativePredicate.register(Expr)
 def _(expr, assumptions):
     ret = expr.is_negative
     if ret is None:
         raise MDNotImplementedError
     return ret
 
-@NegativePredicate.register(Add) # type: ignore
+@NegativePredicate.register(Add)
 def _(expr, assumptions):
     """
     Positive + Positive -> Positive,
@@ -76,7 +76,7 @@ def _(expr, assumptions):
         if nonpos < len(expr.args):
             return True
 
-@NegativePredicate.register(Mul) # type: ignore
+@NegativePredicate.register(Mul)
 def _(expr, assumptions):
     if expr.is_number:
         return _NegativePredicate_number(expr, assumptions)
@@ -92,7 +92,7 @@ def _(expr, assumptions):
             return
     return result
 
-@NegativePredicate.register(Pow) # type: ignore
+@NegativePredicate.register(Pow)
 def _(expr, assumptions):
     """
     Real ** Even -> NonNegative
@@ -116,11 +116,11 @@ def _(expr, assumptions):
         if ask(Q.odd(expr.exp), assumptions):
             return ask(Q.negative(expr.base), assumptions)
 
-@NegativePredicate.register_many(Abs, ImaginaryUnit) # type: ignore
+@NegativePredicate.register_many(Abs, ImaginaryUnit)
 def _(expr, assumptions):
     return False
 
-@NegativePredicate.register(exp) # type: ignore
+@NegativePredicate.register(exp)
 def _(expr, assumptions):
     if ask(Q.real(expr.exp), assumptions):
         return False
@@ -129,7 +129,7 @@ def _(expr, assumptions):
 
 # NonNegativePredicate
 
-@NonNegativePredicate.register(Basic) # type: ignore
+@NonNegativePredicate.register(Basic)
 def _(expr, assumptions):
     if expr.is_number:
         notnegative = fuzzy_not(_NegativePredicate_number(expr, assumptions))
@@ -138,7 +138,7 @@ def _(expr, assumptions):
         else:
             return notnegative
 
-@NonNegativePredicate.register(Expr) # type: ignore
+@NonNegativePredicate.register(Expr)
 def _(expr, assumptions):
     ret = expr.is_nonnegative
     if ret is None:
@@ -148,14 +148,14 @@ def _(expr, assumptions):
 
 # NonZeroPredicate
 
-@NonZeroPredicate.register(Expr) # type: ignore
+@NonZeroPredicate.register(Expr)
 def _(expr, assumptions):
     ret = expr.is_nonzero
     if ret is None:
         raise MDNotImplementedError
     return ret
 
-@NonZeroPredicate.register(Basic) # type: ignore
+@NonZeroPredicate.register(Basic)
 def _(expr, assumptions):
     if ask(Q.real(expr)) is False:
         return False
@@ -167,13 +167,13 @@ def nonz(i):
                 return i != 0
         return fuzzy_or(nonz(i) for i in i.as_real_imag())
 
-@NonZeroPredicate.register(Add) # type: ignore
+@NonZeroPredicate.register(Add)
 def _(expr, assumptions):
     if all(ask(Q.positive(x), assumptions) for x in expr.args) \
             or all(ask(Q.negative(x), assumptions) for x in expr.args):
         return True
 
-@NonZeroPredicate.register(Mul) # type: ignore
+@NonZeroPredicate.register(Mul)
 def _(expr, assumptions):
     for arg in expr.args:
         result = ask(Q.nonzero(arg), assumptions)
@@ -182,34 +182,34 @@ def _(expr, assumptions):
         return result
     return True
 
-@NonZeroPredicate.register(Pow) # type: ignore
+@NonZeroPredicate.register(Pow)
 def _(expr, assumptions):
     return ask(Q.nonzero(expr.base), assumptions)
 
-@NonZeroPredicate.register(Abs) # type: ignore
+@NonZeroPredicate.register(Abs)
 def _(expr, assumptions):
     return ask(Q.nonzero(expr.args[0]), assumptions)
 
-@NonZeroPredicate.register(NaN) # type: ignore
+@NonZeroPredicate.register(NaN)
 def _(expr, assumptions):
     return None
 
 
 # ZeroPredicate
 
-@ZeroPredicate.register(Expr) # type: ignore
+@ZeroPredicate.register(Expr)
 def _(expr, assumptions):
     ret = expr.is_zero
     if ret is None:
         raise MDNotImplementedError
     return ret
 
-@ZeroPredicate.register(Basic) # type: ignore
+@ZeroPredicate.register(Basic)
 def _(expr, assumptions):
     return fuzzy_and([fuzzy_not(ask(Q.nonzero(expr), assumptions)),
         ask(Q.real(expr), assumptions)])
 
-@ZeroPredicate.register(Mul) # type: ignore
+@ZeroPredicate.register(Mul)
 def _(expr, assumptions):
     # TODO: This should be deducible from the nonzero handler
     return fuzzy_or(ask(Q.zero(arg), assumptions) for arg in expr.args)
@@ -217,14 +217,14 @@ def _(expr, assumptions):
 
 # NonPositivePredicate
 
-@NonPositivePredicate.register(Expr) # type: ignore
+@NonPositivePredicate.register(Expr)
 def _(expr, assumptions):
     ret = expr.is_nonpositive
     if ret is None:
         raise MDNotImplementedError
     return ret
 
-@NonPositivePredicate.register(Basic) # type: ignore
+@NonPositivePredicate.register(Basic)
 def _(expr, assumptions):
     if expr.is_number:
         notpositive = fuzzy_not(_PositivePredicate_number(expr, assumptions))
@@ -255,19 +255,19 @@ def _PositivePredicate_number(expr, assumptions):
             if r._prec != 1:
                 return r > 0
 
-@PositivePredicate.register(Expr) # type: ignore
+@PositivePredicate.register(Expr)
 def _(expr, assumptions):
     ret = expr.is_positive
     if ret is None:
         raise MDNotImplementedError
     return ret
 
-@PositivePredicate.register(Basic) # type: ignore
+@PositivePredicate.register(Basic)
 def _(expr, assumptions):
     if expr.is_number:
         return _PositivePredicate_number(expr, assumptions)
 
-@PositivePredicate.register(Mul) # type: ignore
+@PositivePredicate.register(Mul)
 def _(expr, assumptions):
     if expr.is_number:
         return _PositivePredicate_number(expr, assumptions)
@@ -281,7 +281,7 @@ def _(expr, assumptions):
             return
     return result
 
-@PositivePredicate.register(Add) # type: ignore
+@PositivePredicate.register(Add)
 def _(expr, assumptions):
     if expr.is_number:
         return _PositivePredicate_number(expr, assumptions)
@@ -301,7 +301,7 @@ def _(expr, assumptions):
         if nonneg < len(expr.args):
             return True
 
-@PositivePredicate.register(Pow) # type: ignore
+@PositivePredicate.register(Pow)
 def _(expr, assumptions):
     if expr.base == E:
         if ask(Q.real(expr.exp), assumptions):
@@ -321,14 +321,14 @@ def _(expr, assumptions):
         if ask(Q.odd(expr.exp), assumptions):
             return False
 
-@PositivePredicate.register(exp) # type: ignore
+@PositivePredicate.register(exp)
 def _(expr, assumptions):
     if ask(Q.real(expr.exp), assumptions):
         return True
     if ask(Q.imaginary(expr.exp), assumptions):
         return ask(Q.even(expr.exp/(I*pi)), assumptions)
 
-@PositivePredicate.register(log)  # type: ignore
+@PositivePredicate.register(log)
 def _(expr, assumptions):
     r = ask(Q.real(expr.args[0]), assumptions)
     if r is not True:
@@ -338,41 +338,41 @@ def _(expr, assumptions):
     if ask(Q.negative(expr.args[0] - 1), assumptions):
         return False
 
-@PositivePredicate.register(factorial) # type: ignore
+@PositivePredicate.register(factorial)
 def _(expr, assumptions):
     x = expr.args[0]
     if ask(Q.integer(x) & Q.positive(x), assumptions):
             return True
 
-@PositivePredicate.register(ImaginaryUnit) # type: ignore
+@PositivePredicate.register(ImaginaryUnit)
 def _(expr, assumptions):
     return False
 
-@PositivePredicate.register(Abs) # type: ignore
+@PositivePredicate.register(Abs)
 def _(expr, assumptions):
     return ask(Q.nonzero(expr), assumptions)
 
-@PositivePredicate.register(Trace) # type: ignore
+@PositivePredicate.register(Trace)
 def _(expr, assumptions):
     if ask(Q.positive_definite(expr.arg), assumptions):
         return True
 
-@PositivePredicate.register(Determinant) # type: ignore
+@PositivePredicate.register(Determinant)
 def _(expr, assumptions):
     if ask(Q.positive_definite(expr.arg), assumptions):
         return True
 
-@PositivePredicate.register(MatrixElement) # type: ignore
+@PositivePredicate.register(MatrixElement)
 def _(expr, assumptions):
     if (expr.i == expr.j
             and ask(Q.positive_definite(expr.parent), assumptions)):
         return True
 
-@PositivePredicate.register(atan) # type: ignore
+@PositivePredicate.register(atan)
 def _(expr, assumptions):
     return ask(Q.positive(expr.args[0]), assumptions)
 
-@PositivePredicate.register(asin) # type: ignore
+@PositivePredicate.register(asin)
 def _(expr, assumptions):
     x = expr.args[0]
     if ask(Q.positive(x) & Q.nonpositive(x - 1), assumptions):
@@ -380,38 +380,38 @@ def _(expr, assumptions):
     if ask(Q.negative(x) & Q.nonnegative(x + 1), assumptions):
         return False
 
-@PositivePredicate.register(acos) # type: ignore
+@PositivePredicate.register(acos)
 def _(expr, assumptions):
     x = expr.args[0]
     if ask(Q.nonpositive(x - 1) & Q.nonnegative(x + 1), assumptions):
         return True
 
-@PositivePredicate.register(acot) # type: ignore
+@PositivePredicate.register(acot)
 def _(expr, assumptions):
     return ask(Q.real(expr.args[0]), assumptions)
 
-@PositivePredicate.register(NaN) # type: ignore
+@PositivePredicate.register(NaN)
 def _(expr, assumptions):
     return None
 
 
 # ExtendedNegativePredicate
 
-@ExtendedNegativePredicate.register(object) # type: ignore
+@ExtendedNegativePredicate.register(object)
 def _(expr, assumptions):
     return ask(Q.negative(expr) | Q.negative_infinite(expr), assumptions)
 
 
 # ExtendedPositivePredicate
 
-@ExtendedPositivePredicate.register(object) # type: ignore
+@ExtendedPositivePredicate.register(object)
 def _(expr, assumptions):
     return ask(Q.positive(expr) | Q.positive_infinite(expr), assumptions)
 
 
 # ExtendedNonZeroPredicate
 
-@ExtendedNonZeroPredicate.register(object) # type: ignore
+@ExtendedNonZeroPredicate.register(object)
 def _(expr, assumptions):
     return ask(
         Q.negative_infinite(expr) | Q.negative(expr) | Q.positive(expr) | Q.positive_infinite(expr),
@@ -420,7 +420,7 @@ def _(expr, assumptions):
 
 # ExtendedNonPositivePredicate
 
-@ExtendedNonPositivePredicate.register(object) # type: ignore
+@ExtendedNonPositivePredicate.register(object)
 def _(expr, assumptions):
     return ask(
         Q.negative_infinite(expr) | Q.negative(expr) | Q.zero(expr),
@@ -429,7 +429,7 @@ def _(expr, assumptions):
 
 # ExtendedNonNegativePredicate
 
-@ExtendedNonNegativePredicate.register(object) # type: ignore
+@ExtendedNonNegativePredicate.register(object)
 def _(expr, assumptions):
     return ask(
         Q.zero(expr) | Q.positive(expr) | Q.positive_infinite(expr),
diff --git a/sympy/assumptions/handlers/sets.py b/sympy/assumptions/handlers/sets.py
index 377ce28eae07..b53bcfedef30 100644
--- a/sympy/assumptions/handlers/sets.py
+++ b/sympy/assumptions/handlers/sets.py
@@ -41,19 +41,19 @@ def _IntegerPredicate_number(expr, assumptions):
 def _(expr, assumptions):
     return True
 
-@IntegerPredicate.register_many(Exp1, GoldenRatio, ImaginaryUnit, Infinity, # type: ignore
+@IntegerPredicate.register_many(Exp1, GoldenRatio, ImaginaryUnit, Infinity,
         NegativeInfinity, Pi, Rational, TribonacciConstant)
 def _(expr, assumptions):
     return False
 
-@IntegerPredicate.register(Expr) # type: ignore
+@IntegerPredicate.register(Expr)
 def _(expr, assumptions):
     ret = expr.is_integer
     if ret is None:
         raise MDNotImplementedError
     return ret
 
-@IntegerPredicate.register_many(Add, Pow) # type: ignore
+@IntegerPredicate.register_many(Add, Pow)
 def _(expr, assumptions):
     """
     * Integer + Integer       -> Integer
@@ -64,7 +64,7 @@ def _(expr, assumptions):
         return _IntegerPredicate_number(expr, assumptions)
     return test_closed_group(expr, assumptions, Q.integer)
 
-@IntegerPredicate.register(Mul) # type: ignore
+@IntegerPredicate.register(Mul)
 def _(expr, assumptions):
     """
     * Integer*Integer      -> Integer
@@ -92,38 +92,38 @@ def _(expr, assumptions):
 
     return _output
 
-@IntegerPredicate.register(Abs) # type: ignore
+@IntegerPredicate.register(Abs)
 def _(expr, assumptions):
     return ask(Q.integer(expr.args[0]), assumptions)
 
-@IntegerPredicate.register_many(Determinant, MatrixElement, Trace) # type: ignore
+@IntegerPredicate.register_many(Determinant, MatrixElement, Trace)
 def _(expr, assumptions):
     return ask(Q.integer_elements(expr.args[0]), assumptions)
 
 
 # RationalPredicate
 
-@RationalPredicate.register(Rational) # type: ignore
+@RationalPredicate.register(Rational)
 def _(expr, assumptions):
     return True
 
-@RationalPredicate.register(Float) # type: ignore
+@RationalPredicate.register(Float)
 def _(expr, assumptions):
     return None
 
-@RationalPredicate.register_many(Exp1, GoldenRatio, ImaginaryUnit, Infinity, # type: ignore
+@RationalPredicate.register_many(Exp1, GoldenRatio, ImaginaryUnit, Infinity,
     NegativeInfinity, Pi, TribonacciConstant)
 def _(expr, assumptions):
     return False
 
-@RationalPredicate.register(Expr) # type: ignore
+@RationalPredicate.register(Expr)
 def _(expr, assumptions):
     ret = expr.is_rational
     if ret is None:
         raise MDNotImplementedError
     return ret
 
-@RationalPredicate.register_many(Add, Mul) # type: ignore
+@RationalPredicate.register_many(Add, Mul)
 def _(expr, assumptions):
     """
     * Rational + Rational     -> Rational
@@ -135,7 +135,7 @@ def _(expr, assumptions):
             return False
     return test_closed_group(expr, assumptions, Q.rational)
 
-@RationalPredicate.register(Pow) # type: ignore
+@RationalPredicate.register(Pow)
 def _(expr, assumptions):
     """
     * Rational ** Integer      -> Rational
@@ -154,25 +154,25 @@ def _(expr, assumptions):
         if ask(Q.prime(expr.base), assumptions):
             return False
 
-@RationalPredicate.register_many(asin, atan, cos, sin, tan) # type: ignore
+@RationalPredicate.register_many(asin, atan, cos, sin, tan)
 def _(expr, assumptions):
     x = expr.args[0]
     if ask(Q.rational(x), assumptions):
         return ask(~Q.nonzero(x), assumptions)
 
-@RationalPredicate.register(exp) # type: ignore
+@RationalPredicate.register(exp)
 def _(expr, assumptions):
     x = expr.exp
     if ask(Q.rational(x), assumptions):
         return ask(~Q.nonzero(x), assumptions)
 
-@RationalPredicate.register_many(acot, cot) # type: ignore
+@RationalPredicate.register_many(acot, cot)
 def _(expr, assumptions):
     x = expr.args[0]
     if ask(Q.rational(x), assumptions):
         return False
 
-@RationalPredicate.register_many(acos, log) # type: ignore
+@RationalPredicate.register_many(acos, log)
 def _(expr, assumptions):
     x = expr.args[0]
     if ask(Q.rational(x), assumptions):
@@ -181,14 +181,14 @@ def _(expr, assumptions):
 
 # IrrationalPredicate
 
-@IrrationalPredicate.register(Expr) # type: ignore
+@IrrationalPredicate.register(Expr)
 def _(expr, assumptions):
     ret = expr.is_irrational
     if ret is None:
         raise MDNotImplementedError
     return ret
 
-@IrrationalPredicate.register(Basic) # type: ignore
+@IrrationalPredicate.register(Basic)
 def _(expr, assumptions):
     _real = ask(Q.real(expr), assumptions)
     if _real:
@@ -211,23 +211,23 @@ def _RealPredicate_number(expr, assumptions):
     # allow None to be returned if we couldn't show for sure
     # that i was 0
 
-@RealPredicate.register_many(Abs, Exp1, Float, GoldenRatio, im, Pi, Rational, # type: ignore
+@RealPredicate.register_many(Abs, Exp1, Float, GoldenRatio, im, Pi, Rational,
     re, TribonacciConstant)
 def _(expr, assumptions):
     return True
 
-@RealPredicate.register_many(ImaginaryUnit, Infinity, NegativeInfinity) # type: ignore
+@RealPredicate.register_many(ImaginaryUnit, Infinity, NegativeInfinity)
 def _(expr, assumptions):
     return False
 
-@RealPredicate.register(Expr) # type: ignore
+@RealPredicate.register(Expr)
 def _(expr, assumptions):
     ret = expr.is_real
     if ret is None:
         raise MDNotImplementedError
     return ret
 
-@RealPredicate.register(Add) # type: ignore
+@RealPredicate.register(Add)
 def _(expr, assumptions):
     """
     * Real + Real              -> Real
@@ -237,7 +237,7 @@ def _(expr, assumptions):
         return _RealPredicate_number(expr, assumptions)
     return test_closed_group(expr, assumptions, Q.real)
 
-@RealPredicate.register(Mul) # type: ignore
+@RealPredicate.register(Mul)
 def _(expr, assumptions):
     """
     * Real*Real               -> Real
@@ -257,7 +257,7 @@ def _(expr, assumptions):
     else:
         return result
 
-@RealPredicate.register(Pow) # type: ignore
+@RealPredicate.register(Pow)
 def _(expr, assumptions):
     """
     * Real**Integer              -> Real
@@ -321,29 +321,29 @@ def _(expr, assumptions):
             elif ask(Q.negative(expr.base), assumptions):
                 return False
 
-@RealPredicate.register_many(cos, sin) # type: ignore
+@RealPredicate.register_many(cos, sin)
 def _(expr, assumptions):
     if ask(Q.real(expr.args[0]), assumptions):
             return True
 
-@RealPredicate.register(exp) # type: ignore
+@RealPredicate.register(exp)
 def _(expr, assumptions):
     return ask(
         Q.integer(expr.exp/I/pi) | Q.real(expr.exp), assumptions
     )
 
-@RealPredicate.register(log) # type: ignore
+@RealPredicate.register(log)
 def _(expr, assumptions):
     return ask(Q.positive(expr.args[0]), assumptions)
 
-@RealPredicate.register_many(Determinant, MatrixElement, Trace) # type: ignore
+@RealPredicate.register_many(Determinant, MatrixElement, Trace)
 def _(expr, assumptions):
     return ask(Q.real_elements(expr.args[0]), assumptions)
 
 
 # ExtendedRealPredicate
 
-@ExtendedRealPredicate.register(object) # type: ignore
+@ExtendedRealPredicate.register(object)
 def _(expr, assumptions):
     return ask(Q.negative_infinite(expr)
                | Q.negative(expr)
@@ -352,7 +352,7 @@ def _(expr, assumptions):
                | Q.positive_infinite(expr),
             assumptions)
 
-@ExtendedRealPredicate.register_many(Infinity, NegativeInfinity) # type: ignore
+@ExtendedRealPredicate.register_many(Infinity, NegativeInfinity)
 def _(expr, assumptions):
     return True
 
diff --git a/sympy/assumptions/sathandlers.py b/sympy/assumptions/sathandlers.py
index b96154b60db2..48579a87274e 100644
--- a/sympy/assumptions/sathandlers.py
+++ b/sympy/assumptions/sathandlers.py
@@ -200,7 +200,7 @@ def __call__(self, expr):
 
 ## Abs ##
 
-@class_fact_registry.multiregister(Abs) # type: ignore
+@class_fact_registry.multiregister(Abs)
 def _(expr):
     arg = expr.args[0]
     return [Q.nonnegative(expr),
@@ -213,7 +213,7 @@ def _(expr):
 
 ### Add ##
 
-@class_fact_registry.multiregister(Add) # type: ignore
+@class_fact_registry.multiregister(Add)
 def _(expr):
     return [allargs(x, Q.positive(x), expr) >> Q.positive(expr),
             allargs(x, Q.negative(x), expr) >> Q.negative(expr),
@@ -223,7 +223,7 @@ def _(expr):
             exactlyonearg(x, ~Q.integer(x), expr) >> ~Q.integer(expr),
             ]
 
-@class_fact_registry.register(Add) # type: ignore
+@class_fact_registry.register(Add)
 def _(expr):
     allargs_real = allargs(x, Q.real(x), expr)
     onearg_irrational = exactlyonearg(x, Q.irrational(x), expr)
@@ -232,7 +232,7 @@ def _(expr):
 
 ### Mul ###
 
-@class_fact_registry.multiregister(Mul) # type: ignore
+@class_fact_registry.multiregister(Mul)
 def _(expr):
     return [Equivalent(Q.zero(expr), anyarg(x, Q.zero(x), expr)),
             allargs(x, Q.positive(x), expr) >> Q.positive(expr),
@@ -243,7 +243,7 @@ def _(expr):
             allargs(x, Q.commutative(x), expr) >> Q.commutative(expr),
             ]
 
-@class_fact_registry.register(Mul) # type: ignore
+@class_fact_registry.register(Mul)
 def _(expr):
     # Implicitly assumes Mul has more than one arg
     # Would be allargs(x, Q.prime(x) | Q.composite(x)) except 1 is composite
@@ -252,20 +252,20 @@ def _(expr):
     allargs_prime = allargs(x, Q.prime(x), expr)
     return Implies(allargs_prime, ~Q.prime(expr))
 
-@class_fact_registry.register(Mul) # type: ignore
+@class_fact_registry.register(Mul)
 def _(expr):
     # General Case: Odd number of imaginary args implies mul is imaginary(To be implemented)
     allargs_imag_or_real = allargs(x, Q.imaginary(x) | Q.real(x), expr)
     onearg_imaginary = exactlyonearg(x, Q.imaginary(x), expr)
     return Implies(allargs_imag_or_real, Implies(onearg_imaginary, Q.imaginary(expr)))
 
-@class_fact_registry.register(Mul) # type: ignore
+@class_fact_registry.register(Mul)
 def _(expr):
     allargs_real = allargs(x, Q.real(x), expr)
     onearg_irrational = exactlyonearg(x, Q.irrational(x), expr)
     return Implies(allargs_real, Implies(onearg_irrational, Q.irrational(expr)))
 
-@class_fact_registry.register(Mul) # type: ignore
+@class_fact_registry.register(Mul)
 def _(expr):
     # Including the integer qualification means we don't need to add any facts
     # for odd, since the assumptions already know that every integer is
@@ -277,7 +277,7 @@ def _(expr):
 
 ### MatMul ###
 
-@class_fact_registry.register(MatMul) # type: ignore
+@class_fact_registry.register(MatMul)
 def _(expr):
     allargs_square = allargs(x, Q.square(x), expr)
     allargs_invertible = allargs(x, Q.invertible(x), expr)
@@ -286,7 +286,7 @@ def _(expr):
 
 ### Pow ###
 
-@class_fact_registry.multiregister(Pow) # type: ignore
+@class_fact_registry.multiregister(Pow)
 def _(expr):
     base, exp = expr.base, expr.exp
     return [
@@ -312,7 +312,7 @@ def _(expr):
     Q.composite: lambda o: o.is_composite,
 }
 
-@class_fact_registry.multiregister(Number, NumberSymbol, ImaginaryUnit) # type: ignore
+@class_fact_registry.multiregister(Number, NumberSymbol, ImaginaryUnit)
 def _(expr):
     ret = []
     for p, getter in _old_assump_getters.items():
diff --git a/sympy/sets/handlers/add.py b/sympy/sets/handlers/add.py
index a1b6f9bbd8ee..8c07b25ed19d 100644
--- a/sympy/sets/handlers/add.py
+++ b/sympy/sets/handlers/add.py
@@ -1,8 +1,7 @@
 from sympy.core.numbers import oo, Infinity, NegativeInfinity
 from sympy.core.singleton import S
-from sympy.core.symbol import symbols
 from sympy.core import Basic, Expr
-from sympy.multipledispatch import dispatch
+from sympy.multipledispatch import Dispatcher
 from sympy.sets import Interval, FiniteSet
 
 
@@ -10,21 +9,22 @@
 # XXX: The functions in this module are clearly not tested and are broken in a
 # number of ways.
 
-_x, _y = symbols("x y")
+_set_add = Dispatcher('_set_add')
+_set_sub = Dispatcher('_set_sub')
 
 
-@dispatch(Basic, Basic)  # type: ignore # noqa:F811
-def _set_add(x, y): # noqa:F811
+@_set_add.register(Basic, Basic)
+def _(x, y):
     return None
 
 
-@dispatch(Expr, Expr)  # type: ignore # noqa:F811
-def _set_add(x, y): # noqa:F811
+@_set_add.register(Expr, Expr)
+def _(x, y):
     return x+y
 
 
-@dispatch(Interval, Interval)  # type: ignore # noqa:F811
-def _set_add(x, y): # noqa:F811
+@_set_add.register(Interval, Interval)
+def _(x, y):
     """
     Additions in interval arithmetic
     https://en.wikipedia.org/wiki/Interval_arithmetic
@@ -33,31 +33,31 @@ def _set_add(x, y): # noqa:F811
                     x.left_open or y.left_open, x.right_open or y.right_open)
 
 
-@dispatch(Interval, Infinity)  # type: ignore # noqa:F811
-def _set_add(x, y): # noqa:F811
+@_set_add.register(Interval, Infinity)
+def _(x, y):
     if x.start is S.NegativeInfinity:
         return Interval(-oo, oo)
     return FiniteSet({S.Infinity})
 
-@dispatch(Interval, NegativeInfinity)  # type: ignore # noqa:F811
-def _set_add(x, y): # noqa:F811
+@_set_add.register(Interval, NegativeInfinity)
+def _(x, y):
     if x.end is S.Infinity:
         return Interval(-oo, oo)
     return FiniteSet({S.NegativeInfinity})
 
 
-@dispatch(Basic, Basic)  # type: ignore
-def _set_sub(x, y): # noqa:F811
+@_set_sub.register(Basic, Basic)
+def _(x, y):
     return None
 
 
-@dispatch(Expr, Expr)  # type: ignore # noqa:F811
-def _set_sub(x, y): # noqa:F811
+@_set_sub.register(Expr, Expr)
+def _(x, y):
     return x-y
 
 
-@dispatch(Interval, Interval)  # type: ignore # noqa:F811
-def _set_sub(x, y): # noqa:F811
+@_set_sub.register(Interval, Interval)
+def _(x, y):
     """
     Subtractions in interval arithmetic
     https://en.wikipedia.org/wiki/Interval_arithmetic
@@ -66,14 +66,14 @@ def _set_sub(x, y): # noqa:F811
                     x.left_open or y.right_open, x.right_open or y.left_open)
 
 
-@dispatch(Interval, Infinity)  # type: ignore # noqa:F811
-def _set_sub(x, y): # noqa:F811
+@_set_sub.register(Interval, Infinity)
+def _(x, y):
     if x.start is S.NegativeInfinity:
         return Interval(-oo, oo)
     return FiniteSet(-oo)
 
-@dispatch(Interval, NegativeInfinity)  # type: ignore # noqa:F811
-def _set_sub(x, y): # noqa:F811
+@_set_sub.register(Interval, NegativeInfinity)
+def _(x, y):
     if x.start is S.NegativeInfinity:
         return Interval(-oo, oo)
     return FiniteSet(-oo)
diff --git a/sympy/sets/handlers/functions.py b/sympy/sets/handlers/functions.py
index 88df9e2cda6d..2529dbfd4584 100644
--- a/sympy/sets/handlers/functions.py
+++ b/sympy/sets/handlers/functions.py
@@ -8,7 +8,7 @@
 from sympy.functions.elementary.exponential import exp, log
 from sympy.functions.elementary.miscellaneous import Min, Max
 from sympy.logic.boolalg import true
-from sympy.multipledispatch import dispatch
+from sympy.multipledispatch import Dispatcher
 from sympy.sets import (imageset, Interval, FiniteSet, Union, ImageSet,
     Intersection, Range, Complement)
 from sympy.sets.sets import EmptySet, is_function_invertible_in_set
@@ -20,17 +20,19 @@
 
 FunctionUnion = (FunctionClass, Lambda)
 
+_set_function = Dispatcher('_set_function')
 
-@dispatch(FunctionClass, Set)  # type: ignore # noqa:F811
-def _set_function(f, x): # noqa:F811
+
+@_set_function.register(FunctionClass, Set)
+def _(f, x):
     return None
 
-@dispatch(FunctionUnion, FiniteSet)  # type: ignore # noqa:F811
-def _set_function(f, x): # noqa:F811
+@_set_function.register(FunctionUnion, FiniteSet)
+def _(f, x):
     return FiniteSet(*map(f, x))
 
-@dispatch(Lambda, Interval)  # type: ignore # noqa:F811
-def _set_function(f, x): # noqa:F811
+@_set_function.register(Lambda, Interval)
+def _(f, x):
     from sympy.solvers.solveset import solveset
     from sympy.series import limit
     # TODO: handle functions with infinitely many solutions (eg, sin, tan)
@@ -120,36 +122,36 @@ def _set_function(f, x): # noqa:F811
                     for i in range(0, len(sing) - 1)]) + \
             imageset(f, Interval(sing[-1], x.end, True, x.right_open))
 
-@dispatch(FunctionClass, Interval)  # type: ignore # noqa:F811
-def _set_function(f, x): # noqa:F811
+@_set_function.register(FunctionClass, Interval)
+def _(f, x):
     if f == exp:
         return Interval(exp(x.start), exp(x.end), x.left_open, x.right_open)
     elif f == log:
         return Interval(log(x.start), log(x.end), x.left_open, x.right_open)
     return ImageSet(Lambda(_x, f(_x)), x)
 
-@dispatch(FunctionUnion, Union)  # type: ignore # noqa:F811
-def _set_function(f, x): # noqa:F811
+@_set_function.register(FunctionUnion, Union)
+def _(f, x):
     return Union(*(imageset(f, arg) for arg in x.args))
 
-@dispatch(FunctionUnion, Intersection)  # type: ignore # noqa:F811
-def _set_function(f, x): # noqa:F811
+@_set_function.register(FunctionUnion, Intersection)
+def _(f, x):
     # If the function is invertible, intersect the maps of the sets.
     if is_function_invertible_in_set(f, x):
         return Intersection(*(imageset(f, arg) for arg in x.args))
     else:
         return ImageSet(Lambda(_x, f(_x)), x)
 
-@dispatch(FunctionUnion, EmptySet)  # type: ignore # noqa:F811
-def _set_function(f, x): # noqa:F811
+@_set_function.register(FunctionUnion, EmptySet)
+def _(f, x):
     return x
 
-@dispatch(FunctionUnion, Set)  # type: ignore # noqa:F811
-def _set_function(f, x): # noqa:F811
+@_set_function.register(FunctionUnion, Set)
+def _(f, x):
     return ImageSet(Lambda(_x, f(_x)), x)
 
-@dispatch(FunctionUnion, Range)  # type: ignore # noqa:F811
-def _set_function(f, self): # noqa:F811
+@_set_function.register(FunctionUnion, Range)
+def _(f, self):
     if not self:
         return S.EmptySet
     if not isinstance(f.expr, Expr):
@@ -172,8 +174,8 @@ def _set_function(f, self): # noqa:F811
     if F != expr:
         return imageset(x, F, Range(self.size))
 
-@dispatch(FunctionUnion, Integers)  # type: ignore # noqa:F811
-def _set_function(f, self): # noqa:F811
+@_set_function.register(FunctionUnion, Integers)
+def _(f, self):
     expr = f.expr
     if not isinstance(expr, Expr):
         return
@@ -225,8 +227,8 @@ def _set_function(f, self): # noqa:F811
         return ImageSet(Lambda(n, expr), S.Integers)
 
 
-@dispatch(FunctionUnion, Naturals)  # type: ignore # noqa:F811
-def _set_function(f, self): # noqa:F811
+@_set_function.register(FunctionUnion, Naturals)
+def _(f, self):
     expr = f.expr
     if not isinstance(expr, Expr):
         return
@@ -252,8 +254,8 @@ def _set_function(f, self): # noqa:F811
             return Range(c, -oo, step)
 
 
-@dispatch(FunctionUnion, Reals)  # type: ignore # noqa:F811
-def _set_function(f, self): # noqa:F811
+@_set_function.register(FunctionUnion, Reals)
+def _(f, self):
     expr = f.expr
     if not isinstance(expr, Expr):
         return
diff --git a/sympy/sets/handlers/intersection.py b/sympy/sets/handlers/intersection.py
index 1980251c5d97..9305721b3cb7 100644
--- a/sympy/sets/handlers/intersection.py
+++ b/sympy/sets/handlers/intersection.py
@@ -6,7 +6,7 @@
 from sympy.core.symbol import (Dummy, symbols)
 from sympy.sets.fancysets import ComplexRegion
 from sympy.sets.sets import (FiniteSet, Intersection, Interval, Set, Union)
-from sympy.multipledispatch import dispatch
+from sympy.multipledispatch import Dispatcher
 from sympy.sets.conditionset import ConditionSet
 from sympy.sets.fancysets import (Integers, Naturals, Reals, Range,
     ImageSet, Rationals)
@@ -14,28 +14,31 @@
 from sympy.simplify.radsimp import numer
 
 
-@dispatch(ConditionSet, ConditionSet)  # type: ignore # noqa:F811
-def intersection_sets(a, b): # noqa:F811
+intersection_sets = Dispatcher('intersection_sets')
+
+
+@intersection_sets.register(ConditionSet, ConditionSet)
+def _(a, b):
     return None
 
-@dispatch(ConditionSet, Set)  # type: ignore # noqa:F811
-def intersection_sets(a, b): # noqa:F811
+@intersection_sets.register(ConditionSet, Set)
+def _(a, b):
     return ConditionSet(a.sym, a.condition, Intersection(a.base_set, b))
 
-@dispatch(Naturals, Integers)  # type: ignore # noqa:F811
-def intersection_sets(a, b): # noqa:F811
+@intersection_sets.register(Naturals, Integers)
+def _(a, b):
     return a
 
-@dispatch(Naturals, Naturals)  # type: ignore # noqa:F811
-def intersection_sets(a, b): # noqa:F811
+@intersection_sets.register(Naturals, Naturals)
+def _(a, b):
     return a if a is S.Naturals else b
 
-@dispatch(Interval, Naturals)  # type: ignore # noqa:F811
-def intersection_sets(a, b): # noqa:F811
+@intersection_sets.register(Interval, Naturals)
+def _(a, b):
     return intersection_sets(b, a)
 
-@dispatch(ComplexRegion, Set)  # type: ignore # noqa:F811
-def intersection_sets(self, other): # noqa:F811
+@intersection_sets.register(ComplexRegion, Set)
+def _(self, other):
     if other.is_ComplexRegion:
         # self in rectangular form
         if (not self.polar) and (not other.polar):
@@ -81,12 +84,12 @@ def intersection_sets(self, other): # noqa:F811
             new_interval = Union(*new_interval)
             return Intersection(new_interval, other)
 
-@dispatch(Integers, Reals)  # type: ignore # noqa:F811
-def intersection_sets(a, b): # noqa:F811
+@intersection_sets.register(Integers, Reals)
+def _(a, b):
     return a
 
-@dispatch(Range, Interval)  # type: ignore # noqa:F811
-def intersection_sets(a, b): # noqa:F811
+@intersection_sets.register(Range, Interval)
+def _(a, b):
     # Check that there are no symbolic arguments
     if not all(i.is_number for i in a.args + b.args[:2]):
         return
@@ -106,12 +109,12 @@ def intersection_sets(a, b): # noqa:F811
         end -= 1
     return intersection_sets(a, Range(start, end + 1))
 
-@dispatch(Range, Naturals)  # type: ignore # noqa:F811
-def intersection_sets(a, b): # noqa:F811
+@intersection_sets.register(Range, Naturals)
+def _(a, b):
     return intersection_sets(a, Interval(b.inf, S.Infinity))
 
-@dispatch(Range, Range)  # type: ignore # noqa:F811
-def intersection_sets(a, b): # noqa:F811
+@intersection_sets.register(Range, Range)
+def _(a, b):
     # Check that there are no symbolic range arguments
     if not all(all(v.is_number for v in r.args) for r in [a, b]):
         return None
@@ -226,13 +229,13 @@ def _updated_range(r, first):
     return Range(start, stop, step)
 
 
-@dispatch(Range, Integers)  # type: ignore # noqa:F811
-def intersection_sets(a, b): # noqa:F811
+@intersection_sets.register(Range, Integers)
+def _(a, b):
     return a
 
 
-@dispatch(ImageSet, Set)  # type: ignore # noqa:F811
-def intersection_sets(self, other): # noqa:F811
+@intersection_sets.register(ImageSet, Set)
+def _(self, other):
     from sympy.solvers.diophantine import diophantine
 
     # Only handle the straight-forward univariate case
@@ -395,15 +398,15 @@ def _solution_union(exprs, sym):
             return
 
 
-@dispatch(ProductSet, ProductSet)  # type: ignore # noqa:F811
-def intersection_sets(a, b): # noqa:F811
+@intersection_sets.register(ProductSet, ProductSet)
+def _(a, b):
     if len(b.args) != len(a.args):
         return S.EmptySet
     return ProductSet(*(i.intersect(j) for i, j in zip(a.sets, b.sets)))
 
 
-@dispatch(Interval, Interval)  # type: ignore # noqa:F811
-def intersection_sets(a, b): # noqa:F811
+@intersection_sets.register(Interval, Interval)
+def _(a, b):
     # handle (-oo, oo)
     infty = S.NegativeInfinity, S.Infinity
     if a == Interval(*infty):
@@ -449,39 +452,39 @@ def intersection_sets(a, b): # noqa:F811
 
     return Interval(start, end, left_open, right_open)
 
-@dispatch(EmptySet, Set)  # type: ignore # noqa:F811
-def intersection_sets(a, b): # noqa:F811
+@intersection_sets.register(EmptySet, Set)
+def _(a, b):
     return S.EmptySet
 
-@dispatch(UniversalSet, Set)  # type: ignore # noqa:F811
-def intersection_sets(a, b): # noqa:F811
+@intersection_sets.register(UniversalSet, Set)
+def _(a, b):
     return b
 
-@dispatch(FiniteSet, FiniteSet)  # type: ignore # noqa:F811
-def intersection_sets(a, b): # noqa:F811
+@intersection_sets.register(FiniteSet, FiniteSet)
+def _(a, b):
     return FiniteSet(*(a._elements & b._elements))
 
-@dispatch(FiniteSet, Set)  # type: ignore # noqa:F811
-def intersection_sets(a, b): # noqa:F811
+@intersection_sets.register(FiniteSet, Set)
+def _(a, b):
     try:
         return FiniteSet(*[el for el in a if el in b])
     except TypeError:
         return None  # could not evaluate `el in b` due to symbolic ranges.
 
-@dispatch(Set, Set)  # type: ignore # noqa:F811
-def intersection_sets(a, b): # noqa:F811
+@intersection_sets.register(Set, Set)
+def _(a, b):
     return None
 
-@dispatch(Integers, Rationals)  # type: ignore # noqa:F811
-def intersection_sets(a, b): # noqa:F811
+@intersection_sets.register(Integers, Rationals)
+def _(a, b):
     return a
 
-@dispatch(Naturals, Rationals)  # type: ignore # noqa:F811
-def intersection_sets(a, b): # noqa:F811
+@intersection_sets.register(Naturals, Rationals)
+def _(a, b):
     return a
 
-@dispatch(Rationals, Reals)  # type: ignore # noqa:F811
-def intersection_sets(a, b): # noqa:F811
+@intersection_sets.register(Rationals, Reals)
+def _(a, b):
     return a
 
 def _intlike_interval(a, b):
@@ -494,10 +497,10 @@ def _intlike_interval(a, b):
     except ValueError:
         return None
 
-@dispatch(Integers, Interval)  # type: ignore # noqa:F811
-def intersection_sets(a, b): # noqa:F811
+@intersection_sets.register(Integers, Interval)
+def _(a, b):
     return _intlike_interval(a, b)
 
-@dispatch(Naturals, Interval)  # type: ignore # noqa:F811
-def intersection_sets(a, b): # noqa:F811
+@intersection_sets.register(Naturals, Interval)
+def _(a, b):
     return _intlike_interval(a, b)
diff --git a/sympy/sets/handlers/issubset.py b/sympy/sets/handlers/issubset.py
index f39c594101cf..cc23e8bf56f1 100644
--- a/sympy/sets/handlers/issubset.py
+++ b/sympy/sets/handlers/issubset.py
@@ -4,17 +4,21 @@
 from sympy.core.relational import Eq
 from sympy.sets.sets import FiniteSet, Interval, Set, Union, ProductSet
 from sympy.sets.fancysets import Complexes, Reals, Range, Rationals
-from sympy.multipledispatch import dispatch
+from sympy.multipledispatch import Dispatcher
 
 
 _inf_sets = [S.Naturals, S.Naturals0, S.Integers, S.Rationals, S.Reals, S.Complexes]
 
-@dispatch(Set, Set)  # type: ignore # noqa:F811
-def is_subset_sets(a, b): # noqa:F811
+
+is_subset_sets = Dispatcher('is_subset_sets')
+
+
+@is_subset_sets.register(Set, Set)
+def _(a, b):
     return None
 
-@dispatch(Interval, Interval)  # type: ignore # noqa:F811
-def is_subset_sets(a, b): # noqa:F811
+@is_subset_sets.register(Interval, Interval)
+def _(a, b):
     # This is correct but can be made more comprehensive...
     if fuzzy_bool(a.start < b.start):
         return False
@@ -25,15 +29,15 @@ def is_subset_sets(a, b): # noqa:F811
     if (b.right_open and not a.right_open and fuzzy_bool(Eq(a.end, b.end))):
         return False
 
-@dispatch(Interval, FiniteSet)  # type: ignore # noqa:F811
-def is_subset_sets(a_interval, b_fs): # noqa:F811
+@is_subset_sets.register(Interval, FiniteSet)
+def _(a_interval, b_fs):
     # An Interval can only be a subset of a finite set if it is finite
     # which can only happen if it has zero measure.
     if fuzzy_not(a_interval.measure.is_zero):
         return False
 
-@dispatch(Interval, Union)  # type: ignore # noqa:F811
-def is_subset_sets(a_interval, b_u): # noqa:F811
+@is_subset_sets.register(Interval, Union)
+def _(a_interval, b_u):
     if all(isinstance(s, (Interval, FiniteSet)) for s in b_u.args):
         intervals = [s for s in b_u.args if isinstance(s, Interval)]
         if all(fuzzy_bool(a_interval.start < s.start) for s in intervals):
@@ -48,14 +52,14 @@ def is_subset_sets(a_interval, b_u): # noqa:F811
             if all(no_overlap(s, a_interval) for s in intervals):
                 return False
 
-@dispatch(Range, Range)  # type: ignore # noqa:F811
-def is_subset_sets(a, b): # noqa:F811
+@is_subset_sets.register(Range, Range)
+def _(a, b):
     if a.step == b.step == 1:
         return fuzzy_and([fuzzy_bool(a.start >= b.start),
                           fuzzy_bool(a.stop <= b.stop)])
 
-@dispatch(Range, Interval)  # type: ignore # noqa:F811
-def is_subset_sets(a_range, b_interval): # noqa:F811
+@is_subset_sets.register(Range, Interval)
+def _(a_range, b_interval):
     if a_range.step.is_positive:
         if b_interval.left_open and a_range.inf.is_finite:
             cond_left = a_range.inf > b_interval.left
@@ -67,8 +71,8 @@ def is_subset_sets(a_range, b_interval): # noqa:F811
             cond_right = a_range.sup <= b_interval.right
         return fuzzy_and([cond_left, cond_right])
 
-@dispatch(Range, FiniteSet)  # type: ignore # noqa:F811
-def is_subset_sets(a_range, b_finiteset): # noqa:F811
+@is_subset_sets.register(Range, FiniteSet)
+def _(a_range, b_finiteset):
     try:
         a_size = a_range.size
     except ValueError:
@@ -101,40 +105,40 @@ def is_subset_sets(a_range, b_finiteset): # noqa:F811
                 return True
         return None
 
-@dispatch(Interval, Range)  # type: ignore # noqa:F811
-def is_subset_sets(a_interval, b_range): # noqa:F811
+@is_subset_sets.register(Interval, Range)
+def _(a_interval, b_range):
     if a_interval.measure.is_extended_nonzero:
         return False
 
-@dispatch(Interval, Rationals)  # type: ignore # noqa:F811
-def is_subset_sets(a_interval, b_rationals): # noqa:F811
+@is_subset_sets.register(Interval, Rationals)
+def _(a_interval, b_rationals):
     if a_interval.measure.is_extended_nonzero:
         return False
 
-@dispatch(Range, Complexes)  # type: ignore # noqa:F811
-def is_subset_sets(a, b): # noqa:F811
+@is_subset_sets.register(Range, Complexes)
+def _(a, b):
     return True
 
-@dispatch(Complexes, Interval)  # type: ignore # noqa:F811
-def is_subset_sets(a, b): # noqa:F811
+@is_subset_sets.register(Complexes, Interval)
+def _(a, b):
     return False
 
-@dispatch(Complexes, Range)  # type: ignore # noqa:F811
-def is_subset_sets(a, b): # noqa:F811
+@is_subset_sets.register(Complexes, Range)
+def _(a, b):
     return False
 
-@dispatch(Complexes, Rationals)  # type: ignore # noqa:F811
-def is_subset_sets(a, b): # noqa:F811
+@is_subset_sets.register(Complexes, Rationals)
+def _(a, b):
     return False
 
-@dispatch(Rationals, Reals)  # type: ignore # noqa:F811
-def is_subset_sets(a, b): # noqa:F811
+@is_subset_sets.register(Rationals, Reals)
+def _(a, b):
     return True
 
-@dispatch(Rationals, Range)  # type: ignore # noqa:F811
-def is_subset_sets(a, b): # noqa:F811
+@is_subset_sets.register(Rationals, Range)
+def _(a, b):
     return False
 
-@dispatch(ProductSet, FiniteSet)  # type: ignore # noqa:F811
-def is_subset_sets(a_ps, b_fs): # noqa:F811
+@is_subset_sets.register(ProductSet, FiniteSet)
+def _(a_ps, b_fs):
     return fuzzy_and(b_fs.contains(x) for x in a_ps)
diff --git a/sympy/sets/handlers/mul.py b/sympy/sets/handlers/mul.py
index 984ac0571930..0dedc8068b79 100644
--- a/sympy/sets/handlers/mul.py
+++ b/sympy/sets/handlers/mul.py
@@ -1,27 +1,32 @@
 from sympy.core import Basic, Expr
 from sympy.core.numbers import oo
 from sympy.core.symbol import symbols
-from sympy.multipledispatch import dispatch
+from sympy.multipledispatch import Dispatcher
 from sympy.sets.setexpr import set_mul
 from sympy.sets.sets import Interval, Set
 
+
 _x, _y = symbols("x y")
 
 
-@dispatch(Basic, Basic)  # type: ignore # noqa:F811
-def _set_mul(x, y): # noqa:F811
+_set_mul = Dispatcher('_set_mul')
+_set_div = Dispatcher('_set_div')
+
+
+@_set_mul.register(Basic, Basic)
+def _(x, y):
     return None
 
-@dispatch(Set, Set)  # type: ignore # noqa:F811
-def _set_mul(x, y): # noqa:F811
+@_set_mul.register(Set, Set)
+def _(x, y):
     return None
 
-@dispatch(Expr, Expr)  # type: ignore # noqa:F811
-def _set_mul(x, y): # noqa:F811
+@_set_mul.register(Expr, Expr)
+def _(x, y):
     return x*y
 
-@dispatch(Interval, Interval)  # type: ignore # noqa:F811
-def _set_mul(x, y): # noqa:F811
+@_set_mul.register(Interval, Interval)
+def _(x, y):
     """
     Multiplications in interval arithmetic
     https://en.wikipedia.org/wiki/Interval_arithmetic
@@ -43,20 +48,20 @@ def _set_mul(x, y): # noqa:F811
         maxopen
     )
 
-@dispatch(Basic, Basic)  # type: ignore # noqa:F811
-def _set_div(x, y): # noqa:F811
+@_set_div.register(Basic, Basic)
+def _(x, y):
     return None
 
-@dispatch(Expr, Expr)  # type: ignore # noqa:F811
-def _set_div(x, y): # noqa:F811
+@_set_div.register(Expr, Expr)
+def _(x, y):
     return x/y
 
-@dispatch(Set, Set)  # type: ignore # noqa:F811 # noqa:F811
-def _set_div(x, y): # noqa:F811
+@_set_div.register(Set, Set)
+def _(x, y):
     return None
 
-@dispatch(Interval, Interval)  # type: ignore # noqa:F811
-def _set_div(x, y): # noqa:F811
+@_set_div.register(Interval, Interval)
+def _(x, y):
     """
     Divisions in interval arithmetic
     https://en.wikipedia.org/wiki/Interval_arithmetic
diff --git a/sympy/sets/handlers/power.py b/sympy/sets/handlers/power.py
index 2e510deb1653..3cad4ee49ab2 100644
--- a/sympy/sets/handlers/power.py
+++ b/sympy/sets/handlers/power.py
@@ -7,30 +7,33 @@
 from sympy.sets.fancysets import ImageSet
 from sympy.sets.setexpr import set_div
 from sympy.sets.sets import Set, Interval, FiniteSet, Union
-from sympy.multipledispatch import dispatch
+from sympy.multipledispatch import Dispatcher
 
 
 _x, _y = symbols("x y")
 
 
-@dispatch(Basic, Basic)  # type: ignore # noqa:F811
-def _set_pow(x, y): # noqa:F811
+_set_pow = Dispatcher('_set_pow')
+
+
+@_set_pow.register(Basic, Basic)
+def _(x, y):
     return None
 
-@dispatch(Set, Set)  # type: ignore # noqa:F811
-def _set_pow(x, y): # noqa:F811
+@_set_pow.register(Set, Set)
+def _(x, y):
     return ImageSet(Lambda((_x, _y), (_x ** _y)), x, y)
 
-@dispatch(Expr, Expr)  # type: ignore # noqa:F811
-def _set_pow(x, y): # noqa:F811
+@_set_pow.register(Expr, Expr)
+def _(x, y):
     return x**y
 
-@dispatch(Interval, Zero)  # type: ignore # noqa:F811
-def _set_pow(x, z): # noqa:F811
+@_set_pow.register(Interval, Zero)
+def _(x, z):
     return FiniteSet(S.One)
 
-@dispatch(Interval, Integer)  # type: ignore # noqa:F811
-def _set_pow(x, exponent): # noqa:F811
+@_set_pow.register(Interval, Integer)
+def _(x, exponent):
     """
     Powers in interval arithmetic
     https://en.wikipedia.org/wiki/Interval_arithmetic
@@ -77,8 +80,8 @@ def _set_pow(x, exponent): # noqa:F811
         else:
             return Interval(S.Zero, sleft, S.Zero not in x, left_open)
 
-@dispatch(Interval, Infinity)  # type: ignore # noqa:F811
-def _set_pow(b, e): # noqa:F811
+@_set_pow.register(Interval, Infinity)
+def _(b, e):
     # TODO: add logic for open intervals?
     if b.start.is_nonnegative:
         if b.end < 1:
@@ -99,6 +102,6 @@ def _set_pow(b, e): # noqa:F811
             return Interval(0, oo)
         return Interval(-oo, oo)
 
-@dispatch(Interval, NegativeInfinity)  # type: ignore # noqa:F811
-def _set_pow(b, e): # noqa:F811
+@_set_pow.register(Interval, NegativeInfinity)
+def _(b, e):
     return _set_pow(set_div(S.One, b), oo)
diff --git a/sympy/sets/handlers/union.py b/sympy/sets/handlers/union.py
index fee36c83b97b..35ccf8f6d743 100644
--- a/sympy/sets/handlers/union.py
+++ b/sympy/sets/handlers/union.py
@@ -4,43 +4,46 @@
     Interval, ProductSet, Set, Union, UniversalSet)
 from sympy.sets.fancysets import (ComplexRegion, Naturals, Naturals0,
     Integers, Rationals, Reals)
-from sympy.multipledispatch import dispatch
+from sympy.multipledispatch import Dispatcher
 
 
-@dispatch(Naturals0, Naturals)  # type: ignore # noqa:F811
-def union_sets(a, b): # noqa:F811
+union_sets = Dispatcher('union_sets')
+
+
+@union_sets.register(Naturals0, Naturals)
+def _(a, b):
     return a
 
-@dispatch(Rationals, Naturals)  # type: ignore # noqa:F811
-def union_sets(a, b): # noqa:F811
+@union_sets.register(Rationals, Naturals)
+def _(a, b):
     return a
 
-@dispatch(Rationals, Naturals0)  # type: ignore # noqa:F811
-def union_sets(a, b): # noqa:F811
+@union_sets.register(Rationals, Naturals0)
+def _(a, b):
     return a
 
-@dispatch(Reals, Naturals)  # type: ignore # noqa:F811
-def union_sets(a, b): # noqa:F811
+@union_sets.register(Reals, Naturals)
+def _(a, b):
     return a
 
-@dispatch(Reals, Naturals0)  # type: ignore # noqa:F811
-def union_sets(a, b): # noqa:F811
+@union_sets.register(Reals, Naturals0)
+def _(a, b):
     return a
 
-@dispatch(Reals, Rationals)  # type: ignore # noqa:F811
-def union_sets(a, b): # noqa:F811
+@union_sets.register(Reals, Rationals)
+def _(a, b):
     return a
 
-@dispatch(Integers, Set)  # type: ignore # noqa:F811
-def union_sets(a, b): # noqa:F811
+@union_sets.register(Integers, Set)
+def _(a, b):
     intersect = Intersection(a, b)
     if intersect == a:
         return b
     elif intersect == b:
         return a
 
-@dispatch(ComplexRegion, Set)  # type: ignore # noqa:F811
-def union_sets(a, b): # noqa:F811
+@union_sets.register(ComplexRegion, Set)
+def _(a, b):
     if b.is_subset(S.Reals):
         # treat a subset of reals as a complex region
         b = ComplexRegion.from_real(b)
@@ -54,17 +57,17 @@ def union_sets(a, b): # noqa:F811
             return ComplexRegion(Union(a.sets, b.sets), polar=True)
     return None
 
-@dispatch(EmptySet, Set)  # type: ignore # noqa:F811
-def union_sets(a, b): # noqa:F811
+@union_sets.register(EmptySet, Set)
+def _(a, b):
     return b
 
 
-@dispatch(UniversalSet, Set)  # type: ignore # noqa:F811
-def union_sets(a, b): # noqa:F811
+@union_sets.register(UniversalSet, Set)
+def _(a, b):
     return a
 
-@dispatch(ProductSet, ProductSet)  # type: ignore # noqa:F811
-def union_sets(a, b): # noqa:F811
+@union_sets.register(ProductSet, ProductSet)
+def _(a, b):
     if b.is_subset(a):
         return a
     if len(b.sets) != len(a.sets):
@@ -78,14 +81,14 @@ def union_sets(a, b): # noqa:F811
             return Union(a1, b1) * a2
     return None
 
-@dispatch(ProductSet, Set)  # type: ignore # noqa:F811
-def union_sets(a, b): # noqa:F811
+@union_sets.register(ProductSet, Set)
+def _(a, b):
     if b.is_subset(a):
         return a
     return None
 
-@dispatch(Interval, Interval)  # type: ignore # noqa:F811
-def union_sets(a, b): # noqa:F811
+@union_sets.register(Interval, Interval)
+def _(a, b):
     if a._is_comparable(b):
         from sympy.functions.elementary.miscellaneous import Min, Max
         # Non-overlapping intervals
@@ -104,12 +107,12 @@ def union_sets(a, b): # noqa:F811
                           (b.end != end or b.right_open))
             return Interval(start, end, left_open, right_open)
 
-@dispatch(Interval, UniversalSet)  # type: ignore # noqa:F811
-def union_sets(a, b): # noqa:F811
+@union_sets.register(Interval, UniversalSet)
+def _(a, b):
     return S.UniversalSet
 
-@dispatch(Interval, Set)  # type: ignore # noqa:F811
-def union_sets(a, b): # noqa:F811
+@union_sets.register(Interval, Set)
+def _(a, b):
     # If I have open end points and these endpoints are contained in b
     # But only in case, when endpoints are finite. Because
     # interval does not contain oo or -oo.
@@ -127,18 +130,18 @@ def union_sets(a, b): # noqa:F811
         return {new_a, b}
     return None
 
-@dispatch(FiniteSet, FiniteSet)  # type: ignore # noqa:F811
-def union_sets(a, b): # noqa:F811
+@union_sets.register(FiniteSet, FiniteSet)
+def _(a, b):
     return FiniteSet(*(a._elements | b._elements))
 
-@dispatch(FiniteSet, Set)  # type: ignore # noqa:F811
-def union_sets(a, b): # noqa:F811
+@union_sets.register(FiniteSet, Set)
+def _(a, b):
     # If `b` set contains one of my elements, remove it from `a`
     if any(b.contains(x) == True for x in a):
         return {
             FiniteSet(*[x for x in a if b.contains(x) != True]), b}
     return None
 
-@dispatch(Set, Set)  # type: ignore # noqa:F811
-def union_sets(a, b): # noqa:F811
+@union_sets.register(Set, Set)
+def _(a, b):
     return None
diff --git a/sympy/stats/sampling/sample_numpy.py b/sympy/stats/sampling/sample_numpy.py
index ff4856593dcc..a50b17d256f1 100644
--- a/sympy/stats/sampling/sample_numpy.py
+++ b/sympy/stats/sampling/sample_numpy.py
@@ -17,65 +17,65 @@ def do_sample_numpy(dist, size, rand_state):
 
 # CRV:
 
-@do_sample_numpy.register(BetaDistribution) # type: ignore
+@do_sample_numpy.register(BetaDistribution)
 def _(dist: BetaDistribution, size, rand_state):
     return rand_state.beta(a=float(dist.alpha), b=float(dist.beta), size=size)
 
 
-@do_sample_numpy.register(ChiSquaredDistribution) # type: ignore
+@do_sample_numpy.register(ChiSquaredDistribution)
 def _(dist: ChiSquaredDistribution, size, rand_state):
     return rand_state.chisquare(df=float(dist.k), size=size)
 
 
-@do_sample_numpy.register(ExponentialDistribution) # type: ignore
+@do_sample_numpy.register(ExponentialDistribution)
 def _(dist: ExponentialDistribution, size, rand_state):
     return rand_state.exponential(1 / float(dist.rate), size=size)
 
 
-@do_sample_numpy.register(GammaDistribution) # type: ignore
+@do_sample_numpy.register(GammaDistribution)
 def _(dist: GammaDistribution, size, rand_state):
     return rand_state.gamma(float(dist.k), float(dist.theta), size=size)
 
 
-@do_sample_numpy.register(LogNormalDistribution) # type: ignore
+@do_sample_numpy.register(LogNormalDistribution)
 def _(dist: LogNormalDistribution, size, rand_state):
     return rand_state.lognormal(float(dist.mean), float(dist.std), size=size)
 
 
-@do_sample_numpy.register(NormalDistribution) # type: ignore
+@do_sample_numpy.register(NormalDistribution)
 def _(dist: NormalDistribution, size, rand_state):
     return rand_state.normal(float(dist.mean), float(dist.std), size=size)
 
 
-@do_sample_numpy.register(ParetoDistribution) # type: ignore
+@do_sample_numpy.register(ParetoDistribution)
 def _(dist: ParetoDistribution, size, rand_state):
     return (numpy.random.pareto(a=float(dist.alpha), size=size) + 1) * float(dist.xm)
 
 
-@do_sample_numpy.register(UniformDistribution) # type: ignore
+@do_sample_numpy.register(UniformDistribution)
 def _(dist: UniformDistribution, size, rand_state):
     return rand_state.uniform(low=float(dist.left), high=float(dist.right), size=size)
 
 
 # DRV:
 
-@do_sample_numpy.register(GeometricDistribution) # type: ignore
+@do_sample_numpy.register(GeometricDistribution)
 def _(dist: GeometricDistribution, size, rand_state):
     return rand_state.geometric(p=float(dist.p), size=size)
 
 
-@do_sample_numpy.register(PoissonDistribution) # type: ignore
+@do_sample_numpy.register(PoissonDistribution)
 def _(dist: PoissonDistribution, size, rand_state):
     return rand_state.poisson(lam=float(dist.lamda), size=size)
 
 
-@do_sample_numpy.register(ZetaDistribution) # type: ignore
+@do_sample_numpy.register(ZetaDistribution)
 def _(dist: ZetaDistribution, size, rand_state):
     return rand_state.zipf(a=float(dist.s), size=size)
 
 
 # FRV:
 
-@do_sample_numpy.register(BinomialDistribution) # type: ignore
+@do_sample_numpy.register(BinomialDistribution)
 def _(dist: BinomialDistribution, size, rand_state):
     return rand_state.binomial(n=int(dist.n), p=float(dist.p), size=size)
diff --git a/sympy/stats/sampling/sample_pymc3.py b/sympy/stats/sampling/sample_pymc3.py
index e3c6f8f3aae9..a20e3858e16d 100644
--- a/sympy/stats/sampling/sample_pymc3.py
+++ b/sympy/stats/sampling/sample_pymc3.py
@@ -17,81 +17,81 @@ def do_sample_pymc3(dist):
 
 # CRV:
 
-@do_sample_pymc3.register(BetaDistribution) # type: ignore
+@do_sample_pymc3.register(BetaDistribution)
 def _(dist: BetaDistribution):
     return pymc3.Beta('X', alpha=float(dist.alpha), beta=float(dist.beta))
 
 
-@do_sample_pymc3.register(CauchyDistribution) # type: ignore
+@do_sample_pymc3.register(CauchyDistribution)
 def _(dist: CauchyDistribution):
     return pymc3.Cauchy('X', alpha=float(dist.x0), beta=float(dist.gamma))
 
 
-@do_sample_pymc3.register(ChiSquaredDistribution) # type: ignore
+@do_sample_pymc3.register(ChiSquaredDistribution)
 def _(dist: ChiSquaredDistribution):
     return pymc3.ChiSquared('X', nu=float(dist.k))
 
 
-@do_sample_pymc3.register(ExponentialDistribution) # type: ignore
+@do_sample_pymc3.register(ExponentialDistribution)
 def _(dist: ExponentialDistribution):
     return pymc3.Exponential('X', lam=float(dist.rate))
 
 
-@do_sample_pymc3.register(GammaDistribution) # type: ignore
+@do_sample_pymc3.register(GammaDistribution)
 def _(dist: GammaDistribution):
     return pymc3.Gamma('X', alpha=float(dist.k), beta=1 / float(dist.theta))
 
 
-@do_sample_pymc3.register(LogNormalDistribution) # type: ignore
+@do_sample_pymc3.register(LogNormalDistribution)
 def _(dist: LogNormalDistribution):
     return pymc3.Lognormal('X', mu=float(dist.mean), sigma=float(dist.std))
 
 
-@do_sample_pymc3.register(NormalDistribution) # type: ignore
+@do_sample_pymc3.register(NormalDistribution)
 def _(dist: NormalDistribution):
     return pymc3.Normal('X', float(dist.mean), float(dist.std))
 
 
-@do_sample_pymc3.register(GaussianInverseDistribution) # type: ignore
+@do_sample_pymc3.register(GaussianInverseDistribution)
 def _(dist: GaussianInverseDistribution):
     return pymc3.Wald('X', mu=float(dist.mean), lam=float(dist.shape))
 
 
-@do_sample_pymc3.register(ParetoDistribution) # type: ignore
+@do_sample_pymc3.register(ParetoDistribution)
 def _(dist: ParetoDistribution):
     return pymc3.Pareto('X', alpha=float(dist.alpha), m=float(dist.xm))
 
 
-@do_sample_pymc3.register(UniformDistribution) # type: ignore
+@do_sample_pymc3.register(UniformDistribution)
 def _(dist: UniformDistribution):
     return pymc3.Uniform('X', lower=float(dist.left), upper=float(dist.right))
 
 
 # DRV:
 
-@do_sample_pymc3.register(GeometricDistribution) # type: ignore
+@do_sample_pymc3.register(GeometricDistribution)
 def _(dist: GeometricDistribution):
     return pymc3.Geometric('X', p=float(dist.p))
 
 
-@do_sample_pymc3.register(NegativeBinomialDistribution) # type: ignore
+@do_sample_pymc3.register(NegativeBinomialDistribution)
 def _(dist: NegativeBinomialDistribution):
     return pymc3.NegativeBinomial('X', mu=float((dist.p * dist.r) / (1 - dist.p)),
                                   alpha=float(dist.r))
 
 
-@do_sample_pymc3.register(PoissonDistribution) # type: ignore
+@do_sample_pymc3.register(PoissonDistribution)
 def _(dist: PoissonDistribution):
     return pymc3.Poisson('X', mu=float(dist.lamda))
 
 
 # FRV:
 
-@do_sample_pymc3.register(BernoulliDistribution) # type: ignore
+@do_sample_pymc3.register(BernoulliDistribution)
 def _(dist: BernoulliDistribution):
     return pymc3.Bernoulli('X', p=float(dist.p))
 
 
-@do_sample_pymc3.register(BinomialDistribution) # type: ignore
+@do_sample_pymc3.register(BinomialDistribution)
 def _(dist: BinomialDistribution):
     return pymc3.Binomial('X', n=int(dist.n), p=float(dist.p))
diff --git a/sympy/stats/sampling/sample_scipy.py b/sympy/stats/sampling/sample_scipy.py
index 21af16f8342f..f12508f68844 100644
--- a/sympy/stats/sampling/sample_scipy.py
+++ b/sympy/stats/sampling/sample_scipy.py
@@ -24,7 +24,7 @@ def do_sample_scipy(dist, size, seed):
 
 # CRV
 
-@do_sample_scipy.register(SingleContinuousDistribution) # type: ignore
+@do_sample_scipy.register(SingleContinuousDistribution)
 def _(dist: SingleContinuousDistribution, size, seed):
     # if we don't need to make a handmade pdf, we won't
     import scipy.stats
@@ -41,66 +41,66 @@ def _pdf(dist, x):
     return scipy_rv.rvs(size=size, random_state=seed)
 
 
-@do_sample_scipy.register(ChiSquaredDistribution) # type: ignore
+@do_sample_scipy.register(ChiSquaredDistribution)
 def _(dist: ChiSquaredDistribution, size, seed):
     # same parametrisation
     return scipy.stats.chi2.rvs(df=float(dist.k), size=size, random_state=seed)
 
 
-@do_sample_scipy.register(ExponentialDistribution) # type: ignore
+@do_sample_scipy.register(ExponentialDistribution)
 def _(dist: ExponentialDistribution, size, seed):
     # https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.expon.html#scipy.stats.expon
     return scipy.stats.expon.rvs(scale=1 / float(dist.rate), size=size, random_state=seed)
 
 
-@do_sample_scipy.register(GammaDistribution) # type: ignore
+@do_sample_scipy.register(GammaDistribution)
 def _(dist: GammaDistribution, size, seed):
     # https://stackoverflow.com/questions/42150965/how-to-plot-gamma-distribution-with-alpha-and-beta-parameters-in-python
     return scipy.stats.gamma.rvs(a=float(dist.k), scale=float(dist.theta), size=size, random_state=seed)
 
 
-@do_sample_scipy.register(LogNormalDistribution) # type: ignore
+@do_sample_scipy.register(LogNormalDistribution)
 def _(dist: LogNormalDistribution, size, seed):
     # https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.lognorm.html
     return scipy.stats.lognorm.rvs(scale=float(exp(dist.mean)), s=float(dist.std), size=size, random_state=seed)
 
 
-@do_sample_scipy.register(NormalDistribution) # type: ignore
+@do_sample_scipy.register(NormalDistribution)
 def _(dist: NormalDistribution, size, seed):
     return scipy.stats.norm.rvs(loc=float(dist.mean), scale=float(dist.std), size=size, random_state=seed)
 
 
-@do_sample_scipy.register(ParetoDistribution) # type: ignore
+@do_sample_scipy.register(ParetoDistribution)
 def _(dist: ParetoDistribution, size, seed):
     # https://stackoverflow.com/questions/42260519/defining-pareto-distribution-in-python-scipy
     return scipy.stats.pareto.rvs(b=float(dist.alpha), scale=float(dist.xm), size=size, random_state=seed)
 
 
-@do_sample_scipy.register(StudentTDistribution) # type: ignore
+@do_sample_scipy.register(StudentTDistribution)
 def _(dist: StudentTDistribution, size, seed):
     return scipy.stats.t.rvs(df=float(dist.nu), size=size, random_state=seed)
 
 
-@do_sample_scipy.register(UniformDistribution) # type: ignore
+@do_sample_scipy.register(UniformDistribution)
 def _(dist: UniformDistribution, size, seed):
     # https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.uniform.html
     return scipy.stats.uniform.rvs(loc=float(dist.left), scale=float(dist.right - dist.left), size=size, random_state=seed)
 
 
-@do_sample_scipy.register(BetaDistribution) # type: ignore
+@do_sample_scipy.register(BetaDistribution)
 def _(dist: BetaDistribution, size, seed):
     # same parametrisation
     return scipy.stats.beta.rvs(a=float(dist.alpha), b=float(dist.beta), size=size, random_state=seed)
 
 
-@do_sample_scipy.register(CauchyDistribution) # type: ignore
+@do_sample_scipy.register(CauchyDistribution)
 def _(dist: CauchyDistribution, size, seed):
     return scipy.stats.cauchy.rvs(loc=float(dist.x0), scale=float(dist.gamma), size=size, random_state=seed)
 
 
 # DRV:
 
-@do_sample_scipy.register(DiscreteDistributionHandmade) # type: ignore
+@do_sample_scipy.register(DiscreteDistributionHandmade)
 def _(dist: DiscreteDistributionHandmade, size, seed):
     from scipy.stats import rv_discrete
 
@@ -116,44 +116,44 @@ def _pmf(dist, x):
     return scipy_rv.rvs(size=size, random_state=seed)
 
 
-@do_sample_scipy.register(GeometricDistribution) # type: ignore
+@do_sample_scipy.register(GeometricDistribution)
 def _(dist: GeometricDistribution, size, seed):
     return scipy.stats.geom.rvs(p=float(dist.p), size=size, random_state=seed)
 
 
-@do_sample_scipy.register(LogarithmicDistribution) # type: ignore
+@do_sample_scipy.register(LogarithmicDistribution)
 def _(dist: LogarithmicDistribution, size, seed):
     return scipy.stats.logser.rvs(p=float(dist.p), size=size, random_state=seed)
 
 
-@do_sample_scipy.register(NegativeBinomialDistribution) # type: ignore
+@do_sample_scipy.register(NegativeBinomialDistribution)
 def _(dist: NegativeBinomialDistribution, size, seed):
     return scipy.stats.nbinom.rvs(n=float(dist.r), p=float(dist.p), size=size, random_state=seed)
 
 
-@do_sample_scipy.register(PoissonDistribution) # type: ignore
+@do_sample_scipy.register(PoissonDistribution)
 def _(dist: PoissonDistribution, size, seed):
     return scipy.stats.poisson.rvs(mu=float(dist.lamda), size=size, random_state=seed)
 
 
-@do_sample_scipy.register(SkellamDistribution) # type: ignore
+@do_sample_scipy.register(SkellamDistribution)
 def _(dist: SkellamDistribution, size, seed):
     return scipy.stats.skellam.rvs(mu1=float(dist.mu1), mu2=float(dist.mu2), size=size, random_state=seed)
 
 
-@do_sample_scipy.register(YuleSimonDistribution) # type: ignore
+@do_sample_scipy.register(YuleSimonDistribution)
 def _(dist: YuleSimonDistribution, size, seed):
     return scipy.stats.yulesimon.rvs(alpha=float(dist.rho), size=size, random_state=seed)
 
 
-@do_sample_scipy.register(ZetaDistribution) # type: ignore
+@do_sample_scipy.register(ZetaDistribution)
 def _(dist: ZetaDistribution, size, seed):
     return scipy.stats.zipf.rvs(a=float(dist.s), size=size, random_state=seed)
 
 
 # FRV:
 
-@do_sample_scipy.register(SingleFiniteDistribution) # type: ignore
+@do_sample_scipy.register(SingleFiniteDistribution)
 def _(dist: SingleFiniteDistribution, size, seed):
     # scipy can handle with custom distributions
 
diff --git a/sympy/tensor/array/expressions/array_expressions.py b/sympy/tensor/array/expressions/array_expressions.py
index 75573cbb3200..5ccbe8f39388 100644
--- a/sympy/tensor/array/expressions/array_expressions.py
+++ b/sympy/tensor/array/expressions/array_expressions.py
@@ -33,7 +33,7 @@
 
 
 class _ArrayExpr(Expr):
-    pass
+    shape : tTuple[Expr, ...]
 
 
 class ArraySymbol(_ArrayExpr):
@@ -1571,7 +1571,7 @@ def __init__(self, base_array: typing.Union[ArrayContraction, ArrayDiagonal, Arr
                 args_with_ind[arg_pos].indices[rel_pos] = i
         self.args_with_ind: List[_ArgE] = args_with_ind
         self.number_of_contraction_indices: int = len(contraction_indices)
-        self._track_permutation: Optional[List[List[int]]] = None
+        self._track_permutation: List[List[int]] = None # type: ignore
 
         mapping = _get_mapping_from_subranks(base_array.subranks)
 
diff --git a/sympy/tensor/array/expressions/arrayexpr_derivatives.py b/sympy/tensor/array/expressions/arrayexpr_derivatives.py
index 20ca1b283892..97fa4659f5d5 100644
--- a/sympy/tensor/array/expressions/arrayexpr_derivatives.py
+++ b/sympy/tensor/array/expressions/arrayexpr_derivatives.py
@@ -10,9 +10,11 @@
 from sympy.matrices.expressions.transpose import Transpose
 from sympy.combinatorics.permutations import _af_invert
 from sympy.matrices.expressions.applyfunc import ElementwiseApplyFunction
-from sympy.tensor.array.expressions.array_expressions import ZeroArray, ArraySymbol, ArrayTensorProduct, \
-    ArrayAdd, PermuteDims, ArrayDiagonal, ArrayElementwiseApplyFunc, get_rank, \
-    get_shape, ArrayContraction, _array_tensor_product, _array_contraction, _array_diagonal, _array_add, _permute_dims
+from sympy.tensor.array.expressions.array_expressions import (
+        _ArrayExpr, ZeroArray, ArraySymbol, ArrayTensorProduct, ArrayAdd,
+        PermuteDims, ArrayDiagonal, ArrayElementwiseApplyFunc, get_rank,
+        get_shape, ArrayContraction, _array_tensor_product, _array_contraction,
+        _array_diagonal, _array_add, _permute_dims)
 from sympy.tensor.array.expressions.conv_matrix_to_array import convert_matrix_to_array
 
 
@@ -21,12 +23,12 @@ def array_derive(expr, x):
     raise NotImplementedError(f"not implemented for type {type(expr)}")
 
 
-@array_derive.register(Expr) # type: ignore
-def _(expr: Expr, x: Expr):
-    return ZeroArray(*x.shape)  # type: ignore
+@array_derive.register(Expr)
+def _(expr: Expr, x: _ArrayExpr):
+    return ZeroArray(*x.shape)
 
 
-@array_derive.register(ArrayTensorProduct) # type: ignore
+@array_derive.register(ArrayTensorProduct)
 def _(expr: ArrayTensorProduct, x: Expr):
     args = expr.args
     addend_list = []
@@ -56,33 +58,33 @@ def _(expr: ArrayTensorProduct, x: Expr):
         return _array_add(*addend_list)
 
 
-@array_derive.register(ArraySymbol) # type: ignore
-def _(expr: ArraySymbol, x: Expr):
+@array_derive.register(ArraySymbol)
+def _(expr: ArraySymbol, x: _ArrayExpr):
     if expr == x:
         return _permute_dims(
             ArrayTensorProduct.fromiter(Identity(i) for i in expr.shape),
             [2*i for i in range(len(expr.shape))] + [2*i+1 for i in range(len(expr.shape))]
         )
-    return ZeroArray(*(x.shape + expr.shape))  # type: ignore
+    return ZeroArray(*(x.shape + expr.shape))
 
 
-@array_derive.register(MatrixSymbol) # type: ignore
-def _(expr: MatrixSymbol, x: Expr):
+@array_derive.register(MatrixSymbol)
+def _(expr: MatrixSymbol, x: _ArrayExpr):
     m, n = expr.shape
     if expr == x:
         return _permute_dims(
             _array_tensor_product(Identity(m), Identity(n)),
             [0, 2, 1, 3]
         )
-    return ZeroArray(*(x.shape + expr.shape))  # type: ignore
+    return ZeroArray(*(x.shape + expr.shape))
 
 
-@array_derive.register(Identity) # type: ignore
-def _(expr: Identity, x: Expr):
-    return ZeroArray(*(x.shape + expr.shape))  # type: ignore
+@array_derive.register(Identity)
+def _(expr: Identity, x: _ArrayExpr):
+    return ZeroArray(*(x.shape + expr.shape))
 
 
-@array_derive.register(Transpose) # type: ignore
+@array_derive.register(Transpose)
 def _(expr: Transpose, x: Expr):
     # D(A.T, A) ==> (m,n,i,j) ==> D(A_ji, A_mn) = d_mj d_ni
     # D(B.T, A) ==> (m,n,i,j) ==> D(B_ji, A_mn)
@@ -90,7 +92,7 @@ def _(expr: Transpose, x: Expr):
     return _permute_dims(fd, [0, 1, 3, 2])
 
 
-@array_derive.register(Inverse) # type: ignore
+@array_derive.register(Inverse)
 def _(expr: Inverse, x: Expr):
     mat = expr.I
     dexpr = array_derive(mat, x)
@@ -100,7 +102,7 @@ def _(expr: Inverse, x: Expr):
     return pp
 
 
-@array_derive.register(ElementwiseApplyFunction) # type: ignore
+@array_derive.register(ElementwiseApplyFunction)
 def _(expr: ElementwiseApplyFunction, x: Expr):
     assert get_rank(expr) == 2
     assert get_rank(x) == 2
@@ -116,7 +118,7 @@ def _(expr: ElementwiseApplyFunction, x: Expr):
     return td
 
 
-@array_derive.register(ArrayElementwiseApplyFunc) # type: ignore
+@array_derive.register(ArrayElementwiseApplyFunc)
 def _(expr: ArrayElementwiseApplyFunc, x: Expr):
     fdiff = expr._get_function_fdiff()
     subexpr = expr.expr
@@ -131,18 +133,18 @@ def _(expr: ArrayElementwiseApplyFunc, x: Expr):
     return _array_diagonal(tp, *diag_indices)
 
 
-@array_derive.register(MatrixExpr) # type: ignore
+@array_derive.register(MatrixExpr)
 def _(expr: MatrixExpr, x: Expr):
     cg = convert_matrix_to_array(expr)
     return array_derive(cg, x)
 
 
-@array_derive.register(HadamardProduct) # type: ignore
+@array_derive.register(HadamardProduct)
 def _(expr: HadamardProduct, x: Expr):
     raise NotImplementedError()
 
 
-@array_derive.register(ArrayContraction) # type: ignore
+@array_derive.register(ArrayContraction)
 def _(expr: ArrayContraction, x: Expr):
     fd = array_derive(expr.expr, x)
     rank_x = len(get_shape(x))
@@ -151,7 +153,7 @@ def _(expr: ArrayContraction, x: Expr):
     return _array_contraction(fd, *new_contraction_indices)
 
 
-@array_derive.register(ArrayDiagonal) # type: ignore
+@array_derive.register(ArrayDiagonal)
 def _(expr: ArrayDiagonal, x: Expr):
     dsubexpr = array_derive(expr.expr, x)
     rank_x = len(get_shape(x))
@@ -159,12 +161,12 @@ def _(expr: ArrayDiagonal, x: Expr):
     return _array_diagonal(dsubexpr, *diag_indices)
 
 
-@array_derive.register(ArrayAdd) # type: ignore
+@array_derive.register(ArrayAdd)
 def _(expr: ArrayAdd, x: Expr):
     return _array_add(*[array_derive(arg, x) for arg in expr.args])
 
 
-@array_derive.register(PermuteDims) # type: ignore
+@array_derive.register(PermuteDims)
 def _(expr: PermuteDims, x: Expr):
     de = array_derive(expr.expr, x)
     perm = [0, 1] + [i + 2 for i in expr.permutation.array_form]
diff --git a/sympy/tensor/array/expressions/conv_array_to_matrix.py b/sympy/tensor/array/expressions/conv_array_to_matrix.py
index 50d250dfe1bf..99fb1e0e35f7 100644
--- a/sympy/tensor/array/expressions/conv_array_to_matrix.py
+++ b/sympy/tensor/array/expressions/conv_array_to_matrix.py
@@ -172,7 +172,7 @@ def _array2matrix(expr):
     return expr
 
 
-@_array2matrix.register(ZeroArray) # type: ignore
+@_array2matrix.register(ZeroArray)
 def _(expr: ZeroArray):
     if get_rank(expr) == 2:
         return ZeroMatrix(*expr.shape)
@@ -180,12 +180,12 @@ def _(expr: ZeroArray):
         return expr
 
 
-@_array2matrix.register(ArrayTensorProduct) # type: ignore
+@_array2matrix.register(ArrayTensorProduct)
 def _(expr: ArrayTensorProduct):
     return _a2m_tensor_product(*[_array2matrix(arg) for arg in expr.args])
 
 
-@_array2matrix.register(ArrayContraction) # type: ignore
+@_array2matrix.register(ArrayContraction)
 def _(expr: ArrayContraction):
     expr = expr.flatten_contraction_of_diagonal()
     expr = identify_removable_identity_matrices(expr)
@@ -226,7 +226,7 @@ def _(expr: ArrayContraction):
             return _array_contraction(ret, *expr.contraction_indices)
 
 
-@_array2matrix.register(ArrayDiagonal) # type: ignore
+@_array2matrix.register(ArrayDiagonal)
 def _(expr: ArrayDiagonal):
     pexpr = _array_diagonal(_array2matrix(expr.expr), *expr.diagonal_indices)
     pexpr = identify_hadamard_products(pexpr)
@@ -237,7 +237,7 @@ def _(expr: ArrayDiagonal):
     return _array2matrix(pexpr)
 
 
-@_array2matrix.register(PermuteDims) # type: ignore
+@_array2matrix.register(PermuteDims)
 def _(expr: PermuteDims):
     if expr.permutation.array_form == [1, 0]:
         return _a2m_transpose(_array2matrix(expr.expr))
@@ -283,23 +283,23 @@ def _(expr: PermuteDims):
             p2 = permuted[2*i+1]
             if p1 // 2 != p2 // 2:
                 return _permute_dims(mat_mul_lines, permutation)
-            pos = p1 // 2
+            posi = p1 // 2
             if p1 > p2:
-                args_array[i] = _a2m_transpose(mat_mul_lines.args[pos])  # type: ignore
+                args_array[i] = _a2m_transpose(mat_mul_lines.args[posi])
             else:
-                args_array[i] = mat_mul_lines.args[pos]  # type: ignore
+                args_array[i] = mat_mul_lines.args[posi]
         return _a2m_tensor_product(*args_array)
     else:
         return expr
 
 
-@_array2matrix.register(ArrayAdd) # type: ignore
+@_array2matrix.register(ArrayAdd)
 def _(expr: ArrayAdd):
     addends = [_array2matrix(arg) for arg in expr.args]
     return _a2m_add(*addends)
 
 
-@_array2matrix.register(ArrayElementwiseApplyFunc) # type: ignore
+@_array2matrix.register(ArrayElementwiseApplyFunc)
 def _(expr: ArrayElementwiseApplyFunc):
     subexpr = _array2matrix(expr.expr)
     if isinstance(subexpr, MatrixExpr):
@@ -315,7 +315,7 @@ def _(expr: ArrayElementwiseApplyFunc):
         return ArrayElementwiseApplyFunc(expr.function, subexpr)
 
 
-@_array2matrix.register(ArrayElement) # type: ignore
+@_array2matrix.register(ArrayElement)
 def _(expr: ArrayElement):
     ret = _array2matrix(expr.name)
     if isinstance(ret, MatrixExpr):
@@ -328,7 +328,7 @@ def _remove_trivial_dims(expr):
     return expr, []
 
 
-@_remove_trivial_dims.register(ArrayTensorProduct) # type: ignore
+@_remove_trivial_dims.register(ArrayTensorProduct)
 def _(expr: ArrayTensorProduct):
     # Recognize expressions like [x, y] with shape (k, 1, k, 1) as `x*y.T`.
     # The matrix expression has to be equivalent to the tensor product of the
@@ -403,7 +403,7 @@ def _(expr: ArrayTensorProduct):
     return newexpr, newremoved
 
 
-@_remove_trivial_dims.register(ArrayAdd) # type: ignore
+@_remove_trivial_dims.register(ArrayAdd)
 def _(expr: ArrayAdd):
     rec = [_remove_trivial_dims(arg) for arg in expr.args]
     newargs, removed = zip(*rec)
@@ -412,7 +412,7 @@ def _(expr: ArrayAdd):
     return _a2m_add(*newargs), removed[0]
 
 
-@_remove_trivial_dims.register(PermuteDims) # type: ignore
+@_remove_trivial_dims.register(PermuteDims)
 def _(expr: PermuteDims):
     subexpr, subremoved = _remove_trivial_dims(expr.expr)
     p = expr.permutation.array_form
@@ -429,7 +429,7 @@ def _(expr: PermuteDims):
     return newexpr, premoved
 
 
-@_remove_trivial_dims.register(ArrayContraction) # type: ignore
+@_remove_trivial_dims.register(ArrayContraction)
 def _(expr: ArrayContraction):
     new_expr, removed0 = _array_contraction_to_diagonal_multiple_identity(expr)
     if new_expr != expr:
@@ -483,21 +483,21 @@ def _remove_diagonalized_identity_matrices(expr: ArrayDiagonal):
     return editor.to_array_contraction(), removed
 
 
-@_remove_trivial_dims.register(ArrayDiagonal) # type: ignore
+@_remove_trivial_dims.register(ArrayDiagonal)
 def _(expr: ArrayDiagonal):
     newexpr, removed = _remove_trivial_dims(expr.expr)
     shifts = list(accumulate([0] + [1 if i in removed else 0 for i in range(get_rank(expr.expr))]))
-    new_diag_indices = {i: tuple(j for j in i if j not in removed) for i in expr.diagonal_indices}
-    for old_diag_tuple, new_diag_tuple in new_diag_indices.items():
+    new_diag_indices_map = {i: tuple(j for j in i if j not in removed) for i in expr.diagonal_indices}
+    for old_diag_tuple, new_diag_tuple in new_diag_indices_map.items():
         if len(new_diag_tuple) == 1:
             removed = [i for i in removed if i not in old_diag_tuple]
-    new_diag_indices = [tuple(j - shifts[j] for j in i) for i in new_diag_indices.values()]  # type: ignore
+    new_diag_indices = [tuple(j - shifts[j] for j in i) for i in new_diag_indices_map.values()]
     rank = get_rank(expr.expr)
     removed = ArrayDiagonal._push_indices_up(expr.diagonal_indices, removed, rank)
     removed = sorted({i for i in removed})
     # If there are single axes to diagonalize remaining, it means that their
     # corresponding dimension has been removed, they no longer need diagonalization:
-    new_diag_indices = [i for i in new_diag_indices if len(i) > 0]  # type: ignore
+    new_diag_indices = [i for i in new_diag_indices if len(i) > 0]
     if len(new_diag_indices) > 0:
         newexpr2 = _array_diagonal(newexpr, *new_diag_indices, allow_trivial_diags=True)
     else:
@@ -510,7 +510,7 @@ def _(expr: ArrayDiagonal):
         return newexpr2, removed
 
 
-@_remove_trivial_dims.register(ElementwiseApplyFunction) # type: ignore
+@_remove_trivial_dims.register(ElementwiseApplyFunction)
 def _(expr: ElementwiseApplyFunction):
     subexpr, removed = _remove_trivial_dims(expr.expr)
     if subexpr.shape == (1, 1):
@@ -519,7 +519,7 @@ def _(expr: ElementwiseApplyFunction):
     return ElementwiseApplyFunction(expr.function, subexpr), []
 
 
-@_remove_trivial_dims.register(ArrayElementwiseApplyFunc) # type: ignore
+@_remove_trivial_dims.register(ArrayElementwiseApplyFunc)
 def _(expr: ArrayElementwiseApplyFunc):
     subexpr, removed = _remove_trivial_dims(expr.expr)
     return ArrayElementwiseApplyFunc(expr.function, subexpr), removed
@@ -956,7 +956,7 @@ def _array_contraction_to_diagonal_multiple_identity(expr: ArrayContraction):
                 flag = True
                 break
             free_pos = list(range(*editor.get_absolute_free_range(id1)))[0]
-            editor._track_permutation[-1].append(free_pos) # type: ignore
+            editor._track_permutation[-1].append(free_pos)
             id1.element = None
             flag = False
             break
@@ -970,7 +970,7 @@ def _array_contraction_to_diagonal_multiple_identity(expr: ArrayContraction):
     for j, e in enumerate(editor.args_with_ind):
         if e.element is None:
             editor._track_permutation[j] = None # type: ignore
-    editor._track_permutation = [i for i in editor._track_permutation if i is not None] # type: ignore
+    editor._track_permutation = [i for i in editor._track_permutation if i is not None]
     # Renumber permutation array form in order to deal with deleted positions:
     remap = {e: i for i, e in enumerate(sorted({k for j in editor._track_permutation for k in j}))}
     editor._track_permutation = [[remap[j] for j in i] for i in editor._track_permutation]


Code review comments:
diff_hunk: @@ -1571,7 +1571,7 @@ def __init__(self, base_array: typing.Union[ArrayContraction, ArrayDiagonal, Arr
                 args_with_ind[arg_pos].indices[rel_pos] = i
         self.args_with_ind: List[_ArgE] = args_with_ind
         self.number_of_contraction_indices: int = len(contraction_indices)
-        self._track_permutation: Optional[List[List[int]]] = None
+        self._track_permutation: List[List[int]] = None # type: ignore
path: sympy/tensor/array/expressions/array_expressions.py
text: @user1:
Would `List[List[int]] | NoneType` allow the use of `None` here without ignore?

@author:
Maybe. I'll check...

@author:
That gives
```console
$ mypy sympy
sympy/tensor/array/expressions/array_expressions.py:1574: error: X | Y syntax for unions requires Python 3.10
sympy/tensor/array/expressions/array_expressions.py:1574: error: Name "NoneType" is not defined
```


@user1:
Reading the docs, it looks like it is exactly what `Optional` does in the first place: https://docs.python.org/3/library/typing.html#typing.Optional 

Why was that changed? It feels like a step backwards. 

@author:
I'm not sure exactly how to type this given that before use it will always become non-None. If you have Optional here then mypy will complain every time this is used:
```
$ mypy sympy
sympy/tensor/array/expressions/conv_array_to_matrix.py:959: error: Value of type "Optional[List[List[int]]]" is not indexable
sympy/tensor/array/expressions/conv_array_to_matrix.py:973: error: Item "None" of "Optional[List[List[int]]]" has no attribute "__iter__" (not iterable)
Found 2 errors in 1 file (checked 1455 source files)
```

@user1:
I see, it seems that mypy simply doesn't allow you to do this unless you handle "None" in the code explicitly everywhere even if you know it will never be `None`.

@author:
I'll just add back the type: ignore. This class is too complicated for me to figure out how to clean it up. (I don't think it needs to be complicated but the code is really messy)

@user1:
```diff
diff --git a/sympy/tensor/array/expressions/array_expressions.py b/sympy/tensor/array/expressions/array_expressions.py
index 75573cbb32..c42c53babb 100644
--- a/sympy/tensor/array/expressions/array_expressions.py
+++ b/sympy/tensor/array/expressions/array_expressions.py
@@ -1571,7 +1571,7 @@ def __init__(self, base_array: typing.Union[ArrayContraction, ArrayDiagonal, Arr
                 args_with_ind[arg_pos].indices[rel_pos] = i
         self.args_with_ind: List[_ArgE] = args_with_ind
         self.number_of_contraction_indices: int = len(contraction_indices)
-        self._track_permutation: Optional[List[List[int]]] = None
+        self._track_permutation: List[List[int]] = [[]]

         mapping = _get_mapping_from_subranks(base_array.subranks)

@@ -1673,7 +1673,7 @@ def to_array_contraction(self):
         contraction_indices = self.get_contraction_indices()
         expr = _array_contraction(_array_tensor_product(*args), *contraction_indices)
         expr2 = _array_diagonal(expr, *diag_indices_filtered)
-        if self._track_permutation is not None:
+        if any(self._track_permutation):
             permutation2 = _af_invert([j for i in self._track_permutation for j in i])
             expr2 = _permute_dims(expr2, permutation2)

@@ -1755,8 +1755,8 @@ def track_permutation_start(self):
     def track_permutation_merge(self, destination: _ArgE, from_element: _ArgE):
         index_destination = self.args_with_ind.index(destination)
         index_element = self.args_with_ind.index(from_element)
-        self._track_permutation[index_destination].extend(self._track_permutation[index_element]) # type: ignore
-        self._track_permutation.pop(index_element) # type: ignore
+        self._track_permutation[index_destination].extend(self._track_permutation[index_element])
+        self._track_permutation.pop(index_element)

     def get_absolute_free_range(self, arg: _ArgE) -> typing.Tuple[int, int]:
         """
```
It seems to pass `mypy`, `bin/test sympy/tensor`, `bin/doctest/ sympy/tensor`, but haven't done the full tests

@author:
There is this which might be affected:
https://github.com/sympy/sympy/blob/c43550c31ef6355c49c4461e90a23e9629291b93/sympy/tensor/array/expressions/array_expressions.py#L1676

@user1:
The reason why it has that is because it uses `self._track_permutation` in a list comprehension, so the `any(self._track_permutation)` check should identify the uninitialized list.

Alternative is to use what mypy seems to imply:
```diff
diff --git a/sympy/tensor/array/expressions/array_expressions.py b/sympy/tensor/array/expressions/array_expressions.py
index 75573cbb32..079a1febe6 100644
--- a/sympy/tensor/array/expressions/array_expressions.py
+++ b/sympy/tensor/array/expressions/array_expressions.py
@@ -1755,8 +1755,9 @@ def track_permutation_start(self):
     def track_permutation_merge(self, destination: _ArgE, from_element: _ArgE):
         index_destination = self.args_with_ind.index(destination)
         index_element = self.args_with_ind.index(from_element)
-        self._track_permutation[index_destination].extend(self._track_permutation[index_element]) # type: ignore
-        self._track_permutation.pop(index_element) # type: ignore
+        if self._track_permutation is not None:
+            self._track_permutation[index_destination].extend(self._track_permutation[index_element])
+            self._track_permutation.pop(index_element)

     def get_absolute_free_range(self, arg: _ArgE) -> typing.Tuple[int, int]:
         """
```


@author:
This code is too messy for me to try and make changes to it. I don't really understand what it does and I don't know how well tested it is so I'd rather just leave the `type: ignore` there and let someone who wants to improve this code fix it. At least I don't want to smuggle a bug in with a PR that has a large number of trivial changes.

@user1:
Ok, fair enough.

diff_hunk: @@ -283,23 +283,23 @@ def _(expr: PermuteDims):
             p2 = permuted[2*i+1]
             if p1 // 2 != p2 // 2:
                 return _permute_dims(mat_mul_lines, permutation)
-            pos = p1 // 2
+            posi = p1 // 2
path: sympy/tensor/array/expressions/conv_array_to_matrix.py
text: @user1:
Why store this in a variable at all, it gets used only once due to the if-else (unless it gets moved up)

@author:
I'm not sure I understand, It gets used in either of two branches below.

@user1:
Right, it gets used in either branch, so only once. It's not exactly a complicated expression, so why not just put `p1 // 2`` directly where `posi` is used? 

@author:
There can be good reasons for naming something even if it is only used once but since this isn't a particularly good name anyway I'll do that.