11from __future__ import annotations
22
33from pathlib import Path
4- from typing import Iterable
4+ from typing import TypeVar
5+ from functools import wraps
56from argparse import Namespace
6- from warnings import catch_warnings , filterwarnings
7+ from collections .abc import Callable , Iterable
8+ from typing_extensions import ParamSpec , Concatenate
79
810import click
911from alembic .script import Script
1214from .config import plugin_config
1315from .migrate import AlembicConfig
1416
17+ _P = ParamSpec ("_P" )
18+ _R = TypeVar ("_R" )
19+
1520
1621@click .group ()
1722@click .option (
3540)
3641@click .option ("-q" , "--quite" , is_flag = True , help = "不要输出日志到标准输出" )
3742@click .pass_context
38- def orm (
39- ctx : click .Context , config : Path , name : str , x : tuple [str , ...], quite : bool
40- ) -> None :
43+ def orm (ctx : click .Context , config : Path , name : str , ** _ ) -> None :
4144 ctx .show_default = True
42- use_tempdir = ctx .invoked_subcommand in ("revision" , "merge" , "edit" )
4345
4446 if isinstance (plugin_config .alembic_config , AlembicConfig ):
4547 ctx .obj = plugin_config .alembic_config
4648 else :
47- ctx .obj = AlembicConfig (
48- config , name , cmd_opts = Namespace (** ctx .params ), use_tempdir = use_tempdir
49- )
49+ cmd_opts = Namespace (** ctx .params )
50+
51+ if ctx .invoked_subcommand :
52+ arguments = []
53+ options = []
54+
55+ for param in globals ()[ctx .invoked_subcommand ].params :
56+ if isinstance (param , click .Argument ):
57+ arguments .append (param .name )
58+ elif isinstance (param , click .Option ):
59+ options .append (param .name )
60+
61+ cmd_opts .cmd = (
62+ getattr (migrate , ctx .invoked_subcommand ),
63+ arguments ,
64+ options ,
65+ )
66+
67+ ctx .obj = AlembicConfig (config , name , cmd_opts = cmd_opts )
5068
5169 ctx .call_on_close (ctx .obj .close )
52- if use_tempdir :
53- ctx .with_resource (catch_warnings ())
54- filterwarnings ("ignore" , r"Revision \w* is present more than once" , UserWarning )
70+
71+
72+ def update_cmd_opts (
73+ f : Callable [Concatenate [AlembicConfig , _P ], _R ]
74+ ) -> Callable [_P , _R ]:
75+ @wraps (f )
76+ @click .pass_context
77+ def wrapper (ctx : click .Context , * args : _P .args , ** kwargs : _P .kwargs ) -> _R :
78+ for key , value in kwargs .items ():
79+ setattr (ctx .obj .cmd_opts , key , value )
80+
81+ return f (ctx .obj , * args , ** kwargs )
82+
83+ return wrapper
5584
5685
5786@orm .result_callback ()
@@ -65,7 +94,7 @@ def move_script(config_: AlembicConfig, scripts: Iterable[Script] | None, **_) -
6594
6695
6796@orm .command ("list_templates" )
68- @click . pass_obj
97+ @update_cmd_opts
6998def list_templates (* args , ** kwargs ) -> None :
7099 """列出所有可用的模板."""
71100
@@ -80,7 +109,7 @@ def list_templates(*args, **kwargs) -> None:
80109)
81110@click .option ("-t" , "--template" , default = "generic" , help = "使用的迁移环境模板" )
82111@click .option ("--package" , is_flag = True , help = "在脚本目录和版本目录中创建 __init__.py 文件" )
83- @click . pass_obj
112+ @update_cmd_opts
84113def init (* args , ** kwargs ) -> None :
85114 """初始化脚本目录."""
86115
@@ -101,15 +130,15 @@ def init(*args, **kwargs) -> None:
101130)
102131@click .option ("--rev-id" , help = "指定而不是使用生成的迁移 ID" )
103132@click .option ("--depends-on" , help = "依赖的迁移" )
104- @click . pass_obj
133+ @update_cmd_opts
105134def revision (* args , ** kwargs ) -> Iterable [Script ]:
106135 """创建一个新迁移脚本."""
107136
108137 return migrate .revision (* args , ** kwargs )
109138
110139
111140@orm .command ()
112- @click . pass_obj
141+ @update_cmd_opts
113142def check (* args , ** kwargs ) -> None :
114143 """检查数据库是否与模型定义一致."""
115144
@@ -121,7 +150,7 @@ def check(*args, **kwargs) -> None:
121150@click .option ("-m" , "--message" , help = "描述" )
122151@click .option ("--branch-label" , help = "分支标签" )
123152@click .option ("--rev-id" , help = "指定而不是使用生成的迁移 ID" )
124- @click . pass_obj
153+ @update_cmd_opts
125154def merge (* args , ** kwargs ) -> Iterable [Script ]:
126155 """合并多个迁移.创建一个新的迁移脚本."""
127156
@@ -132,7 +161,7 @@ def merge(*args, **kwargs) -> Iterable[Script]:
132161@click .argument ("revision" , required = False )
133162@click .option ("--sql" , is_flag = True , help = "以 SQL 的形式输出迁移脚本" )
134163@click .option ("--tag" , help = "一个任意的字符串, 可在自定义的 env.py 中使用" )
135- @click . pass_obj
164+ @update_cmd_opts
136165def upgrade (* args , ** kwargs ) -> None :
137166 """升级到较新版本."""
138167
@@ -143,7 +172,7 @@ def upgrade(*args, **kwargs) -> None:
143172@click .argument ("revision" )
144173@click .option ("--sql" , is_flag = True , help = "以 SQL 的形式输出迁移脚本" )
145174@click .option ("--tag" , help = "一个任意的字符串, 可在自定义的 env.py 中使用" )
146- @click . pass_obj
175+ @update_cmd_opts
147176def downgrade (* args , ** kwargs ) -> None :
148177 """回退到先前版本."""
149178
@@ -152,7 +181,7 @@ def downgrade(*args, **kwargs) -> None:
152181
153182@orm .command ()
154183@click .argument ("revision" , required = False )
155- @click . pass_obj
184+ @update_cmd_opts
156185def sync (* args , ** kwargs ) -> None :
157186 """同步数据库模式 (仅用于开发)."""
158187
@@ -161,7 +190,7 @@ def sync(*args, **kwargs) -> None:
161190
162191@orm .command ()
163192@click .argument ("revs" , nargs = - 1 )
164- @click . pass_obj
193+ @update_cmd_opts
165194def show (* args , ** kwargs ) -> None :
166195 """显示迁移的信息."""
167196
@@ -172,7 +201,7 @@ def show(*args, **kwargs) -> None:
172201@click .option ("-r" , "--rev-range" , required = False , help = "范围" )
173202@click .option ("-v" , "--verbose" , is_flag = True , help = "显示详细信息" )
174203@click .option ("-i" , "--indicate-current" , is_flag = True , help = "指示出当前迁移" )
175- @click . pass_obj
204+ @update_cmd_opts
176205def history (* args , ** kwargs ) -> None :
177206 """显示迁移的历史."""
178207
@@ -182,7 +211,7 @@ def history(*args, **kwargs) -> None:
182211@orm .command ()
183212@click .option ("-v" , "--verbose" , is_flag = True , help = "显示详细信息" )
184213@click .option ("--resolve-dependencies" , is_flag = True , help = "将依赖的迁移视作父迁移" )
185- @click . pass_obj
214+ @update_cmd_opts
186215def heads (* args , ** kwargs ) -> None :
187216 """显示所有的分支头."""
188217
@@ -191,7 +220,7 @@ def heads(*args, **kwargs) -> None:
191220
192221@orm .command ()
193222@click .option ("-v" , "--verbose" , is_flag = True , help = "显示详细信息" )
194- @click . pass_obj
223+ @update_cmd_opts
195224def branches (* args , ** kwargs ) -> None :
196225 """显示所有的分支."""
197226
@@ -200,7 +229,7 @@ def branches(*args, **kwargs) -> None:
200229
201230@orm .command ()
202231@click .option ("-v" , "--verbose" , is_flag = True , help = "显示详细信息" )
203- @click . pass_obj
232+ @update_cmd_opts
204233def current (* args , ** kwargs ) -> None :
205234 """显示当前的迁移."""
206235
@@ -212,7 +241,7 @@ def current(*args, **kwargs) -> None:
212241@click .option ("--sql" , is_flag = True , help = "以 SQL 的形式输出迁移脚本" )
213242@click .option ("--tag" , help = "一个任意的字符串, 可在自定义的 env.py 中使用" )
214243@click .option ("--purge" , is_flag = True , help = "在标记前清空数据库版本表" )
215- @click . pass_obj
244+ @update_cmd_opts
216245def stamp (* args , ** kwargs ) -> None :
217246 """将数据库标记为特定的迁移版本, 不运行任何迁移."""
218247
@@ -221,7 +250,7 @@ def stamp(*args, **kwargs) -> None:
221250
222251@orm .command ()
223252@click .argument ("rev" , default = "current" )
224- @click . pass_obj
253+ @update_cmd_opts
225254def edit (* args , ** kwargs ) -> None :
226255 """使用 $EDITOR 编辑迁移脚本."""
227256
@@ -230,7 +259,7 @@ def edit(*args, **kwargs) -> None:
230259
231260@orm .command ("ensure_version" )
232261@click .option ("--sql" , is_flag = True , help = "以 SQL 的形式输出迁移脚本" )
233- @click . pass_obj
262+ @update_cmd_opts
234263def ensure_version (* args , ** kwargs ) -> None :
235264 """创建版本表."""
236265
0 commit comments