def load_sidebar_bindings(key_bindings_manager, python_input): """ Load bindings for the navigation in the sidebar. """ handle = key_bindings_manager.registry.add_binding sidebar_visible = Condition(lambda cli: python_input.show_sidebar) @handle(Keys.Up, filter=sidebar_visible) @handle(Keys.ControlP, filter=sidebar_visible) @handle('k', filter=sidebar_visible) def _(event): " Go to previous option. " python_input.selected_option_index = ( (python_input.selected_option_index - 1) % python_input.option_count) @handle(Keys.Down, filter=sidebar_visible) @handle(Keys.ControlN, filter=sidebar_visible) @handle('j', filter=sidebar_visible) def _(event): " Go to next option. " python_input.selected_option_index = ( (python_input.selected_option_index + 1) % python_input.option_count) @handle(Keys.Right, filter=sidebar_visible) @handle('l', filter=sidebar_visible) @handle(' ', filter=sidebar_visible) def _(event): " Select next value for current option. " option = python_input.selected_option option.activate_next() @handle(Keys.Left, filter=sidebar_visible) @handle('h', filter=sidebar_visible) def _(event): " Select previous value for current option. " option = python_input.selected_option option.activate_previous() @handle(Keys.ControlC, filter=sidebar_visible) @handle(Keys.ControlG, filter=sidebar_visible) @handle(Keys.ControlD, filter=sidebar_visible) @handle(Keys.ControlJ, filter=sidebar_visible) @handle(Keys.Escape, filter=sidebar_visible) def _(event): " Hide sidebar. " python_input.show_sidebar = False
def __init__(self, python_input, original_document): """ Create an `Application` for the history screen. This has to be run as a sub application of `python_input`. When this application runs and returns, it retuns the selected lines. """ self.python_input = python_input history_mapping = HistoryMapping(self, python_input.history, original_document) self.history_mapping = history_mapping document = Document(history_mapping.concatenated_history) document = Document( document.text, cursor_position=document.cursor_position + document.get_start_of_line_position(), ) self.history_buffer = Buffer( document=document, on_cursor_position_changed=self._history_buffer_pos_changed, accept_handler=( lambda buff: get_app().exit(result=self.default_buffer.text)), read_only=True, ) self.default_buffer = Buffer( name=DEFAULT_BUFFER, document=history_mapping.get_new_document(), on_cursor_position_changed=self._default_buffer_pos_changed, read_only=True, ) self.help_buffer = Buffer(document=Document(HELP_TEXT, 0), read_only=True) self.history_layout = HistoryLayout(self) self.app = Application( layout=self.history_layout.layout, full_screen=True, style=python_input._current_style, mouse_support=Condition(lambda: python_input.enable_mouse_support), key_bindings=create_key_bindings(self, python_input, history_mapping), )
def _extra_prompt_options(self): """ Return the current layout option for the current Terminal InteractiveShell """ def get_message(): return PygmentsTokens(self.prompts.in_prompt_tokens()) if self.editing_mode == 'emacs': # with emacs mode the prompt is (usually) static, so we call only # the function once. With VI mode it can toggle between [ins] and # [nor] so we can't precompute. # here I'm going to favor the default keybinding which almost # everybody uses to decrease CPU usage. # if we have issues with users with custom Prompts we can see how to # work around this. get_message = get_message() options = { 'complete_in_thread': False, 'lexer': IPythonPTLexer(), 'reserve_space_for_menu': self.space_for_menu, 'message': get_message, 'prompt_continuation': (lambda width, lineno, is_soft_wrap: PygmentsTokens( self.prompts.continuation_prompt_tokens(width))), 'multiline': True, 'complete_style': self.pt_complete_style, # Highlight matching brackets, but only when this setting is # enabled, and only when the DEFAULT_BUFFER has the focus. 'input_processors': [ ConditionalProcessor( processor=HighlightMatchingBracketProcessor( chars='[](){}'), filter=HasFocus(DEFAULT_BUFFER) & ~IsDone() & Condition(lambda: self.highlight_matching_brackets)) ], } if not PTK3: options['inputhook'] = self.inputhook return options
def load_basic_system_bindings(registry, filter=Always()): """ Basic system bindings (For both Emacs and Vi mode.) """ assert isinstance(filter, CLIFilter) handle = create_handle_decorator(registry, filter) suspend_supported = Condition( lambda cli: suspend_to_background_supported()) @handle(Keys.ControlZ, filter=suspend_supported) def _(event): """ Suspend process to background. """ event.cli.suspend_to_background()
def main(): if len(sys.argv) > 1: logout() else: studentID = raw_input("Please Input Your Student ID:") hidden = [True] # Nonlocal key_bindings_manager = KeyBindingManager() @key_bindings_manager.registry.add_binding(Keys.ControlT) def _(event): hidden[0] = not hidden[0] passwd = prompt('Password: ', is_password=Condition(lambda cli: hidden[0]), key_bindings_registry=key_bindings_manager.registry) login(studentID, passwd)
def create_r_keybindings(prase_text_complete, enable_reticulate_prompt): kb = create_prompt_keybindings(prase_text_complete) handle = kb.add # r mode @handle(';', filter=insert_mode & default_focussed & cursor_at_begin) def _(event): event.app.session.change_mode("shell") @handle('~', filter=insert_mode & default_focussed & cursor_at_begin & text_is_empty & Condition(enable_reticulate_prompt)) def _(event): commit_text(event, "reticulate::repl_python()", False) return kb
def __init__(self, python_input): self.python_input = python_input def get_prompt_style(): return python_input.all_prompt_styles[python_input.prompt_style] def get_prompt(cli): return get_prompt_style().in_tokens(cli) def get_continuation_prompt(cli, width): return get_prompt_style().in2_tokens(cli, width) super(PythonPromptMargin, self).__init__( get_prompt, get_continuation_prompt, show_numbers=Condition(lambda cli: python_input.show_line_numbers))
def preceding_text(pattern): try: return _preceding_text_cache[pattern] except KeyError: pass m = re.compile(pattern) def _preceding_text(): app = get_app() return bool( m.match( app.current_buffer.document.current_line_before_cursor)) condition = Condition(_preceding_text) _preceding_text_cache[pattern] = condition return condition
def __init__(self, editor): def get_tokens(): if not editor.debugger.debug_message(): return [] else: if "Debug" not in editor.debugger.debug_message(): print(editor.debugger.debug_message()) return [("", editor.debugger.debug_message())] def condition(): return editor.window_arrangement.active_tab_index == 0 super(DebugWindow, self).__init__(Window(FormattedTextControl(get_tokens), align=WindowAlign.CENTER), filter=Condition(condition))
def status_bar(key_bindings_manager, python_input): """ Create the `Layout` for the status bar. """ TB = Token.Toolbar.Status def get_tokens(cli): python_buffer = cli.buffers[DEFAULT_BUFFER] result = [] append = result.append append((TB, ' ')) result.extend(get_inputmode_tokens(cli, python_input)) append((TB, ' ')) # Position in history. append((TB, '%i/%i ' % (python_buffer.working_index + 1, len(python_buffer._working_lines)))) # Shortcuts. if not python_input.vi_mode and cli.focus_stack.current == 'search': append((TB, '[Ctrl-G] Cancel search [Enter] Go to this position.')) elif bool(cli.current_buffer.selection_state) and not python_input.vi_mode: # Emacs cut/copy keys. append((TB, '[Ctrl-W] Cut [Meta-W] Copy [Ctrl-Y] Paste [Ctrl-G] Cancel')) else: result.extend([ (TB.Key, '[F3]'), (TB, ' History '), (TB.Key, '[F6]'), (TB, ' '), ]) if python_input.paste_mode: append((TB.PasteModeOn, 'Paste mode (on)')) else: append((TB, 'Paste mode')) return result return TokenListToolbar( get_tokens, default_char=Char(token=TB), filter=~IsDone() & RendererHeightIsKnown() & Condition(lambda cli: python_input.show_status_bar and not python_input.show_exit_confirmation))
def __init__(self, playback, save_location=None, *args, **kwargs): self.mainViewCondition = partial(self.mainView, self) self.mainViewCondition = Condition(self.mainViewCondition) self.disabled_bindings = False bindings = KeyBindings() self.init_bindings(bindings) super().__init__(full_screen=True, key_bindings=bindings, mouse_support=True, *args, **kwargs) self.displayingHelpScreen = ( False) # used to toggle between help screen on normal if save_location: self.save_location = save_location else: self.save_location = SAVE_LOCATION self.playback = playback self._savedLayout = Layout(Window()) self.command_cache = deque([], maxlen=5) ########################################## ### Setting up views ########################################## self.old_command_window = FormattedTextControl(text="Output goes here", focusable=True) self.new_command_window = FormattedTextControl(text="Output goes here", focusable=True) self.body = Frame( HSplit([ Frame(Window(self.old_command_window)), Frame(Window(self.new_command_window)), ])) self.toolbar = Window( FormattedTextControl(text=self.toolbar_text), height=Dimension(max=1, weight=10000), dont_extend_height=True, ) self.main_view = HSplit([self.body, self.toolbar], padding_char="-") self.layout = Layout(self.main_view)
def load_basic_system_bindings(): """ Basic system bindings (For both Emacs and Vi mode.) """ registry = Registry() suspend_supported = Condition( lambda cli: suspend_to_background_supported()) @registry.add_binding(Keys.ControlZ, filter=suspend_supported) def _(event): """ Suspend process to background. """ event.cli.suspend_to_background() return registry
def _setup_app(self): label = Label("", style="bg:black fg:white") cc = ConditionalContainer(label, Condition(lambda: label.text != "")) self.hsplit.children.append(to_container(Label(""))) self.hsplit.children.append(to_container(cc)) self.kb.add("a")(lambda event: event.app.exit( AppResult(app_factory=apply_app_factory))) self.kb.add("p")(lambda event: event.app.exit( AppResult(app_factory=persist_app_factory))) def apply_app_factory(): print("Applying network interface changes...") with self.context.get_client() as c: try: c.call("interface.commit") except ClientException as e: return AppResult( app=message_dialog( "Error", "\n".join( textwrap.wrap(format_error(self.context, e), 74))), app_result_handler=lambda _: NetworkInterfaceList( self.context), ) return NetworkInterfaceList(self.context) def persist_app_factory(): with self.context.get_client() as c: c.call("interface.checkin") return NetworkInterfaceList(self.context) def get_text(): with self.context.get_client() as c: if checkin_waiting := c.call("interface.checkin_waiting"): n = int(checkin_waiting) return ( f"Network interface changes have been applied.\n" f"Press <p> to persist them if the network is still operational\n" f"or they will be rolled back in {n} seconds.") elif c.call("interface.has_pending_changes"): return ("You have pending network interface changes.\n" "Press <a> to apply them.")
def __init__(self, server: 'ScriptingServer'): # Customize the default builtins, and use DynamicDict # in order to avoid reflection at startup time. builtins = DynamicDict(globals()["__builtins__"]) builtins["print"] = self._custom_print del builtins["help"] del builtins["input"] del builtins["breakpoint"] # Add customized builtins, types and minecraft dynamic # value and also all builtins class wrappers. from . import std, mc builtins["types"] = server.types builtins.add_dyn("mc", lambda: mc.Minecraft.get_instance(server)) for mod_name, cls_name, cls in self.iter_modules_classes(std, mc): builtins[cls_name] = cls self.locals = {} self.globals = {"__builtins__": builtins} self.code_indent = 0 self.code = [] self.lexer = PygmentsLexer(PythonLexer) self.window = RollingLinesWindow(100, lexer=self.lexer, wrap_lines=True, dont_extend_height=True) self.input = TextArea(height=1, multiline=False, wrap_lines=False, accept_handler=self._input_accept, lexer=self.lexer) self.prompt_processor = BeforeInput(">>> ", "") self.input.control.input_processors.clear() self.input.control.input_processors.append(self.prompt_processor) keys = KeyBindings() keys.add("tab", filter=Condition(self.require_key_tab))(self._handle_tab) self.split = HSplit([Window(), self.window, self.input], key_bindings=keys)
def expanding_object_notification(my_app: "sqlApp"): """ Create the `Layout` for the 'Expanding object' notification. """ def get_text_fragments(): # Show navigation info. return [("fg:red", "Expanding object ...")] return ConditionalContainer( content=Window( FormattedTextControl(get_text_fragments), style="class:sidebar", width=Dimension.exact(45), height=Dimension(max=1), ), filter=~is_done & ShowSidebar(my_app) & Condition(lambda: my_app.show_expanding_object))
def __init__(self, tui): super().__init__(tui) self.list = DynamicContainer(self.get_container) self.back_button = ConditionalContainer( Button("Back", handler=self.pop), filter=Condition(lambda: len(self.pages) > 1), ) self.exit_button = Button("Exit", handler=exit_app) self.container = HSplit( [ self.list, self.back_button, self.exit_button, ], height=Dimension(min=1), width=Dimension(min=1), key_bindings=self.get_key_bindings(), )
def exit_confirmation(python_input, token=Token.ExitConfirmation): """ Create `Layout` for the exit message. """ def get_tokens(cli): # Show "Do you really want to exit?" return [ (token, '\n %s ([y]/n)' % python_input.exit_message), (Token.SetCursorPosition, ''), (token, ' \n'), ] visible = ~IsDone() & Condition(lambda cli: python_input.show_exit_confirmation) return ConditionalContainer( content=Window(TokenListControl( get_tokens, default_char=Char(token=token), has_focus=visible)), filter=visible)
def __init__(self, app=None, show=False, height=Dimension(preferred=20)): self.frame = None self.app = app self.show = show self.args = LineItemsWindow(app=app, show=show, title='[ Arguments ]', show_divider=self._show_divider) self.locals = LineItemsWindow(app=app, show=show, title='[ Locals ]', show_divider=self._show_divider) self.container = ConditionalContainer( content=HSplit([self.args.get_ui(), self.locals.get_ui()]), filter=Condition(lambda: self.show))
def __init__(self, editor): once_hidden = [False] # Nonlocal def condition(cli): # Get editor buffers buffers = editor.window_arrangement.editor_buffers # Only show when there is only one empty buffer, but once the # welcome message has been hidden, don't show it again. result = (len(buffers) == 1 and buffers[0].buffer.text == '' and buffers[0].location is None and not once_hidden[0]) if not result: once_hidden[0] = True return result super(WelcomeMessageWindow, self).__init__( Window(TokenListControl(lambda cli: WELCOME_MESSAGE_TOKENS)), filter=Condition(condition))
def exit_confirmation(python_input, style='class:exit-confirmation'): """ Create `Layout` for the exit message. """ def get_text_fragments(): # Show "Do you really want to exit?" return [ (style, '\n %s ([y]/n)' % python_input.exit_message), ('[SetCursorPosition]', ''), (style, ' \n'), ] visible = ~is_done & Condition(lambda: python_input.show_exit_confirmation) return ConditionalContainer( content=Window(FormattedTextControl(get_text_fragments), style=style), # , has_focus=visible)), filter=visible)
def pt_init(self): def get_prompt_tokens(cli): return [(Token.Prompt, self.prompt)] def patch_stdout(**kwargs): return self.pt_cli.patch_stdout_context(**kwargs) if self._ptcomp is None: compl = IPCompleter( shell=self.shell, namespace={}, global_namespace={}, use_readline=False, parent=self.shell, ) self._ptcomp = IPythonPTCompleter(compl, patch_stdout=patch_stdout) kbmanager = KeyBindingManager.for_prompt() supports_suspend = Condition(lambda cli: hasattr(signal, 'SIGTSTP')) kbmanager.registry.add_binding(Keys.ControlZ, filter=supports_suspend)(suspend_to_bg) if self.shell.display_completions == 'readlinelike': kbmanager.registry.add_binding( Keys.ControlI, filter=(HasFocus(DEFAULT_BUFFER) & ~HasSelection() & ViInsertMode() | EmacsInsertMode() & ~cursor_in_leading_ws ))(display_completions_like_readline) multicolumn = (self.shell.display_completions == 'multicolumn') self._pt_app = create_prompt_application( editing_mode=getattr(EditingMode, self.shell.editing_mode.upper()), key_bindings_registry=kbmanager.registry, history=self.shell.debugger_history, completer=self._ptcomp, enable_history_search=True, mouse_support=self.shell.mouse_support, get_prompt_tokens=get_prompt_tokens, display_completions_in_columns=multicolumn, ) self.pt_cli = CommandLineInterface(self._pt_app, eventloop=self.shell._eventloop)
def exit_confirmation(my_app: "sqlApp", style="class:exit-confirmation") -> Container: """ Create `Layout` for the exit message. """ def get_text_fragments() -> StyleAndTextTuples: # Show "Do you really want to exit?" return [ (style, "\n %s ([y]/n)" % my_app.exit_message), ("[SetCursorPosition]", ""), (style, " \n"), ] visible = ~is_done & Condition(lambda: my_app.show_exit_confirmation) return ConditionalContainer( content=Window(FormattedTextControl(get_text_fragments), style=style), filter=visible, )
def show_sidebar_button_info(python_input: "PythonInput") -> Container: """ Create `Layout` for the information in the right-bottom corner. (The right part of the status bar.) """ @if_mousedown def toggle_sidebar(mouse_event: MouseEvent) -> None: " Click handler for the menu. " python_input.show_sidebar = not python_input.show_sidebar version = sys.version_info tokens: StyleAndTextTuples = [ ("class:status-toolbar.key", "[F2]", toggle_sidebar), ("class:status-toolbar", " Menu", toggle_sidebar), ("class:status-toolbar", " - "), ( "class:status-toolbar.python-version", "%s %i.%i.%i" % (platform.python_implementation(), version[0], version[1], version[2]), ), ("class:status-toolbar", " "), ] width = fragment_list_width(tokens) def get_text_fragments() -> StyleAndTextTuples: # Python version return tokens return ConditionalContainer( content=Window( FormattedTextControl(get_text_fragments), style="class:status-toolbar", height=Dimension.exact(1), width=Dimension.exact(width), ), filter=~is_done & renderer_height_is_known & Condition( lambda: python_input.show_status_bar and not python_input.show_exit_confirmation ), )
def _layout_options(self): """ Return the current layout option for the current Terminal InteractiveShell """ return { 'lexer':IPythonPTLexer(), 'reserve_space_for_menu':self.space_for_menu, 'get_prompt_tokens':self.prompts.in_prompt_tokens, 'get_continuation_tokens':self.prompts.continuation_prompt_tokens, 'multiline':True, 'display_completions_in_columns': (self.display_completions == 'multicolumn'), # Highlight matching brackets, but only when this setting is # enabled, and only when the DEFAULT_BUFFER has the focus. 'extra_input_processors': [ConditionalProcessor( processor=HighlightMatchingBracketProcessor(chars='[](){}'), filter=HasFocus(DEFAULT_BUFFER) & ~IsDone() & Condition(lambda cli: self.highlight_matching_brackets))], }
def __init__(self, pymux): self.pymux = pymux def enable_vi_mode(cli): " Return True when Vi mode is currently active. " client_state = pymux.get_client_state(cli) if client_state.confirm_text or client_state.prompt_command or client_state.command_mode: return pymux.status_keys_vi_mode else: return pymux.mode_keys_vi_mode def get_search_state(cli): " Return the currently active SearchState. (The one for the focussed pane.) " return pymux.arrangement.get_active_pane(cli).search_state # Start from this KeyBindingManager from prompt_toolkit, to have basic # editing functionality for the command line. These key binding are # however only active when the following `enable_all` condition is met. self.pt_key_bindings_manager = pt_KeyBindingManager( enable_vi_mode=Condition(enable_vi_mode), enable_all=(HasFocus(COMMAND) | HasFocus(PROMPT) | InScrollBuffer(pymux)) & ~HasPrefix(pymux), enable_auto_suggest_bindings=True, enable_search= False, # We have our own search bindings, that support multiple panes. enable_extra_page_navigation=True, get_vi_state=self._get_vi_state, get_search_state=get_search_state) self.registry = self.pt_key_bindings_manager.registry self._prefix = (Keys.ControlB, ) self._prefix_binding = None # Load initial bindings. self._load_builtins() self._load_prefix_binding() _load_search_bindings(pymux, self.registry, self._get_vi_state) # Custom user configured key bindings. # { (needs_prefix, key) -> (command, handler) } self.custom_bindings = {}
def meta_enter_message(python_input): """ Create the `Layout` for the 'Meta+Enter` message. """ def get_tokens(cli): return [(Token.AcceptMessage, ' [Meta+Enter] Execute ')] def extra_condition(cli): " Only show when... " b = cli.buffers[DEFAULT_BUFFER] return (python_input.show_meta_enter_message and (not b.document.is_cursor_at_the_end or python_input.accept_input_on_enter is None) and b.is_multiline()) visible = ~IsDone() & HasFocus(DEFAULT_BUFFER) & Condition(extra_condition) return ConditionalContainer(content=Window(TokenListControl(get_tokens)), filter=visible)
def __init__(self, eventloop=None, python_input=None, input=None, output=None): assert python_input is None or isinstance(python_input, PythonInput) python_input = python_input or PythonInput() # Make sure that the prompt_toolkit 'renderer' knows about the # 'true_color' property of PythonInput. if output is None: output = create_output( true_color=Condition(lambda: python_input.true_color)) super(PythonCommandLineInterface, self).__init__(application=python_input.create_application(), eventloop=eventloop, input=input, output=output)
def pt_init(self): def get_prompt_tokens(): return [(Token.Prompt, self.prompt)] if self._ptcomp is None: compl = IPCompleter( shell=self.shell, namespace={}, global_namespace={}, parent=self.shell, ) self._ptcomp = IPythonPTCompleter(compl) kb = KeyBindings() supports_suspend = Condition(lambda: hasattr(signal, 'SIGTSTP')) kb.add('c-z', filter=supports_suspend)(suspend_to_bg) if self.shell.display_completions == 'readlinelike': kb.add('tab', filter=(has_focus(DEFAULT_BUFFER) & ~has_selection & vi_insert_mode | emacs_insert_mode & ~cursor_in_leading_ws ))(display_completions_like_readline) options = dict( message=(lambda: PygmentsTokens(get_prompt_tokens())), editing_mode=getattr(EditingMode, self.shell.editing_mode.upper()), key_bindings=kb, history=self.shell.debugger_history, completer=self._ptcomp, enable_history_search=True, mouse_support=self.shell.mouse_support, complete_style=self.shell.pt_complete_style, style=self.shell.style, color_depth=self.shell.color_depth, ) if not PTK3: options['inputhook'] = self.shell.inputhook self.pt_app = PromptSession(**options)
def create_prompt_bindings(): """ Create the KeyBindings for a prompt application. """ kb = KeyBindings() handle = kb.add default_focussed = has_focus(DEFAULT_BUFFER) @handle('enter', filter=default_focussed) def _(event): " Accept input when enter has been pressed. " event.current_buffer.validate_and_handle() @handle('c-c', filter=default_focussed) def _(event): " Abort when Control-C has been pressed. " event.app.abort() @Condition def ctrl_d_condition(): """ Ctrl-D binding is only active when the default buffer is selected and empty. """ app = get_app() return (app.current_buffer.name == DEFAULT_BUFFER and not app.current_buffer.text) @handle('c-d', filter=ctrl_d_condition & default_focussed) def _(event): " Exit when Control-D has been pressed. " event.app.exit() suspend_supported = Condition(suspend_to_background_supported) @handle('c-z', filter=suspend_supported) def _(event): """ Suspend process to background. """ event.app.suspend_to_background() return kb
def meta_enter_message(python_input): """ Create the `Layout` for the 'Meta+Enter` message. """ def get_text_fragments(): return [('class:accept-message', ' [Meta+Enter] Execute ')] def extra_condition(): " Only show when... " b = python_input.default_buffer return (python_input.show_meta_enter_message and (not b.document.is_cursor_at_the_end or python_input.accept_input_on_enter is None) and '\n' in b.text) visible = ~is_done & has_focus(DEFAULT_BUFFER) & Condition(extra_condition) return ConditionalContainer(content=Window( FormattedTextControl(get_text_fragments)), filter=visible)
def test_condition_filter_args(): c = Condition(lambda a, b, c: True) assert c.test_args('a', 'b', 'c') assert not c.test_args() assert not c.test_args('a') assert not c.test_args('a', 'b') assert not c.test_args('a', 'b', 'c', 'd') c2 = Condition(lambda a, b=1: True) assert c2.test_args('a') assert c2.test_args('a', 'b') assert not c2.test_args('a', 'b', 'c') assert not c2.test_args() c3 = Condition(lambda *a: True) assert c3.test_args() assert c3.test_args('a') assert c3.test_args('a', 'b')
def test_condition_filter_args(self): c = Condition(lambda a, b, c:True) self.assertTrue(c.test_args('a', 'b', 'c')) self.assertFalse(c.test_args()) self.assertFalse(c.test_args('a')) self.assertFalse(c.test_args('a', 'b')) self.assertFalse(c.test_args('a', 'b', 'c', 'd')) c2 = Condition(lambda a, b=1:True) self.assertTrue(c2.test_args('a')) self.assertTrue(c2.test_args('a', 'b')) self.assertFalse(c2.test_args('a', 'b', 'c')) self.assertFalse(c2.test_args()) c3 = Condition(lambda *a: True) self.assertTrue(c3.test_args()) self.assertTrue(c3.test_args('a')) self.assertTrue(c3.test_args('a', 'b'))