Explore using the Python class "type" to generate Bootstrap components.

Extend the dict type to create classes that output Bootstrap components using Chameleon templates.

Resources

In [53]:
from abc import ABC, abstractmethod
In [54]:
from chameleon import PageTemplate
from IPython.display import display, HTML

Note: It's not certain to me that this is a use case for an ABC or not.

Left here in case there is an opportunity to make an abstractmethod.

I wasn't sure where this was going to go.

In [55]:
class _Component(ABC):
    """Use Chameleon templates to replace only the attributes and inner
    content of Bootstrap components that change."""

    template = ""
    attrib = dict(foo="bar")

    def __init__(self, **kwargs):
        """Set :self.__dict__: to self when dict is extended.
        This allows spreading :self: into Chameleon templates as context."""
        
        super().__init__(**kwargs)
        self.__dict__ = self

    @property
    def html(self):
        """Create a Chameleon template from :self.template:.
        Spread :self: and :self.attrib: into render method on Chameleon template."""
        return PageTemplate(self.template).render(**{"attrib": self.attrib, **self})

Code a simple example of _Component to see if desired results are achieved.

In [56]:
class SuccessAlert(dict, _Component):
    template = """<div tal:attributes="attrib">
  ${inner_content}
</div>"""
    attrib={"class": "alert alert-success", "role": "alert"}
In [57]:
# Only the inner content changes.

success_alert = SuccessAlert(
    inner_content="A simple success alert—check it out!",
)
success_alert
Out[57]:
{'inner_content': 'A simple success alert—check it out!'}
In [58]:
display(HTML(success_alert.html))

Get the the eight required contextual classes

Extract the eight contextual classes from the example HTML in the Bootstrap docs.

Create classes programatically with type to render all the supported alerts.

In [59]:
from lxml import etree
from faker import Faker

fake = Faker()
In [60]:
alerts_html = """<div class="alert alert-primary" role="alert">
  A simple primary alert—check it out!
</div>
<div class="alert alert-secondary" role="alert">
  A simple secondary alert—check it out!
</div>
<div class="alert alert-success" role="alert">
  A simple success alert—check it out!
</div>
<div class="alert alert-danger" role="alert">
  A simple danger alert—check it out!
</div>
<div class="alert alert-warning" role="alert">
  A simple warning alert—check it out!
</div>
<div class="alert alert-info" role="alert">
  A simple info alert—check it out!
</div>
<div class="alert alert-light" role="alert">
  A simple light alert—check it out!
</div>
<div class="alert alert-dark" role="alert">
  A simple dark alert—check it out!
</div>"""

tree = etree.fromstring(alerts_html, etree.HTMLParser())
In [61]:
CLASS = "class"
ALERT_CLASSES_ATTRIBS = [
    e.attrib for e in tree.iterdescendants() if e.attrib
]
ALERT_CLASSES_ATTRIBS
Out[61]:
[{'class': 'alert alert-primary', 'role': 'alert'},
 {'class': 'alert alert-secondary', 'role': 'alert'},
 {'class': 'alert alert-success', 'role': 'alert'},
 {'class': 'alert alert-danger', 'role': 'alert'},
 {'class': 'alert alert-warning', 'role': 'alert'},
 {'class': 'alert alert-info', 'role': 'alert'},
 {'class': 'alert alert-light', 'role': 'alert'},
 {'class': 'alert alert-dark', 'role': 'alert'}]
In [62]:
COMPONENT_CLASS_NAMES = [
    "".join(word.title() for word in item[CLASS].split()[-1].split("-"))
    for item in ALERT_CLASSES_ATTRIBS
]
ROLE_LOOKUP = dict(zip(COMPONENT_CLASS_NAMES, ALERT_CLASSES_ATTRIBS))
ALERT_TEMPLATE = """<div tal:attributes="attrib">
  ${inner_content}
</div>"""

Each kind of Bootstrap alert is generated by looping over the classes created from parsing Bootstrap example and using the type type to make classes.

In [63]:
ALERT_COMPONENTS = dict(
    zip(
        COMPONENT_CLASS_NAMES,
        (
            type(
                class_name,
                (dict, _Component),
                dict(template=ALERT_TEMPLATE, attrib=ROLE_LOOKUP[class_name]),
            )
            for class_name in COMPONENT_CLASS_NAMES
        ),
    )
)
ALERT_COMPONENTS
Out[63]:
{'AlertPrimary': abc.AlertPrimary,
 'AlertSecondary': abc.AlertSecondary,
 'AlertSuccess': abc.AlertSuccess,
 'AlertDanger': abc.AlertDanger,
 'AlertWarning': abc.AlertWarning,
 'AlertInfo': abc.AlertInfo,
 'AlertLight': abc.AlertLight,
 'AlertDark': abc.AlertDark}
In [64]:
for key, value in ALERT_COMPONENTS.items():
    inner_html = value(inner_content=fake.paragraph()).html
    print(inner_html)
    display(HTML(inner_html))
<div class="alert alert-primary" role="alert">
  Four color TV let heavy on figure. Opportunity partner wife inside only.
</div>
<div class="alert alert-secondary" role="alert">
  Close tax tonight as. Head lose through growth establish speech truth. Generation green morning force.
</div>
<div class="alert alert-success" role="alert">
  My surface natural arm yourself stay black. Owner tonight official vote.
</div>
<div class="alert alert-danger" role="alert">
  More address real write skin media. Sell enough beautiful still.
</div>
<div class="alert alert-warning" role="alert">
  City blood issue go try. Star new story under real.
</div>
<div class="alert alert-info" role="alert">
  Strategy anyone here marriage involve entire never step. Prove claim likely note. Operation remain way material treat Mr paper help.
</div>
<div class="alert alert-light" role="alert">
  Wonder for management color themselves. Reflect glass product phone decide face.
</div>
<div class="alert alert-dark" role="alert">
  Soon need event she.
</div>