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 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.PandaModules import VirtualFileSystem, Filename, Multifile, loadPrcFileData, unloadPrcFile, getModelPath, Thread, WindowProperties, ExecutionEnvironment, PandaSystem, Notify, StreamWriter, ConfigVariableString, initAppForGui
 from pandac import PandaModules
 from pandac import PandaModules
-from direct.stdpy import file
+from direct.stdpy import file, glob
 from direct.task.TaskManagerGlobal import taskMgr
 from direct.task.TaskManagerGlobal import taskMgr
 from direct.showbase.MessengerGlobal import messenger
 from direct.showbase.MessengerGlobal import messenger
 from direct.showbase import AppRunnerGlobal
 from direct.showbase import AppRunnerGlobal
@@ -406,7 +406,7 @@ class AppRunner(DirectObject):
         exception handler.  This is generally the program's main loop
         exception handler.  This is generally the program's main loop
         when running in a p3d environment (except on unusual platforms
         when running in a p3d environment (except on unusual platforms
         like the iPhone, which have to hand the main loop off to the
         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:
         try:
             taskMgr.run()
             taskMgr.run()
@@ -478,7 +478,7 @@ class AppRunner(DirectObject):
             # Hang a hook so we know when the window is actually opened.
             # Hang a hook so we know when the window is actually opened.
             self.acceptOnce('window-event', self.__windowEvent)
             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
             # filename (like "__main__", or any filename that contains
             # invalid module characters), so we can't just import it
             # invalid module characters), so we can't just import it
             # directly; instead, we go through the low-level importer.
             # directly; instead, we go through the low-level importer.
@@ -958,10 +958,19 @@ def dummyAppRunner(tokens = [], argv = None):
 
 
     appRunner.initPackedAppEnvironment()
     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__.file = file.file
     __builtin__.open = file.open
     __builtin__.open = file.open
     os.listdir = file.listdir
     os.listdir = file.listdir
     os.walk = file.walk
     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
     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. """
 for I/O to complete. """
 
 
 __all__ = [
 __all__ = [
-    'file', 'open', 'listdir', 'walk', 'join'
+    'file', 'open', 'listdir', 'walk', 'join',
+    'isfile', 'isdir', 'exists', 'lexists', 'getmtime', 'getsize',
     ]
     ]
 
 
 from pandac import PandaModules as pm
 from pandac import PandaModules as pm
@@ -286,3 +287,28 @@ def walk(top, topdown = True, onerror = None, followlinks = True):
 
 
 def join(a, b):
 def join(a, b):
     return '%s/%s' % (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