Repository: lektor/lektor-tags (language: python)

Issue:
default template cannot be found
It's not clear from the setting up instructions that a `/templates/tags.html` template is required. The readme says that the plugin ships with a default template. I've not checked this, but it definatelly does not get called correctly:

ValueError: The given value (u'~/Library/Caches/Lektor/packages/c2c6742dbf0a90d60bcba133cf9f3d16/templates/tags.html') is not below the source folder (u~/Dropbox/Lektor/vandragt.com')

default template cannot be found
It's not clear from the setting up instructions that a `/templates/tags.html` template is required. The readme says that the plugin ships with a default template. I've not checked this, but it definatelly does not get called correctly:

ValueError: The given value (u'~/Library/Caches/Lektor/packages/c2c6742dbf0a90d60bcba133cf9f3d16/templates/tags.html') is not below the source folder (u~/Dropbox/Lektor/vandragt.com')



Discussion / hints:
Creating lektor_tags_default_template.html under `templates` fixed this for me.
Cheers Phil how did you figure that out?
> The readme says that the plugin ships with a default template.

Moreover, this template *is* included in the wheel package, but:
- (optional) it is installed as `/home/louis/.cache/lektor/packages/62cf2206d7ac8eab5a919bd7c9e83b0e/templates/tag.html`: weird: it is not installed in the `lektor-tags` directory;
- it is named `tag.html` (althought `lektor-tag` looks for a default template named `lektor_tags_default_template.html`);
- even if we rename it, `lektor_tags` does not want to use it because: `ValueError: The given value ('/home/louis/.cache/lektor/packages/62cf2206d7ac8eab5a919bd7c9e83b0e/templates/lektor_tags_default_template.html') is not below the source folder ('/home/louis/tmp/lektor-mwe')`.
I think the issue is that shipping [_package data_](https://setuptools.pypa.io/en/latest/userguide/datafiles.html#package-data) in a Python distribution requires that the data files live within a _package_'s directory.  As things stand, we do not have a package in which to include any package data: `lektor_tags` is a _module_ (its source code is in a top-level file named `lektor_tags.py`), rather than a _package_ (in which its code would be in `lektor_tags/__init__.py`).

(Currently, at least, it appears as if the default template is not included in the wheel at all.  When `lektor_tags` is installed from source in editable mode, the default template can be found, however, if installed from the wheel, the template doesn't exist.)
Creating lektor_tags_default_template.html under `templates` fixed this for me.
Cheers Phil how did you figure that out?
> The readme says that the plugin ships with a default template.

Moreover, this template *is* included in the wheel package, but:
- (optional) it is installed as `/home/louis/.cache/lektor/packages/62cf2206d7ac8eab5a919bd7c9e83b0e/templates/tag.html`: weird: it is not installed in the `lektor-tags` directory;
- it is named `tag.html` (althought `lektor-tag` looks for a default template named `lektor_tags_default_template.html`);
- even if we rename it, `lektor_tags` does not want to use it because: `ValueError: The given value ('/home/louis/.cache/lektor/packages/62cf2206d7ac8eab5a919bd7c9e83b0e/templates/lektor_tags_default_template.html') is not below the source folder ('/home/louis/tmp/lektor-mwe')`.
I think the issue is that shipping [_package data_](https://setuptools.pypa.io/en/latest/userguide/datafiles.html#package-data) in a Python distribution requires that the data files live within a _package_'s directory.  As things stand, we do not have a package in which to include any package data: `lektor_tags` is a _module_ (its source code is in a top-level file named `lektor_tags.py`), rather than a _package_ (in which its code would be in `lektor_tags/__init__.py`).

(Currently, at least, it appears as if the default template is not included in the wheel at all.  When `lektor_tags` is installed from source in editable mode, the default template can be found, however, if installed from the wheel, the template doesn't exist.)

Fix patch:
diff --git a/lektor_tags.py b/lektor_tags/__init__.py
similarity index 92%
rename from lektor_tags.py
rename to lektor_tags/__init__.py
index cd23978..095d81c 100644
--- a/lektor_tags.py
+++ b/lektor_tags/__init__.py
@@ -1,12 +1,17 @@
 # -*- coding: utf-8 -*-
+from __future__ import annotations
+
 import collections
 import contextlib
 import posixpath
+import sys
 from dataclasses import dataclass
+from functools import partial
 from functools import total_ordering
 from math import log
+from pathlib import Path
+from weakref import finalize
 
-import pkg_resources
 from lektor.build_programs import BuildProgram
 from lektor.context import get_ctx
 from lektor.environment import Expression
@@ -16,6 +21,17 @@
 from lektor.utils import bool_from_string
 from lektor.utils import build_url
 
+if sys.version_info >= (3, 12):
+    from importlib import resources
+else:
+    import importlib_resources as resources
+
+if sys.version_info >= (3, 8):
+    from functools import cached_property
+else:
+    from werkzeug.utils import cached_property
+
+
 DEFAULT_ITEMS_QUERY = "this.parent.children.filter(F.tags.contains(tag))"
 DEFAULT_URL_PATH_EXP = "{{ this.parent.url_path }}tag/{{ tag }}"
 
@@ -156,8 +172,7 @@ class TagsPlugin(Plugin):
     reverse_url_map = {}
 
     def on_setup_env(self, **extra):
-        pkg_dir = pkg_resources.resource_filename("lektor_tags", "templates")
-        self.env.jinja_env.loader.searchpath.append(pkg_dir)
+        self.env.jinja_env.loader.searchpath.append(self._templates_path)
         self.env.jinja_env.globals["tagweights"] = self.tagweights
         self.env.add_build_program(TagPage, TagPageBuildProgram)
 
@@ -267,3 +282,11 @@ def tagweights(self):
             tag: TagWeight(count, min(tagcount.values()), max(tagcount.values()))
             for tag, count in tagcount.items()
         }
+
+    @cached_property
+    def _templates_path(self) -> Path:
+        """Get path to our packages template directory."""
+        pkgdata = resources.files()
+        cm = resources.as_file(pkgdata / "templates")
+        finalize(self, partial(cm.__exit__, None, None, None))
+        return cm.__enter__()
diff --git a/templates/lektor_tags_default_template.html b/lektor_tags/templates/lektor_tags_default_template.html
similarity index 100%
rename from templates/lektor_tags_default_template.html
rename to lektor_tags/templates/lektor_tags_default_template.html
diff --git a/setup.cfg b/setup.cfg
index 7f5220b..9e22336 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -16,13 +16,14 @@ classifiers =
     License :: OSI Approved :: MIT License
 
 [options]
-py_modules = lektor_tags
+packages = lektor_tags
 include_package_data = True
 setup_requires =
     setuptools >= 45
     setuptools_scm >= 6
 install_requires =
     dataclasses;python_version<"3.7"
+    importlib_resources>=5.10; python_version<"3.12"
 
 [options.entry_points]
 lektor.plugins =
diff --git a/templates/tag.html b/templates/tag.html
deleted file mode 100644
index 4b37d39..0000000
--- a/templates/tag.html
+++ /dev/null
@@ -1,9 +0,0 @@
-<h1>Tag: {{ this.tag }}</h1>
-<h1>Items:</h1>
-<ul>
-  {% for i in this.items %}
-    <li><a href="{{ i|url }}">{{ i._id }}</a></li>
-  {% else %}
-    <li><em>No items.</em></li>
-  {% endfor %}
-</ul>


Test patch:
diff --git a/tests/test_lektor_tags.py b/tests/test_lektor_tags.py
index 106edcb..94f5504 100644
--- a/tests/test_lektor_tags.py
+++ b/tests/test_lektor_tags.py
@@ -56,3 +56,7 @@ def test_tags_expression(pad, builder, env):
 
         conf["tags"] = '["foo", "bar", "bar"]'
         assert plugin.get_all_tags(parent) == {"bar", "foo"}
+
+
+def test_default_template(env):
+    env.jinja_env.get_template("lektor_tags_default_template.html")