0

I was trying to log a completion of a scheduled event I set to run on Django. I was trying my very best to make my code look presentable, So instead of putting the string into a single line, I have used a multiline string to output to the logger within a Command Management class method. The example as code shown:

# the usual imports...
# ....
import textwrap

logger = logging.getLogger(__name__)

class Command(BaseCommand):
    def handle(self, *args, **kwargs):
        # some codes here
        # ....
        final_statement = f'''\
                           this is the final statements \
                           with multiline string to have \
                           a neater code.'''

        dedented_text = textwrap.dedent(final_statment)
        logger.info(dedent.replace('  ',''))

I have tried a few methods I found, however, most quick and easy methods still left a big chunk of spaces on the terminal. As shown here:

this is the final statement            with multiline string to have             a neater code.

So I have come up with a creative solution to solve my problem. By using.

dedent.replace('  ','')

Making sure to replace two spaces with no space in order not to get rid of the normal spaces between words. Which finally produced:

this is the final statement with multiline string to have a neater code.

Is this an elegant solution or did I missed something on the internet?

Wong Siwei
  • 115
  • 2
  • 6

3 Answers3

0

You could use regex to simply remove all white space after a newline. Additionally, wrapping it into a function leads to less repetitive code, so let's do that.

import re
def single_line(string):
    return re.sub("\n\s+", "", string)

final_statement = single_line(f'''
                   this is the final statements 
                   with multiline string to have 
                   a neater code.''')
print(final_statement)

Alternatively, if you wish to avoid this particular problem (and don't mine the developmental overhead), you could store them inside a file, like JSON so you can quickly edit prompts while keeping your code clean.

Neil
  • 14,063
  • 3
  • 30
  • 51
  • I was thinking about it, then I realised, by simply using final_statement.replace(' ','') would already solve the problem. textwrap.dedent is just not working. I don't really need to create a function for it as the final_statement will be the only super long one that I need to push to logger. So it is not face repetitive code issue. – Wong Siwei Jul 12 '18 at 15:44
  • @WongSiwei I would still strongly recommend wrapping this in a function in case you decide to implement it differently later on. – Neil Jul 12 '18 at 15:45
  • That's absolutely true. Thanks for your feedback! – Wong Siwei Jul 12 '18 at 15:50
0

Thanks to Neil's suggestion, I have come out with a more elegant solution. By creating a function to replace the two spaces with none.

def single_line(string):
    return string.replace('  ','')

final_statement = '''\
    this is a much neater
    final statement
    to present my code
    '''

print(single_line(final_statement)

As improvised from Neil's solution, I have cut down the regex import. That's one line less of code!

Also, making it a function improves on readability as the whole print statement just read like English. "Print single line final statement"

Any better idea?

Wong Siwei
  • 115
  • 2
  • 6
  • This replaces spaces anywhere in your string. Also, it doesn’t work if your indentation is not a multiple of 2. – bfontaine Jul 12 '18 at 16:10
  • Yes, you are right, but PEP8 suggested using 4 spaces to indent our code, with that said, instead of replacing 2 spaces, we can also replace 4 spaces which will guarantee any indentation to be removed. But, 2 spaces is enough for the job anyway. – Wong Siwei Jul 12 '18 at 16:16
  • It only replaces 2 spaces, and will ignore 1 space, so the print statement will still be readable. – Wong Siwei Jul 12 '18 at 16:18
  • Your function shouldn’t depend on the coding style of the developper who wrote the string it works on. Edit: also, the string you showed in the question is indented by 27 spaces. – bfontaine Jul 12 '18 at 16:24
  • You are right to a certain extent. However, Python's indentation rule goes beyond simply coding style. Well, you can argue that what if another developer decided to use 1 spacing for indentation rule, the code might still run I suppose. but the whole shenanigans will make it so unreadable. which already defeat my purpose of making it presentable. I'm all out for best practices anyway. You could also argue that some might use 3 spaces or any other odd number spaces. In that case, you could be right. But they might not even be thinking of using multiline string to be PEP8 compliance anyway. – Wong Siwei Jul 12 '18 at 16:44
  • How did you count 27 spaces? It's clearly 12 spaces on the indentation. By which each large chunk of space is only 4 spaces. In my actual code I have indented up to 24 spaces and it is still within modulus of 2. – Wong Siwei Jul 12 '18 at 16:47
  • Edit: Ok I might have missed some spaces on my question. Will edit to end the confusion. – Wong Siwei Jul 12 '18 at 16:52
  • If you check `final_statement`, it’s a string that begins with 27 spaces. – bfontaine Jul 13 '18 at 08:29
0

The issue with both Neil’s and Wong Siwei’s answers is they don’t work if your multiline string contains lines more indented than others:

my_string = """\
   this is my
   string and
          it has various
   identation
   levels"""

What you want in the case above is to remove the two-spaces indentation, not every space at the beginning of a line.

The solution below should work in all cases:

import re

def dedent(s):
    indent_level = None

    for m in re.finditer(r"^ +", s):
        line_indent_level = len(m.group())
        if indent_level is None or indent_level > line_indent_level:
            indent_level = line_indent_level

    if not indent_level:
        return s

    return re.sub(r"(?:^|\n) {%s}" % indent_level, "", s)

It first scans the whole string to find the lowest indentation level then uses that information to dedent all lines of it.


If you only care about making your code easier to read, you may instead use C-like strings "concatenation":

my_string = (
  "this is my string"
  " and I write it on"
  " multiple lines"
)

print(repr(my_string))
# => "this is my string and I write it on multiple lines"

You may also want to make it explicit with +s:

my_string = "this is my string" + \
            " and I write it on" + \
            " multiple lines"
bfontaine
  • 18,169
  • 13
  • 73
  • 107
  • Your code is not working. terminal still showing indented string. – Wong Siwei Jul 12 '18 at 16:59
  • Also, you might have missed the point that I'm using multiline string to make my code neater. With your multiline string it doesn't make it more presentable. – Wong Siwei Jul 12 '18 at 17:13
  • @WongSiwei The `it has various` line is still indented on purpose, because its indentation level is deeper than the others. I did however miss the point that you only did that to make your code neater and wasn’t just looking for a general-purpose dedent function. I added alternative solutions in my answer for that. – bfontaine Jul 13 '18 at 08:31
  • 1
    Yes, I like your second suggestion. I started coding with C# so this kind of string concatenation is home ground to me! – Wong Siwei Jul 13 '18 at 15:20