Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove functools.walk() and the Jinja2 walk filter #1604

Merged
merged 1 commit into from
Jun 21, 2024
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
26 changes: 0 additions & 26 deletions betty/functools.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,38 +22,12 @@
TYPE_CHECKING,
)

from betty.warnings import deprecated

if TYPE_CHECKING:
from collections.abc import MutableSequence

_T = TypeVar("_T")


@deprecated(
"This function is deprecated as of Betty 0.3.5, and will be removed in Betty 0.4.x. Instead, use a custom function, tailored to your data type."
)
def walk(item: Any, attribute_name: str) -> Iterable[Any]:
"""
Walk over a graph of objects by following a single attribute.
"""
child = getattr(item, attribute_name)

# If the child has the requested attribute, yield it,
if hasattr(child, attribute_name):
yield child
yield from walk(child, attribute_name)

# Otherwise loop over the children and yield their attributes.
try:
child_children = iter(child)
except TypeError:
return
for child_child in child_children:
yield child_child
yield from walk(child_child, attribute_name)


def slice_to_range(indices: slice, iterable: Sized) -> Iterable[int]:
"""
Apply a slice to an iterable, and return the corresponding range.
Expand Down
9 changes: 0 additions & 9 deletions betty/jinja2/filter.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@
from pdf2image.pdf2image import convert_from_path

from betty import _resizeimage
from betty.functools import walk
from betty.hashid import hashid_file_meta, hashid
from betty.locale import (
negotiate_localizeds,
Expand Down Expand Up @@ -147,13 +146,6 @@ async def filter_flatten(values_of_values: Iterable[Iterable[_T]]) -> AsyncItera
yield value


def filter_walk(value: Any, attribute_name: str) -> Iterable[Any]:
"""
Walk over a data structure.
"""
return walk(value, attribute_name)


_paragraph_re = re.compile(r"(?:\r\n|\r|\n){2,}")


Expand Down Expand Up @@ -588,7 +580,6 @@ def filter_public_js(context: Context, public_path: str) -> None:
"upper_camel_case_to_lower_camel_case": upper_camel_case_to_lower_camel_case,
"url": filter_url,
"void_none": void_none,
"walk": filter_walk,
"public_css": filter_public_css,
"public_js": filter_public_js,
}
65 changes: 2 additions & 63 deletions betty/tests/test_functools.py
Original file line number Diff line number Diff line change
@@ -1,74 +1,13 @@
from collections.abc import Awaitable, Callable, Iterable
from typing import Any, Self, TypeVar
from typing import Any, TypeVar

import pytest

from betty.functools import walk, slice_to_range, Do, Uniquifier
from betty.warnings import BettyDeprecationWarning
from betty.functools import slice_to_range, Do, Uniquifier

_T = TypeVar("_T")


class TestWalk:
class _Item:
def __init__(self, child: Self | Iterable[Self] | None):
self.child = child

@pytest.mark.parametrize(
"item",
[
None,
True,
object(),
[],
],
)
async def test_with_invalid_attribute(self, item: Any) -> None:
with pytest.warns(BettyDeprecationWarning), pytest.raises(AttributeError):
list(walk(item, "invalid_attribute_name"))

async def test_one_to_one_without_descendants(self) -> None:
with pytest.warns(BettyDeprecationWarning):
item = self._Item(None)
actual = walk(item, "child")
expected: list[None] = []
assert expected == list(actual)

async def test_one_to_one_with_descendants(self) -> None:
with pytest.warns(BettyDeprecationWarning):
grandchild = self._Item(None)
child = self._Item(grandchild)
item = self._Item(child)
actual = walk(item, "child")
expected = [child, grandchild]
assert expected == list(actual)

async def test_one_to_many_without_descendants(self) -> None:
with pytest.warns(BettyDeprecationWarning):
item = self._Item([])
actual = walk(item, "child")
expected: list[None] = []
assert expected == list(actual)

async def test_with_one_to_many_descendants(self) -> None:
with pytest.warns(BettyDeprecationWarning):
grandchild = self._Item([])
child = self._Item([grandchild])
item = self._Item([child])
actual = walk(item, "child")
expected = [child, grandchild]
assert expected == list(actual)

async def test_with_mixed_descendants(self) -> None:
with pytest.warns(BettyDeprecationWarning):
grandchild = self._Item([])
child = self._Item(grandchild)
item = self._Item([child])
actual = walk(item, "child")
expected = [child, grandchild]
assert expected == list(actual)


class TestSliceToRange:
@pytest.mark.parametrize(
("expected_range_items", "ranged_slice"),
Expand Down
40 changes: 1 addition & 39 deletions betty/tests/test_jinja2.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from __future__ import annotations

from pathlib import Path
from typing import Iterable, Any, Self
from typing import Iterable, Any

import aiofiles
import pytest
Expand All @@ -24,7 +24,6 @@
Citation,
)
from betty.tests import TemplateTestCase
from betty.warnings import BettyDeprecationWarning


class TestJinja2Provider:
Expand Down Expand Up @@ -71,43 +70,6 @@ async def test(self, expected: str, template: str) -> None:
assert expected == actual


class TestFilterWalk(TemplateTestCase):
class WalkData:
def __init__(self, label: str, children: Iterable[Self] | None = None):
self._label = label
self.children = children or []

def __str__(self) -> str:
return self._label

@pytest.mark.parametrize(
("expected", "template", "data"),
[
("", '{{ data | walk("children") | join }}', WalkData("parent")),
(
"child1, child1child1, child2",
'{{ data | walk("children") | join(", ") }}',
WalkData(
"parent",
[
WalkData("child1", [WalkData("child1child1")]),
WalkData("child2"),
],
),
),
],
)
async def test(self, expected: str, template: str, data: WalkData) -> None:
with pytest.warns(BettyDeprecationWarning):
async with self._render(
template_string=template,
data={
"data": data,
},
) as (actual, _):
assert expected == actual


class TestFilterParagraphs(TemplateTestCase):
@pytest.mark.parametrize(
("expected", "template"),
Expand Down
Loading