[Done] Re.sub in client side

re.sub() does not work on the client side.

Are there any plans to implement it any time soon?

1 Like

While waiting for the real thing my workaround is to use this:

def re_sub(pattern, repl, text):
  groups = re.match(pattern, text)
  if groups:
    for i in range(1, groups.lastindex + 1):
      repl = repl.replace('\\{}'.format(i), groups[i])
  return repl

It works just fine with simple cases like:

>>> re_sub('(\d{3}).*(\d{3}).*(\d{4})', r'(\1) \2-\3', '1234567890')
'(123) 456-7890'
>>> re_sub('(\d{3}).*(\d{3}).*(\d{4})', r'(\1) \2-\3', '123 456 7890')
'(123) 456-7890'

I made this solution for myself since it’s 2020’oct but re.sub still not supported in anvil.works:

def re_sub(pattern, repl, text):
    result = ""
    while True:
        m = re.search(pattern, text)
        if m is None:
            result += text
            break
        if len(result) > 0:
            pass
        s = text.index(m.group(0))
        e = s + len(m.group(0))
        result += text[:s] + repl
        text = text[e:]
    return result
1 Like

Here is an updated version of my old function that works on multiline strings containing \n:

def re_sub(pattern, repl, text):
  texts_1 = text.split('\n')
  texts_2 = [repl] * len(texts_1)
  for n_text in range(len(texts_1)):
    matches = re.match(pattern, texts_1[n_text])
    if matches:
      for n_match, match in enumerate(matches.groups(), 1):
        texts_2[n_text] = texts_2[n_text].replace(f'\\{n_match}', match or '')
    else:
      texts_2[n_text] = texts_1[n_text]
  return '\n'.join(texts_2)
1 Like

Here is a solution I’ve come up with. I put this code into a utils module that I can use anywhere in my client code.

Instead of calling:

import re

Call:

from .my_module import re

Place this code in “my_module”

""" GET MODULES """
def get_re():
  """ Get re module with sub function added """
  global re
  
  import re
  regex = re
  
  def sub(pattern, repl, string, count=0, flags=0):
    """Return the string obtained by replacing the leftmost
    non-overlapping occurrences of the pattern in string by the
    replacement repl.  repl can be either a string or a callable;
    if a string, backslash escapes in it are processed.  If it is
    a callable, it's passed the Match object and must return
    a replacement string to be used."""
    if isinstance(pattern, str):
      pattern = re.compile(pattern, flags)
    if isinstance(string, str):
      pattern = re.compile(pattern, flags)
      matches = pattern.findall(string)
      if isinstance(count, int) and count > 0:
        matches = matches[:count]
      for match in matches:
        string = string.replace(match, repl)
    
      return string
    
  regex.sub = sub
  
  re = regex
  
  return re

re = get_re()

Then you can use re.sub as you typically would.

Example:

from .my_module import re


pattern = '[a-z]+'
string = 'Account Number - 12345, Amount - 586.32'
repl = 'AA'

print('Original string')
print(string)

result = re.sub(pattern, repl, string, flags=re.IGNORECASE)

print('After replacement')
print(result)

Note that if you intend to use this updated re in a nested module, you’ll have to load that module after assigning it as such:

re = get_re()

from . import my_nested_module

This will make the “my_nested_module” module available in your utils module and make the re module available in your “my_nested_module” module

2 Likes

And this one works with more than 10 matches:

def re_sub(pattern, repl, text):
    texts_1 = text.split('\n')
    texts_2 = [repl] * len(texts_1)
    for n_text in range(len(texts_1)):
        matches = re.match(pattern, texts_1[n_text])
        if matches:
            groups = matches.groups()
            for n_match in range(len(groups) - 1, -1, -1):  # go backwards to fill double digit numbers first
                texts_2[n_text] = texts_2[n_text].replace(f'\\{n_match + 1}', groups[n_match] or '')
        else:
            texts_2[n_text] = texts_1[n_text]
    return '\n'.join(texts_2)
1 Like

[I moved this post to Feature Requests.]

1 Like

Another re.sub alternative in the client, this one taking advantage of Javascript. I’ve found the Javascript regex implementation to be as complete as I’ve needed.

from anvil.js.window import RegExp
from anvil.js.window import String

def sub(pattern, repl, text, flags = 'gmi'):
  regex = RegExp(pattern, flags)
  result = String.prototype.replace.call(text, regex, repl)
  return result
4 Likes

This has now been implemented

4 Likes

Any other new modules to note, maybe enum or abc ; -)

Best to create a separate feature requests, or add a like/comment to any existing requests.
(fractions was also added)

2 Likes