4141import threading
4242import time
4343from code import InteractiveConsole
44- from collections import (
45- deque ,
46- namedtuple ,
47- )
44+ from collections import deque
4845from collections .abc import (
4946 Callable ,
5047 Iterable ,
6158 TYPE_CHECKING ,
6259 Any ,
6360 ClassVar ,
61+ NamedTuple ,
6462 TextIO ,
6563 TypeVar ,
6664 cast ,
@@ -219,8 +217,15 @@ def __init__(self) -> None:
219217 self .completer : Callable [[str , int ], str | None ] | None = None
220218
221219
222- # Contains data about a disabled command which is used to restore its original functions when the command is enabled
223- DisabledCommand = namedtuple ("DisabledCommand" , ["command_function" , "help_function" , "completer_function" ]) # noqa: PYI024
220+ class DisabledCommand (NamedTuple ):
221+ """Stores data about a disabled command.
222+
223+ This data is used to restore its functions when the command is enabled.
224+ """
225+
226+ command_func : BoundCommandFunc
227+ help_func : Callable [[], Any ] | None
228+ completer_func : BoundCompleter | None
224229
225230
226231class CommandParsers :
@@ -2448,12 +2453,12 @@ def _perform_completion(
24482453 completer_func = func_attr
24492454 else :
24502455 # There's no completer function, next see if the command uses argparse
2451- func = self .get_command_func (command )
2452- argparser = None if func is None else self .command_parsers .get (func )
2456+ command_func = self .get_command_func (command )
2457+ argparser = None if command_func is None else self .command_parsers .get (command_func )
24532458
2454- if func is not None and argparser is not None :
2459+ if command_func is not None and argparser is not None :
24552460 # Get arguments for complete()
2456- preserve_quotes = getattr (func , constants .CMD_ATTR_PRESERVE_QUOTES )
2461+ preserve_quotes = getattr (command_func , constants .CMD_ATTR_PRESERVE_QUOTES )
24572462 cmd_set = self .find_commandset_for_command (command )
24582463
24592464 # Create the argparse completer
@@ -2718,9 +2723,8 @@ def _get_commands_aliases_and_macros_choices(self) -> Choices:
27182723
27192724 # Add commands
27202725 for command in self .get_visible_commands ():
2721- # Get the command method
2722- func = getattr (self , constants .COMMAND_FUNC_PREFIX + command )
2723- description = strip_doc_annotations (func .__doc__ ).splitlines ()[0 ] if func .__doc__ else ""
2726+ command_func = cast (BoundCommandFunc , self .get_command_func (command ))
2727+ description = strip_doc_annotations (command_func .__doc__ ).splitlines ()[0 ] if command_func .__doc__ else ""
27242728 items .append (CompletionItem (command , display_meta = description ))
27252729
27262730 # Add aliases
@@ -3328,9 +3332,9 @@ def get_command_func(self, command: str) -> BoundCommandFunc | None:
33283332 :param command: the name of the command
33293333 :return: the bound function implementing the command, or None if not found
33303334 """
3331- func_name = constants .COMMAND_FUNC_PREFIX + command
3332- func = getattr (self , func_name , None )
3333- return cast (BoundCommandFunc , func ) if callable (func ) else None
3335+ command_func_name = constants .COMMAND_FUNC_PREFIX + command
3336+ command_func = getattr (self , command_func_name , None )
3337+ return cast (BoundCommandFunc , command_func ) if callable (command_func ) else None
33343338
33353339 def _get_command_category (self , func : BoundCommandFunc ) -> str :
33363340 """Determine the category for a command.
@@ -3363,8 +3367,8 @@ def onecmd(self, statement: Statement | str, *, add_to_history: bool = True) ->
33633367 if not isinstance (statement , Statement ):
33643368 statement = self ._input_line_to_statement (statement )
33653369
3366- func = self .get_command_func (statement .command )
3367- if func :
3370+ command_func = self .get_command_func (statement .command )
3371+ if command_func :
33683372 # Check to see if this command should be stored in history
33693373 if (
33703374 statement .command not in self .exclude_from_history
@@ -3375,7 +3379,7 @@ def onecmd(self, statement: Statement | str, *, add_to_history: bool = True) ->
33753379
33763380 try :
33773381 self .current_command = statement
3378- stop = func (statement )
3382+ stop = command_func (statement )
33793383 finally :
33803384 self .current_command = None
33813385
@@ -4219,7 +4223,9 @@ def complete_help_subcommands(
42194223 return Completions ()
42204224
42214225 # Check if this command uses argparse
4222- if (func := self .get_command_func (command )) is None or (argparser := self .command_parsers .get (func )) is None :
4226+ if (command_func := self .get_command_func (command )) is None or (
4227+ argparser := self .command_parsers .get (command_func )
4228+ ) is None :
42234229 return Completions ()
42244230
42254231 completer = argparse_completer .DEFAULT_AP_COMPLETER (argparser , self )
@@ -4245,8 +4251,8 @@ def _build_command_info(self) -> tuple[dict[str, list[str]], list[str]]:
42454251 help_topics .remove (command )
42464252
42474253 # Store the command within its category
4248- func = cast (BoundCommandFunc , self .get_command_func (command ))
4249- category = self ._get_command_category (func )
4254+ command_func = cast (BoundCommandFunc , self .get_command_func (command ))
4255+ category = self ._get_command_category (command_func )
42504256 cmds_cats .setdefault (category , []).append (command )
42514257
42524258 return cmds_cats , help_topics
@@ -4320,16 +4326,16 @@ def do_help(self, args: argparse.Namespace) -> None:
43204326 if help_func is not None :
43214327 help_func ()
43224328 else :
4323- # This is a defensive fallback in case someone disabled a command
4324- # without using disable_command() resulting in no help function .
4329+ # Handle potential case where command is disabled by manually editing
4330+ # self.disabled_commands instead of using disable_command().
43254331 self ._report_disabled_command_usage (message_to_print = f"{ args .command } is currently disabled." )
43264332 return
43274333
4328- func = self .get_command_func (args .command )
4329- argparser = None if func is None else self .command_parsers .get (func )
4334+ command_func = self .get_command_func (args .command )
4335+ argparser = None if command_func is None else self .command_parsers .get (command_func )
43304336
43314337 # If the command function uses argparse, then use argparse's help
4332- if func is not None and argparser is not None :
4338+ if command_func is not None and argparser is not None :
43334339 completer = argparse_completer .DEFAULT_AP_COMPLETER (argparser , self )
43344340 completer .print_help (args .subcommands , self .stdout )
43354341
@@ -4338,8 +4344,8 @@ def do_help(self, args: argparse.Namespace) -> None:
43384344 help_func ()
43394345
43404346 # If the command function has a docstring, then print it
4341- elif func is not None and func .__doc__ is not None :
4342- self .poutput (pydoc .getdoc (func ))
4347+ elif command_func is not None and command_func .__doc__ is not None :
4348+ self .poutput (pydoc .getdoc (command_func ))
43434349
43444350 # If there is no help information then print an error
43454351 else :
@@ -4380,15 +4386,15 @@ def print_topics(self, header: str, cmds: Sequence[str] | None, cmdlen: int, max
43804386 self .columnize (cmds , maxcol )
43814387 self .poutput ()
43824388
4383- def _print_documented_command_topics (self , header : str , cmds : Sequence [str ], verbose : bool ) -> None :
4389+ def _print_documented_command_topics (self , header : str , commands : Sequence [str ], verbose : bool ) -> None :
43844390 """Print topics which are documented commands, switching between verbose or traditional output."""
43854391 import io
43864392
4387- if not cmds :
4393+ if not commands :
43884394 return
43894395
43904396 if not verbose :
4391- self .print_topics (header , cmds , 15 , 80 )
4397+ self .print_topics (header , commands , 15 , 80 )
43924398 return
43934399
43944400 topic_table = Cmd2SimpleTable (
@@ -4398,8 +4404,8 @@ def _print_documented_command_topics(self, header: str, cmds: Sequence[str], ver
43984404
43994405 # Try to get the documentation string for each command
44004406 topics = self .get_help_topics ()
4401- for command in cmds :
4402- if (cmd_func := self .get_command_func (command )) is None :
4407+ for command in commands :
4408+ if (command_func := self .get_command_func (command )) is None :
44034409 continue
44044410
44054411 doc : str | None
@@ -4424,7 +4430,7 @@ def _print_documented_command_topics(self, header: str, cmds: Sequence[str], ver
44244430 doc = result .getvalue ()
44254431
44264432 else :
4427- doc = cmd_func .__doc__
4433+ doc = command_func .__doc__
44284434
44294435 # Attempt to locate the first documentation block
44304436 cmd_desc = strip_doc_annotations (doc ) if doc else ""
@@ -5593,25 +5599,25 @@ def enable_command(self, command: str) -> None:
55935599 if command not in self .disabled_commands :
55945600 return
55955601
5596- cmd_func_name = constants .COMMAND_FUNC_PREFIX + command
5602+ command_func_name = constants .COMMAND_FUNC_PREFIX + command
55975603 help_func_name = constants .HELP_FUNC_PREFIX + command
55985604 completer_func_name = constants .COMPLETER_FUNC_PREFIX + command
55995605
56005606 # Restore the command function to its original value
56015607 dc = self .disabled_commands [command ]
5602- setattr (self , cmd_func_name , dc .command_function )
5608+ setattr (self , command_func_name , dc .command_func )
56035609
56045610 # Restore the help function to its original value
5605- if dc .help_function is None :
5611+ if dc .help_func is None :
56065612 delattr (self , help_func_name )
56075613 else :
5608- setattr (self , help_func_name , dc .help_function )
5614+ setattr (self , help_func_name , dc .help_func )
56095615
56105616 # Restore the completer function to its original value
5611- if dc .completer_function is None :
5617+ if dc .completer_func is None :
56125618 delattr (self , completer_func_name )
56135619 else :
5614- setattr (self , completer_func_name , dc .completer_function )
5620+ setattr (self , completer_func_name , dc .completer_func )
56155621
56165622 # Remove the disabled command entry
56175623 del self .disabled_commands [command ]
@@ -5625,15 +5631,15 @@ def enable_category(self, category: str) -> None:
56255631 if category not in self .disabled_categories :
56265632 return
56275633
5628- for cmd_name in list (self .disabled_commands ):
5629- func = self .disabled_commands [cmd_name ]. command_function
5630- if self ._get_command_category (func ) == category :
5631- self .enable_command (cmd_name )
5634+ for command in list (self .disabled_commands ):
5635+ command_func = self .disabled_commands [command ]. command_func
5636+ if self ._get_command_category (command_func ) == category :
5637+ self .enable_command (command )
56325638
56335639 del self .disabled_categories [category ]
56345640
56355641 def disable_command (self , command : str , message_to_print : str ) -> None :
5636- """Disable a command and overwrite its functions.
5642+ """Disable a command and replace its functions with disabled versions .
56375643
56385644 :param command: the command being disabled
56395645 :param message_to_print: what to print when this command is run or help is called on it while disabled
@@ -5647,41 +5653,48 @@ def disable_command(self, command: str, message_to_print: str) -> None:
56475653 return
56485654
56495655 # Make sure this is an actual command
5650- command_function = self .get_command_func (command )
5651- if command_function is None :
5656+ command_func = self .get_command_func (command )
5657+ if command_func is None :
56525658 raise AttributeError (f"'{ command } ' does not refer to a command" )
56535659
5654- cmd_func_name = constants .COMMAND_FUNC_PREFIX + command
5660+ command_func_name = constants .COMMAND_FUNC_PREFIX + command
5661+
56555662 help_func_name = constants .HELP_FUNC_PREFIX + command
5663+ help_func = getattr (self , help_func_name , None )
5664+
56565665 completer_func_name = constants .COMPLETER_FUNC_PREFIX + command
5666+ completer_func = getattr (self , completer_func_name , None )
56575667
56585668 # Add the disabled command record
56595669 self .disabled_commands [command ] = DisabledCommand (
5660- command_function = command_function ,
5661- help_function = getattr ( self , help_func_name , None ) ,
5662- completer_function = getattr ( self , completer_func_name , None ) ,
5670+ command_func = command_func ,
5671+ help_func = help_func ,
5672+ completer_func = completer_func ,
56635673 )
56645674
5665- # Overwrite command function to print the message
5675+ # Replace command and help functions to report the disabled message
56665676 message_to_print = message_to_print .replace (constants .COMMAND_NAME , command )
5667- new_cmd_func = functools .partial (self ._report_disabled_command_usage , message_to_print = message_to_print )
5677+ new_cmd_func = functools .partial (
5678+ self ._report_disabled_command_usage ,
5679+ message_to_print = message_to_print ,
5680+ )
56685681
5669- # Preserve the metadata of the original command function
5670- functools .update_wrapper (new_cmd_func , command_function )
5671- setattr (self , cmd_func_name , new_cmd_func )
5682+ # Ensure the replacement function identifies as the original for introspection
5683+ functools .update_wrapper (new_cmd_func , command_func )
5684+ setattr (self , command_func_name , new_cmd_func )
56725685
5673- # Overwrite the help function to print the message
5674- new_help_func = functools .partial (self ._report_disabled_command_usage , message_to_print = message_to_print )
5675- if (help_function := self .disabled_commands [command ].help_function ) is not None :
5676- # Preserve the metadata of the original help function
5677- functools .update_wrapper (new_help_func , help_function )
5686+ new_help_func = functools .partial (
5687+ self ._report_disabled_command_usage ,
5688+ message_to_print = message_to_print ,
5689+ )
5690+ if help_func is not None :
5691+ functools .update_wrapper (new_help_func , help_func )
56785692 setattr (self , help_func_name , new_help_func )
56795693
5680- # Set the completer to a function that returns a nothing
5694+ # Replace completer with a function that returns nothing
56815695 new_completer_func = functools .partial (self ._disabled_completer )
5682- if (completer_function := self .disabled_commands [command ].completer_function ) is not None :
5683- # Preserve the metadata of the original completer function
5684- functools .update_wrapper (new_completer_func , completer_function )
5696+ if completer_func is not None :
5697+ functools .update_wrapper (new_completer_func , completer_func )
56855698 setattr (self , completer_func_name , new_completer_func )
56865699
56875700 def disable_category (self , category : str , message_to_print : str ) -> None :
@@ -5699,10 +5712,10 @@ def disable_category(self, category: str, message_to_print: str) -> None:
56995712
57005713 all_commands = self .get_all_commands ()
57015714
5702- for cmd_name in all_commands :
5703- func = cast (BoundCommandFunc , self .get_command_func (cmd_name ))
5704- if self ._get_command_category (func ) == category :
5705- self .disable_command (cmd_name , message_to_print )
5715+ for command in all_commands :
5716+ command_func = cast (BoundCommandFunc , self .get_command_func (command ))
5717+ if self ._get_command_category (command_func ) == category :
5718+ self .disable_command (command , message_to_print )
57065719
57075720 self .disabled_categories [category ] = message_to_print
57085721
0 commit comments