text: """
  Dialogs that query users and verify the answer before accepting.
  
  Query is the generic base class for a popup dialog.
  The user must either enter a valid answer or close the dialog.
  Entries are validated when <Return> is entered or [Ok] is clicked.
  Entries are ignored when [Cancel] or [X] are clicked.
  The 'return value' is .result set to either a valid answer or None.
  
  Subclass SectionName gets a name for a new config file section.
  Configdialog uses it for new highlight theme and keybinding set names.
  Subclass ModuleName gets a name for File => Open Module.
  Subclass HelpSource gets menu item and path for additions to Help menu.
  """
  # Query and Section name result from splitting GetCfgSectionNameDialog
  # of configSectionNameDialog.py (temporarily config_sec.py) into
  # generic and specific parts.  3.6 only, July 2016.
  # ModuleName.entry_ok came from editor.EditorWindow.load_module.
  # HelpSource was extracted from configHelpSourceEdit.py (temporarily
  # config_help.py), with darwin code moved from ok to path_ok.
  
  import importlib
  import os
  import shlex
  from sys import executable, platform  # Platform is set for one test.
  
  from tkinter import Toplevel, StringVar, BooleanVar, W, E, S
  from tkinter.ttk import Frame, Button, Entry, Label, Checkbutton
  from tkinter import filedialog
  from tkinter.font import Font
  
  class Query(Toplevel):
      """Base class for getting verified answer from a user.
  
      For this base class, accept any non-blank string.
      """
      def __init__(self, parent, title, message, *, text0='', used_names={},
                   _htest=False, _utest=False):
          """Create modal popup, return when destroyed.
  
          Additional subclass init must be done before this unless
          _utest=True is passed to suppress wait_window().
  
          title - string, title of popup dialog
          message - string, informational message to display
          text0 - initial value for entry
          used_names - names already in use
          _htest - bool, change box location when running htest
          _utest - bool, leave window hidden and not modal
          """
          self.parent = parent  # Needed for Font call.
          self.message = message
          self.text0 = text0
          self.used_names = used_names
  
          Toplevel.__init__(self, parent)
          self.withdraw()  # Hide while configuring, especially geometry.
          self.title(title)
          self.transient(parent)
          self.grab_set()
  
          windowingsystem = self.tk.call('tk', 'windowingsystem')
          if windowingsystem == 'aqua':
              try:
                  self.tk.call('::tk::unsupported::MacWindowStyle', 'style',
                               self._w, 'moveableModal', '')
              except:
                  pass
              self.bind("<Command-.>", self.cancel)
          self.bind('<Key-Escape>', self.cancel)
          self.protocol("WM_DELETE_WINDOW", self.cancel)
          self.bind('<Key-Return>', self.ok)
          self.bind("<KP_Enter>", self.ok)
  
          self.create_widgets()
          self.update_idletasks()  # Need here for winfo_reqwidth below.
          self.geometry(  # Center dialog over parent (or below htest box).
                  "+%d+%d" % (
                      parent.winfo_rootx() +
                      (parent.winfo_width()/2 - self.winfo_reqwidth()/2),
                      parent.winfo_rooty() +
                      ((parent.winfo_height()/2 - self.winfo_reqheight()/2)
                      if not _htest else 150)
                  ) )
          self.resizable(height=False, width=False)
  
          if not _utest:
              self.deiconify()  # Unhide now that geometry set.
              self.wait_window()
  
      def create_widgets(self, ok_text='OK'):  # Do not replace.
          """Create entry (rows, extras, buttons.
  
          Entry stuff on rows 0-2, spanning cols 0-2.
          Buttons on row 99, cols 1, 2.
          """
          # Bind to self the widgets needed for entry_ok or unittest.
          self.frame = frame = Frame(self, padding=10)
          frame.grid(column=0, row=0, sticky='news')
          frame.grid_columnconfigure(0, weight=1)
  
          entrylabel = Label(frame, anchor='w', justify='left',
                             text=self.message)
          self.entryvar = StringVar(self, self.text0)
          self.entry = Entry(frame, width=30, textvariable=self.entryvar)
          self.entry.focus_set()
          self.error_font = Font(name='TkCaptionFont',
                                 exists=True, root=self.parent)
          self.entry_error = Label(frame, text=' ', foreground='red',
                                   font=self.error_font)
          # Display or blank error by setting ['text'] =.
          entrylabel.grid(column=0, row=0, columnspan=3, padx=5, sticky=W)
          self.entry.grid(column=0, row=1, columnspan=3, padx=5, sticky=W+E,
                          pady=[10,0])
          self.entry_error.grid(column=0, row=2, columnspan=3, padx=5,
                                sticky=W+E)
  
          self.create_extra()
  
          self.button_ok = Button(
                  frame, text=ok_text, default='active', command=self.ok)
          self.button_cancel = Button(
                  frame, text='Cancel', command=self.cancel)
  
          self.button_ok.grid(column=1, row=99, padx=5)
          self.button_cancel.grid(column=2, row=99, padx=5)
  
      def create_extra(self): pass  # Override to add widgets.
  
      def showerror(self, message, widget=None):
          #self.bell(displayof=self)
          (widget or self.entry_error)['text'] = 'ERROR: ' + message
  
      def entry_ok(self):  # Example: usually replace.
          "Return non-blank entry or None."
          entry = self.entry.get().strip()
          if not entry:
              self.showerror('blank line.')
              return None
          return entry
  
      def ok(self, event=None):  # Do not replace.
          '''If entry is valid, bind it to 'result' and destroy tk widget.
  
          Otherwise leave dialog open for user to correct entry or cancel.
          '''
          self.entry_error['text'] = ''
          entry = self.entry_ok()
          if entry is not None:
              self.result = entry
              self.destroy()
          else:
              # [Ok] moves focus.  (<Return> does not.)  Move it back.
              self.entry.focus_set()
  
      def cancel(self, event=None):  # Do not replace.
          "Set dialog result to None and destroy tk widget."
          self.result = None
          self.destroy()
  
      def destroy(self):
          self.grab_release()
          super().destroy()
  
  
  class SectionName(Query):
      "Get a name for a config file section name."
      # Used in ConfigDialog.GetNewKeysName, .GetNewThemeName (837)
  
      def __init__(self, parent, title, message, used_names,
                   *, _htest=False, _utest=False):
          super().__init__(parent, title, message, used_names=used_names,
                           _htest=_htest, _utest=_utest)
  
      def entry_ok(self):
          "Return sensible ConfigParser section name or None."
          name = self.entry.get().strip()
          if not name:
              self.showerror('no name specified.')
              return None
          elif len(name)>30:
              self.showerror('name is longer than 30 characters.')
              return None
          elif name in self.used_names:
              self.showerror('name is already in use.')
              return None
          return name
  
  
  class ModuleName(Query):
      "Get a module name for Open Module menu entry."
      # Used in open_module (editor.EditorWindow until move to iobinding).
  
      def __init__(self, parent, title, message, text0,
                   *, _htest=False, _utest=False):
          super().__init__(parent, title, message, text0=text0,
                         _htest=_htest, _utest=_utest)
  
      def entry_ok(self):
          "Return entered module name as file path or None."
          name = self.entry.get().strip()
          if not name:
              self.showerror('no name specified.')
              return None
          # XXX Ought to insert current file's directory in front of path.
          try:
              spec = importlib.util.find_spec(name)
          except (ValueError, ImportError) as msg:
              self.showerror(str(msg))
              return None
          if spec is None:
              self.showerror("module not found")
              return None
          if not isinstance(spec.loader, importlib.abc.SourceLoader):
              self.showerror("not a source-based module")
              return None
          try:
              file_path = spec.loader.get_filename(name)
          except AttributeError:
              self.showerror("loader does not support get_filename",
                        parent=self)
              return None
          return file_path
  
  
  class HelpSource(Query):
      "Get menu name and help source for Help menu."
      # Used in ConfigDialog.HelpListItemAdd/Edit, (941/9)
  
      def __init__(self, parent, title, *, menuitem='', filepath='',
                   used_names={}, _htest=False, _utest=False):
          """Get menu entry and url/local file for Additional Help.
  
          User enters a name for the Help resource and a web url or file
          name. The user can browse for the file.
          """
          self.filepath = filepath
          message = 'Name for item on Help menu:'
          super().__init__(
                  parent, title, message, text0=menuitem,
                  used_names=used_names, _htest=_htest, _utest=_utest)
  
      def create_extra(self):
          "Add path widjets to rows 10-12."
          frame = self.frame
          pathlabel = Label(frame, anchor='w', justify='left',
                            text='Help File Path: Enter URL or browse for file')
          self.pathvar = StringVar(self, self.filepath)
          self.path = Entry(frame, textvariable=self.pathvar, width=40)
          browse = Button(frame, text='Browse', width=8,
                          command=self.browse_file)
          self.path_error = Label(frame, text=' ', foreground='red',
                                  font=self.error_font)
  
          pathlabel.grid(column=0, row=10, columnspan=3, padx=5, pady=[10,0],
                         sticky=W)
          self.path.grid(column=0, row=11, columnspan=2, padx=5, sticky=W+E,
                         pady=[10,0])
          browse.grid(column=2, row=11, padx=5, sticky=W+S)
          self.path_error.grid(column=0, row=12, columnspan=3, padx=5,
                               sticky=W+E)
  
      def askfilename(self, filetypes, initdir, initfile):  # htest #
          # Extracted from browse_file so can mock for unittests.
          # Cannot unittest as cannot simulate button clicks.
          # Test by running htest, such as by running this file.
          return filedialog.Open(parent=self, filetypes=filetypes)\
                 .show(initialdir=initdir, initialfile=initfile)
  
      def browse_file(self):
          filetypes = [
              ("HTML Files", "*.htm *.html", "TEXT"),
              ("PDF Files", "*.pdf", "TEXT"),
              ("Windows Help Files", "*.chm"),
              ("Text Files", "*.txt", "TEXT"),
              ("All Files", "*")]
          path = self.pathvar.get()
          if path:
              dir, base = os.path.split(path)
          else:
              base = None
              if platform[:3] == 'win':
                  dir = os.path.join(os.path.dirname(executable), 'Doc')
                  if not os.path.isdir(dir):
                      dir = os.getcwd()
              else:
                  dir = os.getcwd()
          file = self.askfilename(filetypes, dir, base)
          if file:
              self.pathvar.set(file)
  
      item_ok = SectionName.entry_ok  # localize for test override
  
      def path_ok(self):
          "Simple validity check for menu file path"
          path = self.path.get().strip()
          if not path: #no path specified
              self.showerror('no help file path specified.', self.path_error)
              return None
          elif not path.startswith(('www.', 'http')):
              if path[:5] == 'file:':
                  path = path[5:]
              if not os.path.exists(path):
                  self.showerror('help file path does not exist.',
                                 self.path_error)
                  return None
              if platform == 'darwin':  # for Mac Safari
                  path =  "file://" + path
          return path
  
      def entry_ok(self):
          "Return apparently valid (name, path) or None"
          self.path_error['text'] = ''
          name = self.item_ok()
          path = self.path_ok()
          return None if name is None or path is None else (name, path)
  
  class CustomRun(Query):
      """Get settings for custom run of module.
  
      1. Command line arguments to extend sys.argv.
      2. Whether to restart Shell or not.
      """
      # Used in runscript.run_custom_event
  
      def __init__(self, parent, title, *, cli_args=[],
                   _htest=False, _utest=False):
          """cli_args is a list of strings.
  
          The list is assigned to the default Entry StringVar.
          The strings are displayed joined by ' ' for display.
          """
          message = 'Command Line Arguments for sys.argv:'
          super().__init__(
                  parent, title, message, text0=cli_args,
                  _htest=_htest, _utest=_utest)
  
      def create_extra(self):
          "Add run mode on rows 10-12."
          frame = self.frame
          self.restartvar = BooleanVar(self, value=True)
          restart = Checkbutton(frame, variable=self.restartvar, onvalue=True,
                                offvalue=False, text='Restart shell')
          self.args_error = Label(frame, text=' ', foreground='red',
                                  font=self.error_font)
  
          restart.grid(column=0, row=10, columnspan=3, padx=5, sticky='w')
          self.args_error.grid(column=0, row=12, columnspan=3, padx=5,
                               sticky='we')
  
      def cli_args_ok(self):
          "Validity check and parsing for command line arguments."
          cli_string = self.entry.get().strip()
          try:
              cli_args = shlex.split(cli_string, posix=True)
          except ValueError as err:
              self.showerror(str(err))
              return None
          return cli_args
  
      def entry_ok(self):
          "Return apparently valid (cli_args, restart) or None"
          cli_args = self.cli_args_ok()
          restart = self.restartvar.get()
          return None if cli_args is None else (cli_args, restart)
  
  
  if __name__ == '__main__':
      from unittest import main
      main('idlelib.idle_test.test_query', verbosity=2, exit=False)
  
      from idlelib.idle_test.htest import run
      run(Query, HelpSource, CustomRun)
  