PageRenderTime 35ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 1ms

/IPython/external/mglob/_mglob.py

https://github.com/cboos/ipython
Python | 227 lines | 218 code | 0 blank | 9 comment | 0 complexity | 056dc30d8cf3726792b5892da1c51404 MD5 | raw file
  1. r""" mglob - enhanced file list expansion module
  2. Use as stand-alone utility (for xargs, `backticks` etc.),
  3. or a globbing library for own python programs. Globbing the sys.argv is something
  4. that almost every Windows script has to perform manually, and this module is here
  5. to help with that task. Also Unix users will benefit from enhanced modes
  6. such as recursion, exclusion, directory omission...
  7. Unlike glob.glob, directories are not included in the glob unless specified
  8. with 'dir:'
  9. 'expand' is the function to use in python programs. Typical use
  10. to expand argv (esp. in windows)::
  11. try:
  12. import mglob
  13. files = mglob.expand(sys.argv[1:])
  14. except ImportError:
  15. print "mglob not found; try 'easy_install mglob' for extra features"
  16. files = sys.argv[1:]
  17. Note that for unix, shell expands *normal* wildcards (*.cpp, etc.) in argv.
  18. Therefore, you might want to use quotes with normal wildcards to prevent this
  19. expansion, in order for mglob to see the wildcards and get the wanted behaviour.
  20. Not quoting the wildcards is harmless and typically has equivalent results, though.
  21. Author: Ville Vainio <vivainio@gmail.com>
  22. License: MIT Open Source license
  23. """
  24. #Assigned in variable for "usage" printing convenience"
  25. globsyntax = """\
  26. This program allows specifying filenames with "mglob" mechanism.
  27. Supported syntax in globs (wilcard matching patterns)::
  28. *.cpp ?ellowo*
  29. - obvious. Differs from normal glob in that dirs are not included.
  30. Unix users might want to write this as: "*.cpp" "?ellowo*"
  31. rec:/usr/share=*.txt,*.doc
  32. - get all *.txt and *.doc under /usr/share,
  33. recursively
  34. rec:/usr/share
  35. - All files under /usr/share, recursively
  36. rec:*.py
  37. - All .py files under current working dir, recursively
  38. foo
  39. - File or dir foo
  40. !*.bak readme*
  41. - readme*, exclude files ending with .bak
  42. !.svn/ !.hg/ !*_Data/ rec:.
  43. - Skip .svn, .hg, foo_Data dirs (and their subdirs) in recurse.
  44. Trailing / is the key, \ does not work! Use !.*/ for all hidden.
  45. dir:foo
  46. - the directory foo if it exists (not files in foo)
  47. dir:*
  48. - all directories in current folder
  49. foo.py bar.* !h* rec:*.py
  50. - Obvious. !h* exclusion only applies for rec:*.py.
  51. foo.py is *not* included twice.
  52. @filelist.txt
  53. - All files listed in 'filelist.txt' file, on separate lines.
  54. "cont:class \wak:" rec:*.py
  55. - Match files containing regexp. Applies to subsequent files.
  56. note quotes because of whitespace.
  57. """
  58. __version__ = "0.2"
  59. import os,glob,fnmatch,sys,re
  60. def expand(flist,exp_dirs = False):
  61. """ Expand the glob(s) in flist.
  62. flist may be either a whitespace-separated list of globs/files
  63. or an array of globs/files.
  64. if exp_dirs is true, directory names in glob are expanded to the files
  65. contained in them - otherwise, directory names are returned as is.
  66. """
  67. if isinstance(flist, basestring):
  68. import shlex
  69. flist = shlex.split(flist)
  70. done_set = set()
  71. denied_set = set()
  72. cont_set = set()
  73. cur_rejected_dirs = set()
  74. def recfind(p, pats = ["*"]):
  75. denied_dirs = [os.path.dirname(d) for d in denied_set if d.endswith("/")]
  76. for (dp,dnames,fnames) in os.walk(p):
  77. # see if we should ignore the whole directory
  78. dp_norm = dp.replace("\\","/") + "/"
  79. deny = False
  80. # do not traverse under already rejected dirs
  81. for d in cur_rejected_dirs:
  82. if dp.startswith(d):
  83. deny = True
  84. break
  85. if deny:
  86. continue
  87. #print "dp",dp
  88. bname = os.path.basename(dp)
  89. for deny_pat in denied_dirs:
  90. if fnmatch.fnmatch( bname, deny_pat):
  91. deny = True
  92. cur_rejected_dirs.add(dp)
  93. break
  94. if deny:
  95. continue
  96. for f in fnames:
  97. matched = False
  98. for p in pats:
  99. if fnmatch.fnmatch(f,p):
  100. matched = True
  101. break
  102. if matched:
  103. yield os.path.join(dp,f)
  104. def once_filter(seq):
  105. for it in seq:
  106. p = os.path.abspath(it)
  107. if p in done_set:
  108. continue
  109. done_set.add(p)
  110. deny = False
  111. for deny_pat in denied_set:
  112. if fnmatch.fnmatch(os.path.basename(p), deny_pat):
  113. deny = True
  114. break
  115. if cont_set:
  116. try:
  117. cont = open(p).read()
  118. except IOError:
  119. # deny
  120. continue
  121. for pat in cont_set:
  122. if not re.search(pat,cont, re.IGNORECASE):
  123. deny = True
  124. break
  125. if not deny:
  126. yield it
  127. return
  128. res = []
  129. for ent in flist:
  130. ent = os.path.expanduser(os.path.expandvars(ent))
  131. if ent.lower().startswith('rec:'):
  132. fields = ent[4:].split('=')
  133. if len(fields) == 2:
  134. pth, patlist = fields
  135. elif len(fields) == 1:
  136. if os.path.isdir(fields[0]):
  137. # single arg is dir
  138. pth, patlist = fields[0], '*'
  139. else:
  140. # single arg is pattern
  141. pth, patlist = '.', fields[0]
  142. elif len(fields) == 0:
  143. pth, pathlist = '.','*'
  144. pats = patlist.split(',')
  145. res.extend(once_filter(recfind(pth, pats)))
  146. # filelist
  147. elif ent.startswith('@') and os.path.isfile(ent[1:]):
  148. res.extend(once_filter(open(ent[1:]).read().splitlines()))
  149. # exclusion
  150. elif ent.startswith('!'):
  151. denied_set.add(ent[1:])
  152. # glob only dirs
  153. elif ent.lower().startswith('dir:'):
  154. res.extend(once_filter(filter(os.path.isdir,glob.glob(ent[4:]))))
  155. elif ent.lower().startswith('cont:'):
  156. cont_set.add(ent[5:])
  157. # get all files in the specified dir
  158. elif os.path.isdir(ent) and exp_dirs:
  159. res.extend(once_filter(filter(os.path.isfile,glob.glob(ent + os.sep+"*"))))
  160. # glob only files
  161. elif '*' in ent or '?' in ent:
  162. res.extend(once_filter(filter(os.path.isfile,glob.glob(ent))))
  163. else:
  164. res.extend(once_filter([ent]))
  165. return res
  166. def test():
  167. assert (
  168. expand("*.py ~/.ipython/*.py rec:/usr/share/doc-base") ==
  169. expand( ['*.py', '~/.ipython/*.py', 'rec:/usr/share/doc-base'] )
  170. )
  171. def main():
  172. if len(sys.argv) < 2:
  173. print globsyntax
  174. return
  175. print "\n".join(expand(sys.argv[1:])),
  176. def mglob_f(self, arg):
  177. from IPython.utils.text import SList
  178. if arg.strip():
  179. return SList(expand(arg))
  180. print "Please specify pattern!"
  181. print globsyntax
  182. def init_ipython(ip):
  183. """ register %mglob for IPython """
  184. mglob_f.__doc__ = globsyntax
  185. ip.define_magic("mglob",mglob_f)
  186. # test()
  187. if __name__ == "__main__":
  188. main()