# PyHTML Enhanced
A library for building HTML documents with a simple and learnable syntax,
inspired by (and similar to)
[Cenk Altı's PyHTML library](https://github.com/cenkalti/pyhtml), but
with improved documentation and type safety.
## Features
* Inline documentation and type safety for all tags.
* Editor support for many common tags (attribute suggestions).
* A modern and readable codebase.
* No dependencies.
* 100% test coverage
## Usage
```py
>>> import pyhtml as p
>>> my_website = p.html(
... p.head(
... p.title("Hello, world!"),
... p.script(src="http://example.com/script.js"),
... ),
... p.body(
... p.h1("Hello, world!"),
... p.p("This is my amazing website!"),
... ),
... )
>>> print(str(my_website))
<!DOCTYPE html>
<html>
<head>
<title>
Hello, world!
</title>
<script type="text/javascript" src="http://example.com/script.js"></script>
</head>
<body>
<h1>
Hello, world!
</h1>
<p>
This is my amazing website!
</p>
</body>
</html>
```
### Creating elements
Every HTML tag is represented by a `class` that generates that HTML code. For
example, to create a `<br>` element, you could use:
```py
>>> line_break = p.br()
>>> print(str(line_break))
<br/>
```
### Adding children to elements
Any arguments to a tag are used as a child element to the created HTML element.
For example, to create a heading with the text `"My awesome website"`, you
could use
```py
>>> heading = p.h1("My awesome website")
>>> print(str(heading))
<h1>
My awesome website
</h1>
```
### Adding attributes to elements
Any keyword arguments to a tag are used as an attribute of the created HTML
element. For example, to create a form submit button, you could use
```py
>>> submit_button = p.input(type="submit")
>>> print(str(submit_button))
<input type="submit"/>
```
### Adding attributes and children
In HTML, attributes are specified within the opening tag. Contrastingly, Python
requires keyword arguments (attributes) to be specified after regular arguments
(children). To maintain similarity to writing regular HTML, you can call an
element in order to add more attributes and children. For example, to create
a link to PyHTML's GitHub page, you could use
```py
>>> my_link = p.a(href="https://github.com/COMP1010UNSW/pyhtml-enhanced")("Take a look at the code")
>>> print(str(my_link))
<a href="https://github.com/COMP1010UNSW/pyhtml-enhanced">
Take a look at the code
</a>
```
### HTML comments
You can add comments to HTML (useful for debugging) by using the `Comment` tag.
```py
>>> comment = p.Comment("This is an HTML comment")
>>> print(str(comment))
<!--
This is an HTML comment
-->
```
### Rendering HTML
Converting your PyHTML into HTML is as simple as stringifying it!
```py
>>> print(str(p.i("How straightforward!")))
<i>
How straightforward!
</i>
```
### Custom tags
Since this library includes all modern HTML tags, it is very unlikely that
you'll need to do create a custom tag. However if you really need to, you can
create a class deriving from `Tag`.
```py
>>> class fancytag(p.Tag):
... ...
>>> print(fancytag())
<fancytag></fancytag>
```
#### Tag base classes
You can derive from various other classes to get more control over how your tag
is rendered:
* `Tag`: default rendering.
* `SelfClosingTag`: tag is self-closing, meaning that no child elements are
accepted.
* `WhitespaceSensitiveTag`: tag is whitespace-sensitive, meaning that its
child elements are not indented.
#### Class properties
* `children`: child elements
* `attributes`: element attributes
#### Rendering control functions
You can also override various functions to control the existing rendering.
* `_get_tag_name`: return the name to use for the tag. For example returning
`"foo"` would produce `<foo>`.
* `_get_default_attributes`: return the default values for attributes.
* `_get_tag_pre_content`: return the pre-content for the tag. For example, the
`<html>` tag uses this to add the `<!DOCTYPE html>` before the opening tag.
* `_escape_children`: return whether the string child elements should be
escaped to prevent HTML injection.
* `_render`: render the element and its children, returning the list of lines
to use for the output. Overriding this should be a last resort, as it is easy
to subtly break the rendering process if you aren't careful.
Refer to the documentation for `Tag` for more information.
## Differences to PyHTML
There are some minor usage differences compared to the original PyHTML library.
Uninstantiated classes are only rendered if they are given as the child of an
instantiated element.
```py
>>> p.br
<class 'pyhtml.__tags.generated.br'>
>>> print(str(p.html(p.body(p.br))))
<!DOCTYPE html>
<html>
<body>
<br/>
</body>
</html>
```
Calling an instance of a `Tag` will return a new tag containing all elements of
the original tag combined with the new attributes and children, but will not
modify the original instance, as I found the old behaviour confusing and
bug-prone.
```py
>>> para = p.p("Base paragraph")
>>> para2 = para("Extra text")
>>> para2
<p>
Base paragraph
Extra text
</p>
>>> para
<p>
Base paragraph
</p>
```
## Known issues
There are a couple of things I haven't gotten round to sorting out yet
* [ ] Add default attributes to more tags
* [ ] Some tags (eg `<pre>`, `<script>`) currently aren't properly implemented
and escape their contents.
## How it works
Since there are so many HTML tags, it would be extremely tedious to document
them all manually. In the `meta` directory, you will find code that solves this
problem with the following steps:
1. Download the Markdown source for
[MDN's documentation of all HTML elements](https://developer.mozilla.org/en-US/docs/Web/HTML/Element).
2. Parse the markdown to gather all tag names and descriptions, discarding
garbage data and obsolete tags.
3. Use data from a YAML configuration file ([`meta/tags.yml`](meta/tags.yml))
to gather information on suggested attributes and base classes to use for
each tag.
4. Generate Python code to represent all of these tags, including their
documentation.
## Credits
### [Cenkalti/PyHTML](https://github.com/cenkalti/pyhtml)
Cenk Altı's work was used as a source of inspiration and reference. Although
all the code in `pyhtml-enhanced` was written by me, I want to thank them for
the significant help their hard work provided while creating this project,
going as far as to give design advice on request.
### [MDN Web Docs](https://developer.mozilla.org/en-US/)
Almost all of the documentation was gathered from the MDN Web Docs. It's super
neat that all their documentation is open (licensed as
[CC-BY-SA-2.5](https://creativecommons.org/licenses/by-sa/2.5/) if you're
interested).
### COMP1010 students and staff
COMP1010's students and staff members have uncovered and helped to resolve many
bugs, and have suggested many improvements. I'd like to thank them for all of
their help!
## License
### Source code
Copyright (c) 2023 Miguel Guthridge, COMP1010 UNSW
Source code for the library is open source, using the
[MIT license](https://choosealicense.com/licenses/mit/). A copy of the license
text is available in [LICENSE.md](https://github.com/COMP1010UNSW/pyhtml-enhanced/blob/main/LICENSE.md)
### Documentation
Documentation is copied from MDN Web Docs, and is license under
[CC-BY-SA-2.5](https://creativecommons.org/licenses/by-sa/2.5/). A copy of the
license text is available in [LICENSE_DOCS.md](https://github.com/COMP1010UNSW/pyhtml-enhanced/blob/main/LICENSE_DOCS.md)
Raw data
{
"_id": null,
"home_page": "https://github.com/COMP1010UNSW/pyhtml-enhanced",
"name": "pyhtml-enhanced",
"maintainer": null,
"docs_url": null,
"requires_python": "<4.0,>=3.9",
"maintainer_email": null,
"keywords": "html, template, pyhtml, markup, documentation",
"author": "Miguel Guthridge",
"author_email": "miguel.guthridge@unsw.edu.au",
"download_url": "https://files.pythonhosted.org/packages/79/22/5cf00dbfe21b01a598039223a10cf20cb53ab3308cd83c7575e4cd52d919/pyhtml_enhanced-2.1.0.tar.gz",
"platform": null,
"description": "# PyHTML Enhanced\n\nA library for building HTML documents with a simple and learnable syntax,\ninspired by (and similar to)\n[Cenk Alt\u0131's PyHTML library](https://github.com/cenkalti/pyhtml), but\nwith improved documentation and type safety.\n\n## Features\n\n* Inline documentation and type safety for all tags.\n\n* Editor support for many common tags (attribute suggestions).\n\n* A modern and readable codebase.\n\n* No dependencies.\n\n* 100% test coverage\n\n## Usage\n\n```py\n>>> import pyhtml as p\n>>> my_website = p.html(\n... p.head(\n... p.title(\"Hello, world!\"),\n... p.script(src=\"http://example.com/script.js\"),\n... ),\n... p.body(\n... p.h1(\"Hello, world!\"),\n... p.p(\"This is my amazing website!\"),\n... ),\n... )\n>>> print(str(my_website))\n<!DOCTYPE html>\n<html>\n <head>\n <title>\n Hello, world!\n </title>\n <script type=\"text/javascript\" src=\"http://example.com/script.js\"></script>\n </head>\n <body>\n <h1>\n Hello, world!\n </h1>\n <p>\n This is my amazing website!\n </p>\n </body>\n</html>\n\n```\n\n### Creating elements\n\nEvery HTML tag is represented by a `class` that generates that HTML code. For\nexample, to create a `<br>` element, you could use:\n\n```py\n>>> line_break = p.br()\n>>> print(str(line_break))\n<br/>\n\n```\n\n### Adding children to elements\n\nAny arguments to a tag are used as a child element to the created HTML element.\nFor example, to create a heading with the text `\"My awesome website\"`, you\ncould use\n\n```py\n>>> heading = p.h1(\"My awesome website\")\n>>> print(str(heading))\n<h1>\n My awesome website\n</h1>\n\n```\n\n### Adding attributes to elements\n\nAny keyword arguments to a tag are used as an attribute of the created HTML\nelement. For example, to create a form submit button, you could use\n\n```py\n>>> submit_button = p.input(type=\"submit\")\n>>> print(str(submit_button))\n<input type=\"submit\"/>\n\n```\n\n### Adding attributes and children\n\nIn HTML, attributes are specified within the opening tag. Contrastingly, Python\nrequires keyword arguments (attributes) to be specified after regular arguments\n(children). To maintain similarity to writing regular HTML, you can call an\nelement in order to add more attributes and children. For example, to create\na link to PyHTML's GitHub page, you could use\n\n```py\n>>> my_link = p.a(href=\"https://github.com/COMP1010UNSW/pyhtml-enhanced\")(\"Take a look at the code\")\n>>> print(str(my_link))\n<a href=\"https://github.com/COMP1010UNSW/pyhtml-enhanced\">\n Take a look at the code\n</a>\n\n```\n\n### HTML comments\n\nYou can add comments to HTML (useful for debugging) by using the `Comment` tag.\n\n```py\n>>> comment = p.Comment(\"This is an HTML comment\")\n>>> print(str(comment))\n<!--\n This is an HTML comment\n-->\n\n```\n\n### Rendering HTML\n\nConverting your PyHTML into HTML is as simple as stringifying it!\n\n```py\n>>> print(str(p.i(\"How straightforward!\")))\n<i>\n How straightforward!\n</i>\n\n```\n\n### Custom tags\n\nSince this library includes all modern HTML tags, it is very unlikely that\nyou'll need to do create a custom tag. However if you really need to, you can\ncreate a class deriving from `Tag`.\n\n```py\n>>> class fancytag(p.Tag):\n... ...\n>>> print(fancytag())\n<fancytag></fancytag>\n\n```\n\n#### Tag base classes\n\nYou can derive from various other classes to get more control over how your tag\nis rendered:\n\n* `Tag`: default rendering.\n\n* `SelfClosingTag`: tag is self-closing, meaning that no child elements are\n accepted.\n\n* `WhitespaceSensitiveTag`: tag is whitespace-sensitive, meaning that its\n child elements are not indented.\n\n#### Class properties\n\n* `children`: child elements\n* `attributes`: element attributes\n\n#### Rendering control functions\n\nYou can also override various functions to control the existing rendering.\n\n* `_get_tag_name`: return the name to use for the tag. For example returning\n `\"foo\"` would produce `<foo>`.\n\n* `_get_default_attributes`: return the default values for attributes.\n\n* `_get_tag_pre_content`: return the pre-content for the tag. For example, the\n `<html>` tag uses this to add the `<!DOCTYPE html>` before the opening tag.\n\n* `_escape_children`: return whether the string child elements should be\n escaped to prevent HTML injection.\n\n* `_render`: render the element and its children, returning the list of lines\n to use for the output. Overriding this should be a last resort, as it is easy\n to subtly break the rendering process if you aren't careful.\n\nRefer to the documentation for `Tag` for more information.\n\n## Differences to PyHTML\n\nThere are some minor usage differences compared to the original PyHTML library.\n\nUninstantiated classes are only rendered if they are given as the child of an\ninstantiated element.\n\n```py\n>>> p.br\n<class 'pyhtml.__tags.generated.br'>\n>>> print(str(p.html(p.body(p.br))))\n<!DOCTYPE html>\n<html>\n <body>\n <br/>\n </body>\n</html>\n\n```\n\nCalling an instance of a `Tag` will return a new tag containing all elements of\nthe original tag combined with the new attributes and children, but will not\nmodify the original instance, as I found the old behaviour confusing and\nbug-prone.\n\n```py\n>>> para = p.p(\"Base paragraph\")\n>>> para2 = para(\"Extra text\")\n>>> para2\n<p>\n Base paragraph\n Extra text\n</p>\n>>> para\n<p>\n Base paragraph\n</p>\n\n```\n\n## Known issues\n\nThere are a couple of things I haven't gotten round to sorting out yet\n\n* [ ] Add default attributes to more tags\n* [ ] Some tags (eg `<pre>`, `<script>`) currently aren't properly implemented\n and escape their contents.\n\n## How it works\n\nSince there are so many HTML tags, it would be extremely tedious to document\nthem all manually. In the `meta` directory, you will find code that solves this\nproblem with the following steps:\n\n1. Download the Markdown source for\n [MDN's documentation of all HTML elements](https://developer.mozilla.org/en-US/docs/Web/HTML/Element).\n\n2. Parse the markdown to gather all tag names and descriptions, discarding\n garbage data and obsolete tags.\n\n3. Use data from a YAML configuration file ([`meta/tags.yml`](meta/tags.yml))\n to gather information on suggested attributes and base classes to use for\n each tag.\n\n4. Generate Python code to represent all of these tags, including their\n documentation.\n\n## Credits\n\n### [Cenkalti/PyHTML](https://github.com/cenkalti/pyhtml)\n\nCenk Alt\u0131's work was used as a source of inspiration and reference. Although\nall the code in `pyhtml-enhanced` was written by me, I want to thank them for\nthe significant help their hard work provided while creating this project,\ngoing as far as to give design advice on request.\n\n### [MDN Web Docs](https://developer.mozilla.org/en-US/)\n\nAlmost all of the documentation was gathered from the MDN Web Docs. It's super\nneat that all their documentation is open (licensed as\n[CC-BY-SA-2.5](https://creativecommons.org/licenses/by-sa/2.5/) if you're\ninterested).\n\n### COMP1010 students and staff\n\nCOMP1010's students and staff members have uncovered and helped to resolve many\nbugs, and have suggested many improvements. I'd like to thank them for all of\ntheir help!\n\n## License\n\n### Source code\n\nCopyright (c) 2023 Miguel Guthridge, COMP1010 UNSW\n\nSource code for the library is open source, using the\n[MIT license](https://choosealicense.com/licenses/mit/). A copy of the license\ntext is available in [LICENSE.md](https://github.com/COMP1010UNSW/pyhtml-enhanced/blob/main/LICENSE.md)\n\n### Documentation\n\nDocumentation is copied from MDN Web Docs, and is license under\n[CC-BY-SA-2.5](https://creativecommons.org/licenses/by-sa/2.5/). A copy of the\nlicense text is available in [LICENSE_DOCS.md](https://github.com/COMP1010UNSW/pyhtml-enhanced/blob/main/LICENSE_DOCS.md)\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "A library for building HTML documents with a simple and learnable syntax",
"version": "2.1.0",
"project_urls": {
"Bug Tracker": "https://github.com/COMP1010UNSW/pyhtml-enhanced/issues",
"Documentation": "https://github.com/COMP1010UNSW/pyhtml-enhanced#README",
"Homepage": "https://github.com/COMP1010UNSW/pyhtml-enhanced",
"Repository": "https://github.com/COMP1010UNSW/pyhtml-enhanced"
},
"split_keywords": [
"html",
" template",
" pyhtml",
" markup",
" documentation"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "97f7edce1f5fb9fb106f8253c7489ac312f43c24966207f46114fe44b1fd183d",
"md5": "ef84ea8112b651304bcf1e63939eacac",
"sha256": "cb87c88cafdb7c50e8a1a572e1afb086eff1a5b1989a96dceb4cffbb8eb7b455"
},
"downloads": -1,
"filename": "pyhtml_enhanced-2.1.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "ef84ea8112b651304bcf1e63939eacac",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": "<4.0,>=3.9",
"size": 39979,
"upload_time": "2024-05-03T07:27:15",
"upload_time_iso_8601": "2024-05-03T07:27:15.576079Z",
"url": "https://files.pythonhosted.org/packages/97/f7/edce1f5fb9fb106f8253c7489ac312f43c24966207f46114fe44b1fd183d/pyhtml_enhanced-2.1.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "79225cf00dbfe21b01a598039223a10cf20cb53ab3308cd83c7575e4cd52d919",
"md5": "093b723afe1e99a771ea09982f630570",
"sha256": "e7c267f65037981d45c8965b39adda4394fb05f71c6ee2221f06fcdc38ddbbc2"
},
"downloads": -1,
"filename": "pyhtml_enhanced-2.1.0.tar.gz",
"has_sig": false,
"md5_digest": "093b723afe1e99a771ea09982f630570",
"packagetype": "sdist",
"python_version": "source",
"requires_python": "<4.0,>=3.9",
"size": 34828,
"upload_time": "2024-05-03T07:27:17",
"upload_time_iso_8601": "2024-05-03T07:27:17.172541Z",
"url": "https://files.pythonhosted.org/packages/79/22/5cf00dbfe21b01a598039223a10cf20cb53ab3308cd83c7575e4cd52d919/pyhtml_enhanced-2.1.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-05-03 07:27:17",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "COMP1010UNSW",
"github_project": "pyhtml-enhanced",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "pyhtml-enhanced"
}