Commits

Jonathan Eunice committed da0b883

added repace and re_replace to Text and TextRange

Comments (0)

Files changed (5)

 
 from say.core import fmt
 import inspect
-import sys, os
+import sys, os, re
 
 _PY3 = sys.version_info[0] > 2
+if _PY3:
+    basestring = str
 
 class Text(object):
     def __init__(self, data=None, interpolate=True, dedent=True):
         """
         for i, line in enumerate(self._lines):
             self._lines[i] = line.replace(target, replacement)
-        # TODO: should lines be recalibrated, if there are any \n in replacement?
+            
+        # TODO: should lines be recalibrated, if there are any \n in replacement? or just assume there won't be?
+    
+    def re_replace(self, target, replacement):
+        """
+        Regular expression replacement. Target is either compiled re object or
+        string that will be compiled into one. Replacement is either string or
+        function that takes re match object as a parameter and returns replacement
+        string.
+        """
+        if isinstance(target, basestring):
+            target = re.compile(target)
+        for i, line in enumerate(self._lines):
+            self._lines[i] = target.sub(replacement, line)
     
     def read_from(self, filepath, interpolate=True, dedent=True):
         """
 TextRange are completely fine.
 """
 
+# TextRange shows the difficulty of 'sub object' references in
+# Python. On the one hand, if t is a Text object, t[1:4] should
+# return a list of strings for those lines. OTOH, maybe it should
+# return an abstracted TextRange object. But then if the underlying
+# Text object changes, how to have the TextRange then adjust? I.e.
+# if a few lines are prepended to t, shouldn't the text range still
+# refer to the new lines, albeit at a higher index range? Building
+# this invovles dependencies, and Text knowing about TextRange, and
+# vice versa. For the meanwhile, lacking a generalized dependency
+# mechanism (not to mention a generalized lvalue mechanism),
+# just going to make TextRange be static.
+
+# Also note how TextRange is not really a subclass of Text, though
+# logically it could, or should, or sorta wants to be. But even if
+# it were, we'd still have to do lots of the copy-paste-modify
+# style of programming here.
+
+# For further investigation:
+# http://pypi.python.org/pypi/Cellulose
+
 from say import Text
 import inspect
+import sys, re
+
+_PY3 = sys.version_info[0] > 2
+if _PY3:
+    basestring = str
+
 
 class TextRange(object):
     
             self._text._lines[i] = line.replace(target, replacement)
             
         # TODO: should lines be recalibrated, if there are any \n in replacement?
-        
+
+    def re_replace(self, target, replacement):
+        """
+        Regular expression replacement. Target is either compiled re object or
+        string that will be compiled into one. Replacement is either string or
+        function that takes re match object as a parameter and returns replacement
+        string.
+        """
+        if isinstance(target, basestring):
+            target = re.compile(target)
+        for i, line in enumerate(self._text._lines[self._indices], start=self._indices.start):
+            self._text._lines[i] = target.sub(replacement, line)
+    
     def copy(self):
         """
         Make a copy.
 
 setup(
     name='say',
-    version=verno("0.925"),
+    version=verno("0.932"),
     author='Jonathan Eunice',
     author_email='jonathan.eunice@gmail.com',
     description='Super-simple templated printing. E.g.: say("Hello, {whoever}!", indent=1)',

test/test_text.py

    t.replace('b', 'B')
    t.replace('d', 'D')
    assert t.lines ==  'a B c D'.split()
+   
+def test_re_replace():
+   t = Text('a\nb\nc\nd')
+   assert t.lines == 'a b c d'.split()
+   
+   t.re_replace(r'[b]', 'B')
+   t.re_replace(r'd', lambda m: m.group(0).upper())
+   assert t.lines ==  'a B c D'.split()
     
 def test_set_text():
     t = Text()

test/test_textrange.py

       assert tr[2] == 'd'
       
 def test_replace():
-   t = Text('a\na\n\a\nb')
-   assert t.lines == 'a a a b'.split()
+   t = Text('a\na\na\nb\na')
+   assert t.lines == 'a a a b a'.split()
    
    tr = TextRange(t, 2, 4)
    assert tr.lines == 'a b'.split()
    
    tr.replace('a', 'A')
    assert tr.lines == 'A b'.split()
-   assert t.lines == 'a a A b'.split()
+   assert t.lines == 'a a A b a'.split()
+
+def test_re_replace():
+   t = Text('a\na\na\nb\na')
+   assert t.lines == 'a a a b a'.split()
    
+   tr = TextRange(t, 2, 4)
+   assert tr.lines == 'a b'.split()
+   
+   tr.re_replace(r'[b]', 'B')
+   tr.re_replace(r'a', lambda m: m.group(0).upper())
+   assert t.lines == 'a a A B a'.split()
+
       
 def test_interpolation():
    x = 21