1
###############################################################################
3
# Purpose: File Management Utilities. #
4
# Author: Cody Precord <cprecord@editra.org> #
5
# Copyright: (c) 2009 Cody Precord <staff@editra.org> #
6
# Licence: wxWindows Licence #
7
###############################################################################
10
Editra Business Model Library: File Utilities
12
Utility functions for managing and working with files.
16
__author__ = "Cody Precord <cprecord@editra.org>"
17
__svnid__ = "$Id: fileutil.py 71689 2012-06-07 18:55:45Z CJP $"
18
__revision__ = "$Revision: 71689 $"
20
__all__ = [ 'GetAbsPath', 'GetFileExtension', 'GetFileModTime', 'GetFileName',
21
'GetFileSize', 'GetPathName', 'GetPathFromURI', 'GetUniqueName',
22
'IsLink', 'MakeNewFile', 'MakeNewFolder', 'PathExists',
23
'ResolveRealPath', 'IsExecutable', 'Which', 'ComparePaths',
24
'AddFileExtension', 'GetDirectoryObject', 'File', 'Directory',
25
'GetFileManagerCmd', 'OpenWithFileManager', 'IsHidden', 'IsSubPath' ]
27
#-----------------------------------------------------------------------------#
38
if wx.Platform == '__WXMSW__':
41
# Check for if win32 extensions are available
42
import win32com.client as win32client
54
#-----------------------------------------------------------------------------#
57
"""Decorator method to convert path arguments that may be uri's to
58
real file system paths. Arg 0 must be a file path or uri.
61
def WrapURI(*args, **kwargs):
63
args[0] = GetPathFromURI(args[0])
64
return func(*args, **kwargs)
66
WrapURI.__name__ = func.__name__
67
WrapURI.__doc__ = func.__doc__
70
#-----------------------------------------------------------------------------#
72
def AddFileExtension(path, ext):
73
"""Add a file extension to a path if it doesn't already exist
74
@param path: file path
75
@param ext: file extension
78
assert isinstance(ext, basestring)
79
if not ext.startswith('.'):
81
if not path.endswith(ext):
85
def ComparePaths(path1, path2):
86
"""Determine whether the two given paths are equivalent
92
path1 = GetAbsPath(path1)
93
path2 = GetAbsPath(path2)
99
def CopyFile(orig, dest):
100
"""Copy the given file to the destination
101
@param orig: file to copy (full path)
102
@param dest: where to copy to
105
raise NotImplementedError
108
def GetAbsPath(path):
109
"""Get the absolute path of a file of a file.
112
@note: on windows if win32api is available short notation paths will be
113
converted to the proper long name.
116
rpath = os.path.abspath(path)
117
# Resolve short path notation on Windows when possible
118
if WIN and win32api is not None and u"~" in rpath:
120
rpath = win32api.GetLongPathNameW(rpath)
122
# Ignore errors from win32api calls
126
def GetFileExtension(file_str):
127
"""Gets last atom at end of string as extension if
128
no extension whole string is returned
129
@param file_str: path or file name to get extension from
132
return file_str.split('.')[-1]
134
def GetFileModTime(file_name):
135
"""Returns the time that the given file was last modified on
136
@param file_name: path of file to get mtime of
140
mod_time = os.path.getmtime(file_name)
141
except (OSError, EnvironmentError):
145
def GetFileName(path):
146
"""Gets last atom on end of string as filename
147
@param path: full path to get filename from
150
return os.path.split(path)[-1]
153
def GetFileSize(path):
154
"""Get the size of the file at a given path
155
@param path: Path to file
160
return os.stat(path)[stat.ST_SIZE]
164
def GetPathFromURI(path):
165
"""Get a local path from a file:// uri
166
@return: normalized path
169
if path.startswith(u"file:"):
170
path = path.replace(u"file:", u"")
171
path = path.lstrip(u"/")
172
if platform.system().lower() in ('windows', 'microsoft'):
173
path = path.replace(u"/", u"\\")
174
if len(path) >= 2 and path[1] != u':':
175
# A valid windows file uri should start with the drive
176
# letter. If not make the assumption that it should be
178
path = u"C:\\\\" + path
181
path = urllib2.unquote(path)
186
def GetPathName(path):
187
"""Gets the path minus filename
188
@param path: full path to get base of
191
return os.path.split(path)[0]
195
"""Is the file a link
200
return path.endswith(".lnk") or os.path.islink(path)
202
return os.path.islink(path)
204
def IsSubPath(path1, path2):
205
"""Is path1 a subpath of path2
206
i.e) /usr/bin/foo is a subpath of /usr/bin
211
path1 = path1.lower()
212
path2 = path2.lower()
213
path1 = GetAbsPath(path1)
214
path2 = GetAbsPath(path2)
215
return path1.startswith(path2)
219
"""Is the path a hidden path
220
@param path: path to check
228
attrs = ctypes.windll.kernel32.GetFileAttributesW(path)
230
bHidden = bool(attrs & 2)
231
except (AttributeError, AssertionError):
234
dname = GetFileName(path)
235
bHidden = dname.startswith('.')
239
def PathExists(path):
240
"""Does the path exist.
241
@param path: file path or uri
245
return os.path.exists(path)
248
def IsExecutable(path):
249
"""Is the file at the given path an executable file
250
@param path: file path
254
return os.path.isfile(path) and os.access(path, os.X_OK)
257
def ResolveRealPath(link):
258
"""Return the real path of the link file
259
@param link: path of link file
263
assert IsLink(link), "ResolveRealPath expects a link file!"
265
if WIN and win32client is not None:
266
shell = win32client.Dispatch("WScript.Shell")
267
shortcut = shell.CreateShortCut(link)
268
realpath = shortcut.Targetpath
270
realpath = os.path.realpath(link)
273
def GetFileManagerCmd():
274
"""Get the file manager open command for the current os. Under linux
275
it will check for xdg-open, nautilus, konqueror, and Thunar, it will then
276
return which one it finds first or 'nautilus' it finds nothing.
280
if wx.Platform == '__WXMAC__':
282
elif wx.Platform == '__WXMSW__':
285
# Check for common linux filemanagers returning first one found
286
# Gnome/ubuntu KDE/kubuntu xubuntu
287
for cmd in ('xdg-open', 'nautilus', 'konqueror', 'Thunar'):
288
result = os.system("which %s > /dev/null" % cmd)
294
def OpenWithFileManager(path):
295
"""Open the given path with the systems file manager
296
@param path: file/directory path
299
cmd = GetFileManagerCmd()
300
subprocess.call([cmd, path])
303
"""Find the path of the given executable
304
@param program: executable name (i.e 'python')
305
@return: executable path or None
308
# Check local directory first
309
if IsExecutable(program):
312
# Start looking on the $PATH
313
for path in os.environ["PATH"].split(os.pathsep):
314
exe_file = os.path.join(path, program)
315
if IsExecutable(exe_file):
319
def GetDirectoryObject(path, recurse=True, includedot=False):
320
"""Gets a L{Directory} object representing the filesystem of the
322
@param path: base path to list
323
@keyword recurse: recurse into subdirectories
324
@keyword includedot: include '.' files
325
@return: L{Directory} object instance
328
assert os.path.isdir(path)
329
def _BuildDir(thedir):
330
dirAddFile = thedir.Files.append
331
isdir = os.path.isdir
333
for fname in os.listdir(thedir.Path):
334
if not includedot and fname.startswith('.'):
336
fpath = pjoin(thedir.Path, fname)
338
newobj = Directory(fpath)
345
dobj = Directory(path)
349
#-----------------------------------------------------------------------------#
352
"""Basic file data structure"""
353
__slots__ = ('path', 'modtime')
354
def __init__(self, path):
355
super(File, self).__init__()
358
self.modtime = GetFileModTime(self.path)
360
Path = property(lambda self: self.path)
361
Name = property(lambda self: os.path.basename(self.Path))
362
ModTime = property(lambda self: self.modtime,
363
lambda self, mod: setattr(self, 'modtime', mod))
368
def __eq__(self, other):
369
assert isinstance(other, File)
370
return ComparePaths(self.Path, other.Path)
372
class Directory(File):
373
"""Basic directory data structure.
374
Is a container class that provides a simple in memory representation of
378
__slots__ = ('files',)
379
def __init__(self, path):
380
super(Directory, self).__init__(path)
384
Files = property(lambda self: self.files)
386
#-----------------------------------------------------------------------------#
388
def GetUniqueName(path, name):
389
"""Make a file name that will be unique in case a file of the
390
same name already exists at that path.
391
@param path: Root path to folder of files destination
392
@param name: desired file name base
396
tmpname = os.path.join(path, name)
397
if os.path.exists(tmpname):
402
ext = '.' + name.split('.')[-1]
403
fbase = name[:-1 * len(ext)]
405
inc = len([x for x in os.listdir(path) if x.startswith(fbase)])
406
tmpname = os.path.join(path, "%s-%d%s" % (fbase, inc, ext))
407
while os.path.exists(tmpname):
409
tmpname = os.path.join(path, "%s-%d%s" % (fbase, inc, ext))
414
#-----------------------------------------------------------------------------#
416
def MakeNewFile(path, name):
417
"""Make a new file at the given path with the given name.
418
If the file already exists, the given name will be changed to
419
a unique name in the form of name + -NUMBER + .extension
420
@param path: path to directory to create file in
421
@param name: desired name of file
422
@return: Tuple of (success?, Path of new file OR Error message)
425
if not os.path.isdir(path):
426
path = os.path.dirname(path)
427
fname = GetUniqueName(path, name)
430
open(fname, 'w').close()
431
except (IOError, OSError), msg:
432
return (False, str(msg))
436
def MakeNewFolder(path, name):
437
"""Make a new folder at the given path with the given name.
438
If the folder already exists, the given name will be changed to
439
a unique name in the form of name + -NUMBER.
440
@param path: path to create folder on
441
@param name: desired name for folder
442
@return: Tuple of (success?, new dirname OR Error message)
445
if not os.path.isdir(path):
446
path = os.path.dirname(path)
447
folder = GetUniqueName(path, name)
450
except (OSError, IOError), msg:
451
return (False, str(msg))
453
return (True, folder)