blurry-cli 0.18.0__tar.gz → 0.19.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {blurry_cli-0.18.0 → blurry_cli-0.19.0}/PKG-INFO +3 -3
- {blurry_cli-0.18.0 → blurry_cli-0.19.0}/blurry/__init__.py +8 -3
- {blurry_cli-0.18.0 → blurry_cli-0.19.0}/blurry/cli.py +0 -9
- {blurry_cli-0.18.0 → blurry_cli-0.19.0}/blurry/file_processors/__init__.py +2 -0
- {blurry_cli-0.18.0 → blurry_cli-0.19.0}/blurry/images.py +41 -44
- {blurry_cli-0.18.0 → blurry_cli-0.19.0}/blurry/markdown/__init__.py +9 -18
- {blurry_cli-0.18.0 → blurry_cli-0.19.0}/blurry/plugins/jinja_plugins/blurry_image_extension.py +11 -12
- {blurry_cli-0.18.0 → blurry_cli-0.19.0}/blurry/utils.py +8 -0
- {blurry_cli-0.18.0 → blurry_cli-0.19.0}/pyproject.toml +5 -4
- {blurry_cli-0.18.0 → blurry_cli-0.19.0}/LICENSE +0 -0
- {blurry_cli-0.18.0 → blurry_cli-0.19.0}/README.md +0 -0
- {blurry_cli-0.18.0 → blurry_cli-0.19.0}/blurry/__main__.py +0 -0
- {blurry_cli-0.18.0 → blurry_cli-0.19.0}/blurry/async_typer.py +0 -0
- {blurry_cli-0.18.0 → blurry_cli-0.19.0}/blurry/commands/__init__.py +0 -0
- {blurry_cli-0.18.0 → blurry_cli-0.19.0}/blurry/commands/clean.py +0 -0
- {blurry_cli-0.18.0 → blurry_cli-0.19.0}/blurry/commands/init.py +0 -0
- {blurry_cli-0.18.0 → blurry_cli-0.19.0}/blurry/commands/version.py +0 -0
- {blurry_cli-0.18.0 → blurry_cli-0.19.0}/blurry/constants.py +0 -0
- {blurry_cli-0.18.0 → blurry_cli-0.19.0}/blurry/gather_file_data_by_directory.py +0 -0
- {blurry_cli-0.18.0 → blurry_cli-0.19.0}/blurry/markdown/front_matter.py +0 -0
- {blurry_cli-0.18.0 → blurry_cli-0.19.0}/blurry/markdown/renderer_functions/__init__.py +0 -0
- {blurry_cli-0.18.0 → blurry_cli-0.19.0}/blurry/markdown/renderer_functions/render_video.py +0 -0
- {blurry_cli-0.18.0 → blurry_cli-0.19.0}/blurry/open_graph.py +0 -0
- {blurry_cli-0.18.0 → blurry_cli-0.19.0}/blurry/plugins/__init__.py +0 -0
- {blurry_cli-0.18.0 → blurry_cli-0.19.0}/blurry/plugins/html_plugins/__init__.py +0 -0
- {blurry_cli-0.18.0 → blurry_cli-0.19.0}/blurry/plugins/html_plugins/minify_html_plugin.py +0 -0
- {blurry_cli-0.18.0 → blurry_cli-0.19.0}/blurry/plugins/jinja_plugins/__init__.py +0 -0
- {blurry_cli-0.18.0 → blurry_cli-0.19.0}/blurry/plugins/jinja_plugins/filters.py +0 -0
- {blurry_cli-0.18.0 → blurry_cli-0.19.0}/blurry/plugins/markdown_plugins/__init__.py +0 -0
- {blurry_cli-0.18.0 → blurry_cli-0.19.0}/blurry/plugins/markdown_plugins/container_plugin.py +0 -0
- {blurry_cli-0.18.0 → blurry_cli-0.19.0}/blurry/plugins/markdown_plugins/punctuation_plugin.py +0 -0
- {blurry_cli-0.18.0 → blurry_cli-0.19.0}/blurry/plugins/markdown_plugins/python_code_plugin.py +0 -0
- {blurry_cli-0.18.0 → blurry_cli-0.19.0}/blurry/py.typed +0 -0
- {blurry_cli-0.18.0 → blurry_cli-0.19.0}/blurry/runserver_handlers.py +0 -0
- {blurry_cli-0.18.0 → blurry_cli-0.19.0}/blurry/schema_validation/__init__.py +0 -0
- {blurry_cli-0.18.0 → blurry_cli-0.19.0}/blurry/schema_validation/models.py +0 -0
- {blurry_cli-0.18.0 → blurry_cli-0.19.0}/blurry/settings.py +0 -0
- {blurry_cli-0.18.0 → blurry_cli-0.19.0}/blurry/sitemap.py +0 -0
- {blurry_cli-0.18.0 → blurry_cli-0.19.0}/blurry/templates.py +0 -0
- {blurry_cli-0.18.0 → blurry_cli-0.19.0}/blurry/types.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: blurry-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.19.0
|
|
4
4
|
Summary: A Mistune-based static site generator for Python
|
|
5
5
|
License: MIT
|
|
6
6
|
License-File: LICENSE
|
|
@@ -23,14 +23,14 @@ Classifier: Topic :: Text Processing :: Markup :: HTML
|
|
|
23
23
|
Classifier: Topic :: Text Processing :: Markup :: Markdown
|
|
24
24
|
Requires-Dist: Jinja2 (>=3.1.5,<4.0.0)
|
|
25
25
|
Requires-Dist: PyLD (>=2.0.4,<3.0.0)
|
|
26
|
-
Requires-Dist: Wand (>=0.6.13,<0.7.0)
|
|
27
26
|
Requires-Dist: dpath (>=2.2.0,<3.0.0)
|
|
28
27
|
Requires-Dist: ffmpeg-python (>=0.2.0,<0.3.0)
|
|
29
28
|
Requires-Dist: htmlmin2 (>=0.1.13,<0.2.0)
|
|
30
29
|
Requires-Dist: jinja2-simple-tags (>=0.6.1,<0.7.0)
|
|
31
|
-
Requires-Dist: jinjax (>=0.48,<0.
|
|
30
|
+
Requires-Dist: jinjax (>=0.48,<0.62)
|
|
32
31
|
Requires-Dist: livereload (>=2.7.1,<3.0.0)
|
|
33
32
|
Requires-Dist: mistune (>=3.1.0,<4.0.0)
|
|
33
|
+
Requires-Dist: pillow (>=12.0.0,<13.0.0)
|
|
34
34
|
Requires-Dist: pydantic (>=2.10.5,<3.0.0)
|
|
35
35
|
Requires-Dist: rich (>=13.9.4,<15.0.0)
|
|
36
36
|
Requires-Dist: selectolax (>=0.3.27,<0.4.0)
|
|
@@ -16,7 +16,7 @@ from rich.progress import TaskProgressColumn
|
|
|
16
16
|
from rich.progress import TextColumn
|
|
17
17
|
|
|
18
18
|
from blurry.async_typer import AsyncTyper
|
|
19
|
-
from blurry.cli import
|
|
19
|
+
from blurry.cli import print_blurry_name
|
|
20
20
|
from blurry.cli import print_plugin_table
|
|
21
21
|
from blurry.commands.clean import clean_build_directory
|
|
22
22
|
from blurry.commands.init import initialize_new_project
|
|
@@ -34,6 +34,7 @@ from blurry.settings import get_settings
|
|
|
34
34
|
from blurry.settings import update_settings
|
|
35
35
|
from blurry.sitemap import write_sitemap_file
|
|
36
36
|
from blurry.templates import get_jinja_env
|
|
37
|
+
from blurry.utils import handle_future_result
|
|
37
38
|
|
|
38
39
|
|
|
39
40
|
warning_console = Console(stderr=True, style="bold yellow")
|
|
@@ -66,7 +67,6 @@ async def build(release=True, clean: bool = False):
|
|
|
66
67
|
update_settings()
|
|
67
68
|
print_blurry_name()
|
|
68
69
|
print_plugin_table()
|
|
69
|
-
check_avif_support(console=warning_console)
|
|
70
70
|
|
|
71
71
|
build_directory = get_build_directory()
|
|
72
72
|
|
|
@@ -97,6 +97,12 @@ async def build(release=True, clean: bool = False):
|
|
|
97
97
|
other_task = progress.add_task("[blue]Other")
|
|
98
98
|
|
|
99
99
|
sitemap_future = executor.submit(write_sitemap_file, file_data_by_directory)
|
|
100
|
+
sitemap_future.add_done_callback(
|
|
101
|
+
lambda future: handle_future_result(
|
|
102
|
+
future,
|
|
103
|
+
"Could not generate sitemap",
|
|
104
|
+
)
|
|
105
|
+
)
|
|
100
106
|
sitemap_future.add_done_callback(lambda _: progress.advance(other_task))
|
|
101
107
|
|
|
102
108
|
for filepath in content_directory.rglob("*"):
|
|
@@ -165,7 +171,6 @@ def runserver():
|
|
|
165
171
|
"""Starts HTTP server with live reloading."""
|
|
166
172
|
print_blurry_name()
|
|
167
173
|
print_plugin_table()
|
|
168
|
-
check_avif_support(console=warning_console)
|
|
169
174
|
|
|
170
175
|
SETTINGS = get_settings()
|
|
171
176
|
os.environ.setdefault(f"{ENV_VAR_PREFIX}BUILD_MODE", "dev")
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
from rich.console import Console
|
|
2
2
|
from rich.table import Table
|
|
3
|
-
import wand.version
|
|
4
3
|
|
|
5
4
|
from blurry.plugins import discovered_html_plugins
|
|
6
5
|
from blurry.plugins import discovered_jinja_extensions
|
|
@@ -36,11 +35,3 @@ def print_plugin_table():
|
|
|
36
35
|
)
|
|
37
36
|
|
|
38
37
|
console.print(plugin_table)
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
def check_avif_support(console: Console):
|
|
42
|
-
if "AVIF" in wand.version.formats():
|
|
43
|
-
return
|
|
44
|
-
console.print(
|
|
45
|
-
"AVIF support not found. Falling back to WEBP. See https://trac.ffmpeg.org/wiki/Encode/AV1"
|
|
46
|
-
)
|
|
@@ -2,14 +2,12 @@ import concurrent.futures
|
|
|
2
2
|
from concurrent.futures import Future
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
|
|
5
|
-
from
|
|
6
|
-
import wand.version
|
|
5
|
+
from PIL import Image
|
|
7
6
|
|
|
8
7
|
from blurry.settings import get_build_directory
|
|
9
8
|
from blurry.settings import get_content_directory
|
|
10
9
|
from blurry.settings import get_settings
|
|
11
|
-
|
|
12
|
-
has_avif_support = "AVIF" in wand.version.formats()
|
|
10
|
+
from blurry.utils import handle_future_result
|
|
13
11
|
|
|
14
12
|
|
|
15
13
|
def get_target_image_widths():
|
|
@@ -33,30 +31,32 @@ def add_image_width_to_path(image_path: Path, width: int) -> Path:
|
|
|
33
31
|
return Path(new_filename)
|
|
34
32
|
|
|
35
33
|
|
|
36
|
-
def
|
|
34
|
+
def convert_image_to_avif(image_path: Path, target_path: Path | None = None):
|
|
37
35
|
SETTINGS = get_settings()
|
|
38
36
|
IMAGE_COMPRESSION_QUALITY = SETTINGS["IMAGE_COMPRESSION_QUALITY"]
|
|
39
|
-
|
|
40
|
-
new_image_format = "avif" if has_avif_support else "webp"
|
|
41
|
-
new_filepath = str(target_path or image_path).replace(
|
|
42
|
-
image_suffix, f".{new_image_format}"
|
|
43
|
-
)
|
|
37
|
+
new_filepath = (target_path or image_path).with_suffix(".avif")
|
|
44
38
|
if Path(new_filepath).exists():
|
|
45
39
|
return
|
|
46
|
-
with Image(
|
|
47
|
-
image.
|
|
48
|
-
image.compression_quality = IMAGE_COMPRESSION_QUALITY
|
|
49
|
-
image.save(filename=new_filepath)
|
|
40
|
+
with Image.open(image_path) as image:
|
|
41
|
+
image.save(new_filepath, quality=IMAGE_COMPRESSION_QUALITY)
|
|
50
42
|
|
|
51
43
|
|
|
52
|
-
def
|
|
53
|
-
image_path
|
|
54
|
-
)
|
|
44
|
+
def clone_and_resize_image_as_avif_or_webp(image_path: Path, target_width: int):
|
|
45
|
+
resized_image_suffix = ".webp" if image_path.suffix == ".webp" else ".avif"
|
|
46
|
+
resized_image_destination = get_build_directory() / (
|
|
47
|
+
add_image_width_to_path(
|
|
48
|
+
image_path.with_suffix(resized_image_suffix), target_width
|
|
49
|
+
)
|
|
50
|
+
).relative_to(get_content_directory())
|
|
55
51
|
if resized_image_destination.exists():
|
|
56
52
|
return
|
|
57
|
-
with Image(
|
|
58
|
-
image.
|
|
59
|
-
|
|
53
|
+
with Image.open(image_path) as image:
|
|
54
|
+
width, height = image.size
|
|
55
|
+
resize_factor = target_width / width
|
|
56
|
+
resized_image = image.resize(
|
|
57
|
+
(target_width, int(height * resize_factor)), Image.Resampling.LANCZOS
|
|
58
|
+
)
|
|
59
|
+
resized_image.save(resized_image_destination)
|
|
60
60
|
|
|
61
61
|
|
|
62
62
|
async def generate_images_for_srcset(image_path: Path):
|
|
@@ -66,37 +66,34 @@ async def generate_images_for_srcset(image_path: Path):
|
|
|
66
66
|
|
|
67
67
|
build_path = BUILD_DIR / image_path.resolve().relative_to(CONTENT_DIR)
|
|
68
68
|
with concurrent.futures.ThreadPoolExecutor() as executor:
|
|
69
|
-
# Convert original image
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
image_futures.append(full_sized_image_future)
|
|
74
|
-
|
|
75
|
-
# Store resized image futures so AVIF/WEBP versions can be created once they're done
|
|
76
|
-
resize_future_to_filepath: dict[Future, Path] = {}
|
|
77
|
-
|
|
78
|
-
width = Image(filename=str(image_path)).width
|
|
79
|
-
|
|
80
|
-
for target_width in get_widths_for_image_width(width):
|
|
81
|
-
new_filepath = add_image_width_to_path(image_path, target_width)
|
|
82
|
-
new_filepath_in_build = BUILD_DIR / new_filepath.relative_to(CONTENT_DIR)
|
|
83
|
-
|
|
84
|
-
resized_original_image_type_future = executor.submit(
|
|
85
|
-
clone_and_resize_image, image_path, target_width, new_filepath_in_build
|
|
69
|
+
# Convert original image only if it is in an inefficient format
|
|
70
|
+
if image_path.suffix not in [".avif", ".webp"]:
|
|
71
|
+
full_sized_image_future = executor.submit(
|
|
72
|
+
convert_image_to_avif, image_path=image_path, target_path=build_path
|
|
86
73
|
)
|
|
87
|
-
|
|
88
|
-
|
|
74
|
+
full_sized_image_future.add_done_callback(
|
|
75
|
+
lambda future: handle_future_result(
|
|
76
|
+
future,
|
|
77
|
+
f"Could not convert image: {image_path.relative_to(CONTENT_DIR)}",
|
|
78
|
+
)
|
|
89
79
|
)
|
|
80
|
+
image_futures.append(full_sized_image_future)
|
|
90
81
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
82
|
+
width = Image.open(image_path).width
|
|
83
|
+
|
|
84
|
+
for target_width in get_widths_for_image_width(width):
|
|
94
85
|
resized_image_future = executor.submit(
|
|
95
|
-
|
|
86
|
+
clone_and_resize_image_as_avif_or_webp, image_path, target_width
|
|
87
|
+
)
|
|
88
|
+
resized_image_future.add_done_callback(
|
|
89
|
+
lambda future: handle_future_result(
|
|
90
|
+
future,
|
|
91
|
+
f"Could not resize image: {image_path.relative_to(CONTENT_DIR)} to {target_width}",
|
|
92
|
+
)
|
|
96
93
|
)
|
|
97
94
|
image_futures.append(resized_image_future)
|
|
98
95
|
|
|
99
|
-
|
|
96
|
+
concurrent.futures.wait(image_futures)
|
|
100
97
|
|
|
101
98
|
|
|
102
99
|
def get_widths_for_image_width(image_width: int) -> list[int]:
|
|
@@ -14,7 +14,7 @@ from mistune.plugins.table import table
|
|
|
14
14
|
from mistune.plugins.task_lists import task_lists
|
|
15
15
|
from mistune.plugins.url import url
|
|
16
16
|
from mistune.util import escape
|
|
17
|
-
from
|
|
17
|
+
from PIL import Image
|
|
18
18
|
|
|
19
19
|
from .front_matter import parse_front_matter
|
|
20
20
|
from .renderer_functions.render_video import render_video
|
|
@@ -74,14 +74,11 @@ class BlurryRenderer(mistune.HTMLRenderer):
|
|
|
74
74
|
return render_video(src, absolute_path, extension, title=title)
|
|
75
75
|
|
|
76
76
|
# Tailor srcset and sizes to image width
|
|
77
|
-
with Image(
|
|
78
|
-
image_width = img.
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
image_is_animated = img.animation or extension.lower() == "webp"
|
|
83
|
-
attributes["width"] = image_width
|
|
84
|
-
attributes["height"] = image_height
|
|
77
|
+
with Image.open(absolute_path) as img:
|
|
78
|
+
image_width, image_height = img.size
|
|
79
|
+
image_is_animated = getattr(img, "n_frames", 0) > 1
|
|
80
|
+
attributes["width"] = str(image_width)
|
|
81
|
+
attributes["height"] = str(image_height)
|
|
85
82
|
|
|
86
83
|
if image_is_animated or extension.lower() == "svg":
|
|
87
84
|
attributes_str = " ".join(
|
|
@@ -91,25 +88,19 @@ class BlurryRenderer(mistune.HTMLRenderer):
|
|
|
91
88
|
|
|
92
89
|
image_widths = get_widths_for_image_width(image_width)
|
|
93
90
|
|
|
94
|
-
|
|
95
|
-
attributes["srcset"] = generate_srcset_string(src, image_widths)
|
|
91
|
+
sizes = generate_sizes_string(image_widths)
|
|
96
92
|
avif_srcset = generate_srcset_string(
|
|
97
93
|
src.replace(extension, "avif"), image_widths
|
|
98
94
|
)
|
|
99
95
|
source_tag = '<source srcset="{}" sizes="{}" type="image/avif" />'.format(
|
|
100
|
-
avif_srcset,
|
|
96
|
+
avif_srcset, sizes
|
|
101
97
|
)
|
|
102
98
|
|
|
103
99
|
attributes_str = " ".join(
|
|
104
100
|
f'{name}="{value}"' for name, value in attributes.items()
|
|
105
101
|
)
|
|
106
102
|
|
|
107
|
-
return
|
|
108
|
-
f"<figure>"
|
|
109
|
-
f"<picture>{source_tag}<img {attributes_str}></picture>"
|
|
110
|
-
f"<figcaption>{attributes['alt']}</figcaption>"
|
|
111
|
-
f"</figure>"
|
|
112
|
-
)
|
|
103
|
+
return f"<figure><picture>{source_tag}<img {attributes_str}></picture></figure>"
|
|
113
104
|
|
|
114
105
|
def link(self, text, url, title: str | None = None) -> str:
|
|
115
106
|
CONTENT_DIR = get_content_directory()
|
{blurry_cli-0.18.0 → blurry_cli-0.19.0}/blurry/plugins/jinja_plugins/blurry_image_extension.py
RENAMED
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import mimetypes
|
|
2
|
-
from pathlib import Path
|
|
3
2
|
from urllib.parse import urlparse
|
|
4
3
|
|
|
5
4
|
from jinja2_simple_tags import StandaloneTag
|
|
6
5
|
from rich.console import Console
|
|
7
|
-
from
|
|
8
|
-
from wand.image import Image
|
|
6
|
+
from PIL import Image
|
|
9
7
|
|
|
10
8
|
from blurry.images import add_image_width_to_path
|
|
11
9
|
from blurry.settings import get_build_directory
|
|
@@ -31,17 +29,16 @@ class BlurryImage(StandaloneTag):
|
|
|
31
29
|
image_path = get_build_directory() / image_relative_pathname
|
|
32
30
|
|
|
33
31
|
try:
|
|
34
|
-
with Image(
|
|
35
|
-
image_width = image.
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
except BlobError:
|
|
32
|
+
with Image.open(image_content_path) as image:
|
|
33
|
+
image_width, image_height = image.size
|
|
34
|
+
image_mimetype = image.get_format_mimetype()
|
|
35
|
+
except Exception:
|
|
39
36
|
warning_console.print(f"Could not find image: {image_content_path}")
|
|
40
37
|
return ""
|
|
41
38
|
|
|
42
39
|
attributes = {
|
|
43
|
-
"width": image_width,
|
|
44
|
-
"height": image_height,
|
|
40
|
+
"width": str(image_width),
|
|
41
|
+
"height": str(image_height),
|
|
45
42
|
}
|
|
46
43
|
for attribute_key in kwargs:
|
|
47
44
|
if attribute_key in ["width", "height"]:
|
|
@@ -49,7 +46,9 @@ class BlurryImage(StandaloneTag):
|
|
|
49
46
|
f"blurry_image: Received {attribute_key} in template {self.template} but this attribute is dynamic. Skipping."
|
|
50
47
|
)
|
|
51
48
|
continue
|
|
52
|
-
|
|
49
|
+
attribute_value = kwargs.get(attribute_key)
|
|
50
|
+
if attribute_value is not None:
|
|
51
|
+
attributes[attribute_key] = attribute_value
|
|
53
52
|
|
|
54
53
|
if width:
|
|
55
54
|
image_path = add_image_width_to_path(image_path, width)
|
|
@@ -58,7 +57,7 @@ class BlurryImage(StandaloneTag):
|
|
|
58
57
|
mimetypes.types_map[".jpg"],
|
|
59
58
|
mimetypes.types_map[".png"],
|
|
60
59
|
]:
|
|
61
|
-
image_path =
|
|
60
|
+
image_path = image_path.with_suffix(".avif")
|
|
62
61
|
|
|
63
62
|
attributes["src"] = build_path_to_url(image_path)
|
|
64
63
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
from concurrent.futures import Future
|
|
1
2
|
from pathlib import Path
|
|
2
3
|
|
|
3
4
|
from blurry.settings import get_build_directory
|
|
@@ -99,3 +100,10 @@ def format_schema_data(schema_data: dict) -> dict:
|
|
|
99
100
|
formatted_schema_data = {"@context": "https://schema.org"}
|
|
100
101
|
formatted_schema_data.update(schema_data)
|
|
101
102
|
return formatted_schema_data
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def handle_future_result(future: Future, message: str):
|
|
106
|
+
exception = future.exception()
|
|
107
|
+
if not exception:
|
|
108
|
+
return
|
|
109
|
+
print(message, "-", exception)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "blurry-cli"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.19.0"
|
|
4
4
|
description = "A Mistune-based static site generator for Python"
|
|
5
5
|
authors = ["John Franey <franey@duck.com>"]
|
|
6
6
|
license = "MIT"
|
|
@@ -13,6 +13,7 @@ classifiers = [
|
|
|
13
13
|
"Programming Language :: Python :: 3.11",
|
|
14
14
|
"Programming Language :: Python :: 3.12",
|
|
15
15
|
"Programming Language :: Python :: 3.13",
|
|
16
|
+
"Programming Language :: Python :: 3.14",
|
|
16
17
|
"Topic :: Internet :: WWW/HTTP :: Dynamic Content :: Content Management System",
|
|
17
18
|
"Topic :: Internet :: WWW/HTTP :: Site Management",
|
|
18
19
|
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
@@ -25,7 +26,6 @@ packages = [{ include = "blurry" }]
|
|
|
25
26
|
[tool.poetry.dependencies]
|
|
26
27
|
Jinja2 = "^3.1.5"
|
|
27
28
|
PyLD = "^2.0.4"
|
|
28
|
-
Wand = "^0.6.13"
|
|
29
29
|
ffmpeg-python = "^0.2.0"
|
|
30
30
|
livereload = "^2.7.1"
|
|
31
31
|
mistune = "^3.1.0"
|
|
@@ -36,8 +36,9 @@ typer = ">=0.15.1,<0.17.0"
|
|
|
36
36
|
htmlmin2 = "^0.1.13"
|
|
37
37
|
dpath = "^2.2.0"
|
|
38
38
|
jinja2-simple-tags = "^0.6.1"
|
|
39
|
-
jinjax = ">=0.48,<0.
|
|
39
|
+
jinjax = ">=0.48,<0.62"
|
|
40
40
|
pydantic = "^2.10.5"
|
|
41
|
+
pillow = "^12.0.0"
|
|
41
42
|
|
|
42
43
|
[tool.poetry.scripts]
|
|
43
44
|
blurry = 'blurry:main'
|
|
@@ -50,7 +51,7 @@ black = ">=24.10,<26.0"
|
|
|
50
51
|
nox = ">=2024.10.9,<2026.0.0"
|
|
51
52
|
pyright = "^1.1.391"
|
|
52
53
|
pytest = "^8.3.4"
|
|
53
|
-
pytest-cov = "
|
|
54
|
+
pytest-cov = ">=6,<8"
|
|
54
55
|
ruff = ">=0.8.6,<0.15.0"
|
|
55
56
|
pytest-watcher = "^0.4.3"
|
|
56
57
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{blurry_cli-0.18.0 → blurry_cli-0.19.0}/blurry/plugins/markdown_plugins/punctuation_plugin.py
RENAMED
|
File without changes
|
{blurry_cli-0.18.0 → blurry_cli-0.19.0}/blurry/plugins/markdown_plugins/python_code_plugin.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|