Cómo usar el modificador de expresiones regulares en línea en Python

Tengo una expresión regular:

(.*\n)+DOCUMENTATION.*(\"\"\"|''')\n-*\n?((.*\n)+?)(\2)(?s:.*) 

Estoy tratando de procesar algunos archivos como este:

 #!/usr/bin/python # -*- coding: utf-8 -*- #  DOCUMENTATION = """ module: foo short_description: baz  """  

Necesito obtener la parte de DOCUMENTACIÓN de ella.

Funciona bastante bien pero no con python. El problema es con los modificadores en línea ?s:.* Que solía capturar el rest del archivo (cualquier carácter que incluya una nueva línea cero o más veces). Parece que de alguna manera es diferente en python.

Aquí en regex101 está el ejemplo. Muestra un error cuando lo cambio a python.

NOTA: No puedo configurar modificadores globalmente. (Solo puedo pasar la regla de expresiones regulares a algún módulo de Python).

Modificadores en línea en el módulo re

Python implementa modificadores en línea (incrustados) , como (?s) , (?i) o (?aiLmsux) , pero no como parte de un modificador de grupo que no captura como el que intentabas usar.
(?smi:subpattern) funciona en Perl y PCRE, pero no en Python.

Además, el uso de un modificador en línea en cualquier parte del patrón se aplica a toda la coincidencia y no se puede desactivar.

Desde regular-expressions.info :
En Python, poner un modificador en el centro de la expresión regular afecta a la expresión regular completa. Por lo tanto, en Python, (?i)caseless y sin caseless(?i) son insensibles a mayúsculas y minúsculas.


Ejemplo:

 import re text = "A\nB" print("Text: '%s'\n---" % text) patterns = [ "a", "a(?i)", "A.*B", "A(?s).*B", "A.*(?s)B"] for p in patterns: match = re.search( p, text) print("Pattern: '%s' \tMatch: %s" % (p, match.span() if match else None)) 

Salida:

 Text: 'A B' --- Pattern: 'a' Match: None Pattern: 'a(?i)' Match: (0, 1) Pattern: 'A.*B' Match: None Pattern: 'A(?s).*B' Match: (0, 3) Pattern: 'A.*(?s)B' Match: (0, 3) 

ideone demo


Solución

(?s) (aka singleline o re.DOTALL ) hace . también emparejar nuevas líneas. Y ya que está intentando establecerlo solo en una parte del patrón, hay 2 alternativas:

  1. Coincidir con cualquier cosa excepto nuevas líneas :
    Establezca (?s) para el patrón completo (ya sea pasado como bandera o en línea), y use [^\n]* lugar de un punto, para hacer coincidir cualquier carácter excepto las nuevas líneas.
  2. Coincidir con todo, incluyendo nuevas líneas :
    Utilice [\S\s]* lugar de un punto, para hacer coincidir cualquier carácter, incluidas las nuevas líneas. La clase de caracteres incluye todos los espacios en blanco y todo lo que no es un espacio en blanco (por lo tanto, todos los caracteres).

Para el caso específico que presentaste, puedes usar la siguiente expresión:

 (?m)^DOCUMENTATION.*(\"{3}|'{3})\n-*\n?([\s\S]+?)^\1[\s\S]* 

regex101 demo


Nota: esta publicación cubre los modificadores en línea en el módulo re , mientras que el módulo de expresiones regulares de Matthew Barnett implementa de hecho modificadores en línea (indicadores de ámbito) con el mismo comportamiento observado en PCRE y Perl.