filename globbing utility import contextlib import os import re import fnmatch import itertools import stat import sys all glob iglob escape def globpathname rootdirnone dirfdnone recursivefalse includehiddenfalse return listiglobpathname rootdirrootdir dirfddirfd recursiverecursive includehiddenincludehidden def iglobpathname rootdirnone dirfdnone recursivefalse includehiddenfalse sys auditglob glob pathname recursive sys auditglob glob2 pathname recursive rootdir dirfd if rootdir is not none rootdir os fspathrootdir else rootdir pathname 0 it iglobpathname rootdir dirfd recursive false includehiddenincludehidden if not pathname or recursive and isrecursivepathname 2 try s nextit skip empty string if s it itertools chains it except stopiteration pass return it def iglobpathname rootdir dirfd recursive dironly includehiddenfalse dirname basename os path splitpathname if not hasmagicpathname assert not dironly if basename if lexistsjoinrootdir pathname dirfd yield pathname else patterns ending with a slash should match only directories if isdirjoinrootdir dirname dirfd yield pathname return if not dirname if recursive and isrecursivebasename yield from glob2rootdir basename dirfd dironly includehiddenincludehidden else yield from glob1rootdir basename dirfd dironly includehiddenincludehidden return os path split returns the argument itself as a dirname if it is a drive or unc path prevent an infinite recursion if a drive or unc path contains magic characters i e r c if dirname pathname and hasmagicdirname dirs iglobdirname rootdir dirfd recursive true includehiddenincludehidden else dirs dirname if hasmagicbasename if recursive and isrecursivebasename globindir glob2 else globindir glob1 else globindir glob0 for dirname in dirs for name in globindirjoinrootdir dirname basename dirfd dironly includehiddenincludehidden yield os path joindirname name these 2 helper functions nonrecursively glob inside a literal directory they return a list of basenames glob1 accepts a pattern while glob0 takes a literal basename so it only has to check for its existence def glob1dirname pattern dirfd dironly includehiddenfalse names listdirdirname dirfd dironly if includehidden or not ishiddenpattern names x for x in names if includehidden or not ishiddenx return fnmatch filternames pattern def glob0dirname basename dirfd dironly includehiddenfalse if basename if lexistsjoindirname basename dirfd return basename else os path split returns an empty basename for paths ending with a directory separator qx should match only directories if isdirdirname dirfd return basename return following functions are not public but can be used by thirdparty code def glob0dirname pattern return glob0dirname pattern none false def glob1dirname pattern return glob1dirname pattern none false this helper function recursively yields relative pathnames inside a literal directory def glob2dirname pattern dirfd dironly includehiddenfalse assert isrecursivepattern yield pattern 0 yield from rlistdirdirname dirfd dironly includehiddenincludehidden if dironly is false yields all file names inside a directory if dironly is true yields only directory names def iterdirdirname dirfd dironly try fd none fsencode none if dirfd is not none if dirname fd arg os opendirname diropenflags dirfddirfd else arg dirfd if isinstancedirname bytes fsencode os fsencode elif dirname arg dirname elif isinstancedirname bytes arg bytesos curdir ascii else arg os curdir try with os scandirarg as it for entry in it try if not dironly or entry isdir if fsencode is not none yield fsencodeentry name else yield entry name except oserror pass finally if fd is not none os closefd except oserror return def listdirdirname dirfd dironly with contextlib closingiterdirdirname dirfd dironly as it return listit recursively yields relative pathnames inside a literal directory def rlistdirdirname dirfd dironly includehiddenfalse names listdirdirname dirfd dironly for x in names if includehidden or not ishiddenx yield x path joindirname x if dirname else x for y in rlistdirpath dirfd dironly includehiddenincludehidden yield joinx y def lexistspathname dirfd same as os path lexists but with dirfd if dirfd is none return os path lexistspathname try os lstatpathname dirfddirfd except oserror valueerror return false else return true def isdirpathname dirfd same as os path isdir but with dirfd if dirfd is none return os path isdirpathname try st os statpathname dirfddirfd except oserror valueerror return false else return stat sisdirst stmode def joindirname basename it is common if dirname or basename is empty if not dirname or not basename return dirname or basename return os path joindirname basename magiccheck re compile magiccheckbytes re compileb def hasmagics if isinstances bytes match magiccheckbytes searchs else match magiccheck searchs return match is not none def ishiddenpath return path0 in b 0 def isrecursivepattern if isinstancepattern bytes return pattern b else return pattern def escapepathname escaping is done by wrapping any of between square brackets metacharacters do not work in the drive part and shouldn t be escaped drive pathname os path splitdrivepathname if isinstancepathname bytes pathname magiccheckbytes subbr 1 pathname else pathname magiccheck subr 1 pathname return drive pathname diropenflags os ordonly getattros odirectory 0 def translatepat recursivefalse includehiddenfalse sepsnone if not seps if os path altsep seps os path sep os path altsep else seps os path sep escapedseps joinmapre escape seps anysep f escapedseps if lenseps 1 else escapedseps notsep f escapedseps if includehidden onelastsegment f notsep onesegment f onelastsegmentanysep anysegments f anysep anylastsegments else onelastsegment f escapedseps notsep onesegment f onelastsegmentanysep anysegments f onesegment anylastsegments f anysegments onelastsegment results parts re splitanysep pat lastpartidx lenparts 1 for idx part in enumerateparts if part results appendonesegment if idx lastpartidx else onelastsegment continue if recursive if part if idx lastpartidx if partsidx 1 results appendanysegments else results appendanylastsegments continue elif in part raise valueerrorinvalid pattern can only be an entire path component if part if not includehidden and part0 in results appendr results extendfnmatch translatepart f notsep notsep if idx lastpartidx results appendanysep res joinresults return fr s resz return a list of paths matching a pathname pattern the pattern may contain simple shell style wildcards a la fnmatch unlike fnmatch filenames starting with a dot are special cases that are not matched by and patterns by default if include_hidden is true the patterns will match hidden directories if recursive is true the pattern will match any files and zero or more directories and subdirectories return an iterator which yields the paths matching a pathname pattern the pattern may contain simple shell style wildcards a la fnmatch however unlike fnmatch filenames starting with a dot are special cases that are not matched by and patterns if recursive is true the pattern will match any files and zero or more directories and subdirectories skip empty string patterns ending with a slash should match only directories os path split returns the argument itself as a dirname if it is a drive or unc path prevent an infinite recursion if a drive or unc path contains magic characters i e r c these 2 helper functions non recursively glob inside a literal directory they return a list of basenames _glob1 accepts a pattern while _glob0 takes a literal basename so it only has to check for its existence os path split returns an empty basename for paths ending with a directory separator q x should match only directories following functions are not public but can be used by third party code this helper function recursively yields relative pathnames inside a literal directory if dironly is false yields all file names inside a directory if dironly is true yields only directory names recursively yields relative pathnames inside a literal directory same as os path lexists but with dir_fd same as os path isdir but with dir_fd it is common if dirname or basename is empty escape all special characters escaping is done by wrapping any of between square brackets metacharacters do not work in the drive part and shouldn t be escaped translate a pathname with shell wildcards to a regular expression if recursive is true the pattern segment will match any number of path segments if appears outside its own segment valueerror will be raised if include_hidden is true wildcards can match path segments beginning with a dot if a sequence of separator characters is given to seps they will be used to split the pattern into segments and match path separators if not given os path sep and os path altsep where available are used
import contextlib
import os
import re
import fnmatch
import itertools
import stat
import sys

__all__ = ["glob", "iglob", "escape"]

def glob(pathname, *, root_dir=None, dir_fd=None, recursive=False,
        include_hidden=False):
    
    return list(iglob(pathname, root_dir=root_dir, dir_fd=dir_fd, recursive=recursive,
                      include_hidden=include_hidden))

def iglob(pathname, *, root_dir=None, dir_fd=None, recursive=False,
          include_hidden=False):
    
    sys.audit("glob.glob", pathname, recursive)
    sys.audit("glob.glob/2", pathname, recursive, root_dir, dir_fd)
    if root_dir is not None:
        root_dir = os.fspath(root_dir)
    else:
        root_dir = pathname[:0]
    it = _iglob(pathname, root_dir, dir_fd, recursive, False,
                include_hidden=include_hidden)
    if not pathname or recursive and _isrecursive(pathname[:2]):
        try:
            s = next(it)  
            if s:
                it = itertools.chain((s,), it)
        except StopIteration:
            pass
    return it

def _iglob(pathname, root_dir, dir_fd, recursive, dironly,
           include_hidden=False):
    dirname, basename = os.path.split(pathname)
    if not has_magic(pathname):
        assert not dironly
        if basename:
            if _lexists(_join(root_dir, pathname), dir_fd):
                yield pathname
        else:
            
            if _isdir(_join(root_dir, dirname), dir_fd):
                yield pathname
        return
    if not dirname:
        if recursive and _isrecursive(basename):
            yield from _glob2(root_dir, basename, dir_fd, dironly,
                             include_hidden=include_hidden)
        else:
            yield from _glob1(root_dir, basename, dir_fd, dironly,
                              include_hidden=include_hidden)
        return
    
    
    
    if dirname != pathname and has_magic(dirname):
        dirs = _iglob(dirname, root_dir, dir_fd, recursive, True,
                      include_hidden=include_hidden)
    else:
        dirs = [dirname]
    if has_magic(basename):
        if recursive and _isrecursive(basename):
            glob_in_dir = _glob2
        else:
            glob_in_dir = _glob1
    else:
        glob_in_dir = _glob0
    for dirname in dirs:
        for name in glob_in_dir(_join(root_dir, dirname), basename, dir_fd, dironly,
                               include_hidden=include_hidden):
            yield os.path.join(dirname, name)





def _glob1(dirname, pattern, dir_fd, dironly, include_hidden=False):
    names = _listdir(dirname, dir_fd, dironly)
    if include_hidden or not _ishidden(pattern):
        names = (x for x in names if include_hidden or not _ishidden(x))
    return fnmatch.filter(names, pattern)

def _glob0(dirname, basename, dir_fd, dironly, include_hidden=False):
    if basename:
        if _lexists(_join(dirname, basename), dir_fd):
            return [basename]
    else:
        
        
        if _isdir(dirname, dir_fd):
            return [basename]
    return []



def glob0(dirname, pattern):
    return _glob0(dirname, pattern, None, False)

def glob1(dirname, pattern):
    return _glob1(dirname, pattern, None, False)




def _glob2(dirname, pattern, dir_fd, dironly, include_hidden=False):
    assert _isrecursive(pattern)
    yield pattern[:0]
    yield from _rlistdir(dirname, dir_fd, dironly,
                         include_hidden=include_hidden)



def _iterdir(dirname, dir_fd, dironly):
    try:
        fd = None
        fsencode = None
        if dir_fd is not None:
            if dirname:
                fd = arg = os.open(dirname, _dir_open_flags, dir_fd=dir_fd)
            else:
                arg = dir_fd
            if isinstance(dirname, bytes):
                fsencode = os.fsencode
        elif dirname:
            arg = dirname
        elif isinstance(dirname, bytes):
            arg = bytes(os.curdir, 'ASCII')
        else:
            arg = os.curdir
        try:
            with os.scandir(arg) as it:
                for entry in it:
                    try:
                        if not dironly or entry.is_dir():
                            if fsencode is not None:
                                yield fsencode(entry.name)
                            else:
                                yield entry.name
                    except OSError:
                        pass
        finally:
            if fd is not None:
                os.close(fd)
    except OSError:
        return

def _listdir(dirname, dir_fd, dironly):
    with contextlib.closing(_iterdir(dirname, dir_fd, dironly)) as it:
        return list(it)


def _rlistdir(dirname, dir_fd, dironly, include_hidden=False):
    names = _listdir(dirname, dir_fd, dironly)
    for x in names:
        if include_hidden or not _ishidden(x):
            yield x
            path = _join(dirname, x) if dirname else x
            for y in _rlistdir(path, dir_fd, dironly,
                               include_hidden=include_hidden):
                yield _join(x, y)


def _lexists(pathname, dir_fd):
    
    if dir_fd is None:
        return os.path.lexists(pathname)
    try:
        os.lstat(pathname, dir_fd=dir_fd)
    except (OSError, ValueError):
        return False
    else:
        return True

def _isdir(pathname, dir_fd):
    
    if dir_fd is None:
        return os.path.isdir(pathname)
    try:
        st = os.stat(pathname, dir_fd=dir_fd)
    except (OSError, ValueError):
        return False
    else:
        return stat.S_ISDIR(st.st_mode)

def _join(dirname, basename):
    
    if not dirname or not basename:
        return dirname or basename
    return os.path.join(dirname, basename)

magic_check = re.compile('([*?[])')
magic_check_bytes = re.compile(b'([*?[])')

def has_magic(s):
    if isinstance(s, bytes):
        match = magic_check_bytes.search(s)
    else:
        match = magic_check.search(s)
    return match is not None

def _ishidden(path):
    return path[0] in ('.', b'.'[0])

def _isrecursive(pattern):
    if isinstance(pattern, bytes):
        return pattern == b'**'
    else:
        return pattern == '**'

def escape(pathname):
    
    drive, pathname = os.path.splitdrive(pathname)
    if isinstance(pathname, bytes):
        pathname = magic_check_bytes.sub(br'[\1]', pathname)
    else:
        pathname = magic_check.sub(r'[\1]', pathname)
    return drive + pathname


_dir_open_flags = os.O_RDONLY | getattr(os, 'O_DIRECTORY', 0)


def translate(pat, *, recursive=False, include_hidden=False, seps=None):
    
    if not seps:
        if os.path.altsep:
            seps = (os.path.sep, os.path.altsep)
        else:
            seps = os.path.sep
    escaped_seps = ''.join(map(re.escape, seps))
    any_sep = f'[{escaped_seps}]' if len(seps) > 1 else escaped_seps
    not_sep = f'[^{escaped_seps}]'
    if include_hidden:
        one_last_segment = f'{not_sep}+'
        one_segment = f'{one_last_segment}{any_sep}'
        any_segments = f'(?:.+{any_sep})?'
        any_last_segments = '.*'
    else:
        one_last_segment = f'[^{escaped_seps}.]{not_sep}*'
        one_segment = f'{one_last_segment}{any_sep}'
        any_segments = f'(?:{one_segment})*'
        any_last_segments = f'{any_segments}(?:{one_last_segment})?'

    results = []
    parts = re.split(any_sep, pat)
    last_part_idx = len(parts) - 1
    for idx, part in enumerate(parts):
        if part == '*':
            results.append(one_segment if idx < last_part_idx else one_last_segment)
            continue
        if recursive:
            if part == '**':
                if idx < last_part_idx:
                    if parts[idx + 1] != '**':
                        results.append(any_segments)
                else:
                    results.append(any_last_segments)
                continue
            elif '**' in part:
                raise ValueError("Invalid pattern: '**' can only be an entire path component")
        if part:
            if not include_hidden and part[0] in '*?':
                results.append(r'(?!\.)')
            results.extend(fnmatch._translate(part, f'{not_sep}*', not_sep))
        if idx < last_part_idx:
            results.append(any_sep)
    res = ''.join(results)
    return fr'(?s:{res})\Z'