Find all occurrences of a multi-line string between two defined tags in Python with Regex

Viewed 435

I am trying to do something a bit unexpected. I want to parse a Python code in which some specific comment lines define a block of text or of code that I would like to extract. For this example, I will be using two types of tags, one for code, one for text description.

#c
print ("this is the tag I used for code I want to find")
#-c

#d
"""This is the tag I used for a description I want to find"""
#-d

Why am I tagging my code this way? Well, that's for another question. More details in this SO post if you are interested.

Now, I have been trying to catch those tags with regular expression, and as it's my first time using regex, I obviously didn't succeed... Here is my current attempt:

file = 'script.py'
with open(file, 'r') as f:
    text = f.read() 
text = read_python_script(file)

code_blocks = re.compile(r'(?s)(?<=#c\n)(.*\n)(?=#-c)')
desc_blocks = re.compile(r'(?s)(?<=#d\n)(.*\n)(?=#-d)')

code = re.findall(code_blocks, text)
desc = re.findall(desc_blocks, text)

Here is an example of a script I would like to parse:

# -*- coding: utf-8 -*-
"""
Title: blablabla
Author: Mathieu

Description: Report the some measurements
"""

import time
import uuid

from database.model import TestLimit, TestResult
from util import log

#d
"""
blablabla some description
which might be multiline
"""
#-d
# Constants
#c
CONSTANT_1 = 10     # Unit
CONSTANT_2 = 2      # Unit
#-c

class Foo(Fp):
    def __init__(self, #some parameters):
        # some init

    def _run(self, #some parameters):
        #%% Section Title 1
        #%%% Sub section Title 1
        #c
        code I would like to catch
        can be multiple liens
        #-c

        
        with something.open():
            #%%% Sub section title 2
            #d
            """some description I want to catch"""
            #-d
            
            #c
            some more code I want to catch
            #-c

At the moment, I am getting only the first occurrence and I don't know why...

EDIT: As mentioned, I should change my expression to a lazy format:

r'(?s)(?<=#c\n)(.*?)(?=\n#-c)'

This doesn't completely solve the problem as I am still only catching the first occurence.

1 Answers

You may use

re.findall(r'(?s)#c\n(.*?)\n[^\S\r\n]*#-c', text)

See the regex demo.

There are two things here:

  • The lazy matching .*? pattern
  • [^\S\r\n]* matching any 0 or more horizontal whitespaces that must be used before #-c since there may be some indentation before the symbols.

Details

  • (?s) - inline re.DOTALL modifier
  • #c - a literal string
  • \n - a newline
  • (.*?) - Capturing group 1: any zero or more chars as few as possible
  • \n - a newline
  • [^\S\r\n]* - 0 or more horizontal whitespace chars
  • #-c - a literal string.
Related