Add a `re()` method to the `Filter` class.

Callers shouldn't have to duplicate the regular expressions.

Note that the name of the method is consistent with the name
of the property in the `_Match` class.

Change-Id: If418f820fd6d460cfa4d90f8a837f1cb7994608d
Reviewed-on: https://code-review.googlesource.com/c/re2/+/61170
Reviewed-by: Perry Lorier <perryl@google.com>
Reviewed-by: Paul Wankadia <junyer@google.com>
diff --git a/python/_re2.cc b/python/_re2.cc
index 10031ea..8564f8a 100644
--- a/python/_re2.cc
+++ b/python/_re2.cc
@@ -233,6 +233,10 @@
     return matches;
   }
 
+  const RE2& GetRE2(int index) const {
+    return filter_.GetRE2(index);
+  }
+
  private:
   re2::FilteredRE2 filter_;
   std::unique_ptr<RE2::Set> set_;
@@ -326,7 +330,9 @@
   filter.def(py::init<>())
       .def("Add", &Filter::Add)
       .def("Compile", &Filter::Compile)
-      .def("Match", &Filter::Match);
+      .def("Match", &Filter::Match)
+      .def("GetRE2", &Filter::GetRE2,
+           py::return_value_policy::reference_internal);
 }
 
 }  // namespace re2_python
diff --git a/python/re2.py b/python/re2.py
index 8af260f..8a6d985 100644
--- a/python/re2.py
+++ b/python/re2.py
@@ -413,7 +413,7 @@
         group = self._regexp.groupindex[group]
       except KeyError:
         raise IndexError('bad group name')
-    if group < 0 or group > self._regexp.groups:
+    if not 0 <= group <= self._regexp.groups:
       raise IndexError('bad group index')
     span = self._spans[group]
     if span == _NULL_SPAN:
@@ -441,17 +441,17 @@
     return dict(items)
 
   def start(self, group=0):
-    if group < 0 or group > self._regexp.groups:
+    if not 0 <= group <= self._regexp.groups:
       raise IndexError('bad group index')
     return self._spans[group][0]
 
   def end(self, group=0):
-    if group < 0 or group > self._regexp.groups:
+    if not 0 <= group <= self._regexp.groups:
       raise IndexError('bad group index')
     return self._spans[group][1]
 
   def span(self, group=0):
-    if group < 0 or group > self._regexp.groups:
+    if not 0 <= group <= self._regexp.groups:
       raise IndexError('bad group index')
     return self._spans[group]
 
@@ -543,10 +543,11 @@
 class Filter(object):
   """A Pythonic wrapper around FilteredRE2."""
 
-  __slots__ = ('_filter')
+  __slots__ = ('_filter', '_patterns')
 
   def __init__(self):
     self._filter = _re2.Filter()
+    self._patterns = []
 
   def Add(self, pattern, options=None):
     options = options or Options()
@@ -557,6 +558,7 @@
       index = self._filter.Add(pattern, options)
     if index == -1:
       raise error('failed to add %r to Filter' % pattern)
+    self._patterns.append(pattern)
     return index
 
   def Compile(self):
@@ -570,3 +572,11 @@
     else:
       matches = self._filter.Match(text, potential)
     return matches or None
+
+  def re(self, index):
+    if not 0 <= index < len(self._patterns):
+      raise IndexError('bad index')
+    proxy = object.__new__(_Regexp)
+    proxy._pattern = self._patterns[index]
+    proxy._regexp = self._filter.GetRE2(index)
+    return proxy
diff --git a/python/re2_test.py b/python/re2_test.py
index 9161c60..86aa9ae 100644
--- a/python/re2_test.py
+++ b/python/re2_test.py
@@ -471,6 +471,12 @@
     self.assertItemsEqual([0, 1], f.Match('Hello, world.'))
     self.assertIsNone(f.Match('HELLO, WORLD.'))
 
+    self.assertRaises(IndexError, f.re, -1)
+    self.assertRaises(IndexError, f.re, 3)
+    self.assertEqual('Goodbye, \\w+\\.', f.re(2).pattern)
+    # Verify whether the underlying RE2 object is usable.
+    self.assertEqual(0, f.re(2).groups)
+
 
 if __name__ == '__main__':
   absltest.main()