Regex capturing group within non-capturing group
<p>In Python, how do you capture a group within a non-capturing group? Put in another way, how do you repeat a non-capturing sub-pattern that contains a capturing group?</p>

<p>An example of this would be to capture all of the package names on an import string. E.g. the string:</p>

<blockquote>
  <p>import pandas, os, sys</p>
</blockquote>

<p>Would return 'pandas', 'os', and 'sys'. The following pattern captures the first package and gets up to the second package:</p>

<pre><code>import\s+([a-zA-Z0=9]*),*\s*
</code></pre>

<p>From here, I would like to repeat the sub-pattern that captures the group and matches the following characters, i.e.<code>([a-zA-Z0=9]*),*\s*</code>. When I surround this sub-pattern with a non-capturing group and repeat it:</p>

<pre><code>import\s+(?:([a-zA-Z0=9]*),*\s*)*
</code></pre>

<p>It no longer captures the group inside.</p>

<p>A repeated capturing group will <em>only</em> capture the last iteration. This is why you need to restructure your regex to work with <code>re.findall</code>.</p>

<pre><code>\s*
(?:
  (?:^from\s+
    (  # Base (from (base) import ...)
      (?:[a-zA-Z_][a-zA-Z_0-9]*  # Variable name
        (?:\.[a-zA-Z_][a-zA-Z_0-9]*)*  # Attribute (.attr)
      )
    )\s+import\s+
  )
|
  (?:^import\s|,)\s*
)
(  # Name of imported module (import (this))
  (?:[a-zA-Z_][a-zA-Z_0-9]*  # Variable name
    (?:\.[a-zA-Z_][a-zA-Z_0-9]*)*  # Attribute (.attr)
  )
)
(?:
  \s+as\s+
  (  # Variable module is imported into (import foo as bar)
    (?:[a-zA-Z_][a-zA-Z_0-9]*  # Variable name
      (?:\.[a-zA-Z_][a-zA-Z_0-9]*)*  # Attribute (.attr)
    )
  )
)?
\s*
(?=,|$)  # Ensure there is another thing being imported or it is the end of string
</code></pre>

<p><a href="https://regex101.com/r/jL7oQ4/2" rel="nofollow">Try it on regex101.com</a></p>

<p>Capture group 0 will be the <code>Base</code>, capture group 1 will be (What you're after) the name of the imported module, and capture group 2 will be the variable the module is in (<code>from (group 0) import (group 1) as (group 2)</code>)</p>

<pre><code>import re

regex = r"\s*(?:(?:^from\s+((?:[a-zA-Z_][a-zA-Z_0-9]*(?:\.[a-zA-Z_][a-zA-Z_0-9]*)*))\s+import\s+)|(?:^import\s|,)\s*)((?:[a-zA-Z_][a-zA-Z_0-9]*(?:\.[a-zA-Z_][a-zA-Z_0-9]*)*))(?:\s+as\s+((?:[a-zA-Z_][a-zA-Z_0-9]*(?:\.[a-zA-Z_][a-zA-Z_0-9]*)*)))?\s*(?=,|$)"

print(re.findall(regex, "import pandas, os, sys"))
</code></pre>



<pre><code>[('', 'pandas', ''), ('', 'os', ''), ('', 'sys', '')]
</code></pre>

<p>You can remove the other two capturing groups if you don't care for them.</p>

python regex