From 3cf40a0a28eabba7ae1e5c949659f709800c7317 Mon Sep 17 00:00:00 2001 From: David Rose Date: Wed, 24 Feb 2010 01:58:36 +0000 Subject: [PATCH] support glob for vfs --- direct/src/p3d/AppRunner.py | 17 ++++++-- direct/src/stdpy/file.py | 28 ++++++++++++- direct/src/stdpy/glob.py | 82 +++++++++++++++++++++++++++++++++++++ 3 files changed, 122 insertions(+), 5 deletions(-) create mode 100755 direct/src/stdpy/glob.py diff --git a/direct/src/p3d/AppRunner.py b/direct/src/p3d/AppRunner.py index fa625b59dd..d095a8de38 100644 --- a/direct/src/p3d/AppRunner.py +++ b/direct/src/p3d/AppRunner.py @@ -35,7 +35,7 @@ else: from direct.showbase.DirectObject import DirectObject from pandac.PandaModules import VirtualFileSystem, Filename, Multifile, loadPrcFileData, unloadPrcFile, getModelPath, Thread, WindowProperties, ExecutionEnvironment, PandaSystem, Notify, StreamWriter, ConfigVariableString, initAppForGui from pandac import PandaModules -from direct.stdpy import file +from direct.stdpy import file, glob from direct.task.TaskManagerGlobal import taskMgr from direct.showbase.MessengerGlobal import messenger from direct.showbase import AppRunnerGlobal @@ -406,7 +406,7 @@ class AppRunner(DirectObject): exception handler. This is generally the program's main loop when running in a p3d environment (except on unusual platforms like the iPhone, which have to hand the main loop off to the - OS, and don't use this interface. """ + OS, and don't use this interface). """ try: taskMgr.run() @@ -478,7 +478,7 @@ class AppRunner(DirectObject): # Hang a hook so we know when the window is actually opened. self.acceptOnce('window-event', self.__windowEvent) - # Look for the startup Python file. This may be a magic + # Look for the startup Python file. This might be a magic # filename (like "__main__", or any filename that contains # invalid module characters), so we can't just import it # directly; instead, we go through the low-level importer. @@ -958,10 +958,19 @@ def dummyAppRunner(tokens = [], argv = None): appRunner.initPackedAppEnvironment() + # Replace some of the standard Python I/O functions with the Panda + # variants that are specially crafted to respect the vfs. __builtin__.file = file.file __builtin__.open = file.open os.listdir = file.listdir os.walk = file.walk - + os.path.isfile = file.isfile + os.path.isdir = file.isdir + os.path.exists = file.exists + os.path.lexists = file.lexists + os.path.getmtime = file.getmtime + os.path.getsize = file.getsize + sys.modules['glob'] = glob + return appRunner diff --git a/direct/src/stdpy/file.py b/direct/src/stdpy/file.py index 902837e161..2a32b42548 100644 --- a/direct/src/stdpy/file.py +++ b/direct/src/stdpy/file.py @@ -5,7 +5,8 @@ SIMPLE_THREADS model, by avoiding blocking all threads while waiting for I/O to complete. """ __all__ = [ - 'file', 'open', 'listdir', 'walk', 'join' + 'file', 'open', 'listdir', 'walk', 'join', + 'isfile', 'isdir', 'exists', 'lexists', 'getmtime', 'getsize', ] from pandac import PandaModules as pm @@ -286,3 +287,28 @@ def walk(top, topdown = True, onerror = None, followlinks = True): def join(a, b): return '%s/%s' % (a, b) + +def isfile(path): + return _vfs.isRegularFile(pm.Filename.fromOsSpecific(path)) + +def isdir(path): + return _vfs.isDirectory(pm.Filename.fromOsSpecific(path)) + +def exists(path): + return _vfs.exists(pm.Filename.fromOsSpecific(path)) + +def lexists(path): + return _vfs.exists(pm.Filename.fromOsSpecific(path)) + +def getmtime(path): + file = _vfs.getFile(pm.Filename.fromOsSpecific(path), True) + if not file: + raise os.error + return file.getTimestamp() + +def getsize(path): + file = _vfs.getFile(pm.Filename.fromOsSpecific(path), True) + if not file: + raise os.error + return file.getFileSize() + diff --git a/direct/src/stdpy/glob.py b/direct/src/stdpy/glob.py new file mode 100755 index 0000000000..9ff4ec237b --- /dev/null +++ b/direct/src/stdpy/glob.py @@ -0,0 +1,82 @@ +""" This module reimplements Python's native glob module using Panda +vfs constructs. This enables Python to interface more easily with Panda's +virtual file system. """ + +import sys +import os +import re +import fnmatch + +from direct.stdpy import file + +__all__ = ["glob", "iglob"] + +def glob(pathname): + """Return a list of paths matching a pathname pattern. + + The pattern may contain simple shell-style wildcards a la fnmatch. + + """ + return list(iglob(pathname)) + +def iglob(pathname): + """Return an iterator which yields the paths matching a pathname pattern. + + The pattern may contain simple shell-style wildcards a la fnmatch. + + """ + if not has_magic(pathname): + if file.lexists(pathname): + yield pathname + return + dirname, basename = os.path.split(pathname) + if not dirname: + for name in glob1(os.curdir, basename): + yield name + return + if has_magic(dirname): + dirs = iglob(dirname) + else: + dirs = [dirname] + if has_magic(basename): + glob_in_dir = glob1 + else: + glob_in_dir = glob0 + for dirname in dirs: + for name in glob_in_dir(dirname, basename): + yield os.path.join(dirname, name) + +# 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). + +def glob1(dirname, pattern): + if not dirname: + dirname = os.curdir + if isinstance(pattern, unicode) and not isinstance(dirname, unicode): + dirname = unicode(dirname, sys.getfilesystemencoding() or + sys.getdefaultencoding()) + try: + names = os.listdir(dirname) + except os.error: + return [] + if pattern[0] != '.': + names = filter(lambda x: x[0] != '.', names) + return fnmatch.filter(names, pattern) + +def glob0(dirname, basename): + if basename == '': + # `os.path.split()` returns an empty basename for paths ending with a + # directory separator. 'q*x/' should match only directories. + if file.isdir(dirname): + return [basename] + else: + if file.lexists(os.path.join(dirname, basename)): + return [basename] + return [] + + +magic_check = re.compile('[*?[]') + +def has_magic(s): + return magic_check.search(s) is not None