pelican-avatar 1.0.8__py3-none-any.whl → 1.0.9__py3-none-any.whl
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.
Potentially problematic release.
This version of pelican-avatar might be problematic. Click here for more details.
- pelican/plugins/avatar/__init__.py +1 -0
- pelican/plugins/avatar/avatar.py +92 -0
- pelican/plugins/avatar/test_avatar.py +161 -0
- {pelican_avatar-1.0.8.dist-info → pelican_avatar-1.0.9.dist-info}/METADATA +3 -3
- pelican_avatar-1.0.9.dist-info/RECORD +7 -0
- {pelican_avatar-1.0.8.dist-info → pelican_avatar-1.0.9.dist-info}/WHEEL +1 -1
- pelican_avatar-1.0.8.dist-info/RECORD +0 -5
- tasks.py +0 -99
- {pelican_avatar-1.0.8.dist-info → pelican_avatar-1.0.9.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .avatar import * # NOQA: F403
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"""Avatar plugin for Pelican."""
|
|
2
|
+
|
|
3
|
+
# Copyright (C) 2015, 2021-2024 Rafael Laboissière <rafael@laboissiere.net>
|
|
4
|
+
#
|
|
5
|
+
# This program is free software: you can redistribute it and/or modify it
|
|
6
|
+
# under the terms of the GNU General Affero Public License as published by
|
|
7
|
+
# the Free Software Foundation, either version 3 of the License, or (at
|
|
8
|
+
# your option) any later version.
|
|
9
|
+
#
|
|
10
|
+
# This program is distributed in the hope that it will be useful, but
|
|
11
|
+
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
13
|
+
# Affero General Public License for more details.
|
|
14
|
+
#
|
|
15
|
+
# You should have received a copy of the GNU Affero General Public License
|
|
16
|
+
# along with this program. If not, see http://www.gnu.org/licenses/.
|
|
17
|
+
|
|
18
|
+
from libgravatar import Gravatar
|
|
19
|
+
from libravatar import libravatar_url
|
|
20
|
+
|
|
21
|
+
from pelican import signals
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def initialize(pelicanobj):
|
|
25
|
+
"""Initialize the Avatar plugin."""
|
|
26
|
+
pelicanobj.settings.setdefault("AVATAR_MISSING", None)
|
|
27
|
+
pelicanobj.settings.setdefault("AVATAR_SIZE", None)
|
|
28
|
+
pelicanobj.settings.setdefault("AVATAR_USE_GRAVATAR", None)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def gen_avatar_url(settings, email):
|
|
32
|
+
"""Generate the appropriate libravatar/gravatar URL based on the provided email."""
|
|
33
|
+
# Early exit if there is nothing to do
|
|
34
|
+
if not email:
|
|
35
|
+
return None
|
|
36
|
+
|
|
37
|
+
missing = settings.get("AVATAR_MISSING")
|
|
38
|
+
size = settings.get("AVATAR_SIZE")
|
|
39
|
+
|
|
40
|
+
email = email.lower()
|
|
41
|
+
# Compose URL
|
|
42
|
+
if settings.get("AVATAR_USE_GRAVATAR"):
|
|
43
|
+
params = {}
|
|
44
|
+
if missing:
|
|
45
|
+
params["default"] = missing
|
|
46
|
+
if size:
|
|
47
|
+
params["size"] = size
|
|
48
|
+
url = Gravatar(email).get_image(**params)
|
|
49
|
+
else:
|
|
50
|
+
url = libravatar_url(email)
|
|
51
|
+
if missing or size:
|
|
52
|
+
url = url + "?"
|
|
53
|
+
if missing:
|
|
54
|
+
url = url + "d=" + missing
|
|
55
|
+
if size:
|
|
56
|
+
url = url + "&"
|
|
57
|
+
if size:
|
|
58
|
+
url = url + "s=" + str(size)
|
|
59
|
+
|
|
60
|
+
return url
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def add_avatar_context(generator):
|
|
64
|
+
"""Add generator context connector for Avatar plugin."""
|
|
65
|
+
# This adds the avatar URL to the global generator context based on the
|
|
66
|
+
# global setting.
|
|
67
|
+
email = generator.settings.get("AVATAR_AUTHOR_EMAIL")
|
|
68
|
+
url = gen_avatar_url(generator.settings, email)
|
|
69
|
+
if url:
|
|
70
|
+
generator.context["author_avatar"] = url
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def add_avatar(generator, metadata):
|
|
74
|
+
"""Add Avatar URL to the article/page metadata."""
|
|
75
|
+
# Check the presence of the Email header
|
|
76
|
+
if "email" in metadata:
|
|
77
|
+
email = metadata["email"]
|
|
78
|
+
else:
|
|
79
|
+
email = generator.settings.get("AVATAR_AUTHOR_EMAIL")
|
|
80
|
+
|
|
81
|
+
url = gen_avatar_url(generator.settings, email)
|
|
82
|
+
if url:
|
|
83
|
+
# Add URL to the article/page metadata
|
|
84
|
+
metadata["author_avatar"] = url
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def register():
|
|
88
|
+
"""Register the Avatar plugin with Pelican."""
|
|
89
|
+
signals.initialized.connect(initialize)
|
|
90
|
+
signals.article_generator_context.connect(add_avatar)
|
|
91
|
+
signals.page_generator_context.connect(add_avatar)
|
|
92
|
+
signals.generator_init.connect(add_avatar_context)
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
"""Unit testing suite for the Avatar Plugin."""
|
|
2
|
+
|
|
3
|
+
# Copyright (C) 2015, 2021-2023 Rafael Laboissiere <rafael@laboissiere.net>
|
|
4
|
+
#
|
|
5
|
+
# This program is free software: you can redistribute it and/or modify it
|
|
6
|
+
# under the terms of the GNU General Affero Public License as published by
|
|
7
|
+
# the Free Software Foundation, either version 3 of the License, or (at
|
|
8
|
+
# your option) any later version.
|
|
9
|
+
#
|
|
10
|
+
# This program is distributed in the hope that it will be useful, but
|
|
11
|
+
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
13
|
+
# Affero General Public License for more details.
|
|
14
|
+
#
|
|
15
|
+
# You should have received a copy of the GNU Affero General Public License
|
|
16
|
+
# along with this program. If not, see http://www.gnu.org/licenses/.
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
import os
|
|
20
|
+
from shutil import rmtree
|
|
21
|
+
from tempfile import mkdtemp
|
|
22
|
+
import unittest
|
|
23
|
+
|
|
24
|
+
from libgravatar import Gravatar
|
|
25
|
+
from libravatar import libravatar_url
|
|
26
|
+
|
|
27
|
+
from pelican import Pelican
|
|
28
|
+
from pelican.settings import read_settings
|
|
29
|
+
|
|
30
|
+
from . import avatar
|
|
31
|
+
|
|
32
|
+
GLOBAL_AUTHOR_EMAIL = "homer.simpson@example.com"
|
|
33
|
+
ARTICLE_AUTHOR_EMAIL = "bart.simpson@example.com"
|
|
34
|
+
GLOBAL_GRAVATAR_URL = Gravatar(GLOBAL_AUTHOR_EMAIL).get_image()
|
|
35
|
+
GLOBAL_LIBRVATAR_URL = libravatar_url(GLOBAL_AUTHOR_EMAIL)
|
|
36
|
+
ARTICLE_GRAVATAR_URL = Gravatar(ARTICLE_AUTHOR_EMAIL).get_image()
|
|
37
|
+
ARTICLE_LIBRAVATAR_URL = libravatar_url(ARTICLE_AUTHOR_EMAIL)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class TestAvatarURL(unittest.TestCase):
|
|
41
|
+
"""Class for testing the URL output of the Avatar plugin."""
|
|
42
|
+
|
|
43
|
+
def setUp(self, override=None):
|
|
44
|
+
"""Set up the test environment."""
|
|
45
|
+
self.output_path = mkdtemp(prefix="pelicantests.")
|
|
46
|
+
self.content_path = mkdtemp(prefix="pelicantests.")
|
|
47
|
+
with open(
|
|
48
|
+
os.path.join(self.content_path, "article_infos.html"), "w"
|
|
49
|
+
) as article_infos_file:
|
|
50
|
+
article_infos_file.write(
|
|
51
|
+
"""
|
|
52
|
+
<footer class="post-info">
|
|
53
|
+
<div align="center">
|
|
54
|
+
<img src="{{ article.author_avatar }}">
|
|
55
|
+
</div>
|
|
56
|
+
</footer>
|
|
57
|
+
"""
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
with open(
|
|
61
|
+
os.path.join(self.content_path, "global_info.html"), "w"
|
|
62
|
+
) as global_infos_file:
|
|
63
|
+
global_infos_file.write(
|
|
64
|
+
"""
|
|
65
|
+
<footer class="post-info">
|
|
66
|
+
<div align="center">
|
|
67
|
+
<img src="{{ author_avatar }}">
|
|
68
|
+
</div>
|
|
69
|
+
</footer>
|
|
70
|
+
"""
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
settings = {
|
|
74
|
+
"PATH": self.content_path,
|
|
75
|
+
"THEME_TEMPLATES_OVERRIDES": [self.content_path],
|
|
76
|
+
"OUTPUT_PATH": self.output_path,
|
|
77
|
+
"PLUGINS": [avatar],
|
|
78
|
+
"CACHE_CONTENT": False,
|
|
79
|
+
"AVATAR_AUTHOR_EMAIL": GLOBAL_AUTHOR_EMAIL,
|
|
80
|
+
}
|
|
81
|
+
if override:
|
|
82
|
+
settings.update(override)
|
|
83
|
+
|
|
84
|
+
with open(
|
|
85
|
+
os.path.join(self.content_path, "global_test.md"), "w"
|
|
86
|
+
) as test_md_file:
|
|
87
|
+
test_md_file.write("Title: Global Test\nDate: 2019-09-05\n\n")
|
|
88
|
+
|
|
89
|
+
with open(os.path.join(self.content_path, "test.md"), "w") as test_md_file:
|
|
90
|
+
test_md_file.write(
|
|
91
|
+
"Title: Test\nDate: 2019-09-05\nEmail: " + ARTICLE_AUTHOR_EMAIL + "\n\n"
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
self.settings = read_settings(override=settings)
|
|
95
|
+
pelican = Pelican(settings=self.settings)
|
|
96
|
+
pelican.run()
|
|
97
|
+
|
|
98
|
+
def tearDown(self):
|
|
99
|
+
"""Tidy up the test environment."""
|
|
100
|
+
rmtree(self.output_path)
|
|
101
|
+
rmtree(self.content_path)
|
|
102
|
+
|
|
103
|
+
def _assert_url_in_file(self, filename, url, options):
|
|
104
|
+
with open(os.path.join(self.output_path, filename)) as test_html_file:
|
|
105
|
+
found = False
|
|
106
|
+
search_url = url + options
|
|
107
|
+
for line in test_html_file:
|
|
108
|
+
if search_url in line:
|
|
109
|
+
found = True
|
|
110
|
+
break
|
|
111
|
+
assert found
|
|
112
|
+
|
|
113
|
+
def test_url(self, options=""):
|
|
114
|
+
"""Test whether the Avatar URL appears in the generated HTML file."""
|
|
115
|
+
if self.settings["AVATAR_USE_GRAVATAR"]:
|
|
116
|
+
global_base_url = GLOBAL_GRAVATAR_URL
|
|
117
|
+
article_base_url = ARTICLE_GRAVATAR_URL
|
|
118
|
+
else:
|
|
119
|
+
global_base_url = GLOBAL_LIBRVATAR_URL
|
|
120
|
+
article_base_url = ARTICLE_LIBRAVATAR_URL
|
|
121
|
+
|
|
122
|
+
self._assert_url_in_file("test.html", article_base_url, options)
|
|
123
|
+
self._assert_url_in_file("global-test.html", global_base_url, options)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
class TestAvatarMissing(TestAvatarURL):
|
|
127
|
+
"""Class for testing the "missing picture" option."""
|
|
128
|
+
|
|
129
|
+
def setUp(self, override=None):
|
|
130
|
+
"""Set up the test environment."""
|
|
131
|
+
self.library = "wavatar"
|
|
132
|
+
TestAvatarURL.setUp(self, override={"AVATAR_MISSING": self.library})
|
|
133
|
+
|
|
134
|
+
def test_url(self):
|
|
135
|
+
"""Test whether the 'd' option appears in the Avatar URL."""
|
|
136
|
+
TestAvatarURL.test_url(self, r"?d=" + self.library)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
class TestAvatarSize(TestAvatarURL):
|
|
140
|
+
"""Class for testing the size option."""
|
|
141
|
+
|
|
142
|
+
def setUp(self, override=None):
|
|
143
|
+
"""Set up the test environment."""
|
|
144
|
+
self.size = 100
|
|
145
|
+
TestAvatarURL.setUp(self, override={"AVATAR_SIZE": self.size})
|
|
146
|
+
|
|
147
|
+
def test_url(self):
|
|
148
|
+
"""Test whether the 's' option appears in the Avatar URL."""
|
|
149
|
+
TestAvatarURL.test_url(self, r"?s=" + str(self.size))
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
class TestAvatarUseGravatar(TestAvatarURL):
|
|
153
|
+
"""Class for testing the 'use Gravatar' option."""
|
|
154
|
+
|
|
155
|
+
def setUp(self, override=None):
|
|
156
|
+
"""Set up the test environment."""
|
|
157
|
+
TestAvatarURL.setUp(self, override={"AVATAR_USE_GRAVATAR": True})
|
|
158
|
+
|
|
159
|
+
def test_url(self):
|
|
160
|
+
"""Test whether Gravatar is used."""
|
|
161
|
+
TestAvatarURL.test_url(self)
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: pelican-avatar
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.9
|
|
4
4
|
Summary: Libravatar/Gravatar plugin for Pelican
|
|
5
|
-
Keywords: pelican
|
|
6
|
-
Author-Email:
|
|
5
|
+
Keywords: pelican,plugin,libravatar,gravatar
|
|
6
|
+
Author-Email: =?utf-8?q?Rafael_Laboissi=C3=A8re?= <rafael@laboissiere.net>
|
|
7
7
|
License: AGPL-3.0
|
|
8
8
|
Classifier: Development Status :: 5 - Production/Stable
|
|
9
9
|
Classifier: Environment :: Console
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
pelican/plugins/avatar/__init__.py,sha256=tt8WB0hhhW_jgsRgQNEn-r3TKl1RJRglrXKeudL8zO0,36
|
|
2
|
+
pelican/plugins/avatar/avatar.py,sha256=zjaTE3Hp2Q5Fm7cseenMXzOgeCgdYxLFK6kzLUQNavI,3065
|
|
3
|
+
pelican/plugins/avatar/test_avatar.py,sha256=ekuA6r7ch-KTvhhmuBcpi5cbOVdoiO9TN5G60AK0JDM,5512
|
|
4
|
+
pelican_avatar-1.0.9.dist-info/METADATA,sha256=odttfcOVe0Q_aNGpiX9FaRUqxq3PkB0vkGN28kq09Z8,6829
|
|
5
|
+
pelican_avatar-1.0.9.dist-info/WHEEL,sha256=gZRWN_1fGFDwBnXrZ9N3dgvbKlKgo5AUcDIMajlGpKM,90
|
|
6
|
+
pelican_avatar-1.0.9.dist-info/licenses/LICENSE,sha256=hIahDEOTzuHCU5J2nd07LWwkLW7Hko4UFO__ffsvB-8,34523
|
|
7
|
+
pelican_avatar-1.0.9.dist-info/RECORD,,
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
pelican_avatar-1.0.8.dist-info/METADATA,sha256=moAqqvUJzexH52upKopx_BTWAZpC9lftSHhkGohMR70,6813
|
|
2
|
-
pelican_avatar-1.0.8.dist-info/WHEEL,sha256=N2J68yzZqJh3mI_Wg92rwhw0rtJDFpZj9bwQIMJgaVg,90
|
|
3
|
-
pelican_avatar-1.0.8.dist-info/licenses/LICENSE,sha256=hIahDEOTzuHCU5J2nd07LWwkLW7Hko4UFO__ffsvB-8,34523
|
|
4
|
-
tasks.py,sha256=vSB2IaG--1O3QmrCUYQt_ZXF2r3b7t3uRheSmhQ5MPA,3164
|
|
5
|
-
pelican_avatar-1.0.8.dist-info/RECORD,,
|
tasks.py
DELETED
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
from inspect import cleandoc
|
|
2
|
-
import logging
|
|
3
|
-
import os
|
|
4
|
-
from pathlib import Path
|
|
5
|
-
from shutil import which
|
|
6
|
-
|
|
7
|
-
from invoke import task
|
|
8
|
-
|
|
9
|
-
logger = logging.getLogger(__name__)
|
|
10
|
-
|
|
11
|
-
PKG_NAME = "avatar"
|
|
12
|
-
PKG_PATH = Path(f"pelican/plugins/{PKG_NAME}")
|
|
13
|
-
|
|
14
|
-
ACTIVE_VENV = os.environ.get("VIRTUAL_ENV", None)
|
|
15
|
-
VENV_HOME = Path(os.environ.get("WORKON_HOME", "~/.local/share/virtualenvs"))
|
|
16
|
-
VENV_PATH = Path(ACTIVE_VENV) if ACTIVE_VENV else (VENV_HOME.expanduser() / PKG_NAME)
|
|
17
|
-
VENV = str(VENV_PATH.expanduser())
|
|
18
|
-
BIN_DIR = "bin" if os.name != "nt" else "Scripts"
|
|
19
|
-
VENV_BIN = Path(VENV) / Path(BIN_DIR)
|
|
20
|
-
|
|
21
|
-
TOOLS = ("pdm", "pre-commit")
|
|
22
|
-
PDM = which("pdm") if which("pdm") else (VENV_BIN / "pdm")
|
|
23
|
-
CMD_PREFIX = f"{VENV_BIN}/" if ACTIVE_VENV else f"{PDM} run "
|
|
24
|
-
PRECOMMIT = which("pre-commit") if which("pre-commit") else f"{CMD_PREFIX}pre-commit"
|
|
25
|
-
PTY = os.name != "nt"
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
@task
|
|
29
|
-
def tests(c, deprecations=False):
|
|
30
|
-
"""Run the test suite, optionally with `--deprecations`."""
|
|
31
|
-
deprecations_flag = "" if deprecations else "-W ignore::DeprecationWarning"
|
|
32
|
-
c.run(f"{CMD_PREFIX}pytest {deprecations_flag}", pty=PTY)
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
@task
|
|
36
|
-
def black(c, check=False, diff=False):
|
|
37
|
-
"""Run Black auto-formatter, optionally with `--check` or `--diff`."""
|
|
38
|
-
check_flag, diff_flag = "", ""
|
|
39
|
-
if check:
|
|
40
|
-
check_flag = "--check"
|
|
41
|
-
if diff:
|
|
42
|
-
diff_flag = "--diff"
|
|
43
|
-
c.run(f"{CMD_PREFIX}black {check_flag} {diff_flag} {PKG_PATH} tasks.py", pty=PTY)
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
@task
|
|
47
|
-
def ruff(c, fix=False, diff=False):
|
|
48
|
-
"""Run Ruff to ensure code meets project standards."""
|
|
49
|
-
diff_flag, fix_flag = "", ""
|
|
50
|
-
if fix:
|
|
51
|
-
fix_flag = "--fix"
|
|
52
|
-
if diff:
|
|
53
|
-
diff_flag = "--diff"
|
|
54
|
-
c.run(f"{CMD_PREFIX}ruff check {diff_flag} {fix_flag} .", pty=PTY)
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
@task
|
|
58
|
-
def lint(c, fix=False, diff=False):
|
|
59
|
-
"""Check code style via linting tools."""
|
|
60
|
-
ruff(c, fix=fix, diff=diff)
|
|
61
|
-
black(c, check=(not fix), diff=diff)
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
@task
|
|
65
|
-
def tools(c):
|
|
66
|
-
"""Install development tools in the virtual environment if not already on PATH."""
|
|
67
|
-
for tool in TOOLS:
|
|
68
|
-
if not which(tool):
|
|
69
|
-
logger.info(f"** Installing {tool} **")
|
|
70
|
-
c.run(f"{CMD_PREFIX}pip install {tool}")
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
@task
|
|
74
|
-
def precommit(c):
|
|
75
|
-
"""Install pre-commit hooks to .git/hooks/pre-commit."""
|
|
76
|
-
logger.info("** Installing pre-commit hooks **")
|
|
77
|
-
c.run(f"{PRECOMMIT} install")
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
@task
|
|
81
|
-
def setup(c):
|
|
82
|
-
"""Set up the development environment."""
|
|
83
|
-
if which("pdm") or ACTIVE_VENV:
|
|
84
|
-
tools(c)
|
|
85
|
-
c.run(f"{CMD_PREFIX}python -m pip install --upgrade pip", pty=PTY)
|
|
86
|
-
c.run(f"{PDM} update --dev", pty=PTY)
|
|
87
|
-
precommit(c)
|
|
88
|
-
logger.info("\nDevelopment environment should now be set up and ready!\n")
|
|
89
|
-
else:
|
|
90
|
-
error_message = """
|
|
91
|
-
PDM is not installed, and there is no active virtual environment available.
|
|
92
|
-
You can either manually create and activate a virtual environment, or you can
|
|
93
|
-
install PDM via:
|
|
94
|
-
|
|
95
|
-
curl -sSL https://raw.githubusercontent.com/pdm-project/pdm/main/install-pdm.py | python3 -
|
|
96
|
-
|
|
97
|
-
Once you have taken one of the above two steps, run `invoke setup` again.
|
|
98
|
-
""" # noqa: E501
|
|
99
|
-
raise SystemExit(cleandoc(error_message))
|
|
File without changes
|