Skip to content

Commit

Permalink
Prevent PyHTML from rendering in quirks mode
Browse files Browse the repository at this point in the history
  • Loading branch information
MaddyGuthridge committed Apr 27, 2024
1 parent 1b2e076 commit d2027e9
Show file tree
Hide file tree
Showing 12 changed files with 137 additions and 64 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ with improved documentation and type safety.
... ),
... )
>>> print(str(my_website))
<!DOCTYPE html>
<html>
<head>
<title>
Expand Down Expand Up @@ -159,6 +160,7 @@ instantiated element.
>>> p.br
<class 'pyhtml.__tags.generated.br'>
>>> print(str(p.html(p.body(p.br))))
<!DOCTYPE html>
<html>
<body>
<br/>
Expand Down
12 changes: 12 additions & 0 deletions meta/generate_tag_defs.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ def _escape_children(self) -> bool:
return False
"""

GET_PRE_CONTENT = """
def _get_tag_pre_content(self) -> Optional[str]:
return {}
"""


def get_template_class(name: str):
try:
Expand Down Expand Up @@ -96,6 +101,13 @@ def generate_tag_class(output: TextIO, tag: TagInfo):
if not tag.escape_children:
print(NO_ESCAPE_CHILDREN, file=output)

# Add pre-content declaration if needed
if tag.pre_content is not None:
print(
GET_PRE_CONTENT.replace("{}", f"'{tag.pre_content}'"),
file=output,
)

# And a nice trailing newline to make flake8 happy
print(file=output)

Expand Down
21 changes: 21 additions & 0 deletions meta/scrape_tags.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,11 @@ class TagsYmlItem(TypedDict):
escape_children: NotRequired[bool]
"""Whether to escape the contents of the tag (default True)"""

pre_content: NotRequired[str]
"""
Pre-content for the element (eg `<!DOCTYPE html>`)
"""


TagsYaml = dict[str, TagsYmlItem]
"""Type alias for type of tags.yml file"""
Expand Down Expand Up @@ -204,6 +209,11 @@ class TagInfo:
List of attributes and their documentation.
"""

pre_content: Optional[str]
"""
Pre-content for the element (eg `<!DOCTYPE html>`)
"""


def fetch_mdn():
"""
Expand Down Expand Up @@ -449,6 +459,16 @@ def get_tag_escape_children(tags: TagsYaml, tag_name: str) -> bool:
return tag.get('escape_children', True)


def get_tag_pre_content(tags: TagsYaml, tag_name: str) -> Optional[str]:
"""
Return pre-content for the tag
"""
if tag_name not in tags:
return None
tag = tags[tag_name]
return tag.get('pre_content', None)


def make_mdn_link(tag: str) -> str:
"""Generate an MDN docs link for the given tag"""
return f"{MDN_ELEMENT_PAGE}/{tag}"
Expand All @@ -475,6 +495,7 @@ def elements_to_element_structs(
mdn_link=make_mdn_link(name),
escape_children=get_tag_escape_children(tag_attrs, name),
attributes=attr_entries_to_object(tag_attrs, name),
pre_content=get_tag_pre_content(tag_attrs, name)
))

return output
Expand Down
7 changes: 7 additions & 0 deletions meta/tags.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
%YAML 1.2
---

html:
pre_content: "<!DOCTYPE html>"
attributes:
lang:
doc: Language used by the document
type: Optional[str]

# Rename <del> tag as it is a reserved keyword in Python
del:
rename: del_
Expand Down
16 changes: 15 additions & 1 deletion pyhtml/__tag_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Tag base class, including rendering logic
"""
from typing import TypeVar
from typing import Optional, TypeVar
from . import __util as util
from .__types import ChildrenType, AttributeType

Expand Down Expand Up @@ -74,6 +74,15 @@ def _get_default_attributes(
"""
return {}

def _get_tag_pre_content(self) -> Optional[str]:
"""
Return "pre-content" for the tag.
This is used by the `<html>` tag to add a `<!DOCTYPE html>` before the
tag.
"""
return None

def _escape_children(self) -> bool:
"""
Returns whether the contents of the element should be escaped, or
Expand All @@ -97,6 +106,11 @@ def _render(self) -> list[str]:

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

# Add pre-content
if (pre := self._get_tag_pre_content()) is not None:
opening = f"{pre}\n{opening}"

if len(attributes):
opening += f" {util.render_tag_attributes(attributes)}>"
else:
Expand Down
20 changes: 12 additions & 8 deletions pyhtml/__tags/generated.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,48 +15,52 @@ class html(Tag):
"""
Represents the root (top-level element) of an HTML document, so it is also referred to as the _root element_. All other elements must be descendants of this element.
* `lang`: Language used by the document
[View full documentation](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/html)
"""
def __init__(
self,
*children: ChildrenType,

lang: Optional[str] = None,
**attributes: AttributeType,
) -> None:
"""
Represents the root (top-level element) of an HTML document, so it is also referred to as the _root element_. All other elements must be descendants of this element.
* `lang`: Language used by the document
[View full documentation](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/html)
"""
attributes |= {

'lang': lang,
}
super().__init__(*children, **attributes)

def __call__( # type: ignore
self,
*children: ChildrenType,

lang: Optional[str] = None,
**attributes: AttributeType,
):
"""
Represents the root (top-level element) of an HTML document, so it is also referred to as the _root element_. All other elements must be descendants of this element.
* `lang`: Language used by the document
[View full documentation](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/html)
"""
attributes |= {

'lang': lang,
}
return super().__call__(*children, **attributes)

def _get_default_attributes(self, given: dict[str, AttributeType]) -> dict[str, AttributeType]:
return {}
return {'lang': None}


def _get_tag_pre_content(self) -> Optional[str]:
return '<!DOCTYPE html>'


class base(SelfClosingTag):
Expand Down
Loading

0 comments on commit d2027e9

Please sign in to comment.