~ipython-dev/ipython/0.10.1

« back to all changes in this revision

Viewing changes to IPython/external/mglob.py

  • Committer: Fernando Perez
  • Date: 2008-06-02 01:26:30 UTC
  • mfrom: (0.1.130 ipython-local)
  • Revision ID: fernando.perez@berkeley.edu-20080602012630-m14vezrhydzvahf8
Merge in all development done in bzr since February 16 2008.

At that time, a clean bzr branch was started from the SVN tree, but
without SVN history.  That SVN history has now been used as the basis
of this branch, and the development done on the history-less BZR
branch has been added and is the content of this merge.  

This branch will be the new official main line of development in
Launchpad (equivalent to the old SVN trunk).

Show diffs side-by-side

added added

removed removed

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