Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions changelog.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
# 0.11.1
* livereload fix: live server waits for daemon to terminate before restarting
* livereload fix: Fix bug for ignore_glob not firing
* livereload fix: always track stat mtime as one type (int)

# 0.11.0
* Add html_compose.resource module which includes
js_import, css_import, and font_import helpers
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "html-compose"
version = "0.11.0"
version = "0.11.1"
description = "Composable HTML generation in python"
authors = [
{ name = "jealouscloud", email = "github@noaha.org" }
Expand Down
1 change: 1 addition & 0 deletions src/html_compose/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@
```sh
html-compose convert {filename or empty for stdin}
html-compose convert --noimport el # produces el.div() style references
html-convert # an alias for html-compose convert
```

`html-convert` provides access to this tool as shorthand.
Expand Down
2 changes: 1 addition & 1 deletion src/html_compose/base_element.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

class ElementMeta(ABCMeta):
"""
The metaclass for all HTML elements to hack the base class interface
The metaclass for all HTML elements
"""

# We aggressively hack the type checker here
Expand Down
12 changes: 9 additions & 3 deletions src/html_compose/base_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,14 @@ def __html__(self) -> str:
return self.render()


# A node resolver is a callable that returns a Node,
# possibly taking the calling element and parent element as arguments.
NodeResolver = (
Callable[[], "Node"]
| Callable[[ElementBase], "Node"]
| Callable[[ElementBase, ElementBase], "Node"]
)

# The Node type is a union of all possible types that can be rendered
Node = (
None # None will not be appended to the output children
Expand All @@ -76,9 +84,7 @@ def __html__(self) -> str:
| ElementBase # Base class for all HTML elements
| _HasHtml # Returns HTML that does not need escaping
| Iterable["Node"]
| Callable[[], "Node"]
| Callable[[ElementBase], "Node"]
| Callable[[ElementBase, ElementBase], "Node"]
| NodeResolver
)

# These types are used for attribute values
Expand Down
2 changes: 1 addition & 1 deletion src/html_compose/document.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ def document_generator(
lang: str | None = None,
head: el.head | list | None = None,
body: Iterable[Node] | el.body | None = None,
):
) -> str:
"""
Return a full HTML5 document as a string.

Expand Down
4 changes: 2 additions & 2 deletions src/html_compose/elements/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@ def get(value: str) -> BaseAttribute:

"""

import os

from .a_element import a as a
from .abbr_element import abbr as abbr
from .address_element import address as address
Expand Down Expand Up @@ -216,8 +218,6 @@ def get(value: str) -> BaseAttribute:
from .video_element import video as video
from .wbr_element import wbr as wbr

import os

# hack: force PDOC to treat elements as submodules
if not os.environ.get("PDOC_GENERATING", False):
__all__ = [
Expand Down
6 changes: 6 additions & 0 deletions src/html_compose/live/live_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ def live_server(
)

daemon_task = ProcessTask(daemon, delay=0, sync=False)
daemon_stop_task = Task(action=lambda: daemon_task.cancel(), sync=True)
# Run livereload server
server = run_server(host, port)
tr = TaskRunner()
Expand Down Expand Up @@ -186,6 +187,10 @@ def reload():

if reload_tripped:
daemon_task.delay = delay + daemon_delay

# this should make them fire on the same tick, in order
daemon_stop_task.delay = daemon_task.delay

# This constant should mean the server port is up
browser_update_task.delay = (
daemon_task.delay + livereload_delay
Expand All @@ -194,6 +199,7 @@ def reload():
f"Reloading daemon after {daemon_task.delay} seconds..."
)
if any([c.server_reload for c in conds_hit]):
tr.add_task(daemon_stop_task)
tr.add_task(daemon_task)
tr.add_task(browser_update_task)
sleep(loop_delay)
Expand Down
4 changes: 2 additions & 2 deletions src/html_compose/live/watcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ def try_path_hit(self, path: str) -> bool:

for pattern in self.ignore_glob:
if glob_matcher(pattern, path):
return True
return False

for pattern in self.path_glob:
if glob_matcher(pattern, path):
Expand Down Expand Up @@ -415,7 +415,7 @@ def stat_watcher(self):
if not stat.S_ISREG(st.st_mode):
# Not a regular file, skip it
continue
mtime = st.st_mtime
mtime = int(st.st_mtime)
except OSError:
# Might be deleted, we'll catch it later
continue
Expand Down
2 changes: 1 addition & 1 deletion src/html_compose/resource/js_import.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ def __init__(
"If hash is set, crossorigin must be set to ''/'anonymous'"
)

def uri(self):
def uri(self) -> str:
"""
Returns the source URI - with cache busting if enabled
which is implemented by getting the mtime of the local file
Expand Down
12 changes: 5 additions & 7 deletions src/html_compose/resource/util_funcs.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,8 @@ def _cachebust_resource_uri(source: str):

misc_stat_cache = _State.misc_stat_cache
stat_cache = _State.stat_cache
cache_cap = settings.cache_cap
stat_poll_interval = settings.stat_poll_interval
base_dir = settings.base_dir
query_string = settings.query_string
base_dir = base_dir

if misc_stat_cache.get(base_dir) is None:
try:
misc_stat_cache[base_dir] = int(stat(base_dir).st_mtime)
Expand All @@ -35,10 +32,11 @@ def _cachebust_resource_uri(source: str):
"does not exist"
) from exc

source = source.lstrip("/")
resource_path = path.join(base_dir, source)
now = time()
ts = misc_stat_cache.get(path.join(base_dir, source), None)
update_ts = ts is None or (now - ts) > stat_poll_interval
update_ts = ts is None or (now - ts) > settings.stat_poll_interval
if update_ts:
try:
ts = int(stat(resource_path).st_mtime)
Expand All @@ -49,7 +47,7 @@ def _cachebust_resource_uri(source: str):
"does not exist"
) from exc

if len(stat_cache) >= cache_cap:
if len(stat_cache) >= settings.cache_cap:
# Clear if it's too big
stat_cache.clear()
stat_cache[resource_path] = ts
Expand All @@ -64,7 +62,7 @@ def _cachebust_resource_uri(source: str):
errors="surrogateescape",
)
# add our cache buster
pairs.append((query_string, str(int(ts))))
pairs.append((settings.query_string, str(int(ts))))
# re assemble the query string, try our best to preservees exactly
new_qs = urllib.parse.urlencode(
pairs,
Expand Down
2 changes: 1 addition & 1 deletion src/html_compose/util_funcs.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ def get_livereload_env() -> dict | None:


def generate_livereload_env(
host, port, proxy_host: str | None, proxy_uri: str | None = None
host: str, port: int, proxy_host: str | None, proxy_uri: str | None = None
) -> dict:
flags = {
"port": port,
Expand Down
1 change: 1 addition & 0 deletions tests/test_util_funcs.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ def test_glob_func():
"path/nextsection/dir_cursive/nextsection/end.txt",
True,
),
("front/css/", "front/css/utilities/debug-outline.css", True),
]
for pattern, target, expected in test_cases:
assert glob_matcher(pattern, target) == expected, (
Expand Down