Browse Source

support glob for vfs

David Rose 16 years ago
parent
commit
3cf40a0a28
3 changed files with 122 additions and 5 deletions
  1. 13 4
      direct/src/p3d/AppRunner.py
  2. 27 1
      direct/src/stdpy/file.py
  3. 82 0
      direct/src/stdpy/glob.py

+ 13 - 4
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
 

+ 27 - 1
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()
+

+ 82 - 0
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