WhakerPy 0.8

https://sourceforge.net/projects/whakerpy/

Module whakerpy.htmlmaker

Class HTMLTree

Description

Root of an HTML tree.

Since the early days of the World Wide Web, there have been many versions: [source: https://www.w3schools.com/html/html_intro.asp]

  • 1989: Tim Berners-Lee invented www
  • 1991: Tim Berners-Lee invented HTML
  • 1993: Dave Raggett drafted HTML+
  • 1995: HTML Working Group defined HTML 2.0
  • 1997: W3C Recommendation: HTML 3.2
  • 1999: W3C Recommendation: HTML 4.01
  • 2000: W3C Recommendation: XHTML 1.0
  • 2008: WHATWG HTML5 First Public Draft
  • 2012: WHATWG HTML5 Living Standard
  • 2014: W3C Recommendation: HTML5
  • 2016: W3C Candidate Recommendation: HTML 5.1
  • 2017: W3C Recommendation: HTML5.1 2nd Edition
  • 2017: W3C Recommendation: HTML5.2

HTML elements are generally made of a start tag, an optional element content, and an end tag. However, several elements have only a start tag, like "br" or "img", and a few elements don't have tag at all, like comments.

An HTMLTree has two children: a doctype node, and a "html" node. The "html" tag is the container for all HTML elements of the page. The following properties allow to access to "html" children nodes:

  • head
  • body_header
  • body_nav
  • body_main
  • body_footer
  • body_script
Example
 >>> # Create the tree
 >>> htree = HTMLTree("index")
 >>> htree.add_html_attribute("lang", "en")
 >>> # Fill in the <head> element node with:
 >>> # a title, a meta and a link
 >>> htree.head.title("Purpose")
 >>> htree.head.meta({"charset": "utf-8"})
 >>> htree.head.link(rel="icon", href="/static/favicon.ico")
 >>> # Fill in the <body> element node with:
 >>> # A <nav> tag in the header, a <h1> tag in the body and a <p> tag in the footer
 >>> htree.set_body_attribute("class", "colors_scheme_dark")
 >>> nav = HTMLNode(htree.body_header.identifier, "navmenu", "nav")
 >>> htree.body_header.append_child(nav)
 >>> node = HTMLNode(htree.body_main.identifier, None, "h1", value="this is a title")
 >>> htree.body_main.append_child(node)
 >>> node = HTMLNode(htree.body_footer.identifier, None, "p", value="&copy; me now")
 >>> htree.body_footer.append_child(node)
 >>> # Save into a file
 >>> htree.serialize_to_file("/path/to/file.html")

This class does not support yet the global attributes -- i.e. attributes that can be used with all HTML elements. See https://www.w3schools.com/TAgs/ref_standardattributes.asp

Constructor

Create the tree root and children nodes.

The created tree matches the HTML5 recommendations for the document structure. The HTML tree has 2 children: a doctype and an HTML element. The HTML node has 2 children: the "head" and the "body". The body has 5 children: "header", "nav", "main", "footer", "script". The empty nodes are not serialized.

Parameters
  • identifier: (str) An identifier for the tree node.
View Source
def __init__(self, identifier: str):
    """Create the tree root and children nodes.

    The created tree matches the HTML5 recommendations for the document
    structure. The HTML tree has 2 children: a doctype and an HTML element.
    The HTML node has 2 children: the "head" and the "body". The body
    has 5 children: "header", "nav", "main", "footer", "script".
    The empty nodes are not serialized.

    :param identifier: (str) An identifier for the tree node.

    """
    super(HTMLTree, self).__init__(parent=None, identifier=identifier)
    self.__doctype = Doctype()
    self.__html = HTMLNode(identifier, None, 'html')
    self.__html.append_child(HTMLHeadNode(self.__html.identifier))
    body = HTMLNode(self.__html.identifier, 'body', 'body')
    self.__html.append_child(body)
    body.append_child(HTMLHeaderNode(body.identifier))
    body.append_child(HTMLNavNode(body.identifier))
    body.append_child(HTMLMainNode(body.identifier))
    body.append_child(HTMLFooterNode(body.identifier))
    body.append_child(HTMLScriptNode(body.identifier))

Public functions

set_parent

Override. Do not set the parent identifier.

Parameters
  • node_id
View Source
def set_parent(self, node_id: str) -> None:
    """Override. Do not set the parent identifier. """
    return None

is_leaf

Override. Return False.

View Source
def is_leaf(self) -> bool:
    """Override. Return False. """
    return False

add_html_attribute

Add or append a property to the HTML node.

Parameters
  • key: (str) Key property of an HTML attribute
  • value: (str) Value of the attribute
Raises
  • NodeTypeError: if key or value is not a string
  • NodeAttributeError: if unknown key.
View Source
def add_html_attribute(self, key: str, value: str) -> None:
    """Add or append a property to the HTML node.

        :param key: (str) Key property of an HTML attribute
        :param value: (str) Value of the attribute

        :Raises: NodeTypeError: if key or value is not a string
        :Raises: NodeAttributeError: if unknown key.

        """
    self.__html.add_attribute(key, value)

get_body_attribute_value

Get the attribute value of the body element node.

Parameters
  • key: (str) Key property of an HTML attribute
Returns
  • (str) The attribute value of the element
View Source
def get_body_attribute_value(self, key: str) -> str:
    """Get the attribute value of the body element node.

        :param key: (str) Key property of an HTML attribute
        :return: (str) The attribute value of the <body> element

        """
    return self._get_body().get_attribute_value(key)

add_body_attribute

Add an attribute to the body element node.

Parameters
  • key: (str) Key property of an HTML attribute
  • value: (str) Value of the attribute
Raises
  • NodeTypeError: if key or value is not a string
  • NodeAttributeError: if unknown key
Returns
  • normalized key
View Source
def add_body_attribute(self, key: str, value: str) -> str:
    """Add an attribute to the body element node.

        :param key: (str) Key property of an HTML attribute
        :param value: (str) Value of the attribute
        :Raises: NodeTypeError: if key or value is not a string
        :raises: NodeAttributeError: if unknown key
        :return: normalized key

        """
    return self._get_body().add_attribute(key, value)

set_body_attribute

Set an attribute of the body.

Parameters
  • key: (str) Key property of an HTML attribute
  • value: (str) Value of the attribute
Returns
  • (bool) The attribute is set
View Source
def set_body_attribute(self, key: str, value: str) -> None:
    """Set an attribute of the body.

        :param key: (str) Key property of an HTML attribute
        :param value: (str) Value of the attribute
        :return: (bool) The attribute is set

        """
    self._get_body().set_attribute(key, value)

get_body_identifier

Return the identifier of the body node.

Returns
  • (str) the identifier of the body node.
View Source
def get_body_identifier(self) -> str:
    """Return the identifier of the body node.

        :return: (str) the identifier of the body node.

        """
    return self._get_body().identifier

insert_body_child

Insert a html node in the body.

Parameters
  • child: (HTMLNode) the node to append in the body
  • index: (int) Optional, the index where insert the child, by default the index is set to 0
Raises
  • ValueError: If the index is negative
View Source
def insert_body_child(self, child: HTMLNode, index: int=0) -> None:
    """Insert a html node in the body.

        :param child: (HTMLNode) the node to append in the body
        :param index: (int) Optional, the index where insert the child, by default the index is set to 0

        :raises ValueError: If the index is negative

        """
    if index < 0:
        raise ValueError("The index can't be negative !")
    self._get_body().insert_child(index, child)

get_head

Get the head node element.

Returns
  • (HTMLNode) Head node element
View Source
def get_head(self) -> HTMLNode:
    """Get the head node element.

        :return: (HTMLNode) Head node element

        """
    return self.__html.get_child('head')

set_head

Replace the current head node by the given one.

Parameters
  • head_node: (HTMLNode)
Raises
  • NodeTypeError: if head_node is not an HTMLNode
  • NodeIdentifierError: if head_node identifier is not "head"
View Source
def set_head(self, head_node: HTMLNode) -> None:
    """Replace the current head node by the given one.

        :param head_node: (HTMLNode)

        :Raises: NodeTypeError: if head_node is not an HTMLNode
        :raises: NodeIdentifierError: if head_node identifier is not "head"

        """
    if hasattr(head_node, 'identifier') is False:
        raise NodeTypeError(type(head_node))
    if head_node.identifier != 'head':
        raise NodeIdentifierError('head', head_node.identifier)
    head_node.set_parent(self.__html.identifier)
    self.__html.remove_child('head')
    self.__html.insert_child(0, head_node)

get_body_header

Get the body->header element node.

Returns
  • (HTMLNode | None) Body header node element
View Source
def get_body_header(self) -> HTMLNode | None:
    """Get the body->header element node.

        :return: (HTMLNode | None) Body header node element

        """
    return self._get_body().get_child('body_header')

set_body_header

Replace the current body->header element node by the given one.

Parameters
  • body_node: (HTMLNode)
Raises
  • NodeTypeError: if head_node is not an HTMLNode
  • NodeIdentifierError: if headnode identifier is not "bodyheader"
View Source
def set_body_header(self, body_node):
    """Replace the current body->header element node by the given one.

        :param body_node: (HTMLNode)

        :Raises: NodeTypeError: if head_node is not an HTMLNode
        :Raises: NodeIdentifierError: if head_node identifier is not "body_header"

        """
    if hasattr(body_node, 'identifier') is False:
        raise NodeTypeError(type(body_node))
    if body_node.identifier != 'body_header':
        raise NodeIdentifierError('body_header', body_node.identifier)
    body_node.set_parent(self._get_body().identifier)
    self._get_body().remove_child('body_header')
    self._get_body().insert_child(0, body_node)

get_body_nav

Get the body->nav element node.

Returns
  • (HTMLNode) Body nav node element
View Source
def get_body_nav(self):
    """Get the body->nav element node.

        :return: (HTMLNode) Body nav node element

        """
    return self._get_body().get_child('body_nav')

set_body_nav

Replace the current body->nav node by the given one.

Parameters
  • body_node: (HTMLNode)
Raises
  • NodeTypeError: if head_node is not an HTMLNode
  • NodeIdentifierError: if headnode identifier is not "bodynav"
View Source
def set_body_nav(self, body_node):
    """Replace the current body->nav node by the given one.

        :param body_node: (HTMLNode)

        :Raises: NodeTypeError: if head_node is not an HTMLNode
        :raises: NodeIdentifierError: if head_node identifier is not "body_nav"

        """
    if hasattr(body_node, 'identifier') is False:
        raise NodeTypeError(type(body_node))
    if body_node.identifier != 'body_nav':
        raise NodeIdentifierError('body_nav', body_node.identifier)
    body_node.set_parent(self._get_body().identifier)
    self._get_body().remove_child('body_nav')
    self._get_body().insert_child(1, body_node)

get_body_main

Get the body->main element node.

Returns
  • (HTMLNode) Body main node element
View Source
def get_body_main(self):
    """Get the body->main element node.

        :return: (HTMLNode) Body main node element

        """
    return self._get_body().get_child('body_main')

get_body_footer

Get the body->footer element node.

Returns
  • (HTMLNode) Body footer node element
View Source
def get_body_footer(self):
    """Get the body->footer element node.

        :return: (HTMLNode) Body footer node element

        """
    return self._get_body().get_child('body_footer')

set_body_footer

Replace the current body->footer node by the given one.

Parameters
  • body_node: (HTMLNode)
Raises
  • NodeTypeError: if head_node is not an HTMLNode
  • NodeIdentifierError: if headnode identifier is not "bodyfooter"
View Source
def set_body_footer(self, body_node):
    """Replace the current body->footer node by the given one.

        :param body_node: (HTMLNode)
        :Raises: NodeTypeError: if head_node is not an HTMLNode
        :Raises: NodeIdentifierError: if head_node identifier is not "body_footer"

        """
    if hasattr(body_node, 'identifier') is False:
        raise NodeTypeError(type(body_node))
    if body_node.identifier != 'body_footer':
        raise NodeIdentifierError('body_footer', body_node.identifier)
    body_node.set_parent(self._get_body().identifier)
    self._get_body().remove_child('body_footer')
    self._get_body().append_child(body_node)

get_body_script

Get the body->script element node.

Returns
  • (HTMLNode) Body script node element
View Source
def get_body_script(self):
    """Get the body->script element node.

        :return: (HTMLNode) Body script node element

        """
    return self._get_body().get_child('body_script')

set_body_script

Replace the current body->script node by the given one.

Parameters
  • body_node: (HTMLNode)
Raises
  • NodeTypeError: if head_node is not an HTMLNode
  • NodeIdentifierError: if headnode identifier is not "bodyscript"
View Source
def set_body_script(self, body_node):
    """Replace the current body->script node by the given one.

        :param body_node: (HTMLNode)

        :Raises: NodeTypeError: if head_node is not an HTMLNode
        :raises: NodeIdentifierError: if head_node identifier is not "body_script"

        """
    if hasattr(body_node, 'identifier') is False:
        raise NodeTypeError(type(body_node))
    if body_node.identifier != 'body_script':
        raise NodeIdentifierError('body_script', body_node.identifier)
    body_node.set_parent(self._get_body().identifier)
    self._get_body().remove_child('body_script')
    self._get_body().append_child(body_node)

comment

Add a comment to the body->main.

Parameters
  • content
View Source
def comment(self, content):
    """Add a comment to the body->main."""
    node = HTMLComment(self.body_main.identifier, content)
    self.body_main.append_child(node)
    return node

element

Add a node to the body->main.

Parameters
  • tag: (str) HTML element name
  • ident: (str) Identifier of the element
  • class_name: (str) Value of the class attribute
Returns
  • (HTMLNode) The created node
View Source
def element(self, tag: str='div', ident=None, class_name=None) -> HTMLNode:
    """Add a node to the body->main.

        :param tag: (str) HTML element name
        :param ident: (str) Identifier of the element
        :param class_name: (str) Value of the class attribute
        :return: (HTMLNode) The created node

        """
    att = dict()
    if ident is not None:
        att['id'] = str(ident)
    if class_name is not None:
        att['class'] = str(class_name)
    node = HTMLNode(self.body_main.identifier, ident, tag, attributes=att)
    self.body_main.append_child(node)
    return node

button

Add a classic button with given text value and onclick event to the body->main.

Parameters
  • value: (str) The text write in the button
  • on_clik: (str) the onclick event of the button (generally call a js function)
  • identifier: (str) Optional, the identifier of the node (and also the id of the tag in the html generated)
  • class_name: (str) Optional, the classes attribute for css of the button tag
Returns
  • (HTMLNode) The button node created
View Source
def button(self, value: str, on_clik: str, identifier: str=None, class_name: str=None) -> HTMLNode:
    """Add a classic button with given text value and onclick event to the body->main.

        :param value: (str) The text write in the button
        :param on_clik: (str) the onclick event of the button (generally call a js function)
        :param identifier: (str) Optional, the identifier of the node (and also the id of the tag in the html generated)
        :param class_name: (str) Optional, the classes attribute for css of the button tag

        :return: (HTMLNode) The button node created

        """
    attributes = {'onclik': on_clik}
    if identifier is not None:
        attributes['id'] = identifier
    if class_name is not None:
        attributes['class'] = class_name
    button = HTMLNode(self.body_main.identifier, identifier, 'button', value=value, attributes=attributes)
    self.body_main.append_child(button)
    return button

image

Add an image to the body->main.

Parameters
  • src: (str) The path of the image file
  • alt_text: (str) the alternative text if for some reason the image doesn't display or for narrator
  • identifier: (str) Optional, the identifier of the node (and also the id of the tag in the html generated)
  • class_name: (str) Optional, the classes attribute for css of the button tag
Returns
  • (HTMLNode) The image node created
View Source
def image(self, src: str, alt_text: str, identifier: str=None, class_name: str=None) -> HTMLNode:
    """Add an image to the body->main.

        :param src: (str) The path of the image file
        :param alt_text: (str) the alternative text if for some reason the image doesn't display or for narrator
        :param identifier: (str) Optional, the identifier of the node (and also the id of the tag in the html generated)
        :param class_name: (str) Optional, the classes attribute for css of the button tag

        :return: (HTMLNode) The image node created

        """
    attributes = {'src': src, 'alt': alt_text}
    if identifier is not None:
        attributes['id'] = identifier
    if class_name is not None:
        attributes['class'] = class_name
    img = HTMLNode(self.body_main.identifier, identifier, 'img', attributes=attributes)
    self.body_main.append_child(img)
    return img

serialize_element

Serialize an element node only if not empty.

Parameters
  • node: (HTMLNode) Any element node
  • nbs: (int) Number of space for indentation
Raises
  • NodeTypeError: If the given parameter is not an HTMLNode
Returns
  • (str) Serialized node only if it has children or a value.
View Source
@staticmethod
def serialize_element(node: HTMLNode, nbs: int=4) -> str:
    """Serialize an element node only if not empty.

        :param node: (HTMLNode) Any element node
        :param nbs: (int) Number of space for indentation
        :raises: NodeTypeError: If the given parameter is not an HTMLNode
        :return: (str) Serialized node only if it has children or a value.

        """
    if node is None:
        return ''
    if hasattr(node, 'identifier') is False:
        raise NodeTypeError(type(node))
    if node.children_size() > 0 or node.get_value() is not None:
        return node.serialize(nbs)
    return ''

serialize

Override. Serialize the tree into HTML.

Parameters
  • nbs: (int) Number of spaces for the indentation
Returns
  • (str)
View Source
def serialize(self, nbs: int=4) -> str:
    """Override. Serialize the tree into HTML.

        :param nbs: (int) Number of spaces for the indentation
        :return: (str)

        """
    s = self.__doctype.serialize()
    s += '<html'
    for akey in self.__html.get_attribute_keys():
        avalue = self.__html.get_attribute_value(akey)
        s += ' ' + akey
        if avalue is not None:
            s += '="' + avalue + '"'
    s += '>\n'
    s += self.__html.get_child('head').serialize(nbs)
    s += '<body'
    for akey in self._get_body().get_attribute_keys():
        avalue = self._get_body().get_attribute_value(akey)
        s += ' ' + akey
        if avalue is not None:
            s += '="' + avalue + '"'
    s += '>\n'
    if self.get_body_header() is not None:
        s += self.serialize_element(self.get_body_header(), nbs)
    if self.get_body_nav() is not None:
        s += self.serialize_element(self.get_body_nav(), nbs)
    if self.get_body_main() is not None:
        s += self.get_body_main().serialize(nbs)
    if self.get_body_footer() is not None:
        s += self.serialize_element(self.get_body_footer(), nbs)
    if self.get_body_script() is not None:
        s += self.serialize_element(self.get_body_script(), nbs)
    s += '\n</body>\n</html>\n'
    return s

serialize_to_file

Serialize the tree into an HTML file.

The HTML content is saved into the file and its URL is returned.

Parameters
  • filename: (str) A filename to save the serialized HTML string.
  • nbs: (int) Number of spaces for the indentation
Returns
  • (str) file URL
View Source
def serialize_to_file(self, filename: str, nbs: int=4) -> str:
    """Serialize the tree into an HTML file.

        The HTML content is saved into the file and its URL is returned.

        :param filename: (str) A filename to save the serialized HTML string.
        :param nbs: (int) Number of spaces for the indentation
        :returns: (str) file URL

        """
    with open(filename, 'w') as fp:
        fp.write(self.serialize(nbs))
    return 'file://' + os.path.abspath(filename)

Private functions

_get_body

Get the body element node.

Returns
  • (HTMLNode) The body element node
View Source
def _get_body(self) -> HTMLNode:
    """Get the body element node.

        :return: (HTMLNode) The body element node

        """
    return self.__html.get_child('body')

Overloads

__contains__

View Source
def __contains__(self, identifier):
    raise NotImplementedError

__str__

View Source
def __str__(self):
    return 'HTMLTree ({:s})'.format(self.identifier)