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