Skip to content

Commit

Permalink
fix(reporter): describe_build_func fails on functools.partial
Browse files Browse the repository at this point in the history
PR lektor#1104 uses a functools.partial as a sub-artifact's
_build_func_. When verbose logging is enabled, this caused an
exception from `lektor.reporter.describe_build_func`.
  • Loading branch information
dairiki committed Sep 11, 2023
1 parent 032fee2 commit 47c936f
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 6 deletions.
2 changes: 2 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ These are all the changes in Lektor since the first public release.

- Pin `watchfiles>=0.12`. (Our tests use the `stop_event` parameter of `watchfiles.watch`.)

- Fix exception from `describe_build_func` when building thumbnails with verbose logging enabled.

## 3.4.0b6 (2023-05-05)

### Possibly Breaking Changes
Expand Down
17 changes: 11 additions & 6 deletions lektor/reporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,17 @@


def describe_build_func(func):
self = getattr(func, "__self__", None)
if self is not None and any(
x.__name__ == "BuildProgram" for x in self.__class__.__mro__
):
return self.__class__.__module__ + "." + self.__class__.__name__
return func.__module__ + "." + func.__name__
if hasattr(func, "func"):
func = func.func # unwrap functools.partial
try:
qualname = func.__qualname__
class_name, _, method = qualname.rpartition(".")
if class_name and method == "build_artifact":
# Strip method name from methods of BuildProgram instances
qualname = class_name
return f"{func.__module__}.{qualname}"
except AttributeError:
return repr(func)


class Reporter:
Expand Down
39 changes: 39 additions & 0 deletions tests/test_reporter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
from functools import partial

import pytest

from lektor.build_programs import FileAssetBuildProgram
from lektor.builder import Builder
from lektor.reporter import describe_build_func


@pytest.fixture
def build_state(pad, tmp_path):
builder = Builder(pad, destination_path=tmp_path)
return builder.new_build_state()


def dummy_build_func(*args):
pass


def test_describe_build_func():
assert describe_build_func(dummy_build_func) == "test_reporter.dummy_build_func"


def test_describe_build_func_describes_partial():
build_func = partial(dummy_build_func, "dummy-arg")
assert describe_build_func(build_func) == "test_reporter.dummy_build_func"


def test_describe_build_func_BuildProgram(pad, build_state):
build_program = FileAssetBuildProgram(pad.get_asset("static/demo.css"), build_state)
build_func = build_program.build_artifact
assert (
describe_build_func(build_func) == "lektor.build_programs.FileAssetBuildProgram"
)


def test_describe_build_func_deals_with_garbage():
garbage = object()
assert isinstance(describe_build_func(garbage), str)

0 comments on commit 47c936f

Please sign in to comment.