0
# HTML/XML Generation
1
2
Programmatic HTML and XML generation with a fluent API, supporting all standard HTML elements and providing safe string handling for web development. The xml module enables building HTML documents and web components through Python code with type safety and modern web standards support.
3
4
## Capabilities
5
6
### Core XML/HTML Generation
7
8
Fundamental classes for creating and manipulating XML/HTML structures with a fluent, chainable API.
9
10
```python { .api }
11
class FT:
12
"""
13
Fast Tag - Core class for XML/HTML element creation and manipulation.
14
15
Provides a flexible, chainable interface for building HTML/XML structures
16
with automatic attribute handling, content management, and event system.
17
18
Features:
19
- Chainable method calls for fluent API
20
- Automatic attribute name conversion (class_, htmlClass -> class)
21
- Event listener system for dynamic updates
22
- Support for nested elements and content
23
- Void element handling (self-closing tags)
24
25
Parameters:
26
- tag: str, HTML/XML tag name
27
- cs: tuple, child elements and content
28
- attrs: dict, element attributes
29
- void_: bool, whether element is self-closing
30
- **kwargs: additional attributes
31
"""
32
33
def __init__(self, tag, cs, attrs=None, void_=False, **kwargs): ...
34
35
def __call__(self, *c, **kw):
36
"""
37
Add children and/or attributes (chainable).
38
39
Parameters:
40
- *c: child elements or content to add
41
- **kw: attributes to set
42
43
Returns:
44
self: For method chaining
45
"""
46
47
def set(self, *c, **kw):
48
"""
49
Set children and/or attributes, replacing existing (chainable).
50
51
Parameters:
52
- *c: child elements or content to set
53
- **kw: attributes to set
54
55
Returns:
56
self: For method chaining
57
"""
58
59
def get(self, k, default=None):
60
"""
61
Get attribute value.
62
63
Parameters:
64
- k: str, attribute name
65
- default: default value if attribute not found
66
67
Returns:
68
Attribute value or default
69
"""
70
71
def on(self, f):
72
"""
73
Add event listener for element changes.
74
75
Parameters:
76
- f: callable, listener function called on changes
77
"""
78
79
def changed(self):
80
"""
81
Notify listeners of element changes and return self.
82
83
Returns:
84
self: For method chaining
85
"""
86
87
# Properties and indexing
88
@property
89
def list(self):
90
"""Get [tag, children, attrs] representation."""
91
92
def __getitem__(self, idx): ...
93
def __setitem__(self, i, o): ...
94
def __iter__(self): ...
95
96
def ft(tag, *c, **kw):
97
"""
98
Create FT (Fast Tag) element with specified tag, content, and attributes.
99
100
Convenient factory function for creating HTML/XML elements with
101
automatic preprocessing of content and attributes.
102
103
Parameters:
104
- tag: str, HTML/XML tag name
105
- *c: child elements, strings, or other content
106
- **kw: element attributes (automatically converted)
107
108
Returns:
109
FT: Configured element ready for use
110
111
Examples:
112
div = ft('div', 'Hello World', class_='container')
113
link = ft('a', 'Click here', href='https://example.com')
114
"""
115
116
def Html(*c, **kwargs):
117
"""
118
Create complete HTML document structure.
119
120
Generates proper HTML5 document with DOCTYPE and html root element.
121
Automatically handles head and body sections if provided.
122
123
Parameters:
124
- *c: content for the document (head, body, or direct elements)
125
- **kwargs: attributes for html element (lang, etc.)
126
127
Returns:
128
str: Complete HTML document string
129
"""
130
131
class Safe:
132
"""
133
Wrapper for HTML strings that should not be escaped.
134
135
Used to mark strings as safe HTML content that should be
136
inserted directly without HTML entity escaping.
137
138
Parameters:
139
- s: str, HTML content that is safe to insert directly
140
141
Usage:
142
content = Safe('<strong>Bold text</strong>')
143
div = Div(content) # HTML is not escaped
144
"""
145
146
def __init__(self, s): ...
147
148
def to_xml(elm, lvl=0):
149
"""
150
Convert element tree to XML/HTML string representation.
151
152
Serializes FT elements and nested structures to properly
153
formatted XML/HTML with appropriate indentation and escaping.
154
155
Parameters:
156
- elm: FT element or content to serialize
157
- lvl: int, indentation level for formatting
158
159
Returns:
160
str: XML/HTML string representation
161
"""
162
```
163
164
### Attribute and Value Processing
165
166
Utilities for handling HTML attributes and values with proper conversion and escaping.
167
168
```python { .api }
169
def attrmap(o):
170
"""
171
Map Python attribute names to HTML attribute names.
172
173
Converts Python-friendly attribute names to proper HTML attributes:
174
- class_, cls, htmlClass, klass -> class
175
- _for, fr, htmlFor -> for
176
- Leading underscores removed: _id -> id
177
- Underscores converted to hyphens: data_value -> data-value
178
179
Parameters:
180
- o: str, attribute name to convert
181
182
Returns:
183
str: HTML-compatible attribute name
184
"""
185
186
def valmap(o):
187
"""
188
Convert Python values to HTML attribute values.
189
190
Handles various Python types for HTML attribute values:
191
- Lists: joined with spaces (['btn', 'primary'] -> 'btn primary')
192
- Dicts: CSS-style ({"color": "red"} -> 'color:red')
193
- None/empty: returns None (attribute omitted)
194
195
Parameters:
196
- o: value to convert to HTML attribute format
197
198
Returns:
199
str|None: HTML-compatible attribute value or None
200
"""
201
202
voids = {
203
"""
204
Set of HTML void elements (self-closing tags).
205
206
Contains all HTML elements that don't have closing tags:
207
area, base, br, col, embed, hr, img, input, link, meta,
208
param, source, track, wbr
209
210
Used automatically by element creation functions to determine
211
whether to generate self-closing tags.
212
"""
213
}
214
```
215
216
### HTML Element Classes
217
218
Pre-configured classes for all standard HTML elements with appropriate default behaviors.
219
220
```python { .api }
221
# Document structure elements
222
def Head(*c, **kwargs):
223
"""HTML head element for document metadata."""
224
225
def Title(*c, **kwargs):
226
"""HTML title element for document title."""
227
228
def Meta(*c, **kwargs):
229
"""HTML meta element for metadata (void element)."""
230
231
def Link(*c, **kwargs):
232
"""HTML link element for external resources (void element)."""
233
234
def Style(*c, **kwargs):
235
"""HTML style element for embedded CSS."""
236
237
def Body(*c, **kwargs):
238
"""HTML body element for document content."""
239
240
# Text content elements
241
def P(*c, **kwargs):
242
"""HTML paragraph element."""
243
244
def Div(*c, **kwargs):
245
"""HTML div element for generic container."""
246
247
def Span(*c, **kwargs):
248
"""HTML span element for inline content."""
249
250
def Pre(*c, **kwargs):
251
"""HTML pre element for preformatted text."""
252
253
def Code(*c, **kwargs):
254
"""HTML code element for code snippets."""
255
256
# Heading elements
257
def H1(*c, **kwargs):
258
"""HTML h1 element for main heading."""
259
260
def H2(*c, **kwargs):
261
"""HTML h2 element for secondary heading."""
262
263
def H3(*c, **kwargs):
264
"""HTML h3 element for tertiary heading."""
265
266
def H4(*c, **kwargs):
267
"""HTML h4 element for quaternary heading."""
268
269
def H5(*c, **kwargs):
270
"""HTML h5 element for quinary heading."""
271
272
def H6(*c, **kwargs):
273
"""HTML h6 element for senary heading."""
274
275
# Text formatting elements
276
def Strong(*c, **kwargs):
277
"""HTML strong element for strong importance."""
278
279
def Em(*c, **kwargs):
280
"""HTML em element for emphasized text."""
281
282
def B(*c, **kwargs):
283
"""HTML b element for bold text."""
284
285
def I(*c, **kwargs):
286
"""HTML i element for italic text."""
287
288
def U(*c, **kwargs):
289
"""HTML u element for underlined text."""
290
291
def S(*c, **kwargs):
292
"""HTML s element for strikethrough text."""
293
294
def Strike(*c, **kwargs):
295
"""HTML strike element (deprecated, use S)."""
296
297
def Sub(*c, **kwargs):
298
"""HTML sub element for subscript text."""
299
300
def Sup(*c, **kwargs):
301
"""HTML sup element for superscript text."""
302
303
def Mark(*c, **kwargs):
304
"""HTML mark element for highlighted text."""
305
306
def Small(*c, **kwargs):
307
"""HTML small element for smaller text."""
308
309
# Structure elements
310
def Hr(*c, **kwargs):
311
"""HTML hr element for thematic break (void element)."""
312
313
def Br(*c, **kwargs):
314
"""HTML br element for line break (void element)."""
315
316
# Media elements
317
def Img(*c, **kwargs):
318
"""HTML img element for images (void element)."""
319
320
def Video(*c, **kwargs):
321
"""HTML video element for video content."""
322
323
def Audio(*c, **kwargs):
324
"""HTML audio element for audio content."""
325
326
def Source(*c, **kwargs):
327
"""HTML source element for media resources (void element)."""
328
329
def Canvas(*c, **kwargs):
330
"""HTML canvas element for graphics."""
331
332
def Svg(*c, **kwargs):
333
"""HTML svg element for vector graphics."""
334
335
def Iframe(*c, **kwargs):
336
"""HTML iframe element for embedded content."""
337
338
def Object(*c, **kwargs):
339
"""HTML object element for embedded objects."""
340
341
def Embed(*c, **kwargs):
342
"""HTML embed element for embedded content (void element)."""
343
344
def Param(*c, **kwargs):
345
"""HTML param element for object parameters (void element)."""
346
347
# Navigation and linking
348
def A(*c, **kwargs):
349
"""HTML a element for hyperlinks."""
350
351
def Nav(*c, **kwargs):
352
"""HTML nav element for navigation links."""
353
354
# List elements
355
def Ul(*c, **kwargs):
356
"""HTML ul element for unordered lists."""
357
358
def Ol(*c, **kwargs):
359
"""HTML ol element for ordered lists."""
360
361
def Li(*c, **kwargs):
362
"""HTML li element for list items."""
363
364
def Dl(*c, **kwargs):
365
"""HTML dl element for description lists."""
366
367
def Dt(*c, **kwargs):
368
"""HTML dt element for description terms."""
369
370
def Dd(*c, **kwargs):
371
"""HTML dd element for description details."""
372
373
# Table elements
374
def Table(*c, **kwargs):
375
"""HTML table element for tabular data."""
376
377
def Thead(*c, **kwargs):
378
"""HTML thead element for table header."""
379
380
def Tbody(*c, **kwargs):
381
"""HTML tbody element for table body."""
382
383
def Tfoot(*c, **kwargs):
384
"""HTML tfoot element for table footer."""
385
386
def Tr(*c, **kwargs):
387
"""HTML tr element for table rows."""
388
389
def Th(*c, **kwargs):
390
"""HTML th element for table headers."""
391
392
def Td(*c, **kwargs):
393
"""HTML td element for table data."""
394
395
def Caption(*c, **kwargs):
396
"""HTML caption element for table captions."""
397
398
def Col(*c, **kwargs):
399
"""HTML col element for table columns (void element)."""
400
401
def Colgroup(*c, **kwargs):
402
"""HTML colgroup element for column groups."""
403
404
# Form elements
405
def Form(*c, **kwargs):
406
"""HTML form element for user input forms."""
407
408
def Input(*c, **kwargs):
409
"""HTML input element for form inputs (void element)."""
410
411
def Textarea(*c, **kwargs):
412
"""HTML textarea element for multi-line text input."""
413
414
def Button(*c, **kwargs):
415
"""HTML button element for clickable buttons."""
416
417
def Select(*c, **kwargs):
418
"""HTML select element for dropdown selections."""
419
420
def Option(*c, **kwargs):
421
"""HTML option element for select options."""
422
423
def Label(*c, **kwargs):
424
"""HTML label element for form labels."""
425
426
def Fieldset(*c, **kwargs):
427
"""HTML fieldset element for form grouping."""
428
429
def Legend(*c, **kwargs):
430
"""HTML legend element for fieldset captions."""
431
432
# Semantic structure elements
433
def Main(*c, **kwargs):
434
"""HTML main element for main content."""
435
436
def Header(*c, **kwargs):
437
"""HTML header element for introductory content."""
438
439
def Footer(*c, **kwargs):
440
"""HTML footer element for footer content."""
441
442
def Section(*c, **kwargs):
443
"""HTML section element for document sections."""
444
445
def Article(*c, **kwargs):
446
"""HTML article element for standalone content."""
447
448
def Aside(*c, **kwargs):
449
"""HTML aside element for sidebar content."""
450
451
def Figure(*c, **kwargs):
452
"""HTML figure element for figure content."""
453
454
def Figcaption(*c, **kwargs):
455
"""HTML figcaption element for figure captions."""
456
457
# Interactive elements
458
def Details(*c, **kwargs):
459
"""HTML details element for disclosure widget."""
460
461
def Summary(*c, **kwargs):
462
"""HTML summary element for details summary."""
463
464
# Scripting elements
465
def Script(*c, **kwargs):
466
"""HTML script element for JavaScript."""
467
468
def Noscript(*c, **kwargs):
469
"""HTML noscript element for no-script fallback."""
470
471
def Template(*c, **kwargs):
472
"""HTML template element for client-side templates."""
473
474
def Slot(*c, **kwargs):
475
"""HTML slot element for web components."""
476
477
# Mathematical elements
478
def Math(*c, **kwargs):
479
"""HTML math element for mathematical notation."""
480
```
481
482
### Utility Functions
483
484
Helper functions for HTML processing, syntax highlighting, and debugging.
485
486
```python { .api }
487
def highlight(s, lang='xml'):
488
"""
489
Syntax highlight code string.
490
491
Applies syntax highlighting to code strings for display
492
in HTML or terminal environments.
493
494
Parameters:
495
- s: str, code to highlight
496
- lang: str, language for syntax highlighting
497
498
Returns:
499
str: Highlighted code (HTML or ANSI codes)
500
"""
501
502
def showtags(s):
503
"""
504
Display HTML tags in string for debugging.
505
506
Visualizes HTML structure by showing tag boundaries
507
and nesting levels for debugging HTML generation.
508
509
Parameters:
510
- s: str, HTML string to analyze
511
512
Returns:
513
str: Formatted representation showing tag structure
514
"""
515
```
516
517
## Usage Examples
518
519
### Basic HTML Generation
520
521
```python
522
from fastcore.xml import ft, Html, Head, Title, Body, Div, P, A
523
524
# Simple element creation
525
div = ft('div', 'Hello World', class_='greeting')
526
print(to_xml(div))
527
# <div class="greeting">Hello World</div>
528
529
# Using convenience classes
530
paragraph = P('This is a paragraph with ', Strong('bold text'), '.')
531
print(to_xml(paragraph))
532
# <p>This is a paragraph with <strong>bold text</strong>.</p>
533
534
# Chaining methods for fluent API
535
container = (Div()
536
.set('Welcome to our site!')
537
.set(class_='container', id='main')
538
.set(P('This is the main content area.'))
539
)
540
541
print(to_xml(container))
542
# <div class="container" id="main">Welcome to our site!<p>This is the main content area.</p></div>
543
```
544
545
### Complete HTML Documents
546
547
```python
548
from fastcore.xml import *
549
550
# Build complete HTML document
551
def create_webpage(title_text, content):
552
return Html(
553
Head(
554
Title(title_text),
555
Meta(charset='utf-8'),
556
Meta(name='viewport', content='width=device-width, initial-scale=1'),
557
Link(rel='stylesheet', href='styles.css')
558
),
559
Body(
560
Header(
561
H1(title_text),
562
Nav(
563
Ul(
564
Li(A('Home', href='/')),
565
Li(A('About', href='/about')),
566
Li(A('Contact', href='/contact'))
567
)
568
)
569
),
570
Main(
571
Section(content, class_='content'),
572
Aside(
573
H2('Sidebar'),
574
P('This is sidebar content.')
575
)
576
),
577
Footer(
578
P('© 2024 My Website. All rights reserved.')
579
)
580
),
581
lang='en'
582
)
583
584
# Generate the page
585
webpage = create_webpage('My Website', [
586
H2('Welcome'),
587
P('This is the main content of the page.'),
588
P('You can add more paragraphs and elements here.')
589
])
590
591
html_string = to_xml(webpage)
592
print(html_string)
593
```
594
595
### Dynamic Content and Attributes
596
597
```python
598
from fastcore.xml import *
599
600
# Dynamic attribute handling
601
def create_button(text, button_type='button', **kwargs):
602
classes = ['btn']
603
if 'primary' in kwargs:
604
classes.append('btn-primary')
605
if 'large' in kwargs:
606
classes.append('btn-lg')
607
608
return Button(text,
609
type=button_type,
610
class_=classes, # List automatically joined with spaces
611
**{k: v for k, v in kwargs.items()
612
if k not in ['primary', 'large']})
613
614
# Create various buttons
615
submit_btn = create_button('Submit', 'submit', primary=True, id='submit-form')
616
cancel_btn = create_button('Cancel', onclick='closeModal()', large=True)
617
618
print(to_xml(submit_btn))
619
# <button type="submit" class="btn btn-primary" id="submit-form">Submit</button>
620
621
# CSS-style attributes from dictionaries
622
styled_div = Div('Styled content',
623
style={'color': 'red', 'background': 'yellow', 'padding': '10px'}
624
)
625
print(to_xml(styled_div))
626
# <div style="color:red; background:yellow; padding:10px">Styled content</div>
627
628
# Safe HTML content
629
from fastcore.xml import Safe
630
631
# This HTML won't be escaped
632
safe_content = Safe('<em>Emphasized</em> and <strong>bold</strong> text')
633
container = Div('This contains ', safe_content, '!')
634
print(to_xml(container))
635
# <div>This contains <em>Emphasized</em> and <strong>bold</strong> text!</div>
636
```
637
638
### Forms and Interactive Elements
639
640
```python
641
from fastcore.xml import *
642
643
# Complex form generation
644
def create_contact_form():
645
return Form(
646
Fieldset(
647
Legend('Contact Information'),
648
Div(
649
Label('Name:', fr='name'), # fr becomes 'for'
650
Input(type='text', id='name', name='name', required=True),
651
class_='form-group'
652
),
653
Div(
654
Label('Email:', fr='email'),
655
Input(type='email', id='email', name='email', required=True),
656
class_='form-group'
657
),
658
Div(
659
Label('Message:', fr='message'),
660
Textarea('', id='message', name='message', rows=5, cols=50),
661
class_='form-group'
662
)
663
),
664
Fieldset(
665
Legend('Preferences'),
666
Div(
667
Input(type='checkbox', id='newsletter', name='newsletter', value='yes'),
668
Label('Subscribe to newsletter', fr='newsletter'),
669
class_='checkbox-group'
670
),
671
Div(
672
Label('Preferred contact method:', fr='contact-method'),
673
Select(
674
Option('Email', value='email', selected=True),
675
Option('Phone', value='phone'),
676
Option('Text', value='text'),
677
id='contact-method',
678
name='contact_method'
679
),
680
class_='form-group'
681
)
682
),
683
Div(
684
Button('Submit', type='submit', class_='btn btn-primary'),
685
Button('Reset', type='reset', class_='btn btn-secondary'),
686
class_='form-actions'
687
),
688
method='POST',
689
action='/contact'
690
)
691
692
contact_form = create_contact_form()
693
print(to_xml(contact_form))
694
```
695
696
### Tables and Data Display
697
698
```python
699
from fastcore.xml import *
700
701
# Generate data table
702
def create_data_table(headers, rows):
703
return Table(
704
Caption('Data Summary'),
705
Thead(
706
Tr(*[Th(header) for header in headers])
707
),
708
Tbody(*[
709
Tr(*[Td(cell) for cell in row])
710
for row in rows
711
]),
712
class_='data-table'
713
)
714
715
# Sample data
716
headers = ['Name', 'Age', 'City', 'Score']
717
data = [
718
['Alice', 30, 'New York', 95],
719
['Bob', 25, 'San Francisco', 87],
720
['Charlie', 35, 'Chicago', 92]
721
]
722
723
table = create_data_table(headers, data)
724
725
# Add zebra striping and responsive classes
726
table = table.set(class_='data-table table-striped table-responsive')
727
728
print(to_xml(table))
729
```
730
731
### Component-Based HTML Generation
732
733
```python
734
from fastcore.xml import *
735
736
# Reusable components
737
class HTMLComponent:
738
"""Base class for reusable HTML components."""
739
740
def render(self):
741
raise NotImplementedError
742
743
class Card(HTMLComponent):
744
def __init__(self, title, content, image_url=None, actions=None):
745
self.title = title
746
self.content = content
747
self.image_url = image_url
748
self.actions = actions or []
749
750
def render(self):
751
card_content = []
752
753
if self.image_url:
754
card_content.append(
755
Img(src=self.image_url, class_='card-img-top', alt=self.title)
756
)
757
758
body_content = [
759
H5(self.title, class_='card-title'),
760
P(self.content, class_='card-text')
761
]
762
763
if self.actions:
764
body_content.append(
765
Div(*self.actions, class_='card-actions')
766
)
767
768
card_content.append(
769
Div(*body_content, class_='card-body')
770
)
771
772
return Div(*card_content, class_='card')
773
774
class CardGrid(HTMLComponent):
775
def __init__(self, cards, columns=3):
776
self.cards = cards
777
self.columns = columns
778
779
def render(self):
780
return Div(
781
*[card.render() for card in self.cards],
782
class_=f'card-grid cols-{self.columns}'
783
)
784
785
# Use components
786
cards = [
787
Card(
788
'Product 1',
789
'Description of product 1',
790
image_url='/images/product1.jpg',
791
actions=[
792
Button('Buy Now', class_='btn btn-primary'),
793
A('Learn More', href='/product1', class_='btn btn-secondary')
794
]
795
),
796
Card(
797
'Product 2',
798
'Description of product 2',
799
actions=[Button('Buy Now', class_='btn btn-primary')]
800
),
801
Card(
802
'Product 3',
803
'Description of product 3',
804
image_url='/images/product3.jpg'
805
)
806
]
807
808
grid = CardGrid(cards, columns=3)
809
html_output = to_xml(grid.render())
810
```
811
812
### Event System and Dynamic Updates
813
814
```python
815
from fastcore.xml import *
816
817
# Event-driven HTML updates
818
class LiveCounter:
819
def __init__(self, initial_value=0):
820
self.value = initial_value
821
self.display = Span(str(self.value), class_='counter-value')
822
self.container = Div(
823
P('Current count: ', self.display),
824
Button('Increment', onclick='increment()'),
825
Button('Decrement', onclick='decrement()'),
826
class_='counter-widget'
827
)
828
829
# Add event listener for updates
830
self.display.on(self.on_update)
831
832
def increment(self):
833
self.value += 1
834
self.display.set(str(self.value))
835
836
def decrement(self):
837
self.value -= 1
838
self.display.set(str(self.value))
839
840
def on_update(self, element):
841
print(f"Counter updated to: {self.value}")
842
843
def render(self):
844
return self.container
845
846
# Create and use counter
847
counter = LiveCounter(5)
848
counter.increment() # Triggers update event
849
html = to_xml(counter.render())
850
851
# Template system with placeholders
852
class Template:
853
def __init__(self, html_structure):
854
self.structure = html_structure
855
self.placeholders = {}
856
857
def set_placeholder(self, name, content):
858
self.placeholders[name] = content
859
return self
860
861
def render(self):
862
def replace_placeholders(element):
863
if hasattr(element, 'children'):
864
new_children = []
865
for child in element.children:
866
if isinstance(child, str) and child.startswith('{{') and child.endswith('}}'):
867
placeholder_name = child[2:-2].strip()
868
new_children.append(self.placeholders.get(placeholder_name, child))
869
else:
870
new_children.append(replace_placeholders(child) if hasattr(child, 'children') else child)
871
return element.set(*new_children)
872
return element
873
874
return replace_placeholders(self.structure)
875
876
# Use template
877
template = Template(
878
Div(
879
H1('{{title}}'),
880
P('{{description}}'),
881
Div('{{content}}', class_='main-content')
882
)
883
)
884
885
rendered = template.set_placeholder('title', 'Welcome') .set_placeholder('description', 'This is a dynamic page') .set_placeholder('content', 'Main page content here') .render()
886
887
print(to_xml(rendered))
888
```