# Mypy Clean Slate
[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
[![PyPI Latest Release](https://img.shields.io/pypi/v/mypy-clean-slate.svg)](https://pypi.org/project/mypy-clean-slate/)
[![License](https://img.shields.io/pypi/l/mypy-clean-slate.svg)](https://github.com/geo7/mypy_clean_slate/blob/main/LICENSE)
[![image](https://img.shields.io/pypi/pyversions/mypy-clean-slate.svg)](https://pypi.python.org/pypi/mypy-clean-slate)
[![Actions status](https://github.com/geo7/mypy_clean_slate/workflows/CI/badge.svg)](https://github.com/geo7/mypy_clean_slate/actions)
CLI tool for providing a clean slate for mypy usage within a project
## Motivation
It can be difficult to get a large project to the point where `mypy --strict`
can be run on it. Rather than incrementally increasing the severity of mypy,
either overall or per module, `mypy_clean_slate` enables one to ignore all
previous errors so that `mypy --strict` (or similar) can be used almost
immediately. This enables all code written from that point on to be checked with
`mypy --strict` (or whichever flags are preferred), gradually removing the
`type: ignore` comments from that point onwards.
Often running `mypy_clean_slate` will cover all errors cleanly in a single pass,
but there are cases when not all error output is generated first time, and it
can be necessary to run a couple of times, checking the diffs. Example of this
scenario is given.
By default `mypy_clean_slate` works by parsing the output of `mypy --strict` and
adding the relevant `type: ignore[code]` to each line, though custom flags can
be passed to mypy instead. Only errors from the report are considered, notes are
not handled. Meaning something such as `error: Function is missing a type
annotation [no-untyped-def]` will have `# type: ignore[no-untyped-def]`
appended to the end of the line, whereas `note: (Skipping most remaining errors
due to unresolved imports or missing stubs; fix these first)` will be ignored.
Errors relating to unused ignores (which might occur if code changes after
adding the initial ignore) can also be handled.
# Installation
```bash
pip install mypy-clean-slate
```
# Usage
[comment]: # (CLI help split)
```
usage: mypy_clean_slate [options]
CLI tool for providing a clean slate for mypy usage within a project.
Default expectation is to want to get a project into a state that it
will pass mypy when run with `--strict`, if this isn't the case custom
flags can be passed to mypy via the `--mypy_flags` argument.
options:
-h, --help show this help message and exit
-r, --generate_mypy_error_report
Generate 'mypy_error_report.txt' in the cwd.
-p PATH_TO_CODE, --path_to_code PATH_TO_CODE
Where code is that needs report generating for it.
-a, --add_type_ignore
Add "# type: ignore[<error-code>]" to suppress all raised mypy errors.
--remove_unused Remove unused instances of "# type: ignore[<error-code>]" if raised as an error by mypy.
-o MYPY_REPORT_OUTPUT, --mypy_report_output MYPY_REPORT_OUTPUT
File to save report output to (default is mypy_error_report.txt)
--mypy_flags MYPY_FLAGS
Custom flags to pass to mypy (provide them as a single string, default is to use --strict)
```
[comment]: # (CLI help split)
See `./tests/test_mypy_clean_slate.py` for some examples with before/after.
# Examples
## Simple example
Given a project with only:
```txt
➜ simple_example git:(master) ✗ tree
.
`-- simple.py
0 directories, 1 file
```
Containing:
```python
# simple.py
def f(x):
return x + 1
```
The report can be generated, and `simple.py` updated, using `mypy_clean_slate -ra`, resulting in:
```python
def f(x): # type: ignore[no-untyped-def]
return x + 1
```
And `mypy --strict` will now pass.
## Project example, using `pingouin`
Project `pingouin` is located at: https://github.com/raphaelvallat/pingouin, and
commit `ea8b5605a1776aaa0e89dd5c0e3df4320950fb38` is used for this example.
`mypy_clean_slate` needs to be run a couple of times here.
First, generate report and apply `type: ignore[<error code>]`
```sh
mypy_clean_slate -ra
```
Looking at a subset of `git diff`:
```diff
(venv) ➜ pingouin git:(master) ✗ git diff | grep 'type' | head
+import sphinx_bootstrap_theme # type: ignore[import]
+from outdated import warn_if_outdated # type: ignore[import]
+import numpy as np # type: ignore[import]
+from scipy.integrate import quad # type: ignore[import]
+ from scipy.special import gamma, betaln, hyp2f1 # type: ignore[import]
+ from mpmath import hyp3f2 # type: ignore[import]
+ from scipy.stats import binom # type: ignore[import]
+import numpy as np # type: ignore[import]
+from scipy.stats import norm # type: ignore[import]
+import numpy as np # type: ignore[import]
```
Changes are added and committed with message `'mypy_clean_slate first pass'` (commit message used makes no functional difference), and the report re-generated:
```bash
mypy_clean_slate -r
```
Which reports `Found 1107 errors in 39 files (checked 42 source files)`. So, re-running `mypy_clean_slate`
```bash
mypy_clean_slate -a
```
And looking again at the diff:
```diff
(venv) ➜ pingouin git:(master) ✗ gd | grep 'type' | head
+latex_elements = { # type: ignore[var-annotated]
+def setup(app): # type: ignore[no-untyped-def]
@@ -27,4 +27,4 @@ from outdated import warn_if_outdated # type: ignore[import]
+set_default_options() # type: ignore[no-untyped-call]
+def _format_bf(bf, precision=3, trim='0'): # type: ignore[no-untyped-def]
if type(bf) == str:
+def bayesfactor_ttest(t, nx, ny=None, paired=False, tail='two-sided', r=.707): # type: ignore[no-untyped-def]
+ def fun(g, t, n, r, df): # type: ignore[no-untyped-def]
+def bayesfactor_pearson(r, n, tail='two-sided', method='ly', kappa=1.): # type: ignore[no-untyped-def]
+ def fun(g, r, n): # type: ignore[no-untyped-def]
```
Committing these with `'mypy_clean_slate second pass'`, and re-running `mypy_clean_slate -r` outputs the following:
```txt
(venv) ➜ pingouin git:(master) ✗ cat mypy_error_report.txt
Success: no issues found in 42 source files
```
Can now rebase / amend commits as necessary, but could now update CI/pre-commit or whatever to use `mypy --strict` (or a subset of its flags) going forwards.
# Handling of existing comments and `pylint`
Lines which contain existing comments such as:
```python
def ThisFunction(something): # pylint: disable=invalid-name
return f"this is {something}"
```
Will be updated to:
```python
def ThisFunction(something): # type: ignore[no-untyped-def] # pylint: disable=invalid-name
return f"this is {something}"
```
As the `type:` comment needs to precede pylints.
# Issues
## Generating report
The report generation is pretty straightforward, `mypy . --strict --show-error-codes`, so might not be worth having as part of this script. The user can generate the report to a text file and just pass the path to that as an argument.
## Handling `-> None`
Report output for functions which don't return is pretty consistent, so these could be automated if considered worth it.
## Integration with other tooling
I've tried to consider `pylint` comments, but no doubt there are many other arguments for different tools which aren't taken into consideration.
Raw data
{
"_id": null,
"home_page": "https://github.com/geo7/mypy_clean_slate",
"name": "mypy_clean_slate",
"maintainer": "",
"docs_url": null,
"requires_python": ">=3.9,<4.0",
"maintainer_email": "",
"keywords": "mypy,typing,typehint,type-hint",
"author": "George Lenton",
"author_email": "georgelenton@gmail.com",
"download_url": "https://files.pythonhosted.org/packages/12/11/4fd24508ebad069fe0107263139e723e601a9ca5be11769e1730039350de/mypy_clean_slate-0.3.3.tar.gz",
"platform": null,
"description": "# Mypy Clean Slate\n\n\n[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)\n[![PyPI Latest Release](https://img.shields.io/pypi/v/mypy-clean-slate.svg)](https://pypi.org/project/mypy-clean-slate/)\n[![License](https://img.shields.io/pypi/l/mypy-clean-slate.svg)](https://github.com/geo7/mypy_clean_slate/blob/main/LICENSE)\n[![image](https://img.shields.io/pypi/pyversions/mypy-clean-slate.svg)](https://pypi.python.org/pypi/mypy-clean-slate)\n[![Actions status](https://github.com/geo7/mypy_clean_slate/workflows/CI/badge.svg)](https://github.com/geo7/mypy_clean_slate/actions)\n\n\n\nCLI tool for providing a clean slate for mypy usage within a project\n\n## Motivation\n\nIt can be difficult to get a large project to the point where `mypy --strict`\ncan be run on it. Rather than incrementally increasing the severity of mypy,\neither overall or per module, `mypy_clean_slate` enables one to ignore all\nprevious errors so that `mypy --strict` (or similar) can be used almost\nimmediately. This enables all code written from that point on to be checked with\n`mypy --strict` (or whichever flags are preferred), gradually removing the\n`type: ignore` comments from that point onwards.\n\nOften running `mypy_clean_slate` will cover all errors cleanly in a single pass,\nbut there are cases when not all error output is generated first time, and it\ncan be necessary to run a couple of times, checking the diffs. Example of this\nscenario is given.\n\nBy default `mypy_clean_slate` works by parsing the output of `mypy --strict` and\nadding the relevant `type: ignore[code]` to each line, though custom flags can\nbe passed to mypy instead. Only errors from the report are considered, notes are\nnot handled. Meaning something such as `error: Function is missing a type\nannotation [no-untyped-def]` will have `# type: ignore[no-untyped-def]`\nappended to the end of the line, whereas `note: (Skipping most remaining errors\ndue to unresolved imports or missing stubs; fix these first)` will be ignored.\nErrors relating to unused ignores (which might occur if code changes after\nadding the initial ignore) can also be handled.\n\n# Installation\n\n```bash\npip install mypy-clean-slate\n```\n\n# Usage\n\n[comment]: # (CLI help split)\n\n```\nusage: mypy_clean_slate [options]\n\nCLI tool for providing a clean slate for mypy usage within a project.\n\nDefault expectation is to want to get a project into a state that it\nwill pass mypy when run with `--strict`, if this isn't the case custom\nflags can be passed to mypy via the `--mypy_flags` argument.\n\noptions:\n -h, --help show this help message and exit\n -r, --generate_mypy_error_report\n Generate 'mypy_error_report.txt' in the cwd.\n -p PATH_TO_CODE, --path_to_code PATH_TO_CODE\n Where code is that needs report generating for it.\n -a, --add_type_ignore\n Add \"# type: ignore[<error-code>]\" to suppress all raised mypy errors.\n --remove_unused Remove unused instances of \"# type: ignore[<error-code>]\" if raised as an error by mypy.\n -o MYPY_REPORT_OUTPUT, --mypy_report_output MYPY_REPORT_OUTPUT\n File to save report output to (default is mypy_error_report.txt)\n --mypy_flags MYPY_FLAGS\n Custom flags to pass to mypy (provide them as a single string, default is to use --strict)\n```\n\n[comment]: # (CLI help split)\n\nSee `./tests/test_mypy_clean_slate.py` for some examples with before/after.\n\n\n\n# Examples\n\n## Simple example\n\nGiven a project with only:\n\n```txt\n\u279c simple_example git:(master) \u2717 tree\n.\n`-- simple.py\n\n0 directories, 1 file\n```\n\nContaining:\n\n```python\n# simple.py\ndef f(x):\n return x + 1\n```\n\nThe report can be generated, and `simple.py` updated, using `mypy_clean_slate -ra`, resulting in:\n\n\n```python\ndef f(x): # type: ignore[no-untyped-def]\n return x + 1\n```\n\nAnd `mypy --strict` will now pass.\n\n## Project example, using `pingouin`\n\nProject `pingouin` is located at: https://github.com/raphaelvallat/pingouin, and\ncommit `ea8b5605a1776aaa0e89dd5c0e3df4320950fb38` is used for this example.\n`mypy_clean_slate` needs to be run a couple of times here.\n\nFirst, generate report and apply `type: ignore[<error code>]`\n\n```sh\nmypy_clean_slate -ra\n```\n\nLooking at a subset of `git diff`:\n\n```diff\n\n(venv) \u279c pingouin git:(master) \u2717 git diff | grep 'type' | head\n+import sphinx_bootstrap_theme # type: ignore[import]\n+from outdated import warn_if_outdated # type: ignore[import]\n+import numpy as np # type: ignore[import]\n+from scipy.integrate import quad # type: ignore[import]\n+ from scipy.special import gamma, betaln, hyp2f1 # type: ignore[import]\n+ from mpmath import hyp3f2 # type: ignore[import]\n+ from scipy.stats import binom # type: ignore[import]\n+import numpy as np # type: ignore[import]\n+from scipy.stats import norm # type: ignore[import]\n+import numpy as np # type: ignore[import]\n```\n\nChanges are added and committed with message `'mypy_clean_slate first pass'` (commit message used makes no functional difference), and the report re-generated:\n\n```bash\nmypy_clean_slate -r\n```\n\nWhich reports `Found 1107 errors in 39 files (checked 42 source files)`. So, re-running `mypy_clean_slate`\n\n```bash\nmypy_clean_slate -a\n```\n\nAnd looking again at the diff:\n\n```diff\n\n(venv) \u279c pingouin git:(master) \u2717 gd | grep 'type' | head\n+latex_elements = { # type: ignore[var-annotated]\n+def setup(app): # type: ignore[no-untyped-def]\n@@ -27,4 +27,4 @@ from outdated import warn_if_outdated # type: ignore[import]\n+set_default_options() # type: ignore[no-untyped-call]\n+def _format_bf(bf, precision=3, trim='0'): # type: ignore[no-untyped-def]\nif type(bf) == str:\n+def bayesfactor_ttest(t, nx, ny=None, paired=False, tail='two-sided', r=.707): # type: ignore[no-untyped-def]\n+ def fun(g, t, n, r, df): # type: ignore[no-untyped-def]\n+def bayesfactor_pearson(r, n, tail='two-sided', method='ly', kappa=1.): # type: ignore[no-untyped-def]\n+ def fun(g, r, n): # type: ignore[no-untyped-def]\n```\n\n Committing these with `'mypy_clean_slate second pass'`, and re-running `mypy_clean_slate -r` outputs the following:\n\n```txt\n(venv) \u279c pingouin git:(master) \u2717 cat mypy_error_report.txt\nSuccess: no issues found in 42 source files\n```\n\nCan now rebase / amend commits as necessary, but could now update CI/pre-commit or whatever to use `mypy --strict` (or a subset of its flags) going forwards.\n\n\n# Handling of existing comments and `pylint`\n\nLines which contain existing comments such as:\n\n```python\ndef ThisFunction(something): # pylint: disable=invalid-name\n return f\"this is {something}\"\n```\n\nWill be updated to:\n\n```python\ndef ThisFunction(something): # type: ignore[no-untyped-def] # pylint: disable=invalid-name\n return f\"this is {something}\"\n```\n\nAs the `type:` comment needs to precede pylints.\n\n# Issues\n\n## Generating report\n\nThe report generation is pretty straightforward, `mypy . --strict --show-error-codes`, so might not be worth having as part of this script. The user can generate the report to a text file and just pass the path to that as an argument.\n\n## Handling `-> None`\n\nReport output for functions which don't return is pretty consistent, so these could be automated if considered worth it.\n\n## Integration with other tooling\n\nI've tried to consider `pylint` comments, but no doubt there are many other arguments for different tools which aren't taken into consideration.\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "CLI tool for providing a clean slate for mypy usage within a project.",
"version": "0.3.3",
"project_urls": {
"Homepage": "https://github.com/geo7/mypy_clean_slate"
},
"split_keywords": [
"mypy",
"typing",
"typehint",
"type-hint"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "38f11d8369e1c1e77d682d95bac260944b48f0b77115603b5e1a1c0d33b807d1",
"md5": "4e88061b274b792bda391f612e44f6b3",
"sha256": "7f5b016961ad3f2d3bd640e43a574c3cb5d8839c8bb3976a47546113abb4c0bb"
},
"downloads": -1,
"filename": "mypy_clean_slate-0.3.3-py3-none-any.whl",
"has_sig": false,
"md5_digest": "4e88061b274b792bda391f612e44f6b3",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.9,<4.0",
"size": 9619,
"upload_time": "2024-03-11T11:46:23",
"upload_time_iso_8601": "2024-03-11T11:46:23.378241Z",
"url": "https://files.pythonhosted.org/packages/38/f1/1d8369e1c1e77d682d95bac260944b48f0b77115603b5e1a1c0d33b807d1/mypy_clean_slate-0.3.3-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "12114fd24508ebad069fe0107263139e723e601a9ca5be11769e1730039350de",
"md5": "ffb21cd8c7d5e352bda252fb959e7aaf",
"sha256": "2698de362f2b31dc9c813b459a6040a7317d3e48d8259b90efcdff1b9a830e54"
},
"downloads": -1,
"filename": "mypy_clean_slate-0.3.3.tar.gz",
"has_sig": false,
"md5_digest": "ffb21cd8c7d5e352bda252fb959e7aaf",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.9,<4.0",
"size": 9270,
"upload_time": "2024-03-11T11:46:25",
"upload_time_iso_8601": "2024-03-11T11:46:25.215462Z",
"url": "https://files.pythonhosted.org/packages/12/11/4fd24508ebad069fe0107263139e723e601a9ca5be11769e1730039350de/mypy_clean_slate-0.3.3.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-03-11 11:46:25",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "geo7",
"github_project": "mypy_clean_slate",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "mypy_clean_slate"
}