/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/globbing.py

  • Committer: John Ferlito
  • Date: 2009-09-02 04:31:45 UTC
  • mto: (4665.7.1 serve-init)
  • mto: This revision was merged to the branch mainline in revision 4913.
  • Revision ID: johnf@inodes.org-20090902043145-gxdsfw03ilcwbyn5
Add a debian init script for bzr --serve

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006-2011 Canonical Ltd
 
1
# Copyright (C) 2006, 2008 Canonical Ltd
2
2
 
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
22
22
 
23
23
import re
24
24
 
25
 
from . import (
26
 
    lazy_regex,
27
 
    )
28
 
from .trace import (
29
 
    mutter,
30
 
    warning,
 
25
from bzrlib.trace import (
 
26
    warning
31
27
    )
32
28
 
33
29
 
40
36
    must not contain capturing groups.
41
37
    """
42
38
 
43
 
    _expand = lazy_regex.lazy_compile(u'\\\\&')
 
39
    _expand = re.compile(ur'\\&')
44
40
 
45
41
    def __init__(self, source=None):
46
42
        self._pat = None
76
72
 
77
73
    def __call__(self, text):
78
74
        if not self._pat:
79
 
            self._pat = lazy_regex.lazy_compile(
80
 
                u'|'.join([u'(%s)' % p for p in self._pats]),
81
 
                re.UNICODE)
 
75
            self._pat = re.compile(
 
76
                    u'|'.join([u'(%s)' % p for p in self._pats]),
 
77
                    re.UNICODE)
82
78
        return self._pat.sub(self._do_sub, text)
83
79
 
84
80
    def _do_sub(self, m):
90
86
 
91
87
 
92
88
_sub_named = Replacer()
93
 
_sub_named.add(r'\[:digit:\]', r'\d')
94
 
_sub_named.add(r'\[:space:\]', r'\s')
95
 
_sub_named.add(r'\[:alnum:\]', r'\w')
96
 
_sub_named.add(r'\[:ascii:\]', r'\0-\x7f')
97
 
_sub_named.add(r'\[:blank:\]', r' \t')
98
 
_sub_named.add(r'\[:cntrl:\]', r'\0-\x1f\x7f-\x9f')
 
89
_sub_named.add(ur'\[:digit:\]', ur'\d')
 
90
_sub_named.add(ur'\[:space:\]', ur'\s')
 
91
_sub_named.add(ur'\[:alnum:\]', ur'\w')
 
92
_sub_named.add(ur'\[:ascii:\]', ur'\0-\x7f')
 
93
_sub_named.add(ur'\[:blank:\]', ur' \t')
 
94
_sub_named.add(ur'\[:cntrl:\]', ur'\0-\x1f\x7f-\x9f')
99
95
 
100
96
 
101
97
def _sub_group(m):
127
123
 
128
124
_sub_re = Replacer()
129
125
_sub_re.add(u'^RE:', u'')
130
 
_sub_re.add(u'\\((?!\\?)', u'(?:')
131
 
_sub_re.add(u'\\(\\?P<.*>', _invalid_regex(u'(?:'))
132
 
_sub_re.add(u'\\(\\?P=[^)]*\\)', _invalid_regex(u''))
133
 
_sub_re.add(r'\\+$', _trailing_backslashes_regex)
 
126
_sub_re.add(u'\((?!\?)', u'(?:')
 
127
_sub_re.add(u'\(\?P<.*>', _invalid_regex(u'(?:'))
 
128
_sub_re.add(u'\(\?P=[^)]*\)', _invalid_regex(u''))
 
129
_sub_re.add(ur'\\+$', _trailing_backslashes_regex)
134
130
 
135
131
 
136
132
_sub_fullpath = Replacer()
137
 
_sub_fullpath.add(r'^RE:.*', _sub_re)  # RE:<anything> is a regex
138
 
_sub_fullpath.add(r'\[\^?\]?(?:[^][]|\[:[^]]+:\])+\]',
139
 
                  _sub_group)  # char group
140
 
_sub_fullpath.add(r'(?:(?<=/)|^)(?:\.?/)+', u'')  # canonicalize path
141
 
_sub_fullpath.add(r'\\.', r'\&')  # keep anything backslashed
142
 
_sub_fullpath.add(r'[(){}|^$+.]', r'\\&')  # escape specials
143
 
_sub_fullpath.add(r'(?:(?<=/)|^)\*\*+/', r'(?:.*/)?')  # **/ after ^ or /
144
 
_sub_fullpath.add(r'\*+', r'[^/]*')  # * elsewhere
145
 
_sub_fullpath.add(r'\?', r'[^/]')  # ? everywhere
 
133
_sub_fullpath.add(ur'^RE:.*', _sub_re) # RE:<anything> is a regex
 
134
_sub_fullpath.add(ur'\[\^?\]?(?:[^][]|\[:[^]]+:\])+\]', _sub_group) # char group
 
135
_sub_fullpath.add(ur'(?:(?<=/)|^)(?:\.?/)+', u'') # canonicalize path
 
136
_sub_fullpath.add(ur'\\.', ur'\&') # keep anything backslashed
 
137
_sub_fullpath.add(ur'[(){}|^$+.]', ur'\\&') # escape specials
 
138
_sub_fullpath.add(ur'(?:(?<=/)|^)\*\*+/', ur'(?:.*/)?') # **/ after ^ or /
 
139
_sub_fullpath.add(ur'\*+', ur'[^/]*') # * elsewhere
 
140
_sub_fullpath.add(ur'\?', ur'[^/]') # ? everywhere
146
141
 
147
142
 
148
143
_sub_basename = Replacer()
149
 
_sub_basename.add(r'\[\^?\]?(?:[^][]|\[:[^]]+:\])+\]',
150
 
                  _sub_group)  # char group
151
 
_sub_basename.add(r'\\.', r'\&')  # keep anything backslashed
152
 
_sub_basename.add(r'[(){}|^$+.]', r'\\&')  # escape specials
153
 
_sub_basename.add(r'\*+', r'.*')  # * everywhere
154
 
_sub_basename.add(r'\?', r'.')  # ? everywhere
 
144
_sub_basename.add(ur'\[\^?\]?(?:[^][]|\[:[^]]+:\])+\]', _sub_group) # char group
 
145
_sub_basename.add(ur'\\.', ur'\&') # keep anything backslashed
 
146
_sub_basename.add(ur'[(){}|^$+.]', ur'\\&') # escape specials
 
147
_sub_basename.add(ur'\*+', ur'.*') # * everywhere
 
148
_sub_basename.add(ur'\?', ur'.') # ? everywhere
155
149
 
156
150
 
157
151
def _sub_extension(pattern):
183
177
    so are matched first, then the basename patterns, then the fullpath
184
178
    patterns.
185
179
    """
186
 
    # We want to _add_patterns in a specific order (as per type_list below)
187
 
    # starting with the shortest and going to the longest.
188
 
    # As some Python version don't support ordered dicts the list below is
189
 
    # used to select inputs for _add_pattern in a specific order.
190
 
    pattern_types = ["extension", "basename", "fullpath"]
191
 
 
192
 
    pattern_info = {
193
 
        "extension": {
194
 
            "translator": _sub_extension,
195
 
            "prefix": r'(?:.*/)?(?!.*/)(?:.*\.)'
196
 
        },
197
 
        "basename": {
198
 
            "translator": _sub_basename,
199
 
            "prefix": r'(?:.*/)?(?!.*/)'
200
 
        },
201
 
        "fullpath": {
202
 
            "translator": _sub_fullpath,
203
 
            "prefix": r''
204
 
        },
205
 
    }
206
 
 
207
180
    def __init__(self, patterns):
208
181
        self._regex_patterns = []
209
 
        pattern_lists = {
210
 
            "extension": [],
211
 
            "basename": [],
212
 
            "fullpath": [],
213
 
        }
 
182
        path_patterns = []
 
183
        base_patterns = []
 
184
        ext_patterns = []
214
185
        for pat in patterns:
215
186
            pat = normalize_pattern(pat)
216
 
            pattern_lists[Globster.identify(pat)].append(pat)
217
 
        pi = Globster.pattern_info
218
 
        for t in Globster.pattern_types:
219
 
            self._add_patterns(pattern_lists[t], pi[t]["translator"],
220
 
                               pi[t]["prefix"])
 
187
            if pat.startswith(u'RE:') or u'/' in pat:
 
188
                path_patterns.append(pat)
 
189
            elif pat.startswith(u'*.'):
 
190
                ext_patterns.append(pat)
 
191
            else:
 
192
                base_patterns.append(pat)
 
193
        self._add_patterns(ext_patterns,_sub_extension,
 
194
            prefix=r'(?:.*/)?(?!.*/)(?:.*\.)')
 
195
        self._add_patterns(base_patterns,_sub_basename,
 
196
            prefix=r'(?:.*/)?(?!.*/)')
 
197
        self._add_patterns(path_patterns,_sub_fullpath)
221
198
 
222
199
    def _add_patterns(self, patterns, translator, prefix=''):
223
200
        while patterns:
224
 
            grouped_rules = [
225
 
                '(%s)' % translator(pat) for pat in patterns[:99]]
 
201
            grouped_rules = ['(%s)' % translator(pat) for pat in patterns[:99]]
226
202
            joined_rule = '%s(?:%s)$' % (prefix, '|'.join(grouped_rules))
227
 
            # Explicitly use lazy_compile here, because we count on its
228
 
            # nicer error reporting.
229
 
            self._regex_patterns.append((
230
 
                lazy_regex.lazy_compile(joined_rule, re.UNICODE),
 
203
            self._regex_patterns.append((re.compile(joined_rule, re.UNICODE),
231
204
                patterns[:99]))
232
205
            patterns = patterns[99:]
233
206
 
236
209
 
237
210
        :return A matching pattern or None if there is no matching pattern.
238
211
        """
239
 
        try:
240
 
            for regex, patterns in self._regex_patterns:
241
 
                match = regex.match(filename)
242
 
                if match:
243
 
                    return patterns[match.lastindex - 1]
244
 
        except lazy_regex.InvalidPattern as e:
245
 
            # We can't show the default e.msg to the user as thats for
246
 
            # the combined pattern we sent to regex. Instead we indicate to
247
 
            # the user that an ignore file needs fixing.
248
 
            mutter('Invalid pattern found in regex: %s.', e.msg)
249
 
            e.msg = (
250
 
                "File ~/.config/breezy/ignore or "
251
 
                ".bzrignore contains error(s).")
252
 
            bad_patterns = ''
253
 
            for _, patterns in self._regex_patterns:
254
 
                for p in patterns:
255
 
                    if not Globster.is_pattern_valid(p):
256
 
                        bad_patterns += ('\n  %s' % p)
257
 
            e.msg += bad_patterns
258
 
            raise e
 
212
        for regex, patterns in self._regex_patterns:
 
213
            match = regex.match(filename)
 
214
            if match:
 
215
                return patterns[match.lastindex -1]
259
216
        return None
260
217
 
261
 
    @staticmethod
262
 
    def identify(pattern):
263
 
        """Returns pattern category.
264
 
 
265
 
        :param pattern: normalized pattern.
266
 
        Identify if a pattern is fullpath, basename or extension
267
 
        and returns the appropriate type.
268
 
        """
269
 
        if pattern.startswith(u'RE:') or u'/' in pattern:
270
 
            return "fullpath"
271
 
        elif pattern.startswith(u'*.'):
272
 
            return "extension"
273
 
        else:
274
 
            return "basename"
275
 
 
276
 
    @staticmethod
277
 
    def is_pattern_valid(pattern):
278
 
        """Returns True if pattern is valid.
279
 
 
280
 
        :param pattern: Normalized pattern.
281
 
        is_pattern_valid() assumes pattern to be normalized.
282
 
        see: globbing.normalize_pattern
283
 
        """
284
 
        result = True
285
 
        translator = Globster.pattern_info[Globster.identify(
286
 
            pattern)]["translator"]
287
 
        tpattern = '(%s)' % translator(pattern)
288
 
        try:
289
 
            re_obj = lazy_regex.lazy_compile(tpattern, re.UNICODE)
290
 
            re_obj.search("")  # force compile
291
 
        except lazy_regex.InvalidPattern:
292
 
            result = False
293
 
        return result
294
 
 
295
 
 
296
 
class ExceptionGlobster(object):
297
 
    """A Globster that supports exception patterns.
298
 
 
299
 
    Exceptions are ignore patterns prefixed with '!'.  Exception
300
 
    patterns take precedence over regular patterns and cause a
301
 
    matching filename to return None from the match() function.
302
 
    Patterns using a '!!' prefix are highest precedence, and act
303
 
    as regular ignores. '!!' patterns are useful to establish ignores
304
 
    that apply under paths specified by '!' exception patterns.
305
 
    """
306
 
 
307
 
    def __init__(self, patterns):
308
 
        ignores = [[], [], []]
309
 
        for p in patterns:
310
 
            if p.startswith(u'!!'):
311
 
                ignores[2].append(p[2:])
312
 
            elif p.startswith(u'!'):
313
 
                ignores[1].append(p[1:])
314
 
            else:
315
 
                ignores[0].append(p)
316
 
        self._ignores = [Globster(i) for i in ignores]
317
 
 
318
 
    def match(self, filename):
319
 
        """Searches for a pattern that matches the given filename.
320
 
 
321
 
        :return A matching pattern or None if there is no matching pattern.
322
 
        """
323
 
        double_neg = self._ignores[2].match(filename)
324
 
        if double_neg:
325
 
            return "!!%s" % double_neg
326
 
        elif self._ignores[1].match(filename):
327
 
            return None
328
 
        else:
329
 
            return self._ignores[0].match(filename)
330
 
 
331
218
 
332
219
class _OrderedGlobster(Globster):
333
220
    """A Globster that keeps pattern order."""
341
228
        self._regex_patterns = []
342
229
        for pat in patterns:
343
230
            pat = normalize_pattern(pat)
344
 
            t = Globster.identify(pat)
345
 
            self._add_patterns([pat], Globster.pattern_info[t]["translator"],
346
 
                               Globster.pattern_info[t]["prefix"])
347
 
 
348
 
 
349
 
_slashes = lazy_regex.lazy_compile(r'[\\/]+')
 
231
            if pat.startswith(u'RE:') or u'/' in pat:
 
232
                self._add_patterns([pat], _sub_fullpath)
 
233
            elif pat.startswith(u'*.'):
 
234
                self._add_patterns([pat], _sub_extension,
 
235
                    prefix=r'(?:.*/)?(?!.*/)(?:.*\.)')
 
236
            else:
 
237
                self._add_patterns([pat], _sub_basename,
 
238
                    prefix=r'(?:.*/)?(?!.*/)')
350
239
 
351
240
 
352
241
def normalize_pattern(pattern):
354
243
 
355
244
    Doesn't normalize regular expressions - they may contain escapes.
356
245
    """
357
 
    if not (pattern.startswith('RE:') or pattern.startswith('!RE:')):
358
 
        pattern = _slashes.sub('/', pattern)
359
 
    if len(pattern) > 1:
360
 
        pattern = pattern.rstrip('/')
361
 
    return pattern
 
246
    if not pattern.startswith('RE:'):
 
247
        pattern = pattern.replace('\\','/')
 
248
    return pattern.rstrip('/')