1
# Copyright (C) 2006 Canonical Ltd
1
# Copyright (C) 2006, 2008-2011, 2017 Canonical Ltd
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
17
17
"""Lazily compiled regex objects.
19
This module defines a class which creates proxy objects for regex compilation.
20
This allows overriding re.compile() to return lazily compiled objects.
19
This module defines a class which creates proxy objects for regex
20
compilation. This allows overriding re.compile() to return lazily compiled
23
We do this rather than just providing a new interface so that it will also
24
be used by existing Python modules that create regexs.
27
from __future__ import absolute_import
34
class InvalidPattern(errors.BzrError):
36
_fmt = ('Invalid pattern(s) found. %(msg)s')
38
def __init__(self, msg):
26
42
class LazyRegex(object):
27
43
"""A proxy around a real regex, which won't be compiled until accessed."""
30
45
# These are the parameters on a real _sre.SRE_Pattern object, which we
31
46
# will map to local members so that we don't have the proxy overhead.
32
47
_regex_attributes_to_copy = [
33
'__copy__', '__deepcopy__', 'findall', 'finditer', 'match',
34
'scanner', 'search', 'split', 'sub', 'subn'
48
'__copy__', '__deepcopy__', 'findall', 'finditer', 'match',
49
'scanner', 'search', 'split', 'sub', 'subn'
37
52
# We use slots to keep the overhead low. But we need a slot entry for
38
53
# all of the attributes we will copy
39
54
__slots__ = ['_real_regex', '_regex_args', '_regex_kwargs',
40
] + _regex_attributes_to_copy
55
] + _regex_attributes_to_copy
42
57
def __init__(self, args=(), kwargs={}):
43
58
"""Create a new proxy object, passing in the args to pass to re.compile
45
:param args: The *args to pass to re.compile
46
:param kwargs: The **kwargs to pass to re.compile
60
:param args: The `*args` to pass to re.compile
61
:param kwargs: The `**kwargs` to pass to re.compile
48
63
self._real_regex = None
49
64
self._regex_args = args
59
74
def _real_re_compile(self, *args, **kwargs):
60
75
"""Thunk over to the original re.compile"""
61
return _real_re_compile(*args, **kwargs)
77
return _real_re_compile(*args, **kwargs)
79
# raise InvalidPattern instead of re.error as this gives a
80
# cleaner message to the user.
81
raise InvalidPattern('"' + args[0] + '" ' + str(e))
83
def __getstate__(self):
84
"""Return the state to use when pickling."""
86
"args": self._regex_args,
87
"kwargs": self._regex_kwargs,
90
def __setstate__(self, dict):
91
"""Restore from a pickled state."""
92
self._real_regex = None
93
setattr(self, "_regex_args", dict["args"])
94
setattr(self, "_regex_kwargs", dict["kwargs"])
63
96
def __getattr__(self, attr):
64
97
"""Return a member from the proxied regex object.
103
136
_real_re_compile = re.compile
104
137
if _real_re_compile is lazy_compile:
105
138
raise AssertionError(
106
"re.compile has already been overridden as lazy_compile, but this would" \
107
" cause infinite recursion")
139
"re.compile has already been overridden as lazy_compile, but this "
140
"would cause infinite recursion")
143
# Some libraries calls re.finditer which fails it if receives a LazyRegex.
144
if getattr(re, 'finditer', False):
145
def finditer_public(pattern, string, flags=0):
146
if isinstance(pattern, LazyRegex):
147
return pattern.finditer(string)
149
return _real_re_compile(pattern, flags).finditer(string)
150
re.finditer = finditer_public