pglift-cli 1.6.0__tar.gz → 1.7.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (26) hide show
  1. {pglift_cli-1.6.0 → pglift_cli-1.7.0}/PKG-INFO +5 -3
  2. {pglift_cli-1.6.0 → pglift_cli-1.7.0}/pyproject.toml +2 -1
  3. {pglift_cli-1.6.0 → pglift_cli-1.7.0}/src/pglift_cli/base.py +4 -1
  4. {pglift_cli-1.6.0 → pglift_cli-1.7.0}/src/pglift_cli/database.py +53 -39
  5. {pglift_cli-1.6.0 → pglift_cli-1.7.0}/src/pglift_cli/instance.py +39 -25
  6. {pglift_cli-1.6.0 → pglift_cli-1.7.0}/src/pglift_cli/main.py +2 -2
  7. {pglift_cli-1.6.0 → pglift_cli-1.7.0}/src/pglift_cli/model.py +151 -49
  8. {pglift_cli-1.6.0 → pglift_cli-1.7.0}/src/pglift_cli/pgbackrest/__init__.py +3 -3
  9. {pglift_cli-1.6.0 → pglift_cli-1.7.0}/src/pglift_cli/pgconf.py +13 -9
  10. {pglift_cli-1.6.0 → pglift_cli-1.7.0}/src/pglift_cli/postgres.py +1 -1
  11. {pglift_cli-1.6.0 → pglift_cli-1.7.0}/src/pglift_cli/prometheus.py +2 -2
  12. pglift_cli-1.7.0/src/pglift_cli/py.typed +0 -0
  13. {pglift_cli-1.6.0 → pglift_cli-1.7.0}/src/pglift_cli/role.py +60 -27
  14. {pglift_cli-1.6.0 → pglift_cli-1.7.0}/src/pglift_cli/util.py +86 -44
  15. {pglift_cli-1.6.0 → pglift_cli-1.7.0}/.gitignore +0 -0
  16. {pglift_cli-1.6.0 → pglift_cli-1.7.0}/README.md +0 -0
  17. {pglift_cli-1.6.0 → pglift_cli-1.7.0}/hatch.toml +0 -0
  18. {pglift_cli-1.6.0 → pglift_cli-1.7.0}/src/pglift_cli/__init__.py +0 -0
  19. {pglift_cli-1.6.0 → pglift_cli-1.7.0}/src/pglift_cli/__main__.py +0 -0
  20. {pglift_cli-1.6.0 → pglift_cli-1.7.0}/src/pglift_cli/_settings.py +0 -0
  21. {pglift_cli-1.6.0 → pglift_cli-1.7.0}/src/pglift_cli/_site.py +0 -0
  22. {pglift_cli-1.6.0 → pglift_cli-1.7.0}/src/pglift_cli/console.py +0 -0
  23. {pglift_cli-1.6.0 → pglift_cli-1.7.0}/src/pglift_cli/hookspecs.py +0 -0
  24. {pglift_cli-1.6.0 → pglift_cli-1.7.0}/src/pglift_cli/patroni.py +0 -0
  25. {pglift_cli-1.6.0 → pglift_cli-1.7.0}/src/pglift_cli/pgbackrest/repo_path.py +0 -0
  26. {pglift_cli-1.6.0 → pglift_cli-1.7.0}/src/pglift_cli/pm.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: pglift_cli
3
- Version: 1.6.0
3
+ Version: 1.7.0
4
4
  Summary: Command-line interface for pglift
5
5
  Project-URL: Documentation, https://pglift.readthedocs.io/
6
6
  Project-URL: Source, https://gitlab.com/dalibo/pglift/
@@ -31,7 +31,8 @@ Requires-Dist: pyyaml>=6.0.1
31
31
  Requires-Dist: rich>=11.0.0
32
32
  Provides-Extra: dev
33
33
  Requires-Dist: anyio; extra == 'dev'
34
- Requires-Dist: mypy>=1.10.0; extra == 'dev'
34
+ Requires-Dist: mypy!=1.11.*,>=1.10.0; (python_version < '3.10') and extra == 'dev'
35
+ Requires-Dist: mypy>=1.10.0; (python_version >= '3.10') and extra == 'dev'
35
36
  Requires-Dist: patroni[etcd]>=2.1.5; extra == 'dev'
36
37
  Requires-Dist: port-for; extra == 'dev'
37
38
  Requires-Dist: prysk[pytest-plugin]>=0.14.0; extra == 'dev'
@@ -48,7 +49,8 @@ Requires-Dist: pytest; extra == 'test'
48
49
  Requires-Dist: pytest-cov; extra == 'test'
49
50
  Requires-Dist: trustme; extra == 'test'
50
51
  Provides-Extra: typing
51
- Requires-Dist: mypy>=1.10.0; extra == 'typing'
52
+ Requires-Dist: mypy!=1.11.*,>=1.10.0; (python_version < '3.10') and extra == 'typing'
53
+ Requires-Dist: mypy>=1.10.0; (python_version >= '3.10') and extra == 'typing'
52
54
  Requires-Dist: types-pyyaml>=6.0.12.10; extra == 'typing'
53
55
  Description-Content-Type: text/markdown
54
56
 
@@ -57,7 +57,8 @@ test = [
57
57
  "trustme",
58
58
  ]
59
59
  typing = [
60
- "mypy >= 1.10.0",
60
+ "mypy >= 1.10.0 ; python_version >= '3.10'",
61
+ "mypy >= 1.10.0, != 1.11.* ; python_version < '3.10'",
61
62
  "types-PyYAML >= 6.0.12.10",
62
63
  ]
63
64
  dev = [
@@ -38,7 +38,10 @@ class CLIGroup(click.Group):
38
38
 
39
39
  def list_commands(self, context: click.Context) -> list[str]:
40
40
  main_commands = super().list_commands(context)
41
- plugins_commands: list[str] = sorted(g.name for g in hooks(_site.PLUGIN_MANAGER, h.command)) # type: ignore[misc]
41
+ plugins_commands: list[str] = sorted(
42
+ g.name # type: ignore[misc]
43
+ for g in hooks(_site.PLUGIN_MANAGER, h.command)
44
+ )
42
45
  return main_commands + self.submodules + plugins_commands
43
46
 
44
47
  def get_command(self, context: click.Context, name: str) -> click.Command | None:
@@ -30,7 +30,7 @@ from .util import (
30
30
  manifest_option,
31
31
  model_dump,
32
32
  output_format_option,
33
- pass_instance,
33
+ pass_postgresql_instance,
34
34
  print_argspec,
35
35
  print_json_for,
36
36
  print_schema,
@@ -63,11 +63,11 @@ def cli(**kwargs: Any) -> None:
63
63
 
64
64
  @cli.command("create")
65
65
  @model.as_parameters(interface.Database, "create")
66
- @pass_instance
66
+ @pass_postgresql_instance
67
67
  @click.pass_obj
68
68
  @async_command
69
69
  async def create(
70
- obj: Obj, instance: system.Instance, database: interface.Database
70
+ obj: Obj, instance: system.PostgreSQLInstance, database: interface.Database
71
71
  ) -> None:
72
72
  """Create a database in a PostgreSQL instance"""
73
73
  with obj.lock, audit():
@@ -79,13 +79,13 @@ async def create(
79
79
 
80
80
 
81
81
  @cli.command("alter") # type: ignore[arg-type]
82
- @model.as_parameters(interface.Database, "update", parse_model=False)
82
+ @model.as_parameters(interface.Database, "update")
83
83
  @click.argument("dbname")
84
- @pass_instance
84
+ @pass_postgresql_instance
85
85
  @click.pass_obj
86
86
  @async_command
87
87
  async def alter(
88
- obj: Obj, instance: system.Instance, dbname: str, **changes: Any
88
+ obj: Obj, instance: system.PostgreSQLInstance, dbname: str, **changes: Any
89
89
  ) -> None:
90
90
  """Alter a database in a PostgreSQL instance"""
91
91
  with obj.lock, audit():
@@ -100,14 +100,14 @@ async def alter(
100
100
  @manifest_option
101
101
  @output_format_option
102
102
  @dry_run_option
103
- @pass_instance
103
+ @pass_postgresql_instance
104
104
  @click.pass_obj
105
105
  @async_command
106
106
  async def apply(
107
107
  obj: Obj,
108
- instance: system.Instance,
108
+ instance: system.PostgreSQLInstance,
109
109
  data: ManifestData,
110
- output_format: OutputFormat,
110
+ output_format: OutputFormat | None,
111
111
  dry_run: bool,
112
112
  ) -> None:
113
113
  """Apply manifest as a database"""
@@ -118,38 +118,44 @@ async def apply(
118
118
  with obj.lock, audit():
119
119
  async with postgresql.running(instance):
120
120
  ret = await databases.apply(instance, database)
121
- if output_format == OutputFormat.json:
121
+ if output_format == "json":
122
122
  print_json_for(ret)
123
123
 
124
124
 
125
125
  @cli.command("get")
126
126
  @output_format_option
127
127
  @click.argument("name")
128
- @pass_instance
128
+ @pass_postgresql_instance
129
129
  @async_command
130
130
  async def get(
131
- instance: system.Instance, name: str, output_format: OutputFormat
131
+ instance: system.PostgreSQLInstance, name: str, output_format: OutputFormat | None
132
132
  ) -> None:
133
133
  """Get the description of a database"""
134
134
  async with postgresql.running(instance):
135
135
  db = await databases.get(instance, name)
136
- if output_format == OutputFormat.json:
136
+ if output_format == "json":
137
137
  print_json_for(model_dump(db))
138
138
  else:
139
- print_table_for(
140
- [db],
141
- functools.partial(model_dump, exclude={"extensions", "schemas"}),
142
- box=None,
143
- )
139
+ print_table_for([db], functools.partial(model_dump, mode="pretty"), box=None)
144
140
 
145
141
 
146
142
  @cli.command("list")
147
143
  @output_format_option
144
+ @click.option(
145
+ "-x",
146
+ "--exclude-database",
147
+ "exclude_dbnames",
148
+ multiple=True,
149
+ help="Database to exclude from listing.",
150
+ )
148
151
  @click.argument("dbname", nargs=-1)
149
- @pass_instance
152
+ @pass_postgresql_instance
150
153
  @async_command
151
154
  async def ls(
152
- instance: system.Instance, dbname: Sequence[str], output_format: OutputFormat
155
+ instance: system.PostgreSQLInstance,
156
+ dbname: Sequence[str],
157
+ exclude_dbnames: Sequence[str],
158
+ output_format: OutputFormat | None,
153
159
  ) -> None:
154
160
  """List databases (all or specified ones)
155
161
 
@@ -157,8 +163,10 @@ async def ls(
157
163
  """
158
164
 
159
165
  async with postgresql.running(instance):
160
- dbs = await databases.ls(instance, dbnames=dbname)
161
- if output_format == OutputFormat.json:
166
+ dbs = await databases.ls(
167
+ instance, dbnames=dbname, exclude_dbnames=exclude_dbnames
168
+ )
169
+ if output_format == "json":
162
170
  print_json_for([model_dump(db) for db in dbs])
163
171
  else:
164
172
  print_table_for(dbs, model_dump)
@@ -166,11 +174,13 @@ async def ls(
166
174
 
167
175
  @cli.command("drop")
168
176
  @model.as_parameters(interface.DatabaseDropped, "create")
169
- @pass_instance
177
+ @pass_postgresql_instance
170
178
  @click.pass_obj
171
179
  @async_command
172
180
  async def drop(
173
- obj: Obj, instance: system.Instance, databasedropped: interface.DatabaseDropped
181
+ obj: Obj,
182
+ instance: system.PostgreSQLInstance,
183
+ databasedropped: interface.DatabaseDropped,
174
184
  ) -> None:
175
185
  """Drop a database"""
176
186
  with obj.lock, audit():
@@ -183,14 +193,14 @@ async def drop(
183
193
  @click.option("-r", "--role", "roles", multiple=True, help="Role to inspect")
184
194
  @click.option("--default", "defaults", is_flag=True, help="Display default privileges")
185
195
  @output_format_option
186
- @pass_instance
196
+ @pass_postgresql_instance
187
197
  @async_command
188
198
  async def list_privileges(
189
- instance: system.Instance,
199
+ instance: system.PostgreSQLInstance,
190
200
  name: str,
191
201
  roles: Sequence[str],
192
202
  defaults: bool,
193
- output_format: OutputFormat,
203
+ output_format: OutputFormat | None,
194
204
  ) -> None:
195
205
  """List privileges on a database."""
196
206
  async with postgresql.running(instance):
@@ -201,7 +211,7 @@ async def list_privileges(
201
211
  )
202
212
  except ValueError as e:
203
213
  raise click.ClickException(str(e)) from None
204
- if output_format == OutputFormat.json:
214
+ if output_format == "json":
205
215
  print_json_for([model_dump(p) for p in prvlgs])
206
216
  else:
207
217
  print_table_for(prvlgs, model_dump)
@@ -220,14 +230,14 @@ async def list_privileges(
220
230
  help="Database to not run command on",
221
231
  )
222
232
  @output_format_option
223
- @pass_instance
233
+ @pass_postgresql_instance
224
234
  @async_command
225
235
  async def run(
226
- instance: system.Instance,
236
+ instance: system.PostgreSQLInstance,
227
237
  sql_command: str,
228
238
  dbnames: Sequence[str],
229
239
  exclude_dbnames: Sequence[str],
230
- output_format: OutputFormat,
240
+ output_format: OutputFormat | None,
231
241
  ) -> None:
232
242
  """Run given command on databases of a PostgreSQL instance"""
233
243
  async with postgresql.running(instance):
@@ -240,7 +250,7 @@ async def run(
240
250
  )
241
251
  except psycopg.ProgrammingError as e:
242
252
  raise click.ClickException(str(e)) from None
243
- if output_format == OutputFormat.json:
253
+ if output_format == "json":
244
254
  print_json_for(result)
245
255
  else:
246
256
  for dbname, rows in result.items():
@@ -256,9 +266,11 @@ async def run(
256
266
  help="Write dump file(s) to DIRECTORY instead of default dumps directory.",
257
267
  )
258
268
  @click.argument("dbname")
259
- @pass_instance
269
+ @pass_postgresql_instance
260
270
  @async_command
261
- async def dump(instance: system.Instance, dbname: str, output: Path | None) -> None:
271
+ async def dump(
272
+ instance: system.PostgreSQLInstance, dbname: str, output: Path | None
273
+ ) -> None:
262
274
  """Dump a database"""
263
275
  async with postgresql.running(instance):
264
276
  await databases.dump(instance, dbname, output)
@@ -267,17 +279,19 @@ async def dump(instance: system.Instance, dbname: str, output: Path | None) -> N
267
279
  @cli.command("dumps")
268
280
  @click.argument("dbname", nargs=-1)
269
281
  @output_format_option
270
- @pass_instance
282
+ @pass_postgresql_instance
271
283
  @async_command
272
284
  async def dumps(
273
- instance: system.Instance, dbname: Sequence[str], output_format: OutputFormat
285
+ instance: system.PostgreSQLInstance,
286
+ dbname: Sequence[str],
287
+ output_format: OutputFormat | None,
274
288
  ) -> None:
275
289
  """List the database dumps
276
290
 
277
291
  Only dumps created in the default dumps directory are listed.
278
292
  """
279
293
  values = [asdict(dump) async for dump in databases.dumps(instance, dbnames=dbname)]
280
- if output_format == OutputFormat.json:
294
+ if output_format == "json":
281
295
  print_json_for(values)
282
296
  else:
283
297
  dbnames = ", ".join(dbname) if dbname else "all databases"
@@ -287,10 +301,10 @@ async def dumps(
287
301
  @cli.command("restore")
288
302
  @click.argument("dump_id")
289
303
  @click.argument("targetdbname", required=False)
290
- @pass_instance
304
+ @pass_postgresql_instance
291
305
  @async_command
292
306
  async def restore(
293
- instance: system.Instance, dump_id: str, targetdbname: str | None
307
+ instance: system.PostgreSQLInstance, dump_id: str, targetdbname: str | None
294
308
  ) -> None:
295
309
  """Restore a database dump
296
310
 
@@ -26,9 +26,8 @@ from pglift.settings import default_postgresql_version
26
26
  from pglift.settings._postgresql import PostgreSQLVersion
27
27
  from pglift.types import Operation, Status, validation_context
28
28
 
29
- from . import _site
29
+ from . import _site, model
30
30
  from . import hookspecs as h
31
- from . import model
32
31
  from .util import (
33
32
  ManifestData,
34
33
  Obj,
@@ -42,6 +41,7 @@ from .util import (
42
41
  manifest_option,
43
42
  model_dump,
44
43
  output_format_option,
44
+ postgresql_instance_identifier,
45
45
  print_argspec,
46
46
  print_json_for,
47
47
  print_schema,
@@ -115,13 +115,13 @@ async def create(obj: Obj, instance: interface.Instance, drop_on_error: bool) ->
115
115
 
116
116
  @cli.command("alter") # type: ignore[arg-type]
117
117
  @instance_identifier(nargs=1)
118
- @model.as_parameters(_site.INSTANCE_MODEL, "update", parse_model=False)
118
+ @model.as_parameters(_site.INSTANCE_MODEL, "update")
119
119
  @click.pass_obj
120
120
  @async_command
121
121
  async def alter(obj: Obj, instance: system.Instance, **changes: Any) -> None:
122
122
  """Alter PostgreSQL INSTANCE"""
123
123
  with obj.lock, audit():
124
- status = await postgresql.status(instance)
124
+ status = await postgresql.status(instance.postgresql)
125
125
  manifest = await instances._get(instance, status)
126
126
  values = manifest.model_dump(by_alias=True, exclude={"settings"})
127
127
  values = deep_update(values, changes)
@@ -141,7 +141,7 @@ async def alter(obj: Obj, instance: system.Instance, **changes: Any) -> None:
141
141
  @click.pass_obj
142
142
  @async_command
143
143
  async def apply(
144
- obj: Obj, data: ManifestData, output_format: OutputFormat, dry_run: bool
144
+ obj: Obj, data: ManifestData, output_format: OutputFormat | None, dry_run: bool
145
145
  ) -> None:
146
146
  """Apply manifest as a PostgreSQL instance"""
147
147
  name, version = data["name"], data.get("version")
@@ -159,7 +159,7 @@ async def apply(
159
159
  else:
160
160
  with obj.lock, audit():
161
161
  ret = await instances.apply(_site.SETTINGS, instance)
162
- if output_format == OutputFormat.json:
162
+ if output_format == "json":
163
163
  print_json_for(ret)
164
164
 
165
165
 
@@ -177,14 +177,14 @@ async def promote(obj: Obj, instance: system.Instance) -> None:
177
177
  @output_format_option
178
178
  @instance_identifier(nargs=1)
179
179
  @async_command
180
- async def get(instance: system.Instance, output_format: OutputFormat) -> None:
180
+ async def get(instance: system.Instance, output_format: OutputFormat | None) -> None:
181
181
  """Get the description of PostgreSQL INSTANCE.
182
182
 
183
183
  Unless --output-format is specified, 'settings' and 'state' fields are not
184
184
  shown as well as 'standby' information if INSTANCE is not a standby.
185
185
  """
186
186
  i = await instances.get(instance)
187
- if output_format == OutputFormat.json:
187
+ if output_format == "json":
188
188
  print_json_for(model_dump(i))
189
189
  else:
190
190
  exclude = {
@@ -194,7 +194,7 @@ async def get(instance: system.Instance, output_format: OutputFormat) -> None:
194
194
  "wal_directory",
195
195
  "powa",
196
196
  }
197
- if not instance.standby:
197
+ if not instance.postgresql.standby:
198
198
  exclude.add("standby")
199
199
  print_table_for([i], partial(model_dump, exclude=exclude), box=None)
200
200
 
@@ -207,10 +207,12 @@ async def get(instance: system.Instance, output_format: OutputFormat) -> None:
207
207
  )
208
208
  @output_format_option
209
209
  @async_command
210
- async def ls(version: PostgreSQLVersion | None, output_format: OutputFormat) -> None:
210
+ async def ls(
211
+ version: PostgreSQLVersion | None, output_format: OutputFormat | None
212
+ ) -> None:
211
213
  """List the available instances"""
212
214
  items = [i async for i in instances.ls(_site.SETTINGS, version=version)]
213
- if output_format == OutputFormat.json:
215
+ if output_format == "json":
214
216
  print_json_for([model_dump(m) for m in items])
215
217
  else:
216
218
  print_table_for(items, model_dump)
@@ -290,12 +292,12 @@ async def stop(
290
292
 
291
293
 
292
294
  @cli.command("reload")
293
- @instance_identifier(nargs=-1)
295
+ @postgresql_instance_identifier(nargs=-1)
294
296
  @click.option("--all", "all_instances", is_flag=True, help="Reload all instances.")
295
297
  @click.pass_obj
296
298
  @async_command
297
299
  async def reload(
298
- obj: Obj, instance: tuple[system.Instance, ...], all_instances: bool
300
+ obj: Obj, instance: tuple[system.PostgreSQLInstance, ...], all_instances: bool
299
301
  ) -> None:
300
302
  """Reload PostgreSQL INSTANCE"""
301
303
  with obj.lock, audit():
@@ -362,7 +364,7 @@ def shell(instance: system.Instance, shell: str) -> None:
362
364
  @cli.command("env")
363
365
  @instance_identifier(nargs=1)
364
366
  @output_format_option
365
- def env(instance: system.Instance, output_format: OutputFormat) -> None:
367
+ def env(instance: system.Instance, output_format: OutputFormat | None) -> None:
366
368
  """Output environment variables suitable to handle to PostgreSQL INSTANCE.
367
369
 
368
370
  This can be injected in shell using:
@@ -370,7 +372,7 @@ def env(instance: system.Instance, output_format: OutputFormat) -> None:
370
372
  export $(pglift instance env myinstance)
371
373
  """
372
374
  instance_env = instances.env_for(instance, path=True)
373
- if output_format == OutputFormat.json:
375
+ if output_format == "json":
374
376
  print_json_for(instance_env)
375
377
  else:
376
378
  for key, value in sorted(instance_env.items()):
@@ -379,8 +381,8 @@ def env(instance: system.Instance, output_format: OutputFormat) -> None:
379
381
 
380
382
  @cli.command("logs")
381
383
  @click.option("--follow/--no-follow", "-f/", default=False, help="Follow log output.")
382
- @instance_identifier(nargs=1)
383
- def logs(instance: system.Instance, follow: bool) -> None:
384
+ @postgresql_instance_identifier(nargs=1)
385
+ def logs(instance: system.PostgreSQLInstance, follow: bool) -> None:
384
386
  """Output PostgreSQL logs of INSTANCE.
385
387
 
386
388
  This assumes that the PostgreSQL instance is configured to use file-based
@@ -400,7 +402,7 @@ def logs(instance: system.Instance, follow: bool) -> None:
400
402
 
401
403
 
402
404
  @cli.command("privileges")
403
- @instance_identifier(nargs=1)
405
+ @postgresql_instance_identifier(nargs=1)
404
406
  @click.option(
405
407
  "-d",
406
408
  "--database",
@@ -413,11 +415,11 @@ def logs(instance: system.Instance, follow: bool) -> None:
413
415
  @output_format_option
414
416
  @async_command
415
417
  async def list_privileges(
416
- instance: system.Instance,
418
+ instance: system.PostgreSQLInstance,
417
419
  databases: Sequence[str],
418
420
  roles: Sequence[str],
419
421
  defaults: bool,
420
- output_format: OutputFormat,
422
+ output_format: OutputFormat | None,
421
423
  ) -> None:
422
424
  """List privileges on INSTANCE's databases."""
423
425
  async with postgresql.running(instance):
@@ -427,7 +429,7 @@ async def list_privileges(
427
429
  )
428
430
  except ValueError as e:
429
431
  raise click.ClickException(str(e)) from None
430
- if output_format == OutputFormat.json:
432
+ if output_format == "json":
431
433
  print_json_for([model_dump(p) for p in prvlgs])
432
434
  else:
433
435
  if defaults:
@@ -455,8 +457,9 @@ async def list_privileges(
455
457
  "--jobs",
456
458
  required=False,
457
459
  type=click.INT,
458
- help="Number of simultaneous processes or threads to use (from pg_upgrade).",
460
+ help="Number of simultaneous processes or threads to use (from pg_upgrade). Deprecated. Use extra options instead.",
459
461
  )
462
+ @click.argument("extra_opts", nargs=-1, type=click.UNPROCESSED)
460
463
  @click.pass_obj
461
464
  @async_command
462
465
  async def upgrade(
@@ -466,12 +469,23 @@ async def upgrade(
466
469
  newname: str | None,
467
470
  port: int | None,
468
471
  jobs: int | None,
472
+ extra_opts: tuple[str, ...],
469
473
  ) -> None:
470
- """Upgrade INSTANCE using pg_upgrade"""
474
+ """Upgrade INSTANCE using pg_upgrade
475
+
476
+ Extra options can be passed to the pg_upgrade command. They may need to be prefixed
477
+ with -- to separate them from the current command options when confusion arises. When using
478
+ extra options, providing the instance identifier is required.
479
+ """
471
480
  with obj.lock, audit():
472
- await postgresql.check_status(instance, Status.not_running)
481
+ await postgresql.check_status(instance.postgresql, Status.not_running)
473
482
  async with task.async_transaction():
474
483
  new_instance = await instances.upgrade(
475
- instance, version=newversion, name=newname, port=port, jobs=jobs
484
+ instance,
485
+ version=newversion,
486
+ name=newname,
487
+ port=port,
488
+ jobs=jobs,
489
+ extra_opts=extra_opts,
476
490
  )
477
491
  await instances.start(new_instance)
@@ -207,7 +207,7 @@ def site_settings(
207
207
  /,
208
208
  defaults: bool | None,
209
209
  schema: bool,
210
- output_format: OutputFormat,
210
+ output_format: OutputFormat | None,
211
211
  ) -> None:
212
212
  """Show site settings.
213
213
 
@@ -227,7 +227,7 @@ def site_settings(
227
227
  value = _site.SETTINGS.model_dump(
228
228
  mode="json", exclude_defaults=defaults is False
229
229
  )
230
- if output_format == OutputFormat.json:
230
+ if output_format == "json":
231
231
  console.print_json(data=value)
232
232
  else:
233
233
  assert output_format is None