Passing indented code to compile()

Alex Martelli aleax at aleax.it
Wed May 7 13:35:14 EDT 2003


Lulu of the Lotus-Eaters wrote:

> "John Wilson" <tug at wilson.co.uk> wrote previously:
> |"just working" it the apex of my ambition at the moment ;-)
> 
> There is a function I use in several variants of my smart ASCII markup
> tools.  It looks at a code block (or anything), and pushes the block as
> far left as possible, while maintaining relative indents.
> 
>     def LeftMargin(txt):
>         """Remove as many leading spaces as possible from whole block"""
>         for l in range(12,-1,-1):
>             re_lead = '(?sm)'+' '*l+'\S'
>             if re.match(re_lead, txt): break
>         txt = re.sub('(?sm)^'+' '*l, '', txt)
>         return txt

Hmmm, 'as many as' is somewhat of an overbid, as this is limited to 12
at most (and in JW's app one might easily have more than that).

Wouldn't it be somewhat handier to use, say:

def leftMargin(lines):
    def numblanks(line):
        return len(line)-len(line.lstrip(' '))
    minblanks = min([numblanks(line) for line in lines])
    return [line[minblanks:] for line in lines]

A map-fan would of course use min(map(numblanks, lines)) [and
maybe map(lambda x: x[minblanks:], lines) on the return stmt,
and quite possibly a lambda instead of numblanks too], but,
apart from these details, isn't "counting blanks per line",
then taking the min and stripping those off, somewhat better?

Of course, I'm assuming I have the lines in a list of strings
rather than in one big string.  But even if the latter were
the case -- and thus re use probably preferable -- I still don't
know that I'd use the "countdown from 12" approach.  Is it even
_correct_, in fact?

E.g., given:

import re

def LeftMargin(txt):
    """Remove as many leading spaces as possible from whole block"""
    for l in range(12,-1,-1):
        re_lead = '(?sm)'+' '*l+'\S'
        if re.match(re_lead, txt): break
        txt = re.sub('(?sm)^'+' '*l, '', txt)
    return txt

blk='''\
    pepe
      rosso
    con
  patate'''

print LeftMargin(blk)

the output i observe is:

[alex at lancelot psyco-1.0]$ python xy.py
    pepe
rosso
    con
  patate
[alex at lancelot psyco-1.0]$

which doesn't seem to be what the OP is looking for.

'\n'.join(leftMargin(blk.splitlines())) -- while possibly onerous --
might at least give a satisfactory result.

But, you CAN adapt the 'space counting' to re-and-block use, too:

def relem(txt):
    re_leadspace = '(?sm)^ *'
    leads = re.findall(re_leadspace, txt)
    minblanks = min(map(len, leads))
    re_leadspace = '(?sm)^' + ' '*minblanks
    txt = re.sub(re_leadspace, '', txt)
    return txt


Alex





More information about the Python-list mailing list