Skip to content

Commit

Permalink
Additional import options: notably --imports=h
Browse files Browse the repository at this point in the history
  • Loading branch information
OleJoik committed Jun 12, 2024
1 parent be15515 commit 0727a71
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 55 deletions.
51 changes: 32 additions & 19 deletions docs/html2htpy.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,20 @@ html into Python code (htpy!).

```
$ html2htpy -h
usage: html2htpy [-h] [-e] [-f {auto,ruff,black,none}] [-i] [input]
usage: html2htpy [-h] [-e] [-f {auto,ruff,black,none}] [-i {yes,h,no}] [input]
positional arguments:
input input HTML from file or stdin
options:
-h, --help show this help message and exit
-e, --explicit Use explicit `id` and `class_` kwargs instead of the shorthand #id.class syntax
-e, --explicit Use explicit `id` and `class_` kwargs instead of the shorthand
#id.class syntax
-f {auto,ruff,black,none}, --format {auto,ruff,black,none}
Select one of the following formatting options: auto, ruff, black or none
-i, --imports Output imports for htpy elements found
Select one of the following formatting options: auto, ruff, black
or none
-i {yes,h,no}, --imports {yes,h,no}
Output mode for imports of found htpy elements
```


Expand Down Expand Up @@ -56,6 +59,8 @@ $ html2htpy index.html
```

```py
from htpy import body, div, h1, h2, head, html, meta, p, span, title

html(lang="en")[
head[
meta(charset="UTF-8"),
Expand Down Expand Up @@ -106,7 +111,7 @@ powershell Get-Clipboard | html2htpy > output.py
Select the preferred formatter with the `-f`/`--format` flag. Options are `auto`, `ruff`, `black` and `none`.

By default, the selection will be `auto`, formatting if it finds a formatter on path, prefering `ruff` if it's available.
If no formatters are available on path, the output not be formatted.
If no formatters are available on path, the output will not be formatted.


## Explicit id and class kwargs
Expand All @@ -122,43 +127,49 @@ If you prefer the explicit `id="id", class_="class"` kwargs syntax over the defa

#### Default shorthand `#id.class`
```py title="$ html2htpy example.html"
from htpy import p, section

section("#main-section.hero.is-link")[
p(".subtitle.is-3.is-spaced")["Welcome"]
]
```

#### Explicit kwargs `id`, `class_`
```py title="$ html2htpy --explicit example.html"
from htpy import p, section

section(id="main-section", class_="hero is-link")[
p(class_="subtitle is-3 is-spaced")["Welcome"]
]
```

## Detect htpy imports
## Import options

If you pass the `-i`/`--imports` flag, htpy elements detected will be included as
imports in the output. For example:
You have a couple of options regarding imports with the `-i`/`--imports` flag.
Options are `yes` (default), `h`, `no`.

```py title="$ html2htpy --imports example.html"
from htpy import p, section
#### Module import of htpy: `--imports=h`

section("#main-section.hero.is-link")[
p(".subtitle.is-3.is-spaced")["Welcome"]
Some people prefer to `import htpy as h` instead of importing individual elements from htpy.
If this is you, you can use the `--imports=h` option to get corresponding output when using `html2htpy`.

```py title="$ html2htpy --imports=h example.html"
import htpy as h

h.section("#main-section.hero.is-link")[
h.p(".subtitle.is-3.is-spaced")["Welcome"]
]
```



## Template interpolation to f-strings

You might have some templates laying around after using jinja or some other templating language.

`html2htpy` will try to convert the `template {{ variables }}`...
`html2htpy` will try to convert template variables to pythonic f-strings:

...to pythonic f-strings: `f"template { variables }"`
`template {{ variables }}` -> `f"template { variables }"`

Note that other template template syntax, such as loops `{% for x in y %}` can not be transformed at
this time, so you will often have to clean up a bit after `html2htpy` is done with its thing.
Note that other typical template syntax, such as loops `{% for x in y %}`, can not be transformed this way,
so you will often have to clean up a bit after `html2htpy` is done with its thing.

See the example below:

Expand All @@ -180,6 +191,8 @@ See the example below:
```

```py title="$ html2htpy jinja.html"
from htpy import body, h1, h2, h3, li, ol, p

body[
h1[f"{ heading }"],
p[f"Welcome to our cooking site, { user.name }!"],
Expand Down
36 changes: 23 additions & 13 deletions htpy/html2htpy.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ def __init__(
self.parent = parent
self.children: list[Any | str] = []

def serialize(self, shorthand_id_class: bool = False) -> str:
def serialize(self, shorthand_id_class: bool, use_h_prefix: bool) -> str:
_positional_attrs: dict[str, str | None] = {}
_attrs = ""
_kwattrs: list[tuple[str, str | None]] = []
Expand Down Expand Up @@ -110,14 +110,17 @@ def serialize(self, shorthand_id_class: bool = False) -> str:
_children += "["
for c in self.children:
if isinstance(c, Tag):
_children += c.serialize(shorthand_id_class=shorthand_id_class)
_children += c.serialize(shorthand_id_class, use_h_prefix)
else:
_children += str(c)

_children += ","

_children = _children[:-1] + "]"

if use_h_prefix:
return f"h.{self.python_type}{_attrs}{_children}"

return f"{self.python_type}{_attrs}{_children}"


Expand Down Expand Up @@ -196,12 +199,14 @@ def handle_data(self, data: str) -> None:
def serialize_python(
self,
shorthand_id_class: bool = False,
include_imports: bool = False,
import_mode: Literal["yes", "h", "no"] = "yes",
formatter: Formatter | None = None,
) -> str:
o = ""

if include_imports:
use_h_prefix = False

if import_mode == "yes":
unique_tags: set[str] = set()

def _tags_from_children(parent: Tag) -> None:
Expand All @@ -220,13 +225,17 @@ def _tags_from_children(parent: Tag) -> None:

o += f'from htpy import {", ".join(sorted_tags)}\n'

elif import_mode == "h":
o += "import htpy as h\n"
use_h_prefix = True

if len(self._collected) == 1:
o += _serialize(self._collected[0], shorthand_id_class)
o += _serialize(self._collected[0], shorthand_id_class, use_h_prefix)

else:
o += "["
for t in self._collected:
o += _serialize(t, shorthand_id_class) + ","
o += _serialize(t, shorthand_id_class, use_h_prefix) + ","
o = o[:-1] + "]"

if formatter:
Expand All @@ -238,13 +247,13 @@ def _tags_from_children(parent: Tag) -> None:
def html2htpy(
html: str,
shorthand_id_class: bool = True,
include_imports: bool = False,
import_mode: Literal["yes", "h", "no"] = "yes",
formatter: Formatter | None = None,
) -> str:
parser = HTPYParser()
parser.feed(html)

return parser.serialize_python(shorthand_id_class, include_imports, formatter)
return parser.serialize_python(shorthand_id_class, import_mode, formatter)


def _convert_data_to_string(data: str) -> str:
Expand Down Expand Up @@ -294,9 +303,9 @@ def replacer(match: re.Match[str]) -> str:
return _data


def _serialize(el: Tag | str, shorthand_id_class: bool) -> str:
def _serialize(el: Tag | str, shorthand_id_class: bool, use_h_prefix: bool) -> str:
if isinstance(el, Tag):
return el.serialize(shorthand_id_class=shorthand_id_class)
return el.serialize(shorthand_id_class, use_h_prefix)
else:
return str(el)

Expand Down Expand Up @@ -356,8 +365,9 @@ def main() -> None:
parser.add_argument(
"-i",
"--imports",
help="Output imports for htpy elements found",
action="store_true",
choices=["yes", "h", "no"],
help="Output mode for imports of found htpy elements",
default="yes",
)
parser.add_argument(
"input",
Expand Down Expand Up @@ -388,7 +398,7 @@ def main() -> None:
sys.exit(1)

shorthand: bool = False if args.explicit else True
imports: bool = args.imports
imports: Literal["yes", "h", "no"] = args.imports

formatter = _get_formatter(args.format)

Expand Down
Loading

0 comments on commit 0727a71

Please sign in to comment.