Indent Helper

This helper class provides a changable indent level that can be used to create indents for for output files such as a log.

The intent is to be able to provide indent levels so instead of looking like:

starting the process
entering loop
doing something
entering sub-loop
doing something else
trying to fix something
saving the record
exiting the sub loop
entering sub-loop
doing something else
saving the record
exiting the sub loop
entering sub-loop
doing something else
error saving the record
exiting the sub loop
checking the status
exiting the main loop
finishing the procees

The output could look more like this:

starting the process
    entering loop
        doing something
            entering sub-loop
                doing something else
                trying to fix something
                saving the record
            exiting the sub loop
            entering sub-loop
                doing something else
                saving the record
            exiting the sub loop
            entering sub-loop
                doing something else
                error saving the record
            exiting the sub loop
        checking the status
    exiting the main loop
finishing the procees

the code to do the above could look similar to:

def do_something(record_list):
    ih = IndentHelper()
    ih.push('main level')
    print(ih + 'starting the process')
    ih += 1
    for rec in record list:
        print('%s entering loop' % ih)
        ih.add(1)
        for field in rec:
            ih.print('doing something')

            print(ih.indent_text('checking the status something')
            with ih:
                print(ih + 'entering sub loop')
                with ih:
                print (f'{ih}doing something else')
                try:
                    print (f'{ih}doing something else')
                    print (f'{ih}saving the recird')
                except:
                    print (f'{ih}error saving the record')

                print(ih + 'exiting sub loop')
        ih.sub()
        print('checking the status')
        print('%s exiting main loop' % ih)
    ih.pop('main_level')
    print(ih + 'finishing the process')

As you can see, there are several ways of performing the same operation, these are detailed below.

This is based on the AdvCounter object and shares much of the same math approaches.

The main difference is that the main math methods (add/sub/mult/div/set) can be chained. In other words, you can do something like the following:

>>> ih = IndentHelper()
>>> ih.add(3).sub(3).set(29)
IndentHelper(29)

Setup

When instantiating the IndentHelper class, you have a few parameters that you can pass

initial_indent

This is the initial indent level to start with, (defaults to 0)

spaces

This is the size of each indent (defaults to 4)

max_size

This is the max number of indents (defaults to 20)

char

this is the character used to indent (defaults to ‘ ‘)

kwargs

any additional kwargs are used to setup named indent levels (see changing levels, named levels below)

Changing levels

you have several ways of changing the current level

Adding / Subtracting / Setting the level

You can directly change the level by adding/subtracting/or setting the level

Example:

>>> ih = IndentHelper()
>>> ih.add()  # if you do not pass a value, 1 level is added or subtracted
>>> ih += 1
>>> ih.sub(2)
>>> ih -= 1
>>> ih.set(0)

# you can also chain them together to reset the level, then add or subtract from that.
>>> ih.set(0).add()

# as with the AdvCounter class, you can also just call the instance
>>> ih()  # will add 1)
>>> ih(sub=1)  # will subtract 1

Note

The multiplication and division functions from AdvCounter are still present in this sub-class, however we do not see a major use for them in this use case, so have not documented them.

Named levels

You can setup specific levels with names that can be used/recalled as needed. Normally this would be used to setup a base level before going into a function so that if for some reason the function errors out or misses reducing the level, your indents will not get out of sync.

Example:

>>> ih += 3
>>> ih.push('before doing something')
>>> int(ih)
3
>>> ih += 2
>>> ih += 1
>>> ih -= 2  # We missed reducing the indent 1 level here!
>>> int(ih)
4
>>> ih.pop('before doing something')  # This also removed the 'before doing something' key from the memory
>>> int(ih)
3

# This can also be retrieved without removing the key by passing the name to the set method.
>>> ih.set('before doing something').  # Though this would actually error since it was removed by the pop above.

Saved Levels

If you use push/pop without any keys, the system will save the current level to a queue and pop the last level from the queue, this way you dont necessarily have to name everything.

Example:

>>> int(ih)
4
>>> ih.push()
>>> ih += 2
>>> ih.push()
>>> ih()
>>> int(ih)
7

>>> ih.pop()
>>> int(ih)
6

>>> ih.pop()
>>> int(ih)
4

Context manager

An even easier way if handling this without having to worry about push/pop is to use IndentHelper as a context manager.

For example:

from advanced_counter import IndentHelper

def test_indent():
    ih = IndentHelper()
    ih.print('line 1')
    with ih:
        ih.print('line 2')
        ih().print('line3')
    ih.print('line4')

would result in:

>>> test_indent()
line 1
    line 2
        line 3
line 4

Getting output

There are also several ways of getting output from the instance.

Examples:

>>> ih(2)
# the following will return the indent as a string
>>> str(ih)
>>> ih.i
'        '

# this means that it can be used in formatting methods:

>>> 'before indent %s foobar' % ih
>>> f'before indent {ih} foobar'
>>> 'before indent {} foobar'.format(ih)
'before indent          foobar'

# the following will combine the indent with a passed string
>>> ih + 'foobar'
>>> ih.print('foobar')
>>> ih.indent_text('foobar')   # see the api docs for more options with indent_text
'        foobar'

# the following returns the current indent depth
>>> ih.indent
>>> ih.value
2

Other operations

In addition to the above, there are a few standard python methods that are supported

Examples:

# to check to see if there is a saved name:
>>> ih.push('test1')
>>> 'test' in ih
True

# to see the current lenght of the indent string (i.e. the current indent * number of characters)
>>> ih.set(3)
>>> len(ih)
12

# To see if the indent was ever changed:
>>> bool(ih)
True

Other Helpers

Also included are two helper items

indent_text

This is a function that can indent text in various forms. similar to pythons textwrap.indent, but allowing passing of the indent size and character instead of just an indent string.

(see api docs below for more information)

DeferredIndent

This is a class that will defer processing the indent until a later time, saving processing time if it is not needed. This is similar to examples in the python logging cookbook.

(see api docs below for more information)

API