1

To simplify I refer to HTML code though it is something different.

I use decorators to generate such code.

I must format the output by indenting it. I think I need to keep track of the nesting level to adapt the indentation but I don't know if and how I can keep track of the nesting level. As an example (https://www.thecodeship.com/patterns/guide-to-python-function-decorators/)

def p_decorate(func):
   def func_wrapper(name):
       return "<p>{0}</p>".format(func(name))
   return func_wrapper

def strong_decorate(func):
    def func_wrapper(name):
        return "<strong>{0}</strong>".format(func(name))
    return func_wrapper

def div_decorate(func):
    def func_wrapper(name):
        return "<div>{0}</div>".format(func(name))
    return func_wrapper


get_text = div_decorate(p_decorate(strong_decorate(get_text)))

@div_decorate
@p_decorate
@strong_decorate
def get_text(name):
   return "lorem ipsum, {0} dolor sit amet".format(name)

print(get_text("John"))

# Outputs <div><p><strong>lorem ipsum, John dolor sit amet</strong></p></div>

I would like to obtain, using a general algorithm,

<div>
    <p>
        <strong>lorem ipsum, John dolor sit amet</strong>
    </p>
</div>

Is it possible? and how?

I have a code but it is quite long.

EDit: since people asked for the real example here you have the output I aim at getting

config vdom
edit <vdom>
config router static
    edit <number>
        set dst <ip> <netmask>
        set gateway <ip>
        set device <intf>
    next
end
end

Thanks,

Alex
  • 65
  • 9
  • 1
    show some code please – gold_cy Jun 25 '19 at 10:34
  • Please [edit] your question and some example to show what you meant. Why do you want to keep track of nesting level and what your decorator looks like? – heemayl Jun 25 '19 at 10:38
  • Have you tried with elementtree? https://docs.python.org/3/library/xml.etree.elementtree.html , that is all you need. – alec_djinn Jun 25 '19 at 10:42
  • @aws_apprentice, I added the code that returns non-indented code and the example of how I would like the output to be. – Alex Jun 25 '19 at 10:49
  • @alec_djinn, thanks for the hint, but I prefer to use decorators – Alex Jun 25 '19 at 10:49
  • I may need to use/access the function stack depth in Python if ever possible. – Alex Jun 25 '19 at 10:50
  • 1
    why use decorators? you're complicating this problem more than necessary – gold_cy Jun 25 '19 at 10:54
  • @aws_apprentice, what would you suggest? As I said my real examples are not HTML codes but rather networking devices' configuration files. They differ for each vendor. I need to generate the code the vendor uses so that I can copy and paste it on the device. – Alex Jun 25 '19 at 11:12
  • 1
    so show an example of what those look like? there are plenty of config parsing libraries out there – gold_cy Jun 25 '19 at 11:13
  • @Alex why post an example with HTML tags if that's not what you're working on ??? If you expect a correct answer, post a correct example... – bruno desthuilliers Jun 25 '19 at 11:14
  • @brunodesthuilliers, IMO the answer doesn't depend on the generated output (either HTML or proprietary) but since by mentioning HTML I might have misled helpers I eventually edited the original post – Alex Jun 25 '19 at 11:18
  • Ok, seen. The proper solution here is not to use decorators, but to build an AST (https://en.wikipedia.org/wiki/Abstract_syntax_tree) and use it to generate the final code. – bruno desthuilliers Jun 25 '19 at 11:37

2 Answers2

1

You can use recursion to build a nested list of lists to store the overall structure. Since your decorators follow a similar pattern of {tag}_decorate, a class with a __getattr__ can be used to eliminate the need to create separate decorator functions for each desired HTML tag. The returned nested lists can be traversed recursively to generate the desired structure. The traverse function stores a counter to track the indentation:

class HTML:
   def __getattr__(self, tag):
      def outer(f):
        def wrapper(name):
            return [tag, f(name)]
        return wrapper
      return outer


def traverse(d, c = 0):
   a, b = d
   if not isinstance(b, list):
      return f'{"  "*c}<{a}>{b}</{a}>'
   return f'{"  "*c}<{a}>\n{" "*(c+1)}{traverse(b, c+1)}\n{"   "*c}</{a}>'

html = HTML()

@html.div
@html.p
@html.strong
def get_text(name):
  return "lorem ipsum, {0} dolor sit amet".format(name)

print(traverse(get_text("John")))

Output:

<div>
   <p>
      <strong>lorem ipsum, John dolor sit amet</strong>
   </p>
</div>
Ajax1234
  • 69,937
  • 8
  • 61
  • 102
0

Thanks to all for their contributions. Eventually I ended up with this. egt_stack_size taken from this answer

How do I get the current depth of the Python interpreter stack? (second answer, I don't know if there is a direct link)

import sys
TAB=" "
def get_stack_size():
    size = 2  # current frame and caller's frame always exist
    while True:
        try:
            sys._getframe(size)
            size += 1
        except ValueError:
            return size - 1  # subtract current frame

def p_decorate(func):
    def func_wrapper(name):
        ind=get_stack_size()-2
        ret = ind*TAB+"<p>\n"+ \
              "{0}\n".format(func(name))+ \
              ind*TAB+"</p>"
        return ret
    return func_wrapper

def strong_decorate(func):
    def func_wrapper(name):
        ind=get_stack_size()-2
        ret = ind*TAB+"<strong>\n"+ \
              "{0}\n".format(func(name))+ \
              ind*TAB+"</strong>"
        return ret
    return func_wrapper

def div_decorate(func):
    def func_wrapper(name):
        ind=get_stack_size()-2
        ret = ind*TAB+"<div>\n"+ \
              "{0}\n".format(func(name))+ \
              ind*TAB+"</div>"
        return ret
    return func_wrapper

#get_text = div_decorate(p_decorate(strong_decorate(get_text)))

@strong_decorate
@div_decorate
@p_decorate
def get_text(name):
    ind=get_stack_size()-2
    return ind*TAB+"lorem ipsum, {0} dolor sit amet".format(name)

print(get_text("John"))

Mine might not be the best way to do it as @brunodesthuilliers pointed out. @Ajax1234 I fear yours doesn't work for me because the opening and closing tags are not just one word but a sentence, and hence I should process the tag's name in order to have the right "tag" but yours is a very good exercise that saves a lot of code :-)

Alex
  • 65
  • 9