Skip to content

Commit 645e8ae

Browse files
ref: require stronger types for sentry.runner.commands.* (#68130)
<!-- Describe your PR here. -->
1 parent a8fe14c commit 645e8ae

27 files changed

+173
-115
lines changed

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -631,7 +631,7 @@ module = [
631631
"sentry.nodestore.models",
632632
"sentry.relay.config.metric_extraction",
633633
"sentry.reprocessing2",
634-
"sentry.runner.commands.backup",
634+
"sentry.runner.commands.*",
635635
"sentry.snuba.metrics.extraction",
636636
"sentry.tasks.on_demand_metrics",
637637
"sentry.tasks.reprocessing2",

src/sentry/runner/commands/config.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,14 @@
44

55

66
@click.group()
7-
def config():
7+
def config() -> None:
88
"Manage runtime config options."
99

1010

1111
@config.command()
1212
@click.argument("pattern", default="*", required=False)
1313
@configuration
14-
def list(pattern):
14+
def list(pattern: str) -> None:
1515
"List configuration options."
1616
from fnmatch import fnmatch
1717

@@ -26,7 +26,7 @@ def list(pattern):
2626
@click.option("--silent", "-q", default=False, is_flag=True, help="Suppress extraneous output.")
2727
@click.argument("option")
2828
@configuration
29-
def get(option, silent):
29+
def get(option: str, silent: bool) -> None:
3030
"Get a configuration option."
3131
from django.conf import settings
3232

@@ -58,7 +58,7 @@ def get(option, silent):
5858
@click.argument("key")
5959
@click.argument("value", required=False)
6060
@configuration
61-
def set(key, value, secret):
61+
def set(key: str, value: str | None, secret: bool) -> None:
6262
"Set a configuration option to a new value."
6363
from sentry import options
6464
from sentry.options import UpdateChannel
@@ -82,7 +82,7 @@ def set(key, value, secret):
8282
@click.option("--no-input", default=False, is_flag=True, help="Do not show confirmation.")
8383
@click.argument("option")
8484
@configuration
85-
def delete(option, no_input):
85+
def delete(option: str, no_input: bool) -> None:
8686
"Delete/unset a configuration option."
8787
from sentry import options
8888
from sentry.options.manager import UnknownOption
@@ -158,15 +158,15 @@ def dump(flags: int, only_set: bool, pretty_print: bool) -> None:
158158

159159

160160
@config.command(name="generate-secret-key")
161-
def generate_secret_key():
161+
def generate_secret_key() -> None:
162162
"Generate a new cryptographically secure secret key value."
163163
from sentry.runner.settings import generate_secret_key
164164

165165
click.echo(generate_secret_key())
166166

167167

168168
@config.command()
169-
def discover():
169+
def discover() -> None:
170170
"Print paths to config files."
171171
from sentry.runner.settings import discover_configs
172172

src/sentry/runner/commands/configoptions.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ def _attempt_update(
8080
@log_options()
8181
@click.pass_context
8282
@configuration
83-
def configoptions(ctx, dry_run: bool, file: str | None, hide_drift: bool) -> None:
83+
def configoptions(ctx: click.Context, dry_run: bool, file: str | None, hide_drift: bool) -> None:
8484
"""
8585
Makes changes to options in bulk starting from a yaml file.
8686
Contrarily to the `config` command, this is meant to perform
@@ -155,7 +155,7 @@ def configoptions(ctx, dry_run: bool, file: str | None, hide_drift: bool) -> Non
155155
@configoptions.command()
156156
@click.pass_context
157157
@configuration
158-
def patch(ctx) -> None:
158+
def patch(ctx: click.Context) -> None:
159159
"""
160160
Applies to the DB the option values found in the config file.
161161
Only the options present in the file are updated. No deletions
@@ -217,7 +217,7 @@ def patch(ctx) -> None:
217217
@configoptions.command()
218218
@click.pass_context
219219
@configuration
220-
def sync(ctx):
220+
def sync(ctx: click.Context) -> None:
221221
"""
222222
Synchronizes the content of the file with the DB. The source of
223223
truth is the config file, not the DB. If an option is missing in

src/sentry/runner/commands/createuser.py

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,28 @@
1+
from __future__ import annotations
2+
3+
from typing import TYPE_CHECKING
4+
15
import click
26

37
from sentry.runner.decorators import configuration
48

9+
if TYPE_CHECKING:
10+
from django.db.models.fields import Field
11+
12+
from sentry.models.user import User
13+
14+
15+
def _get_field(field_name: str) -> Field[str, str]:
16+
from django.db.models.fields import Field
517

6-
def _get_field(field_name):
718
from sentry.models.user import User
819

9-
return User._meta.get_field(field_name)
20+
ret = User._meta.get_field(field_name)
21+
assert isinstance(ret, Field), ret
22+
return ret
1023

1124

12-
def _get_email():
25+
def _get_email() -> list[str]:
1326
from django.core.exceptions import ValidationError
1427

1528
rv = click.prompt("Email")
@@ -20,7 +33,7 @@ def _get_email():
2033
raise click.ClickException("; ".join(e.messages))
2134

2235

23-
def _get_password():
36+
def _get_password() -> str:
2437
from django.core.exceptions import ValidationError
2538

2639
rv = click.prompt("Password", hide_input=True, confirmation_prompt=True)
@@ -31,11 +44,11 @@ def _get_password():
3144
raise click.ClickException("; ".join(e.messages))
3245

3346

34-
def _get_superuser():
47+
def _get_superuser() -> bool:
3548
return click.confirm("Should this user be a superuser?", default=False)
3649

3750

38-
def _set_superadmin(user):
51+
def _set_superadmin(user: User) -> None:
3952
"""
4053
superadmin role approximates superuser (model attribute) but leveraging
4154
Sentry's role system.
@@ -78,7 +91,16 @@ def _set_superadmin(user):
7891
"--force-update", default=False, is_flag=True, help="If true, will update existing users."
7992
)
8093
@configuration
81-
def createuser(emails, org_id, password, superuser, staff, no_password, no_input, force_update):
94+
def createuser(
95+
emails: list[str] | None,
96+
org_id: str | None,
97+
password: str | None,
98+
superuser: bool | None,
99+
staff: bool | None,
100+
no_password: bool,
101+
no_input: bool,
102+
force_update: bool,
103+
) -> None:
82104
"Create a new user."
83105

84106
from django.conf import settings

src/sentry/runner/commands/devservices.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ def ensure_interface(ports: dict[str, int | tuple[str, int]]) -> dict[str, tuple
166166
return rv
167167

168168

169-
def ensure_docker_cli_context(context: str):
169+
def ensure_docker_cli_context(context: str) -> None:
170170
# this is faster than running docker context use ...
171171
config_file = os.path.expanduser("~/.docker/config.json")
172172
config = {}

src/sentry/runner/commands/django.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
@click.argument("management_args", nargs=-1, type=click.UNPROCESSED)
88
@configuration
99
@click.pass_context
10-
def django(ctx, management_args):
10+
def django(ctx: click.Context, management_args: tuple[str, ...]) -> None:
1111
"Execute Django subcommands."
1212
from django.core.management import execute_from_command_line
1313

14-
execute_from_command_line(argv=[ctx.command_path] + list(management_args))
14+
execute_from_command_line(argv=[ctx.command_path, *management_args])

src/sentry/runner/commands/exec.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
)
2121
@click.option("-c", default="", help="Read script from string.")
2222
@click.argument("file", default=None, required=False)
23-
def exec_(c, file):
23+
def exec_(c: str, file: str | None) -> None:
2424
"""
2525
Execute a script.
2626

src/sentry/runner/commands/execfile.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
name="execfile", context_settings=dict(ignore_unknown_options=True, allow_extra_args=True)
99
)
1010
@click.argument("filename", required=True)
11-
def execfile(filename):
11+
def execfile(filename: str) -> None:
1212
"""Execute a script.
1313
1414
This is very similar to `exec`, with the following differences:
@@ -27,7 +27,7 @@ def execfile(filename):
2727
2828
- __file__ is set to the filename of the script.
2929
"""
30-
filename = pathlib.Path(filename)
30+
filename_src = pathlib.Path(filename).read_text()
3131
preamble = "\n".join(
3232
[
3333
"from sentry.runner import configure; configure()",
@@ -39,5 +39,5 @@ def execfile(filename):
3939
preamble_code = compile(preamble, filename, "exec")
4040
exec(preamble_code, script_globals, script_globals)
4141
sys.argv = sys.argv[1:]
42-
script_code = compile(filename.read_text(), filename, "exec")
42+
script_code = compile(filename_src, filename, "exec")
4343
exec(script_code, script_globals, script_globals)

src/sentry/runner/commands/files.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@
66

77

88
@click.group()
9-
def files():
9+
def files() -> None:
1010
"""Manage files from filestore."""
1111

1212

1313
@files.command()
1414
@click.argument("id", type=click.INT, metavar="FILE_ID")
1515
@configuration
16-
def get(id):
16+
def get(id: int) -> None:
1717
"""Fetch a file's contents by id."""
1818
from sentry.models.files.file import File
1919

@@ -33,7 +33,7 @@ def get(id):
3333
@click.argument("id", type=click.INT, metavar="FILE_ID")
3434
@click.option("--format", default="json", type=click.Choice(("json", "yaml")))
3535
@configuration
36-
def info(id, format):
36+
def info(id: int, format: str) -> None:
3737
"""Show a file's metadata by id."""
3838
from sentry.models.files.file import File
3939

src/sentry/runner/commands/help.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
@click.command()
55
@click.pass_context
6-
def help(ctx):
6+
def help(ctx: click.Context) -> None:
77
"Show this message and exit."
8+
assert ctx.parent is not None
89
click.echo(ctx.parent.get_help())

src/sentry/runner/commands/init.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@
1111
"--no-clobber", default=False, is_flag=True, help="Don't ask to overwrite existing config."
1212
)
1313
@click.argument("directory", required=False)
14-
@click.pass_context
15-
def init(ctx, dev, no_clobber, directory):
14+
def init(dev: bool, no_clobber: bool, directory: str) -> None:
1615
"Initialize new configuration directory."
1716
from sentry.runner.settings import discover_configs, generate_settings
1817

src/sentry/runner/commands/killswitches.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import itertools
22
import textwrap
3+
from typing import IO, Any
34

45
import click
56
import yaml
@@ -8,11 +9,11 @@
89

910

1011
@click.group()
11-
def killswitches():
12+
def killswitches() -> None:
1213
"Manage killswitches for ingestion pipeline."
1314

1415

15-
def _get_edit_template(killswitch_name: str, option_value) -> str:
16+
def _get_edit_template(killswitch_name: str, option_value: Any) -> str:
1617
from sentry import killswitches
1718

1819
comments = [
@@ -46,7 +47,7 @@ def _get_edit_template(killswitch_name: str, option_value) -> str:
4647
@click.argument("killswitch_name", required=True)
4748
@click.argument("outfile", type=click.File("w"), required=True)
4849
@configuration
49-
def _pull(killswitch_name, outfile):
50+
def _pull(killswitch_name: str, outfile: IO[str]) -> None:
5051
"""
5152
Save the current state of the given killswitch in a file.
5253
@@ -71,7 +72,7 @@ def _pull(killswitch_name, outfile):
7172
@click.argument("infile", type=click.File("r"), required=True)
7273
@click.option("--yes", is_flag=True, help="skip confirmation prompts, very dangerous")
7374
@configuration
74-
def _push(killswitch_name, infile, yes):
75+
def _push(killswitch_name: str, infile: IO[str], yes: bool) -> None:
7576
"""
7677
Write back a killswitch into the DB.
7778
@@ -118,7 +119,7 @@ def _push(killswitch_name, infile, yes):
118119

119120
@killswitches.command("list")
120121
@configuration
121-
def _list():
122+
def _list() -> None:
122123
"""
123124
List all killswitches and whether they are enabled (and how).
124125
"""

src/sentry/runner/commands/migrations.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
@click.group()
1010
@configuration
11-
def migrations():
11+
def migrations() -> None:
1212
from sentry.runner.initializer import monkeypatch_django_migrations
1313

1414
# Include our monkeypatches for migrations.
@@ -21,11 +21,8 @@ def migrations():
2121
@migrations.command()
2222
@click.argument("app_name")
2323
@click.argument("migration_name")
24-
@click.pass_context
25-
def run(ctx, app_name: str, migration_name: str) -> None:
24+
def run(app_name: str, migration_name: str) -> None:
2625
"Manually run a single data migration. Will error if migration is not post-deploy/dangerous"
27-
del ctx # assertion: unused argument
28-
2926
for connection_name in settings.DATABASES.keys():
3027
if settings.DATABASES[connection_name].get("REPLICA_OF", False):
3128
continue

src/sentry/runner/commands/openai.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# The sentry utils json cannot pretty print
2-
import json # noqa
2+
import json # noqa: S003
3+
from typing import IO
34

45
import click
56

@@ -8,7 +9,7 @@
89
@click.option("--event", type=click.File("r"))
910
@click.option("--model", default="gpt-3.5-turbo")
1011
@click.option("--dump-prompt", is_flag=True)
11-
def openai(event, model, dump_prompt):
12+
def openai(event: IO[str], model: str, dump_prompt: bool) -> None:
1213
"""
1314
Runs the OpenAI assistent against a JSON event payload.
1415
"""

src/sentry/runner/commands/performance.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ def performance() -> None:
2323
)
2424
@click.option("-v", "--verbose", count=True)
2525
@configuration
26-
def detect(filename, detector_class, verbose):
26+
def detect(filename: str, detector_class: str | None, verbose: int) -> None:
2727
"""
2828
Runs performance problem detection on event data in the supplied filename
2929
using default detector settings with every detector. Filename should be a
@@ -80,7 +80,7 @@ def detect(filename, detector_class, verbose):
8080
"-n", required=False, type=int, default=1000, help="Number of times to run detection."
8181
)
8282
@configuration
83-
def timeit(filename, detector_class, n):
83+
def timeit(filename: str, detector_class: str, n: int) -> None:
8484
"""
8585
Runs timing on performance problem detection on event data in the supplied
8686
filename and report results.
@@ -99,7 +99,7 @@ def timeit(filename, detector_class, n):
9999

100100
detector = performance_detection.__dict__[detector_class](settings, data)
101101

102-
def detect():
102+
def detect() -> None:
103103
performance_detection.run_detector_on_data(detector, data)
104104

105105
result = timeit.timeit(stmt=detect, number=n)

0 commit comments

Comments
 (0)