Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 31 additions & 8 deletions pyhtml/__tag_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,12 @@ def _escape_children(self) -> bool:
"""
return True

def _render(self, indent: str, options: FullRenderOptions) -> list[str]:
def _render(
self,
indent: str,
options: FullRenderOptions,
skip_indent: bool = False,
) -> list[str]:
"""
Renders tag and its children to a list of strings where each string is
a single line of output.
Expand All @@ -132,6 +137,8 @@ def _render(self, indent: str, options: FullRenderOptions) -> list[str]:
string to use for indentation
options : FullOptions
rendering options
skip_indent : bool
whether to skip indentation for this element

Returns
-------
Expand All @@ -148,8 +155,13 @@ def _render(self, indent: str, options: FullRenderOptions) -> list[str]:
)
)

# Indentation to use before opening tag
indent_pre = "" if skip_indent else indent
# Indentation to use before closing tag
indent_post = "" if skip_indent or options.indent is None else indent

# Tag and attributes
opening = f"{indent}<{self._get_tag_name()}"
opening = f"{indent_pre}<{self._get_tag_name()}"

# Add pre-content
if (pre := self._get_tag_pre_content()) is not None:
Expand Down Expand Up @@ -177,7 +189,7 @@ def _render(self, indent: str, options: FullRenderOptions) -> list[str]:
return [
opening,
*children,
f"{indent}{closing}",
f"{indent_post}{closing}",
]
else:
# Children must have at least one line, since we would have
Expand All @@ -190,7 +202,12 @@ def _render(self, indent: str, options: FullRenderOptions) -> list[str]:
# Add the closing tag onto the end
return [
*out[:-1],
out[-1] + options.spacing + closing,
# Only include post indentation if it's on a different line
# to the pre indentation
(indent_post if len(out) > 1 else "")
+ out[-1]
+ options.spacing
+ closing,
]

def render(self) -> str:
Expand All @@ -217,11 +234,17 @@ def __init__(
# Self-closing tags don't allow children
super().__init__(*options, **attributes)

def _render(self, indent: str, options: FullRenderOptions) -> list[str]:
def _render(
self,
indent: str,
options: FullRenderOptions,
skip_indent: bool = False,
) -> list[str]:
"""
Renders tag and its children to a list of strings where each string is
a single line of output
"""
indent_str = "" if skip_indent else indent
attributes = util.filter_attributes(
util.dict_union(
self._get_default_attributes(self.attributes),
Expand All @@ -230,18 +253,18 @@ def _render(self, indent: str, options: FullRenderOptions) -> list[str]:
)
if len(attributes):
return [
f"{indent}<{self._get_tag_name()} "
f"{indent_str}<{self._get_tag_name()} "
f"{util.render_tag_attributes(attributes)}/>"
]
else:
return [f"{indent}<{self._get_tag_name()}/>"]
return [f"{indent_str}<{self._get_tag_name()}/>"]


@deprecated(
"Overload `_get_default_render_options` to return "
"`RenderOptions(indent=None, spacing='')` instead"
)
class WhitespaceSensitiveTag(Tag):
class WhitespaceSensitiveTag(Tag): # pragma: no cover
"""
Whitespace-sensitive tags are tags where whitespace needs to be respected.
"""
Expand Down
7 changes: 6 additions & 1 deletion pyhtml/__tags/comment.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,12 @@ def _get_tag_name(self) -> str:
# and is never used since we override _render
return "!--" # pragma: no cover

def _render(self, indent: str, options: FullRenderOptions) -> list[str]:
def _render(
self,
indent: str,
options: FullRenderOptions,
skip_indent: bool = False,
) -> list[str]:
"""
Override of render, to render comments
"""
Expand Down
7 changes: 6 additions & 1 deletion pyhtml/__tags/dangerous_raw_html.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,12 @@ def _get_tag_name(self) -> str:
# and is never used since we override _render
return "!!!DANGEROUS RAW HTML!!!" # pragma: no cover

def _render(self, indent: str, options: FullRenderOptions) -> list[str]:
def _render(
self,
indent: str,
options: FullRenderOptions,
skip_indent: bool = False,
) -> list[str]:
return self.html_data.splitlines()

def _get_default_render_options(self) -> RenderOptions:
Expand Down
20 changes: 13 additions & 7 deletions pyhtml/__util.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,17 +101,24 @@ def render_inline_element(
"""
from .__tag_base import Tag

skip_indent = options.spacing is not None and "\n" not in options.spacing

if isinstance(ele, Tag):
return ele._render(indent, options)
return ele._render(indent, options, skip_indent)
elif isinstance(ele, type) and issubclass(ele, Tag):
e = ele()
return e._render(indent, options)
return e._render(indent, options, skip_indent)
else:
# Remove newlines from strings when inline rendering
if escape_strings:
return increase_indent([escape_string(str(ele))], indent)
return increase_indent(
[escape_string(line) for line in str(ele).splitlines()],
"" if skip_indent else indent,
)
else:
return increase_indent([str(ele)], indent)
return increase_indent(
str(ele).splitlines(), "" if skip_indent else indent
)


def render_children(
Expand All @@ -135,7 +142,6 @@ def render_children(
else:
# Custom spacing
if len(rendered) == 0:
rendered_child[0] = rendered_child[0].strip()
rendered = rendered_child
else:
*r_head, r_tail = rendered
Expand All @@ -145,8 +151,8 @@ def render_children(
# Join it all nicely
rendered = [
*r_head,
# Remove leading whitespace caused by indentation rules
r_tail + options.spacing + c_head.lstrip(),
# Join using spacing as separator
r_tail + options.spacing + c_head,
*c_tail,
]
return rendered
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "pyhtml-enhanced"
version = "2.2.2"
version = "2.2.3"
description = "A library for building HTML documents with a simple and learnable syntax"
readme = "README.md"
license = "MIT"
Expand Down
26 changes: 25 additions & 1 deletion tests/basic_rendering_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def test_renders_elements_with_children():
)


def test_renders_deeply_nested_children():
def test_renders_nested_children():
doc = body(
div(
span("Hello world"),
Expand All @@ -66,6 +66,30 @@ def test_renders_deeply_nested_children():
)


def test_renders_deeply_nested_children():
doc = body(
div(
span(
div("Hello world"),
),
),
)

assert str(doc) == "\n".join(
[
"<body>",
" <div>",
" <span>",
" <div>",
" Hello world",
" </div>",
" </span>",
" </div>",
"</body>",
]
)


def test_renders_attributes():
doc = body(foo="bar")

Expand Down
39 changes: 38 additions & 1 deletion tests/render_options_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,25 @@ def test_mixed_spacing():
)


def test_spacing_str():
doc = p.body(
p.div(
p.RenderOptions(spacing=" "),
p.span(p.RenderOptions(spacing="\n"), "hi"),
),
)

assert str(doc) == "\n".join(
[
"<body>",
" <div> <span>",
" hi",
" </span> </div>",
"</body>",
]
)


def test_spacing_inner_newline():
doc = p.body(
p.div(
Expand Down Expand Up @@ -121,6 +140,24 @@ def test_indent_and_spacing_inner_newline():
)


def test_default_render_options():
def test_default_render_options_paragraph():
doc = p.p("Paragraph")
assert str(doc) == "<p>Paragraph</p>"


def test_extra_space_is_respected_in_paragraphs():
doc = p.p(" Paragraph ")
assert str(doc) == "<p> Paragraph </p>"


def test_paragraphs_render_in_body():
doc = p.body(
p.p("Paragraph"),
)
assert str(doc) == "\n".join(
[
"<body>",
" <p>Paragraph</p>",
"</body>",
]
)
3 changes: 2 additions & 1 deletion tests/whitespace_sensitive_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ def test_textarea():


def test_indentation_ignored():
assert str(p.body(p.pre("hello\nworld"))) == '\n'.join([
doc = p.body(p.pre("hello\nworld"))
assert str(doc) == '\n'.join([
"<body>",
" <pre>hello",
"world</pre>",
Expand Down