/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 breezy/globbing.py

  • Committer: Jelmer Vernooij
  • Date: 2018-11-06 02:25:29 UTC
  • mto: This revision was merged to the branch mainline in revision 7150.
  • Revision ID: jelmer@jelmer.uk-20181106022529-qlctdqketvoibpvz
Simplify brz-git, drop imports.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006-2010 Canonical Ltd
 
1
# Copyright (C) 2006-2011 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
20
20
expressions.
21
21
"""
22
22
 
 
23
from __future__ import absolute_import
 
24
 
23
25
import re
24
26
 
25
 
from bzrlib.trace import (
26
 
    warning
 
27
from . import (
 
28
    errors,
 
29
    lazy_regex,
 
30
    )
 
31
from .trace import (
 
32
    mutter,
 
33
    warning,
27
34
    )
28
35
 
29
36
 
36
43
    must not contain capturing groups.
37
44
    """
38
45
 
39
 
    _expand = re.compile(ur'\\&')
 
46
    _expand = lazy_regex.lazy_compile(u'\\\\&')
40
47
 
41
48
    def __init__(self, source=None):
42
49
        self._pat = None
72
79
 
73
80
    def __call__(self, text):
74
81
        if not self._pat:
75
 
            self._pat = re.compile(
 
82
            self._pat = lazy_regex.lazy_compile(
76
83
                    u'|'.join([u'(%s)' % p for p in self._pats]),
77
84
                    re.UNICODE)
78
85
        return self._pat.sub(self._do_sub, text)
86
93
 
87
94
 
88
95
_sub_named = Replacer()
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')
 
96
_sub_named.add(r'\[:digit:\]', r'\d')
 
97
_sub_named.add(r'\[:space:\]', r'\s')
 
98
_sub_named.add(r'\[:alnum:\]', r'\w')
 
99
_sub_named.add(r'\[:ascii:\]', r'\0-\x7f')
 
100
_sub_named.add(r'\[:blank:\]', r' \t')
 
101
_sub_named.add(r'\[:cntrl:\]', r'\0-\x1f\x7f-\x9f')
95
102
 
96
103
 
97
104
def _sub_group(m):
123
130
 
124
131
_sub_re = Replacer()
125
132
_sub_re.add(u'^RE:', u'')
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)
 
133
_sub_re.add(u'\\((?!\\?)', u'(?:')
 
134
_sub_re.add(u'\\(\\?P<.*>', _invalid_regex(u'(?:'))
 
135
_sub_re.add(u'\\(\\?P=[^)]*\\)', _invalid_regex(u''))
 
136
_sub_re.add(r'\\+$', _trailing_backslashes_regex)
130
137
 
131
138
 
132
139
_sub_fullpath = Replacer()
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
 
140
_sub_fullpath.add(r'^RE:.*', _sub_re) # RE:<anything> is a regex
 
141
_sub_fullpath.add(r'\[\^?\]?(?:[^][]|\[:[^]]+:\])+\]', _sub_group) # char group
 
142
_sub_fullpath.add(r'(?:(?<=/)|^)(?:\.?/)+', u'') # canonicalize path
 
143
_sub_fullpath.add(r'\\.', r'\&') # keep anything backslashed
 
144
_sub_fullpath.add(r'[(){}|^$+.]', r'\\&') # escape specials
 
145
_sub_fullpath.add(r'(?:(?<=/)|^)\*\*+/', r'(?:.*/)?') # **/ after ^ or /
 
146
_sub_fullpath.add(r'\*+', r'[^/]*') # * elsewhere
 
147
_sub_fullpath.add(r'\?', r'[^/]') # ? everywhere
141
148
 
142
149
 
143
150
_sub_basename = Replacer()
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
 
151
_sub_basename.add(r'\[\^?\]?(?:[^][]|\[:[^]]+:\])+\]', _sub_group) # char group
 
152
_sub_basename.add(r'\\.', r'\&') # keep anything backslashed
 
153
_sub_basename.add(r'[(){}|^$+.]', r'\\&') # escape specials
 
154
_sub_basename.add(r'\*+', r'.*') # * everywhere
 
155
_sub_basename.add(r'\?', r'.') # ? everywhere
149
156
 
150
157
 
151
158
def _sub_extension(pattern):
177
184
    so are matched first, then the basename patterns, then the fullpath
178
185
    patterns.
179
186
    """
 
187
    # We want to _add_patterns in a specific order (as per type_list below)
 
188
    # starting with the shortest and going to the longest.
 
189
    # As some Python version don't support ordered dicts the list below is
 
190
    # used to select inputs for _add_pattern in a specific order.
 
191
    pattern_types = [ "extension", "basename", "fullpath" ]
 
192
 
 
193
    pattern_info = {
 
194
        "extension": {
 
195
            "translator" : _sub_extension,
 
196
            "prefix" : r'(?:.*/)?(?!.*/)(?:.*\.)'
 
197
        },
 
198
        "basename": {
 
199
            "translator" : _sub_basename,
 
200
            "prefix" : r'(?:.*/)?(?!.*/)'
 
201
        },
 
202
        "fullpath": {
 
203
            "translator" : _sub_fullpath,
 
204
            "prefix" : r''
 
205
        },
 
206
    }
 
207
 
180
208
    def __init__(self, patterns):
181
209
        self._regex_patterns = []
182
 
        path_patterns = []
183
 
        base_patterns = []
184
 
        ext_patterns = []
 
210
        pattern_lists = {
 
211
            "extension": [],
 
212
            "basename": [],
 
213
            "fullpath": [],
 
214
        }
185
215
        for pat in patterns:
186
216
            pat = normalize_pattern(pat)
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)
 
217
            pattern_lists[Globster.identify(pat)].append(pat)
 
218
        pi = Globster.pattern_info
 
219
        for t in Globster.pattern_types:
 
220
            self._add_patterns(pattern_lists[t], pi[t]["translator"],
 
221
                pi[t]["prefix"])
198
222
 
199
223
    def _add_patterns(self, patterns, translator, prefix=''):
200
224
        while patterns:
201
 
            grouped_rules = ['(%s)' % translator(pat) for pat in patterns[:99]]
 
225
            grouped_rules = [
 
226
                '(%s)' % translator(pat) for pat in patterns[:99]]
202
227
            joined_rule = '%s(?:%s)$' % (prefix, '|'.join(grouped_rules))
203
 
            self._regex_patterns.append((re.compile(joined_rule, re.UNICODE),
 
228
            # Explicitly use lazy_compile here, because we count on its
 
229
            # nicer error reporting.
 
230
            self._regex_patterns.append((
 
231
                lazy_regex.lazy_compile(joined_rule, re.UNICODE),
204
232
                patterns[:99]))
205
233
            patterns = patterns[99:]
206
234
 
209
237
 
210
238
        :return A matching pattern or None if there is no matching pattern.
211
239
        """
212
 
        for regex, patterns in self._regex_patterns:
213
 
            match = regex.match(filename)
214
 
            if match:
215
 
                return patterns[match.lastindex -1]
 
240
        try:
 
241
            for regex, patterns in self._regex_patterns:
 
242
                match = regex.match(filename)
 
243
                if match:
 
244
                    return patterns[match.lastindex -1]
 
245
        except lazy_regex.InvalidPattern as e:
 
246
            # We can't show the default e.msg to the user as thats for
 
247
            # the combined pattern we sent to regex. Instead we indicate to
 
248
            # the user that an ignore file needs fixing.
 
249
            mutter('Invalid pattern found in regex: %s.', e.msg)
 
250
            e.msg = "File ~/.bazaar/ignore or .bzrignore contains error(s)."
 
251
            bad_patterns = ''
 
252
            for _, patterns in self._regex_patterns:
 
253
                for p in patterns:
 
254
                    if not Globster.is_pattern_valid(p):
 
255
                        bad_patterns += ('\n  %s' % p)
 
256
            e.msg += bad_patterns
 
257
            raise e
216
258
        return None
217
259
 
 
260
    @staticmethod
 
261
    def identify(pattern):
 
262
        """Returns pattern category.
 
263
 
 
264
        :param pattern: normalized pattern.
 
265
        Identify if a pattern is fullpath, basename or extension
 
266
        and returns the appropriate type.
 
267
        """
 
268
        if pattern.startswith(u'RE:') or u'/' in pattern:
 
269
            return "fullpath"
 
270
        elif pattern.startswith(u'*.'):
 
271
            return "extension"
 
272
        else:
 
273
            return "basename"
 
274
 
 
275
    @staticmethod
 
276
    def is_pattern_valid(pattern):
 
277
        """Returns True if pattern is valid.
 
278
 
 
279
        :param pattern: Normalized pattern.
 
280
        is_pattern_valid() assumes pattern to be normalized.
 
281
        see: globbing.normalize_pattern
 
282
        """
 
283
        result = True
 
284
        translator = Globster.pattern_info[Globster.identify(pattern)]["translator"]
 
285
        tpattern = '(%s)' % translator(pattern)
 
286
        try:
 
287
            re_obj = lazy_regex.lazy_compile(tpattern, re.UNICODE)
 
288
            re_obj.search("") # force compile
 
289
        except lazy_regex.InvalidPattern as e:
 
290
            result = False
 
291
        return result
 
292
 
 
293
 
218
294
class ExceptionGlobster(object):
219
295
    """A Globster that supports exception patterns.
220
296
    
226
302
    that apply under paths specified by '!' exception patterns.
227
303
    """
228
304
    
229
 
    def __init__(self,patterns):
 
305
    def __init__(self, patterns):
230
306
        ignores = [[], [], []]
231
307
        for p in patterns:
232
308
            if p.startswith(u'!!'):
262
338
        self._regex_patterns = []
263
339
        for pat in patterns:
264
340
            pat = normalize_pattern(pat)
265
 
            if pat.startswith(u'RE:') or u'/' in pat:
266
 
                self._add_patterns([pat], _sub_fullpath)
267
 
            elif pat.startswith(u'*.'):
268
 
                self._add_patterns([pat], _sub_extension,
269
 
                    prefix=r'(?:.*/)?(?!.*/)(?:.*\.)')
270
 
            else:
271
 
                self._add_patterns([pat], _sub_basename,
272
 
                    prefix=r'(?:.*/)?(?!.*/)')
273
 
 
274
 
 
275
 
_slashes = re.compile(r'[\\/]+')
 
341
            t = Globster.identify(pat)
 
342
            self._add_patterns([pat], Globster.pattern_info[t]["translator"],
 
343
                Globster.pattern_info[t]["prefix"])
 
344
 
 
345
 
 
346
_slashes = lazy_regex.lazy_compile(r'[\\/]+')
276
347
def normalize_pattern(pattern):
277
348
    """Converts backslashes in path patterns to forward slashes.
278
349