platzky 0.3.0__py3-none-any.whl → 0.3.1__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.
- platzky/__init__.py +3 -3
- platzky/admin/admin.py +33 -0
- platzky/admin/templates/admin.html +29 -0
- platzky/admin/templates/login.html +21 -0
- platzky/admin/templates/module.html +9 -0
- platzky/db/db.py +1 -1
- platzky/db/google_json_db.py +0 -5
- platzky/db/graph_ql_db.py +4 -4
- platzky/db/json_db.py +5 -1
- platzky/engine.py +62 -0
- platzky/platzky.py +13 -60
- platzky/plugin/plugin.py +66 -0
- platzky/plugin/plugin_loader.py +109 -0
- {platzky-0.3.0.dist-info → platzky-0.3.1.dist-info}/METADATA +2 -1
- {platzky-0.3.0.dist-info → platzky-0.3.1.dist-info}/RECORD +16 -10
- platzky/plugin_loader.py +0 -43
- {platzky-0.3.0.dist-info → platzky-0.3.1.dist-info}/WHEEL +0 -0
platzky/__init__.py
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
from .
|
|
2
|
-
from .platzky import create_app_from_config as create_app_from_config
|
|
3
|
-
from .platzky import create_engine as create_engine
|
|
1
|
+
from platzky.engine import Engine as Engine
|
|
2
|
+
from platzky.platzky import create_app_from_config as create_app_from_config
|
|
3
|
+
from platzky.platzky import create_engine as create_engine
|
platzky/admin/admin.py
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
from os.path import dirname
|
|
2
|
+
|
|
3
|
+
from flask import Blueprint, render_template, session
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def create_admin_blueprint(login_methods, db, locale_func):
|
|
7
|
+
admin = Blueprint(
|
|
8
|
+
"admin",
|
|
9
|
+
__name__,
|
|
10
|
+
url_prefix="/admin",
|
|
11
|
+
template_folder=f"{dirname(__file__)}/templates",
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
@admin.route("/", methods=["GET"])
|
|
15
|
+
def admin_panel_home():
|
|
16
|
+
user = session.get("user", None)
|
|
17
|
+
|
|
18
|
+
if not user:
|
|
19
|
+
return render_template("login.html", login_methods=login_methods)
|
|
20
|
+
|
|
21
|
+
cms_modules = {"plugins": [plugin.get("name") for plugin in db.get_plugins_data()]}
|
|
22
|
+
return render_template("admin.html", user=user, cms_modules=cms_modules)
|
|
23
|
+
|
|
24
|
+
@admin.route("/module/<module_name>", methods=["GET"])
|
|
25
|
+
def module_settings(module_name):
|
|
26
|
+
user = session.get("user", None)
|
|
27
|
+
|
|
28
|
+
if not user:
|
|
29
|
+
return render_template("login.html", login_methods=login_methods)
|
|
30
|
+
|
|
31
|
+
return render_template("module.html", user=user, module_name=module_name)
|
|
32
|
+
|
|
33
|
+
return admin
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{% extends "base.html" %}
|
|
2
|
+
|
|
3
|
+
{% block description %}
|
|
4
|
+
{{ _("This is the admin page. You can manage your posts here.") }}
|
|
5
|
+
{% endblock %}
|
|
6
|
+
|
|
7
|
+
{% block content %}
|
|
8
|
+
|
|
9
|
+
<div class="admin-contents mx-auto w-75">
|
|
10
|
+
<h1>{% block title %}Admin{% endblock %}</h1>
|
|
11
|
+
{% if user %}
|
|
12
|
+
<div class="alert alert-success" role="alert">You're logged in</div>
|
|
13
|
+
{% endif %}
|
|
14
|
+
</div>
|
|
15
|
+
|
|
16
|
+
{% endblock %}
|
|
17
|
+
|
|
18
|
+
{% block left_panel %}
|
|
19
|
+
<div id="admin-panel">
|
|
20
|
+
{% for cms_module_name, cms_entries in cms_modules.items() %}
|
|
21
|
+
<div class="cms-module mb-2">
|
|
22
|
+
<p>{{ cms_module_name }}</p>
|
|
23
|
+
{% for cms_entry in cms_entries %}
|
|
24
|
+
<a href="{{ url_for('admin.module', name=cms_entry) }}" class="cms-entry btn btn-primary btn-block mb-2">{{ cms_entry }}</a>
|
|
25
|
+
{% endfor %}
|
|
26
|
+
</div>
|
|
27
|
+
{% endfor %}
|
|
28
|
+
</div>
|
|
29
|
+
{% endblock %}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{% extends "base.html" %}
|
|
2
|
+
|
|
3
|
+
{% block description %}
|
|
4
|
+
{{ _("This is the login page. You can log in here.") }}
|
|
5
|
+
|
|
6
|
+
{% endblock %}
|
|
7
|
+
|
|
8
|
+
{% block content %}
|
|
9
|
+
|
|
10
|
+
<div class="login-contents mx-auto w-75">
|
|
11
|
+
<h1>{% block title %}Login{% endblock %}</h1>
|
|
12
|
+
|
|
13
|
+
<div class="row align-items-center">
|
|
14
|
+
|
|
15
|
+
{% for login_method in login_methods %}
|
|
16
|
+
{{ login_method | safe }}
|
|
17
|
+
{% endfor %}
|
|
18
|
+
</div>
|
|
19
|
+
</div>
|
|
20
|
+
|
|
21
|
+
{% endblock %}
|
platzky/db/db.py
CHANGED
platzky/db/google_json_db.py
CHANGED
|
@@ -47,8 +47,3 @@ class GoogleJsonDb(Json):
|
|
|
47
47
|
|
|
48
48
|
self.module_name = "google_json_db"
|
|
49
49
|
self.db_name = "GoogleJsonDb"
|
|
50
|
-
|
|
51
|
-
def __save_entry(self, entry):
|
|
52
|
-
data = get_data(self.blob)
|
|
53
|
-
data["data"].append(entry)
|
|
54
|
-
self.blob.upload_from_string(json.dumps(data), content_type="application/json")
|
platzky/db/graph_ql_db.py
CHANGED
|
@@ -6,8 +6,8 @@ from gql.transport.aiohttp import AIOHTTPTransport
|
|
|
6
6
|
from gql.transport.exceptions import TransportQueryError
|
|
7
7
|
from pydantic import Field
|
|
8
8
|
|
|
9
|
-
from
|
|
10
|
-
from .
|
|
9
|
+
from platzky.db.db import DB, DBConfig
|
|
10
|
+
from platzky.models import Color, Post
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
def db_config_type():
|
|
@@ -29,7 +29,7 @@ def db_from_config(config: GraphQlDbConfig):
|
|
|
29
29
|
|
|
30
30
|
def _standarize_comment(
|
|
31
31
|
comment,
|
|
32
|
-
):
|
|
32
|
+
):
|
|
33
33
|
return {
|
|
34
34
|
"author": comment["author"],
|
|
35
35
|
"comment": comment["comment"],
|
|
@@ -37,7 +37,7 @@ def _standarize_comment(
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
|
|
40
|
-
def _standarize_post(post):
|
|
40
|
+
def _standarize_post(post):
|
|
41
41
|
return {
|
|
42
42
|
"author": post["author"]["name"],
|
|
43
43
|
"slug": post["slug"],
|
platzky/db/json_db.py
CHANGED
|
@@ -73,7 +73,11 @@ class Json(DB):
|
|
|
73
73
|
return menu_items_list
|
|
74
74
|
|
|
75
75
|
def get_posts_by_tag(self, tag, lang):
|
|
76
|
-
return (
|
|
76
|
+
return (
|
|
77
|
+
post
|
|
78
|
+
for post in self._get_site_content()["posts"]
|
|
79
|
+
if tag in post["tags"] and post["language"] == lang
|
|
80
|
+
)
|
|
77
81
|
|
|
78
82
|
def _get_site_content(self):
|
|
79
83
|
content = self.data.get("site_content")
|
platzky/engine.py
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
from flask import Flask, request, session
|
|
4
|
+
from flask_babel import Babel
|
|
5
|
+
|
|
6
|
+
from platzky.config import Config
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Engine(Flask):
|
|
10
|
+
def __init__(self, config: Config, db, import_name):
|
|
11
|
+
super().__init__(import_name)
|
|
12
|
+
self.config.from_mapping(config.model_dump(by_alias=True))
|
|
13
|
+
self.db = db
|
|
14
|
+
self.notifiers = []
|
|
15
|
+
self.login_methods = []
|
|
16
|
+
self.dynamic_body = ""
|
|
17
|
+
self.dynamic_head = ""
|
|
18
|
+
directory = os.path.dirname(os.path.realpath(__file__))
|
|
19
|
+
locale_dir = os.path.join(directory, "locale")
|
|
20
|
+
config.translation_directories.append(locale_dir)
|
|
21
|
+
|
|
22
|
+
babel_translation_directories = ";".join(config.translation_directories)
|
|
23
|
+
self.babel = Babel(
|
|
24
|
+
self,
|
|
25
|
+
locale_selector=self.get_locale,
|
|
26
|
+
default_translation_directories=babel_translation_directories,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
def notify(self, message: str):
|
|
30
|
+
for notifier in self.notifiers:
|
|
31
|
+
notifier(message)
|
|
32
|
+
|
|
33
|
+
def add_notifier(self, notifier):
|
|
34
|
+
self.notifiers.append(notifier)
|
|
35
|
+
|
|
36
|
+
# TODO login_method should be interface
|
|
37
|
+
def add_login_method(self, login_method):
|
|
38
|
+
self.login_methods.append(login_method)
|
|
39
|
+
|
|
40
|
+
def add_dynamic_body(self, body: str):
|
|
41
|
+
self.dynamic_body += body
|
|
42
|
+
|
|
43
|
+
def add_dynamic_head(self, body: str):
|
|
44
|
+
self.dynamic_head += body
|
|
45
|
+
|
|
46
|
+
def get_locale(self) -> str:
|
|
47
|
+
domain = request.headers.get("Host", "localhost")
|
|
48
|
+
domain_to_lang = self.config.get("DOMAIN_TO_LANG")
|
|
49
|
+
|
|
50
|
+
languages = self.config.get("LANGUAGES", {}).keys()
|
|
51
|
+
backup_lang = session.get(
|
|
52
|
+
"language",
|
|
53
|
+
request.accept_languages.best_match(languages, "en"),
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
if domain_to_lang:
|
|
57
|
+
lang = domain_to_lang.get(domain, backup_lang)
|
|
58
|
+
else:
|
|
59
|
+
lang = backup_lang
|
|
60
|
+
|
|
61
|
+
session["language"] = lang
|
|
62
|
+
return lang
|
platzky/platzky.py
CHANGED
|
@@ -1,71 +1,20 @@
|
|
|
1
|
-
import os
|
|
2
1
|
import typing as t
|
|
3
2
|
import urllib.parse
|
|
4
3
|
|
|
5
|
-
from flask import
|
|
6
|
-
from flask_babel import Babel
|
|
4
|
+
from flask import redirect, render_template, request, session
|
|
7
5
|
from flask_minify import Minify
|
|
8
6
|
|
|
9
|
-
from .
|
|
10
|
-
from .
|
|
7
|
+
from platzky.admin import admin
|
|
8
|
+
from platzky.blog import blog
|
|
9
|
+
from platzky.config import (
|
|
11
10
|
Config,
|
|
12
11
|
languages_dict,
|
|
13
12
|
)
|
|
14
|
-
from .db.db_loader import get_db
|
|
15
|
-
from .
|
|
16
|
-
from .
|
|
17
|
-
from .
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
class Engine(Flask):
|
|
21
|
-
def __init__(self, config: Config, db, import_name):
|
|
22
|
-
super().__init__(import_name)
|
|
23
|
-
self.config.from_mapping(config.model_dump(by_alias=True))
|
|
24
|
-
self.db = db
|
|
25
|
-
self.notifiers = []
|
|
26
|
-
self.dynamic_body = ""
|
|
27
|
-
self.dynamic_head = ""
|
|
28
|
-
directory = os.path.dirname(os.path.realpath(__file__))
|
|
29
|
-
locale_dir = os.path.join(directory, "locale")
|
|
30
|
-
config.translation_directories.append(locale_dir)
|
|
31
|
-
|
|
32
|
-
babel_translation_directories = ";".join(config.translation_directories)
|
|
33
|
-
self.babel = Babel(
|
|
34
|
-
self,
|
|
35
|
-
locale_selector=self.get_locale,
|
|
36
|
-
default_translation_directories=babel_translation_directories,
|
|
37
|
-
)
|
|
38
|
-
|
|
39
|
-
def notify(self, message: str):
|
|
40
|
-
for notifier in self.notifiers:
|
|
41
|
-
notifier(message)
|
|
42
|
-
|
|
43
|
-
def add_notifier(self, notifier):
|
|
44
|
-
self.notifiers.append(notifier)
|
|
45
|
-
|
|
46
|
-
def add_dynamic_body(self, body: str):
|
|
47
|
-
self.dynamic_body += body
|
|
48
|
-
|
|
49
|
-
def add_dynamic_head(self, body: str):
|
|
50
|
-
self.dynamic_head += body
|
|
51
|
-
|
|
52
|
-
def get_locale(self) -> str:
|
|
53
|
-
domain = request.headers["Host"]
|
|
54
|
-
domain_to_lang = self.config.get("DOMAIN_TO_LANG")
|
|
55
|
-
|
|
56
|
-
languages = self.config.get("LANGUAGES", {}).keys()
|
|
57
|
-
backup_lang = session.get(
|
|
58
|
-
"language",
|
|
59
|
-
request.accept_languages.best_match(languages, "en"),
|
|
60
|
-
)
|
|
61
|
-
|
|
62
|
-
if domain_to_lang:
|
|
63
|
-
lang = domain_to_lang.get(domain, backup_lang)
|
|
64
|
-
else:
|
|
65
|
-
lang = backup_lang
|
|
66
|
-
|
|
67
|
-
session["language"] = lang
|
|
68
|
-
return lang
|
|
13
|
+
from platzky.db.db_loader import get_db
|
|
14
|
+
from platzky.engine import Engine
|
|
15
|
+
from platzky.plugin.plugin_loader import plugify
|
|
16
|
+
from platzky.seo import seo
|
|
17
|
+
from platzky.www_handler import redirect_nonwww_to_www, redirect_www_to_nonwww
|
|
69
18
|
|
|
70
19
|
|
|
71
20
|
def create_engine(config: Config, db) -> Engine:
|
|
@@ -133,6 +82,9 @@ def create_engine(config: Config, db) -> Engine:
|
|
|
133
82
|
|
|
134
83
|
def create_app_from_config(config: Config) -> Engine:
|
|
135
84
|
engine = create_engine_from_config(config)
|
|
85
|
+
admin_blueprint = admin.create_admin_blueprint(
|
|
86
|
+
login_methods=engine.login_methods, db=engine.db, locale_func=engine.get_locale
|
|
87
|
+
)
|
|
136
88
|
blog_blueprint = blog.create_blog_blueprint(
|
|
137
89
|
db=engine.db,
|
|
138
90
|
blog_prefix=config.blog_prefix,
|
|
@@ -141,6 +93,7 @@ def create_app_from_config(config: Config) -> Engine:
|
|
|
141
93
|
seo_blueprint = seo.create_seo_blueprint(
|
|
142
94
|
db=engine.db, config=engine.config, locale_func=engine.get_locale
|
|
143
95
|
)
|
|
96
|
+
engine.register_blueprint(admin_blueprint)
|
|
144
97
|
engine.register_blueprint(blog_blueprint)
|
|
145
98
|
engine.register_blueprint(seo_blueprint)
|
|
146
99
|
|
platzky/plugin/plugin.py
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from abc import ABC, abstractmethod
|
|
3
|
+
from typing import Any, Dict, Generic, Type, TypeVar
|
|
4
|
+
|
|
5
|
+
from pydantic import BaseModel, ConfigDict
|
|
6
|
+
|
|
7
|
+
from platzky.platzky import Engine as PlatzkyEngine
|
|
8
|
+
|
|
9
|
+
logger = logging.getLogger(__name__)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class PluginError(Exception):
|
|
13
|
+
"""Exception raised for plugin-related errors."""
|
|
14
|
+
|
|
15
|
+
pass
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class ConfigPluginError(PluginError):
|
|
19
|
+
"""Exception raised for plugin configuration-related errors."""
|
|
20
|
+
|
|
21
|
+
pass
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class PluginBaseConfig(BaseModel):
|
|
25
|
+
"""Base Pydantic model for plugin configurations.
|
|
26
|
+
|
|
27
|
+
Plugin developers should extend this class to define their own configuration schema.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
model_config = ConfigDict(extra="allow")
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
T = TypeVar("T", bound=PluginBaseConfig)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class PluginBase(Generic[T], ABC):
|
|
37
|
+
"""Abstract base class for plugins.
|
|
38
|
+
|
|
39
|
+
Plugin developers must extend this class to implement their plugins.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
@classmethod
|
|
43
|
+
def get_config_model(cls) -> Type[PluginBaseConfig]:
|
|
44
|
+
return PluginBaseConfig
|
|
45
|
+
|
|
46
|
+
def __init__(self, config: Dict[str, Any]):
|
|
47
|
+
try:
|
|
48
|
+
config_class = self.get_config_model()
|
|
49
|
+
self.config = config_class.model_validate(config)
|
|
50
|
+
except Exception as e:
|
|
51
|
+
raise ConfigPluginError(f"Invalid configuration: {e}") from e
|
|
52
|
+
|
|
53
|
+
@abstractmethod
|
|
54
|
+
def process(self, app: PlatzkyEngine) -> PlatzkyEngine:
|
|
55
|
+
"""Process the plugin with the given app.
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
app: The Flask application instance
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
Platzky Engine with processed plugins
|
|
62
|
+
|
|
63
|
+
Raises:
|
|
64
|
+
PluginError: If plugin processing fails
|
|
65
|
+
"""
|
|
66
|
+
pass
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import importlib.util
|
|
2
|
+
import inspect
|
|
3
|
+
import logging
|
|
4
|
+
from typing import Any, Optional, Type
|
|
5
|
+
|
|
6
|
+
import deprecation
|
|
7
|
+
|
|
8
|
+
from platzky.engine import Engine
|
|
9
|
+
from platzky.plugin.plugin import PluginBase, PluginError
|
|
10
|
+
|
|
11
|
+
logger = logging.getLogger(__name__)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def find_plugin(plugin_name: str) -> Any:
|
|
15
|
+
"""Find plugin by name and return it as module.
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
plugin_name: name of plugin to find
|
|
19
|
+
|
|
20
|
+
Raises:
|
|
21
|
+
PluginError: if plugin cannot be imported
|
|
22
|
+
|
|
23
|
+
Returns:
|
|
24
|
+
module of plugin
|
|
25
|
+
"""
|
|
26
|
+
try:
|
|
27
|
+
return importlib.import_module(f"platzky_{plugin_name}")
|
|
28
|
+
except ImportError as e:
|
|
29
|
+
raise PluginError(
|
|
30
|
+
f"Plugin {plugin_name} not found. Ensure it's installed and follows "
|
|
31
|
+
f"the 'platzky_<plugin_name>' naming convention"
|
|
32
|
+
) from e
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _is_class_plugin(plugin_module: Any) -> Optional[Type[PluginBase[Any]]]:
|
|
36
|
+
"""Check if the plugin module contains a PluginBase implementation.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
plugin_module: The imported plugin module
|
|
40
|
+
|
|
41
|
+
Returns:
|
|
42
|
+
The plugin class if found, None otherwise
|
|
43
|
+
"""
|
|
44
|
+
# Look for classes in the module that inherit from PluginBase
|
|
45
|
+
for _, obj in inspect.getmembers(plugin_module):
|
|
46
|
+
if inspect.isclass(obj) and issubclass(obj, PluginBase) and obj != PluginBase:
|
|
47
|
+
return obj
|
|
48
|
+
return None
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@deprecation.deprecated(
|
|
52
|
+
deprecated_in="0.3.1",
|
|
53
|
+
removed_in="0.4.0",
|
|
54
|
+
current_version=None, # You should replace this with the current version
|
|
55
|
+
details="Legacy plugin style using the entrypoint process() function is deprecated. "
|
|
56
|
+
"Please migrate to the PluginBase interface.",
|
|
57
|
+
)
|
|
58
|
+
def _process_legacy_plugin(plugin_module, app, plugin_config, plugin_name):
|
|
59
|
+
"""Process a legacy plugin using the entrypoint approach."""
|
|
60
|
+
app = plugin_module.process(app, plugin_config)
|
|
61
|
+
logger.info(f"Processed legacy plugin: {plugin_name}")
|
|
62
|
+
return app
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def plugify(app: Engine) -> Engine:
|
|
66
|
+
"""Load plugins and run their entrypoints.
|
|
67
|
+
|
|
68
|
+
Supports both class-based plugins (PluginBase) and legacy entrypoint plugins.
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
app: Platzky Engine instance
|
|
72
|
+
|
|
73
|
+
Returns:
|
|
74
|
+
Platzky Engine with processed plugins
|
|
75
|
+
|
|
76
|
+
Raises:
|
|
77
|
+
PluginError: if plugin processing fails
|
|
78
|
+
"""
|
|
79
|
+
plugins_data = app.db.get_plugins_data()
|
|
80
|
+
|
|
81
|
+
for plugin_data in plugins_data:
|
|
82
|
+
plugin_config = plugin_data["config"]
|
|
83
|
+
plugin_name = plugin_data["name"]
|
|
84
|
+
|
|
85
|
+
try:
|
|
86
|
+
plugin_module = find_plugin(plugin_name)
|
|
87
|
+
|
|
88
|
+
# Check if this is a class-based plugin
|
|
89
|
+
plugin_class = _is_class_plugin(plugin_module)
|
|
90
|
+
|
|
91
|
+
if plugin_class:
|
|
92
|
+
# Handle new class-based plugins
|
|
93
|
+
plugin_instance = plugin_class(plugin_config)
|
|
94
|
+
app = plugin_instance.process(app)
|
|
95
|
+
logger.info(f"Processed class-based plugin: {plugin_name}")
|
|
96
|
+
elif hasattr(plugin_module, "process"):
|
|
97
|
+
# Handle legacy entrypoint plugins with deprecation warning
|
|
98
|
+
app = _process_legacy_plugin(plugin_module, app, plugin_config, plugin_name)
|
|
99
|
+
else:
|
|
100
|
+
raise PluginError(
|
|
101
|
+
f"Plugin {plugin_name} doesn't implement either the PluginBase interface "
|
|
102
|
+
f"or provide a process() function"
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
except Exception as e:
|
|
106
|
+
logger.error(f"Error processing plugin {plugin_name}: {e}")
|
|
107
|
+
raise PluginError(f"Error processing plugin {plugin_name}: {e}") from e
|
|
108
|
+
|
|
109
|
+
return app
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: platzky
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.1
|
|
4
4
|
Summary: Not only blog engine
|
|
5
5
|
License: MIT
|
|
6
6
|
Requires-Python: >=3.10,<4.0
|
|
@@ -16,6 +16,7 @@ Requires-Dist: Flask-Minify (>=0.42,<0.43)
|
|
|
16
16
|
Requires-Dist: Flask-WTF (>=1.2.1,<2.0.0)
|
|
17
17
|
Requires-Dist: PyYAML (>=6.0,<7.0)
|
|
18
18
|
Requires-Dist: aiohttp (>=3.9.5,<4.0.0)
|
|
19
|
+
Requires-Dist: deprecation (>=2.1.0,<3.0.0)
|
|
19
20
|
Requires-Dist: google-cloud-storage (>=2.5.0,<3.0.0)
|
|
20
21
|
Requires-Dist: gql (>=3.4.0,<4.0.0)
|
|
21
22
|
Requires-Dist: humanize (>=4.9.0,<5.0.0)
|
|
@@ -1,20 +1,26 @@
|
|
|
1
|
-
platzky/__init__.py,sha256=
|
|
1
|
+
platzky/__init__.py,sha256=IhL91rSWxIIJQNfVsqJ1d4yY5D2WyWcefo4Xv2aX_lo,180
|
|
2
|
+
platzky/admin/admin.py,sha256=nQq0IcBhrcX6S4gd71MejOcc2yizVv20UmF4ZRvZnBk,1019
|
|
3
|
+
platzky/admin/templates/admin.html,sha256=aIeQI9lSFvj6kwZg8U_AE7VSiVCxZeUdB_gLXzZDU20,778
|
|
4
|
+
platzky/admin/templates/login.html,sha256=a8dpJabAjvKyJZt9wKt9GtEO2XShuvo2m6BvZqDH6bU,391
|
|
5
|
+
platzky/admin/templates/module.html,sha256=WuQZxKQDD4INl-QF2uiKHf9Fmf2h7cEW9RLe1nWKC8k,175
|
|
2
6
|
platzky/blog/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
7
|
platzky/blog/blog.py,sha256=caBUewnwd6QrJDv20m4JDfDDjiVu7hM0AJnoTyCdwM4,3009
|
|
4
8
|
platzky/blog/comment_form.py,sha256=4lkNJ_S_2DZmJBbz-NPDqahvy2Zz5AGNH2spFeGIop4,513
|
|
5
9
|
platzky/config.py,sha256=M3gmZI9yI-ThgmTA4RKsAPcnJwJjcWhXipYzq3hO-Hk,2346
|
|
6
10
|
platzky/db/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
-
platzky/db/db.py,sha256=
|
|
11
|
+
platzky/db/db.py,sha256=o3jXcA97MwOepJhxtmXpyfDATRcMcJmX6O1Bro5-Rkw,2796
|
|
8
12
|
platzky/db/db_loader.py,sha256=CuEiXxhIa4bFMm0vi7ugzm7j3WycilGRKCU6smgIImE,905
|
|
9
|
-
platzky/db/google_json_db.py,sha256=
|
|
10
|
-
platzky/db/graph_ql_db.py,sha256=
|
|
11
|
-
platzky/db/json_db.py,sha256=
|
|
13
|
+
platzky/db/google_json_db.py,sha256=phRW8196o3lXxO3oSBAMMXfbOKR3K2MGkzF6w41SPBc,1305
|
|
14
|
+
platzky/db/graph_ql_db.py,sha256=aGE1glBmLmx4mE1aysGe6sl0lP2uG89LUnu2hmkdvqk,8528
|
|
15
|
+
platzky/db/json_db.py,sha256=J3sKykFLIv4tors0kEYF55SHLviLhHg8m-2fljXvqFI,3756
|
|
12
16
|
platzky/db/json_file_db.py,sha256=UQ8TadELmqOzj_tgNfmzhtCkDkMAgcB9vaUy0GQXUY4,1010
|
|
17
|
+
platzky/engine.py,sha256=x6cP1oCaqqamfFgDdup7R9pe8z1KafdbNvrcUEOX7Ns,1897
|
|
13
18
|
platzky/locale/en/LC_MESSAGES/messages.po,sha256=WaZGlFAegKRq7CSz69dWKic-mKvQFhVvssvExxNmGaU,1400
|
|
14
19
|
platzky/locale/pl/LC_MESSAGES/messages.po,sha256=sUPxMKDeEOoZ5UIg94rGxZD06YVWiAMWIby2XE51Hrc,1624
|
|
15
20
|
platzky/models.py,sha256=-IIlyeLzACeTUpzuzvzJYxtT57E6wRiERoRgXJYMMtY,1502
|
|
16
|
-
platzky/platzky.py,sha256=
|
|
17
|
-
platzky/
|
|
21
|
+
platzky/platzky.py,sha256=VBLN71eq81EDV5qQYwKUX_InQffmrouAXxblyoZpb-4,3666
|
|
22
|
+
platzky/plugin/plugin.py,sha256=tV8aobIzMDJe1frKUAi4kLbrTAIS0FWE3oYpktSo6Ug,1633
|
|
23
|
+
platzky/plugin/plugin_loader.py,sha256=MeQ8LNbrOomwXgc1ISHuyhjZd2mzYKen70eDShWs-Co,3497
|
|
18
24
|
platzky/seo/seo.py,sha256=N_MmAA4KJZmmrDUh0hYNtD8ycOwpNKow4gVSAv8V3N4,2631
|
|
19
25
|
platzky/static/blog.css,sha256=TrppzgQbj4UtuTufDCdblyNTVAqgIbhD66Cziyv_xnY,7893
|
|
20
26
|
platzky/static/styles.css,sha256=U5ddGIK-VcGRJZ3BdOpMp0pR__k6rNEMsuQXkP4tFQ0,686
|
|
@@ -30,6 +36,6 @@ platzky/templates/post.html,sha256=GSgjIZsOQKtNx3cEbquSjZ5L4whPnG6MzRyoq9k4B8Q,1
|
|
|
30
36
|
platzky/templates/robots.txt,sha256=2_j2tiYtYJnzZUrANiX9pvBxyw5Dp27fR_co18BPEJ0,116
|
|
31
37
|
platzky/templates/sitemap.xml,sha256=iIJZ91_B5ZuNLCHsRtsGKZlBAXojOTP8kffqKLacgvs,578
|
|
32
38
|
platzky/www_handler.py,sha256=pF6Rmvem1sdVqHD7z3RLrDuG-CwAqfGCti50_NPsB2w,725
|
|
33
|
-
platzky-0.3.
|
|
34
|
-
platzky-0.3.
|
|
35
|
-
platzky-0.3.
|
|
39
|
+
platzky-0.3.1.dist-info/METADATA,sha256=iHkJnppWm5npC0Y-fJQywiO9toIVeoxQdbH0S3cAGhk,1686
|
|
40
|
+
platzky-0.3.1.dist-info/WHEEL,sha256=XbeZDeTWKc1w7CSIyre5aMDU_-PohRwTQceYnisIYYY,88
|
|
41
|
+
platzky-0.3.1.dist-info/RECORD,,
|
platzky/plugin_loader.py
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import importlib.util
|
|
2
|
-
import logging
|
|
3
|
-
|
|
4
|
-
logger = logging.getLogger(__name__)
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
class PluginError(Exception):
|
|
8
|
-
pass
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
def find_plugin(plugin_name):
|
|
12
|
-
"""Find plugin by name and return it as module.
|
|
13
|
-
:param plugin_name: name of plugin to find
|
|
14
|
-
:raises PluginError: if plugin cannot be imported
|
|
15
|
-
:return: module of plugin
|
|
16
|
-
"""
|
|
17
|
-
try:
|
|
18
|
-
return importlib.import_module(f"platzky_{plugin_name}")
|
|
19
|
-
except ImportError as e:
|
|
20
|
-
raise PluginError(
|
|
21
|
-
f"Plugin {plugin_name} not found. Ensure it's installed and follows "
|
|
22
|
-
f"the 'platzky_<plugin_name>' naming convention"
|
|
23
|
-
) from e
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
def plugify(app):
|
|
27
|
-
"""Load plugins and run their entrypoints.
|
|
28
|
-
:param app: Flask app
|
|
29
|
-
:return: Flask app
|
|
30
|
-
"""
|
|
31
|
-
|
|
32
|
-
plugins_data = app.db.get_plugins_data()
|
|
33
|
-
|
|
34
|
-
for plugin_data in plugins_data:
|
|
35
|
-
plugin_config = plugin_data["config"]
|
|
36
|
-
plugin_name = plugin_data["name"]
|
|
37
|
-
try:
|
|
38
|
-
plugin = find_plugin(plugin_name)
|
|
39
|
-
plugin.process(app, plugin_config)
|
|
40
|
-
except Exception as e:
|
|
41
|
-
raise PluginError(f"Error processing plugin {plugin_name}: {e}") from e
|
|
42
|
-
|
|
43
|
-
return app
|
|
File without changes
|