@@ -40,6 +40,8 @@ def run(cli_args: Sequence[str]) -> int: # noqa: C901
4040 cli_opts = {
4141 k : v for k , v in vars (arg_parser .parse_args (cli_args )).items () if v is not None
4242 }
43+ cli_core_opts , cli_plugin_opts = separate_core_and_plugin_opts (cli_opts )
44+
4345 if not cli_opts ["paths" ]:
4446 print_paragraphs (["No files have been passed in. Doing nothing." ])
4547 return 0
@@ -57,7 +59,13 @@ def run(cli_args: Sequence[str]) -> int: # noqa: C901
5759 except InvalidConfError as e :
5860 print_error (str (e ))
5961 return 1
60- opts : Mapping = {** DEFAULT_OPTS , ** toml_opts , ** cli_opts }
62+
63+ opts : Mapping = {** DEFAULT_OPTS , ** toml_opts , ** cli_core_opts }
64+ for plugin_id , plugin_opts in cli_plugin_opts .items ():
65+ if plugin_id in opts ["plugin" ]:
66+ opts ["plugin" ][plugin_id ] |= plugin_opts
67+ else :
68+ opts ["plugin" ][plugin_id ] = plugin_opts
6169
6270 if sys .version_info >= (3 , 13 ): # pragma: >=3.13 cover
6371 if is_excluded (path , opts ["exclude" ], toml_path , "exclude" in cli_opts ):
@@ -182,10 +190,38 @@ def make_arg_parser(
182190 )
183191 for plugin in parser_extensions .values ():
184192 if hasattr (plugin , "add_cli_options" ):
193+ # TODO: deprecate in favor of add_cli_argument_group
185194 plugin .add_cli_options (parser )
195+ for plugin_id , plugin in parser_extensions .items ():
196+ if hasattr (plugin , "add_cli_argument_group" ):
197+ group = parser .add_argument_group (title = f"{ plugin_id } plugin" )
198+ plugin .add_cli_argument_group (group )
199+ for action in group ._group_actions :
200+ action .dest = f"plugin.{ plugin_id } .{ action .dest } "
186201 return parser
187202
188203
204+ def separate_core_and_plugin_opts (opts : Mapping ) -> tuple [dict , dict ]:
205+ """Move dotted keys like 'plugin.gfm.some_key' to a separate mapping.
206+
207+ Return a tuple of two mappings. First is for core CLI options, the
208+ second for plugin options. E.g. 'plugin.gfm.some_key' belongs to the
209+ second mapping under {"gfm": {"some_key": <value>}}.
210+ """
211+ cli_core_opts = {}
212+ cli_plugin_opts : dict = {}
213+ for k , v in opts .items ():
214+ if k .startswith ("plugin." ):
215+ _ , plugin_id , plugin_conf_key = k .split ("." , maxsplit = 2 )
216+ if plugin_id in cli_plugin_opts :
217+ cli_plugin_opts [plugin_id ][plugin_conf_key ] = v
218+ else :
219+ cli_plugin_opts [plugin_id ] = {plugin_conf_key : v }
220+ else :
221+ cli_core_opts [k ] = v
222+ return cli_core_opts , cli_plugin_opts
223+
224+
189225class InvalidPath (Exception ):
190226 """Exception raised when a path does not exist."""
191227
0 commit comments