I'm currently building a HTML renderer in Python and have this for the classes to handle the tags:
class SingleTag:
"""A class to represent an html tag"""
# Class initialiser
def __init__(self, inner_html):
self.open_tag = "<" self.__class__.__name__.lower() ">"
self.close_tag = "</" self.__class__.__name__.lower() ">"
self.inner_html = inner_html
# Prints html
def render(self):
return self.open_tag self.inner_html self.close_tag
class ContainingTag:
def __init__(self, children):
self.open_tag = "<" self.__class__.__name__.lower() ">"
self.close_tag = "</" self.__class__.__name__.lower() ">"
self.children = children
def render(self):
print("\t" self.open_tag)
for child in self.children:
print("\t \t" str(child.render()))
print("\t" self.close_tag)
return ""
class Html(ContainingTag):
def __init__(self, children):
super().__init__(children)
self.open_tag = "<!DOCTYPE html>\n" "<" self.__class__.__name__.lower() ">"
def render(self):
print(self.open_tag)
for child in self.children:
print("\t \t" str(child.render()))
print(self.close_tag)
return ""
class Head(ContainingTag):
def __init__(self, children):
super().__init__(children)
class Style(ContainingTag):
def __init__(self, children):
super().__init__(children)
class Body(ContainingTag):
def __init__(self, children):
super().__init__(children)
class Div(ContainingTag):
def __init__(self, children):
super().__init__(children)
class P(SingleTag):
def __init__(self, inner_html):
super().__init__(inner_html)
from tag import Html, P, Div, Head, Style, Body
html = Html([
Div([
Head([])
])
])
print(html.render())
<!DOCTYPE html>
<html>
<div>
<head>
</head>
</div>
</html>
My single tags work fine printing inside a containing tag but when printing a containing tag inside another containing tag, the indention is on the same level, when I want it to be indented. Is there a way to check whether a containing tag is a child of another containing tag, so I can conditionally print the extra indentation?
CodePudding user response:
The way I've always handled recursive indents like this is with a keyword argument to render
(or whatever name the equivalent function has).
Also, you can save a lot of code by inheriting property
functions, which act like attributes, instead of setting static values in the __init__
functions.
class Tag:
@property
def open_tag(self): return f'<{self.__class__.__name__.lower()}>'
@property
def close_tag(self): return f'</{self.__class__.__name__.lower()}>'
def __str__(self):
return self.render()
class SingleTag(Tag):
def __init__(self, inner_html):
self.inner_html = inner_html
def render(self, indent=''):
return f'{indent}{self.open_tag}{self.inner_html}{self.close_tag}\n'
class ContainingTag(Tag):
def __init__(self, children):
self.children = children
def render(self, indent=''):
inner = ''.join(child.render(indent '\t') for child in self.children)
return f'{indent}{self.open_tag}\n{inner}{indent}{self.close_tag}\n'
class Html(ContainingTag):
'''HTML tag'''
@property
def open_tag(self): return '<!DOCTYPE html>\n<html>'
class Head(ContainingTag): '''HEAD tag'''
class Style(ContainingTag): '''STYLE tag'''
class Body(ContainingTag): '''BODY tag'''
class Div(ContainingTag): '''DIV tag'''
class P(SingleTag): '''P tag'''
Then:
html = Html([Div([Head([])])])
print(html)
<!DOCTYPE html>
<html>
<div>
<head>
</head>
</div>
</html>
Note: I've used \t
as the per-level indent here, but you could use whatever makes sense for your needs.