├── .github ├── dependabot.yml └── workflows │ └── test-and-lint.yml ├── .gitignore ├── LICENSE ├── NEWS.md ├── README.md ├── elements.rst ├── htmlgen ├── __init__.py ├── __init__.pyi ├── attribute.py ├── attribute.pyi ├── block.py ├── block.pyi ├── document.py ├── document.pyi ├── element.py ├── element.pyi ├── form.py ├── form.pyi ├── generator.py ├── generator.pyi ├── image.py ├── image.pyi ├── inline.py ├── inline.pyi ├── link.py ├── link.pyi ├── list.py ├── list.pyi ├── py.typed ├── structure.py ├── structure.pyi ├── table.py ├── table.pyi ├── time.py ├── time.pyi ├── timeutil.py ├── timeutil.pyi ├── video.py └── video.pyi ├── mypy.ini ├── poetry.lock ├── pyproject.toml └── test_htmlgen ├── __init__.py ├── attribute.py ├── block.py ├── document.py ├── element.py ├── form.py ├── generator.py ├── image.py ├── inline.py ├── link.py ├── list.py ├── structure.py ├── table.py ├── time.py ├── util.py └── video.py /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: pip 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | time: '04:00' 8 | open-pull-requests-limit: 10 9 | -------------------------------------------------------------------------------- /.github/workflows/test-and-lint.yml: -------------------------------------------------------------------------------- 1 | name: Test and lint 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | strategy: 10 | matrix: 11 | python-version: ["3.7", "3.8", "3.9", "3.10"] 12 | fail-fast: false 13 | 14 | steps: 15 | - uses: actions/checkout@v3 16 | - name: Set up Python ${{ matrix.python-version }} 17 | uses: actions/setup-python@v4 18 | with: 19 | python-version: ${{ matrix.python-version }} 20 | cache: pip 21 | cache-dependency-path: "**/poetry.lock" 22 | - name: Install poetry 23 | run: pip --disable-pip-version-check install -U poetry 24 | - name: Install Python dependencies 25 | run: poetry install 26 | - name: Type checking with mypy 27 | run: | 28 | poetry run mypy --version 29 | poetry run mypy htmlgen test_htmlgen 30 | - name: Test with unittest 31 | run: poetry run python -Wall -m unittest discover -t . -s test_htmlgen -p "*.py" 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[cod] 2 | 3 | # C extensions 4 | *.so 5 | 6 | # Packages 7 | *.egg 8 | *.egg-info 9 | dist 10 | build 11 | eggs 12 | parts 13 | bin 14 | var 15 | sdist 16 | develop-eggs 17 | .installed.cfg 18 | lib 19 | lib64 20 | __pycache__ 21 | 22 | # virtual environments 23 | .venv* 24 | 25 | # mypy 26 | .mypy_cache 27 | 28 | # Installer logs 29 | pip-log.txt 30 | 31 | # Unit test / coverage reports 32 | .coverage 33 | .tox 34 | nosetests.xml 35 | 36 | # Translations 37 | *.mo 38 | 39 | # Mr Developer 40 | .mr.developer.cfg 41 | .project 42 | .pydevproject 43 | 44 | # PyCharm 45 | .idea 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Sebastian Rittau 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /NEWS.md: -------------------------------------------------------------------------------- 1 | # News in version 3.0.1 2 | 3 | ## Bug Fixes 4 | 5 | * Fix import from `collections` instead of `collections.abc` in stubs. 6 | 7 | # News in version 3.0.0 8 | 9 | ## Incompatible Changes 10 | 11 | * Drop support for Python 3.5 and 3.6. 12 | 13 | # News in version 2.0.0 14 | 15 | ## Incompatible Changes 16 | 17 | * Drop support for Python 2.7 and 3.4. 18 | 19 | ## API Additions 20 | 21 | * Add `Video` element. 22 | * Add `Button.disabled`. 23 | * Add `autocomplete` attribute to `Input`, `TextArea`, `Select`, and `Form`. 24 | * Add `enum_attribute`. 25 | 26 | # News in version 1.2.2 27 | 28 | ## API Additions 29 | 30 | * Add `GenValue` and `GenValueGenerator` to `htmlgen`. 31 | 32 | # News in version 1.2.1 33 | 34 | ## API Additions 35 | 36 | * `from htmlgen import ElementBase` now works. 37 | 38 | ## Bug Fixes 39 | 40 | * Add missing `ElementBase` to type stubs. 41 | 42 | # News in version 1.2.0 43 | 44 | ## API Additions 45 | 46 | * Make `ElementBase` public. 47 | * Add `GenValue` and `GenValueGenerator` type aliases. 48 | * Fix annotation of `Heading`. 49 | 50 | # News in version 1.1.0 51 | 52 | ## Improvements 53 | 54 | * PEP 561 support. 55 | * Improve type hints. 56 | 57 | # News in version 1.0.0 58 | 59 | No changes. 60 | 61 | # News in version 0.99.3 62 | 63 | ## API-Incompatible Changes 64 | 65 | * Revert deriving from ABC classes. 66 | 67 | # News in version 0.99.2 68 | 69 | ## API Additions 70 | 71 | * `Element.data` does now derive from MutableMapping and implements all its 72 | methods. 73 | 74 | ## Improvements 75 | 76 | * Derive `ChildGenerator`, `HTMLChildGenerator`, and `Element` from 77 | Sized. 78 | 79 | ## Bug Fixes 80 | 81 | * Fix a few stubs. 82 | * Fix incorrect usage of `AnyStr`. 83 | 84 | # News in version 0.99.1 85 | 86 | ## API-Incompatible Changes 87 | 88 | * `html_attribute()` at al. are now directly implemented using the descriptor 89 | protocol, and not derived from property. 90 | 91 | ## Improvements 92 | 93 | * Add stub files. 94 | 95 | # News in version 0.99.0 96 | 97 | First Beta Release 98 | 99 | ## API Additions 100 | 101 | * Add generate_html_string(). 102 | * Add css_class_attribute(). 103 | * Add Checkbox (<input type="checkbox">), RadioButton (<input type="radio">), 104 | and Label (<label>). 105 | 106 | ## API-Incompatible Changes 107 | 108 | * Remove html_attribute() from htmlgen.element. 109 | 110 | ## Improvements 111 | 112 | * Render CSS classes alphabetically for easier unit testing. 113 | 114 | ## Bug Fixes 115 | 116 | * Fix PendingDeprecationWarnings. 117 | 118 | # News in version 0.9 119 | 120 | ## API Additions 121 | 122 | * Add IteratorGenerator. 123 | * Add HiddenInput (<input type="hidden">), FileInput (<input type="file">), 124 | SearchInput (<input type="search">), and TimeInput (<input type="time">). 125 | * Add LineBreak (<br>). 126 | * Table now has two overridable generator methods generate_header_rows() and 127 | generate_rows(). 128 | * Add TextArea.placeholder property. 129 | * Add NumberInput.number property. 130 | * Add Form.target property and Form.set_blank_target(). 131 | * Add data_attribute(), list_html_attribute() and time_html_attribute(). 132 | 133 | ## API-Incompatible Changes 134 | 135 | * Improve Element.id handling and raise ValueError on invalid ids. 136 | * The default name of all input elements has been changed from None to the 137 | empty string to match Input.name. 138 | * NumberInput constructor: Replace value argument by number. 139 | 140 | # News in version 0.8 141 | 142 | ## API Additions 143 | 144 | * Add form elements TextArea (<textarea>), Select (<select>), OptionGroup 145 | (<optgroup>), and Option (<option>). 146 | * Add is_element() to check whether an object is an element generator of 147 | a certain type. 148 | * Forms now support multipart submissions using the Form.encryption_type and 149 | Form.multipart attributes. 150 | 151 | ## API-Incompatible Changes 152 | 153 | * Fix the default HTTP method to be "GET" for forms as per HTML spec. This 154 | avoids unexpected behaviour and the need for problematic workarounds 155 | with "POST" forms. 156 | 157 | # News in version 0.7 158 | 159 | ## API Additions 160 | 161 | * Add input elements Button (<button>), NumberInput (<input type="number">), 162 | PasswordInput (<input type="password">), and DateInput (<input 163 | type="date">). 164 | 165 | ## API-Incompatible Changes 166 | 167 | * Move attribute functions from htmlgen.elements to htmlgen.attribute. 168 | (But you should import them directly from htmlgen anyway.) 169 | 170 | ## Improvements 171 | 172 | * Improved error handling and reporting. 173 | 174 | ## Documentation 175 | 176 | * Add element list document elements.rst. 177 | 178 | ## Bug Fixes 179 | 180 | * Add float_html_attribute to htmlgen. 181 | 182 | # News in version 0.6.1 183 | 184 | ## Bug Fixes 185 | 186 | * Fixed error when passing elements to TableCell's and TableHeaderCell's 187 | constructor. 188 | 189 | # News in version 0.6 190 | 191 | ## API Additions 192 | 193 | * Add TableHeaderCell to htmlgen (missing from 0.5). 194 | * Division constructor now accepts initial content arguments. 195 | 196 | ## API-Incompatible Changes 197 | 198 | * All element constructors that took an initial content argument now take 199 | any number of content arguments, i.e. the following is now possible: 200 | >>> Paragraph("This is ", Emphasis("initial"), " content.") 201 | 202 | # News in version 0.5 203 | 204 | ## API Additions 205 | 206 | * Add table elements Table (<table>), TableHead (<thead>), 207 | TableBody (<tbody>), TableRow (<tr>), TableHeaderCell (<th>), 208 | TableCell (<td>), ColumnGroup (<colgroup>), and Column (<col>). 209 | 210 | # News in version 0.4 211 | 212 | ## API Additions 213 | 214 | * Add data property to element classes. This provides an API to 215 | easily set and query data-* attributes. 216 | * Add structural element Article (<article>). 217 | * Add inline elements Link (<a>) and Time (<time>). 218 | * Add description list elements DescriptionList (<dl>), 219 | DescriptionTerm (<dt>), and DescriptionDefinition (<dd>). 220 | 221 | # News in version 0.3 222 | 223 | ## API Additions 224 | 225 | * Add child-management methods and properties to ChildGenerator and 226 | HTMLChildGenerator: 227 | * remove() 228 | * remove_raw() (HTMLChildGenerator only) 229 | * children 230 | * Add new base class NonVoidElement, derive Element from this class. 231 | This base class can be used for elements with content that do not 232 | support the usual container interface. 233 | * Add document-level elements Document, HTMLRoot (<html>), Head (<head>), 234 | Body (<body>), Title (<title>), Meta (<meta>), Script (<script>), 235 | HeadLink (<link>), and Main (<main>). 236 | * Add structural elements Section (<section>), Navigation (<nav>), 237 | Aside (<aside>), Header (<header>), Footer (<footer>), and Heading 238 | (<h1> to <h6>). 239 | * Add list elements OrderedList (<ol>), UnorderedList (<ul>), and 240 | ListItem (<li>). 241 | * Add has_css_class() method to elements. 242 | 243 | ## Improvements 244 | 245 | * Element attributes are now always rendered in alphabetical order. This 246 | makes testing elements easier. 247 | 248 | # News in version 0.2 249 | 250 | ## API Additions 251 | 252 | * Add elements Paragraph (<p>), Preformatted (<pre>), Image (<img>), 253 | Highlight (<b>), Strong (<strong>), Alternate (<i>), Emphasis (<em>), 254 | and Small (<small>). 255 | * Add float_html_attribute(). 256 | * Add remove_css_classes() method to elements. 257 | 258 | ## API-Incompatible Changes 259 | 260 | * Rename ShortElement to VoidElement to conform to the HTML 5 standard. 261 | 262 | # News in version 0.1.1 263 | 264 | ## API Additions 265 | 266 | * Add ShortElement to htmlgen. 267 | 268 | ## Bug Fixes 269 | 270 | * Elements are now always truthy. 271 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Python HTML 5 Generator 2 | 3 | [![License](https://img.shields.io/pypi/l/htmlgen.svg)](https://pypi.python.org/pypi/htmlgen/) 4 | [![PyPI - Python Version](https://img.shields.io/pypi/pyversions/htmlgen)](https://pypi.python.org/pypi/htmlgen/) 5 | [![Release](https://img.shields.io/github/release/srittau/python-htmlgen/all.svg)](https://github.com/srittau/python-htmlgen/releases/) 6 | [![PyPI](https://img.shields.io/pypi/v/htmlgen.svg)](https://pypi.python.org/pypi/htmlgen/) 7 | [![Travis CI](https://travis-ci.org/srittau/python-htmlgen.svg?branch=master)](https://travis-ci.org/srittau/python-htmlgen) 8 | 9 | Library to generate HTML from classes. 10 | 11 | Basic usage: 12 | 13 | >>> from htmlgen import Division, Span 14 | >>> Division("This is ", Span("important!"), "!") 15 | 16 | A more verbose example: 17 | 18 | >>> span = Span("important") 19 | >>> span.add_css_classes("important") 20 | >>> div = Division() 21 | >>> div.id = "my-block" 22 | >>> div.append("This is ") 23 | >>> div.append(span) 24 | >>> div.append("!") 25 | 26 | A tree constructed like this can be converted to a string: 27 | 28 | >>> str(div) 29 | '
This is important!
' 30 | >>> "

This is {}!

".format(span) 31 | '

This is important!

' 32 | 33 | Alternatively, all elements can be used as iterators, for example to return 34 | them from a WSGI callback: 35 | 36 | >>> def application(env, start_response): 37 | ... start_response("200 OK", [("Content-Type", "text/html")]) 38 | ... return div 39 | 40 | There are two different ways to render children of HTML elements. The tree 41 | construction approach shown above is mainly suitable for elements with few 42 | children. The disadvantage of this approach is that the whole tree must be 43 | constructed in memory. An alternative way, best suited for custom sub-classes 44 | of elements, is to override the generate_children method of the Element class: 45 | 46 | >>> class MyBlock(Division): 47 | ... def __init__(self): 48 | ... super(MyBlock, self).__init__() 49 | ... self.id = "my-block" 50 | ... def generate_children(self): 51 | ... yield "This is " 52 | ... span = Span("important") 53 | ... span.add_css_classes("important") 54 | ... yield span 55 | ... yield "!" 56 | >>> str(MyBlock()) 57 | '
This is important!
' 58 | -------------------------------------------------------------------------------- /elements.rst: -------------------------------------------------------------------------------- 1 | htmlgen currently supports the following HTML elements: 2 | 3 | * - Link 4 | *
- Article 5 | *