Browse Source

ref: ensure stronger typing list is sorted (#68128)

<!-- Describe your PR here. -->
anthony sottile 11 months ago
parent
commit
c1edfd1936

+ 5 - 0
.pre-commit-config.yaml

@@ -82,6 +82,11 @@ repos:
         entry: '(^# *mypy: *ignore-errors|^# *type: *ignore|\bno_type_check\b)'
         language: pygrep
         types: [python]
+      - id: sort-stronger-mypy-list
+        name: sort stronger mypy list
+        entry: python3 -m tools.mypy_helpers.sort_stronger_modules
+        files: ^pyproject\.toml$
+        language: python
       - id: prevent-push
         name: prevent pushing master
         stages: [pre-push]

+ 2 - 1
Makefile

@@ -161,7 +161,8 @@ test-monolith-dbs:
 
 test-tools:
 	@echo "--> Running tools tests"
-	pytest -c /dev/null --confcutdir tests/tools tests/tools -vv --cov=tools --cov=tests/tools --cov-report="xml:.artifacts/tools.coverage.xml"
+	@# bogus configuration to force vanilla pytest
+	python3 -m pytest -c setup.cfg --confcutdir tests/tools tests/tools -vv --cov=tools --cov=tests/tools --cov-report="xml:.artifacts/tools.coverage.xml"
 	@echo ""
 
 # JavaScript relay tests are meant to be run within Symbolicator test suite, as they are parametrized to verify both processing pipelines during migration process.

+ 8 - 8
pyproject.toml

@@ -615,7 +615,7 @@ disable_error_code = [
 ]
 # end: sentry modules with typing issues
 
-# beginning: stronger typing
+# begin: stronger typing
 [[tool.mypy.overrides]]
 module = [
     "sentry.api.endpoints.issues.*",
@@ -623,24 +623,24 @@ module = [
     "sentry.buffer.redis",
     "sentry.eventstore.reprocessing.redis",
     "sentry.issues.related.*",
-    "sentry.utils.redis",
-    "sentry.utils.redis_metrics",
-    "sentry.utils.locking.backends.redis",
-    "sentry.tasks.on_demand_metrics",
     "sentry.nodestore.base",
-    "sentry.nodestore.models",
     "sentry.nodestore.bigtable.backend",
     "sentry.nodestore.django.backend",
     "sentry.nodestore.django.models",
     "sentry.nodestore.filesystem.backend",
-    "sentry.reprocessing2",
+    "sentry.nodestore.models",
     "sentry.relay.config.metric_extraction",
+    "sentry.reprocessing2",
     "sentry.runner.commands.backup",
     "sentry.snuba.metrics.extraction",
+    "sentry.tasks.on_demand_metrics",
     "sentry.tasks.reprocessing2",
+    "sentry.utils.locking.backends.redis",
+    "sentry.utils.redis",
+    "sentry.utils.redis_metrics",
     "tests.sentry.api.endpoints.issues.*",
-    "tests.sentry.tasks.test_on_demand_metrics",
     "tests.sentry.relay.config.test_metric_extraction",
+    "tests.sentry.tasks.test_on_demand_metrics",
 ]
 disallow_any_generics = true
 disallow_untyped_defs = true

+ 71 - 0
tests/tools/mypy_helpers/test_sort_stronger_modules.py

@@ -0,0 +1,71 @@
+from tools.mypy_helpers.sort_stronger_modules import main
+
+
+def test_sort_stronger_modules(tmp_path):
+    src = """\
+# before
+
+# begin: stronger typing
+[[tool.mypy.overrides]]
+module = [
+    "mod2",
+    "mod1",
+    "mod3",
+]
+some_setting = true
+# end: stronger typing
+
+# after
+"""
+    expected = """\
+# before
+
+# begin: stronger typing
+[[tool.mypy.overrides]]
+module = [
+    "mod1",
+    "mod2",
+    "mod3",
+]
+some_setting = true
+# end: stronger typing
+
+# after
+"""
+
+    f = tmp_path.joinpath("f")
+    f.write_text(src)
+
+    assert main((str(f),)) == 1
+
+    assert f.read_text() == expected
+
+    assert main((str(f),)) == 0
+
+
+def test_removes_duplicates(tmp_path):
+    src = """\
+# begin: stronger typing
+[[tool.mypy.overrides]]
+module = [
+    "mod1",
+    "mod1",
+]
+some_setting = true
+# end: stronger typing
+"""
+    expected = """\
+# begin: stronger typing
+[[tool.mypy.overrides]]
+module = [
+    "mod1",
+]
+some_setting = true
+# end: stronger typing
+"""
+    f = tmp_path.joinpath("f")
+    f.write_text(src)
+
+    assert main((str(f),)) == 1
+
+    assert f.read_text() == expected

+ 35 - 0
tools/mypy_helpers/sort_stronger_modules.py

@@ -0,0 +1,35 @@
+from __future__ import annotations
+
+import argparse
+from collections.abc import Sequence
+
+
+def main(argv: Sequence[str] | None = None) -> int:
+    parser = argparse.ArgumentParser()
+    parser.add_argument("filenames", nargs="*")
+    args = parser.parse_args(argv)
+
+    ret = 0
+    for filename in args.filenames:
+        with open(filename) as f:
+            contents = f.read()
+
+        rest = contents
+        b1, m1, rest = rest.partition("# begin: stronger typing\n")
+        b2, m2, rest = rest.partition("module = [\n")
+        b3, m3, rest = rest.partition("]\n")
+        b4, m4, rest = rest.partition("# end: stronger typing\n")
+
+        b3 = "".join(sorted(frozenset(b3.splitlines(True))))
+
+        new_contents = b1 + m1 + b2 + m2 + b3 + m3 + b4 + m4 + rest
+        if new_contents != contents:
+            with open(filename, "w") as f:
+                f.write(new_contents)
+            ret = 1
+
+    return ret
+
+
+if __name__ == "__main__":
+    raise SystemExit(main())