docs: def parse_arguments():
      """Collect command-line arguments.  Let the caller run parse_args(), as
      sphinx-argparse requires a function that returns an instance of
      argparse.ArgumentParser
      """
      # Pull a few settings from the environment, should they exist
      base_dn = os.environ['PUDL_BASE_DN'] if 'PUDL_BASE_DN' in os.environ \
                else 'OU=Departments,DC=example,DC=com'
      domain = os.environ['PUDL_DOMAIN'].upper() if 'PUDL_DOMAIN' in os.environ else 'EXAMPLE'
      page_size = os.environ['PUDL_PAGE_SIZE'].upper() if 'PUDL_PAGE_SIZE' in os.environ else 300
      tls_no_verify = bool(os.environ['PUDL_TLS_NO_VERIFY'].lower().capitalize()) \
                      if 'PUDL_TLS_NO_VERIFY' in os.environ else False
  
      parser = argparse.ArgumentParser(prog='pudl',
                                       description='A script for interacting with Active ' + \
                                       'Directory, leveraging python-ldap')
      parser.add_argument('-V', '--version', action='version', version='pudl v' + pudl_version,
                          help="Print the version number and exit")
      subparsers = parser.add_subparsers(dest='subcommand', help='Sub-command help')
      parser_common = subparsers.add_parser('common', add_help=False)
      parser_common.add_argument('--user', '-u', action='store', dest='user',
                                 help='The ldap user (bind dn) to connect as. ' + \
                                 'The full DN will work, or often, just the CN may be ' + \
                                 'sufficient, such as "John Smith", or more commonly, ' + \
                                 'specify the domain and sAMAccountName. Defaults to ' + \
                                 '{0}\\username.  The domain '.format(domain) + \
                                 'portion may be overridden with PUDL_DOMAIN',
                                 default='{0}\\{1}'.format(domain, getpass.getuser()))
      parser_common.add_argument('--password', '-p', action='store',
                                 dest='password', help="The connecting user's password")
      parser_common.add_argument('--host', '-H', action='store',
                                 dest='host', help='The AD/LDAP host, defaults to ldap',
                                 default='ldap')
      parser_common.add_argument('--port', '-P', action='store', dest='port',
                                 help='The ldap port, defaults to 389.  389 is ' + \
                                 'is the standard port', type=int, default=389)
      parser_common.add_argument('--page-size', '-s', action='store', dest='page_size',
                                 help='The ldap results are paged, specify the ' + \
                                 'number of results per page, defaults to ' + \
                                 '{0}. May be overridden with PUDL_PAGE_SIZE'.format(page_size),
                                 type=int, default=page_size)
      parser_common.add_argument('--base-dn', '-b', action='store',
                                 dest='base_dn', default=base_dn,
                                 help="The Base DN to use, defaults to {0}. ".format(base_dn) + \
                                 "May be overridden with PUDL_BASE_DN")
      parser_common.add_argument('--attribute', '-a', action='append',
                                 dest='attributes', metavar='ATTRIBUTE',
                                 help="Attributes to include in results objects.  Note that " + \
                                 "any nested objects return all attributes.  Maybe be used " + \
                                 "multiple times, and if not specified, all " + \
                                 "attributes are included in top-level objects")
      parser_common.add_argument('--grep', '-g', action='append', dest='grep',
                                 help='Filter results to only those matching the specified ' + \
                                 'regular expression (compares against all attributes). ' + \
                                 'May be used multiple times')
      parser_common.add_argument('--attributes-only', '-A', action='store_true',
                                 dest='attributes_only', help="Only display a list of attributes " + \
                                 "that are present for the object type returned by the LDAP query")
      parser_common.add_argument('--output-format', '-f', action='store', dest='output_format',
                                 choices=['json', 'yaml'], default='json',
                                 help="Output format, defaults to json.")
      parser_common.add_argument('--verbose', '-v', action='store_true', dest='verbose',
                                 help='Turn on verbose output', default=False)
      parser_common.add_argument('--debug', '-d', action='store_true', dest='debug', default=False,
                                 help="Print out debugging information, very chatty")
      parser_common.add_argument('--tls-no-verify', '-V', action='store_true',
                                 dest='tls_no_verify',
                                 default=tls_no_verify, help="Don't verify the authenticity " + \
                                 "of the server's certificate, defaults to " + \
                                 "{0} and may be overridden with ".format(tls_no_verify) + \
                                 "PUDL_TLS_NO_VERIFY")
      parser_user = subparsers.add_parser('user', parents=[parser_common], conflict_handler='resolve',
                                          help='Pull user objects from AD')
      parser_user.add_argument(nargs="*", dest='samaccountnames',
                               help='sAMAccountNames for any user objects that are to be ' + \
                               'looked up. If unspecified, returns all users under the base ' + \
                               'DN provided')
      parser_user.add_argument('--explicit-membership-only', '-e', action='store_true',
                               dest='explicit_membership_only', default=False,
                               help="Only show membership for users that is explicit, " + \
                               "not taking into account group nesting.  Defaults to False")
      parser_group = subparsers.add_parser('group', parents=[parser_common],
                                           conflict_handler='resolve',
                                           help='Pull group objects from AD')
      parser_group.add_argument(nargs="*", dest='samaccountnames',
                                help="sAMAccountNames for any group objects that are to be " + \
                                'looked up. If unspecified, returns all groups under the base ' + \
                                'DN provided. sAMAccountName may not be present in group ' + \
                                'objects in modern AD schemas')
      parser_group.add_argument('--explicit-membership-only', '-e', action='store_true',
                                dest='explicit_membership_only', default=False,
                                help="Only show membership for users that is explicit, " + \
                                "not taking into account group nesting.  Defaults to False")
      parser_computer = subparsers.add_parser('computer', parents=[parser_common],
                                              conflict_handler='resolve',
                                              help='Pull computer objects from AD')
      parser_computer.add_argument(nargs="*", dest='samaccountnames',
                                   help="sAMAccountNames for any computer objects that are to be " + \
                                   'looked up. If unspecified, returns all computers under ' + \
                                   'the base DN provided.')
  
      # sphinx is not add_help=False aware...
      del subparsers.choices['common']
  
      return parser