diff --git a/CHANGELOG.md b/CHANGELOG.md index f0146dfdec..11011201c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,22 @@ instructions, because git commits are used to generate release notes: + +## v17.0.2 (2024-02-09) + +- [Feature] Several enhancements to the Demo Course (by @kdmccormick): + - The [Open edX Demo Course](https://github.com/openedx/openedx-demo-course) has been re-built from scratch with up-to-date instruction-focused content. Its directory structure has changed. + - In order to support both the old and new structures of the Demo Course's repository, the command `tutor local do importdemocourse` will now auto-determine the course root based on the location of `course.xml`. Use the `--repo-dir` argument to override this behavior. + - The new command `tutor local do importdemolibraries` will import any content libraries defined within the Demo Course repository. At the moment, that is just the "Respiratory System Question Bank", which is an optional but helpful extension to the new Demo Course. + - To try out the new Demo Course now, run: `tutor local do importdemocourse --version master`. + - To try out the demo Respiratory System Question Bank now, run: `tutor local do importdemolibraries --version master`. + - To revert back to an older Demo Course version at any point, run: `tutor local do importdemocourse --version open-release/quince.2`, replacing `quince.2` with your preferred course version. +- [Bugfix] Remove duplicate volume declarations that cause `docker compose` v2.24.1 to fail. +- [Bugfix] Actually update the environment on `tutor plugins enable ...`. (by @regisb) +- [Feature] Introduce a `tutor.hooks.lru_cache` decorator that is automatically cleared whenever a plugin is loaded or unloaded. This is useful, in particular when a plugin implements a costly function that depends on tutor hooks. (by @regisb) +- [Bugfix] Fix compatibility with Python 3.12 by replacing pkg_resources with importlib_metadata and importlib_resources. (by @Danyal-Faheem) +- [Improvement] Upgrade base release to open-release/quince.2. (by @regisb) + ## v17.0.1 (2024-01-25) diff --git a/changelog.d/20240110_101228_kyle_importnewdemocourse.md b/changelog.d/20240110_101228_kyle_importnewdemocourse.md deleted file mode 100644 index 79a7d44b70..0000000000 --- a/changelog.d/20240110_101228_kyle_importnewdemocourse.md +++ /dev/null @@ -1,7 +0,0 @@ -- [Feature] Several enhancements to the Demo Course (by @kdmccormick): - - The [Open edX Demo Course](https://github.com/openedx/openedx-demo-course) has been re-built from scratch with up-to-date instruction-focused content. Its directory structure has changed. - - In order to support both the old and new structures of the Demo Course's repository, the command `tutor local do importdemocourse` will now auto-determine the course root based on the location of `course.xml`. Use the `--repo-dir` argument to override this behavior. - - The new command `tutor local do importdemolibraries` will import any content libraries defined within the Demo Course repository. At the moment, that is just the "Respiratory System Question Bank", which is an optional but helpful extension to the new Demo Course. - - To try out the new Demo Course now, run: `tutor local do importdemocourse --version master`. - - To try out the demo Respiratory System Question Bank now, run: `tutor local do importdemolibraries --version master`. - - To revert back to an older Demo Course version at any point, run: `tutor local do importdemocourse --version open-release/quince.2`, replacing `quince.2` with your preferred course version. diff --git a/changelog.d/20240129_144929_kshitij_fix_tutor_docker_compose.md b/changelog.d/20240129_144929_kshitij_fix_tutor_docker_compose.md deleted file mode 100644 index 4a9625ced2..0000000000 --- a/changelog.d/20240129_144929_kshitij_fix_tutor_docker_compose.md +++ /dev/null @@ -1,2 +0,0 @@ -- [Bugfix] Fixes duplicate volume declarations causing Tutor commands that - invoke Docker to fail. \ No newline at end of file diff --git a/changelog.d/20240130_123351_regis_fix_save_on_plugins.md b/changelog.d/20240130_123351_regis_fix_save_on_plugins.md deleted file mode 100644 index 164dd818c3..0000000000 --- a/changelog.d/20240130_123351_regis_fix_save_on_plugins.md +++ /dev/null @@ -1,2 +0,0 @@ -- [Bugfix] Actually update the environment on `tutor plugins enable ...`. (by @regisb) -- [Feature] Introduce a `tutor.hooks.lru_cache` decorator that is automatically cleared whenever a plugin is loaded or unloaded. This is useful, in particular when a plugin implements a costly function that depends on tutor hooks. (by @regisb) diff --git a/requirements/base.in b/requirements/base.in index eb60ad6912..21e5098ba9 100644 --- a/requirements/base.in +++ b/requirements/base.in @@ -6,3 +6,5 @@ mypy pycryptodome>=3.17.0 pyyaml>=6.0 typing-extensions>=4.4.0 +importlib-metadata>=7.0.1 +importlib-resources>=6.1.1 diff --git a/requirements/base.txt b/requirements/base.txt index 1526e6825f..37cdb88c81 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -20,6 +20,10 @@ google-auth==2.23.3 # via kubernetes idna==3.4 # via requests +importlib-metadata==7.0.1 + # via -r requirements/base.in +importlib-resources==6.1.1 + # via -r requirements/base.in jinja2==3.1.3 # via -r requirements/base.in kubernetes==28.1.0 @@ -72,3 +76,7 @@ urllib3==1.26.18 # requests websocket-client==1.6.4 # via kubernetes +zipp==3.17.0 + # via + # importlib-metadata + # importlib-resources diff --git a/requirements/dev.txt b/requirements/dev.txt index 3515973bea..5c6aba3471 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -58,14 +58,17 @@ idna==3.4 # via # -r requirements/base.txt # requests -importlib-metadata==6.8.0 +importlib-metadata==7.0.1 # via + # -r requirements/base.txt # build # keyring # pyinstaller # twine importlib-resources==6.1.1 - # via keyring + # via + # -r requirements/base.txt + # keyring isort==5.12.0 # via pylint jaraco-classes==3.3.0 @@ -232,6 +235,7 @@ wheel==0.41.2 # via pip-tools zipp==3.17.0 # via + # -r requirements/base.txt # importlib-metadata # importlib-resources diff --git a/requirements/docs.txt b/requirements/docs.txt index 8acbfb5dfb..4ca558d51d 100644 --- a/requirements/docs.txt +++ b/requirements/docs.txt @@ -42,8 +42,12 @@ idna==3.4 # requests imagesize==1.4.1 # via sphinx -importlib-metadata==6.8.0 - # via sphinx +importlib-metadata==7.0.1 + # via + # -r requirements/base.txt + # sphinx +importlib-resources==6.1.1 + # via -r requirements/base.txt jinja2==3.1.3 # via # -r requirements/base.txt @@ -153,4 +157,7 @@ websocket-client==1.6.4 # -r requirements/base.txt # kubernetes zipp==3.17.0 - # via importlib-metadata + # via + # -r requirements/base.txt + # importlib-metadata + # importlib-resources diff --git a/tutor.spec b/tutor.spec index f68c7c08f6..1e6f591924 100644 --- a/tutor.spec +++ b/tutor.spec @@ -1,7 +1,7 @@ # -*- mode: python -*- import importlib import os -import pkg_resources +from importlib_metadata block_cipher = None @@ -10,10 +10,10 @@ hidden_imports = [] # Auto-discover plugins and include patches & templates folders for entrypoint_version in ["tutor.plugin.v0", "tutor.plugin.v1"]: - for entrypoint in pkg_resources.iter_entry_points(entrypoint_version): + for entrypoint in importlib_metadata.entry_points(group=entrypoint_version): plugin_name = entrypoint.name try: - plugin = entrypoint.load() + plugin = importlib.import_module(entrypoint.value) except Exception as e: print(f"ERROR Failed to load plugin {plugin_name}: {e}") continue diff --git a/tutor/__about__.py b/tutor/__about__.py index de71b797e4..19e0e9e129 100644 --- a/tutor/__about__.py +++ b/tutor/__about__.py @@ -2,7 +2,7 @@ # Increment this version number to trigger a new release. See # docs/tutor.html#versioning for information on the versioning scheme. -__version__ = "17.0.1" +__version__ = "17.0.2" # The version suffix will be appended to the actual version, separated by a # dash. Use this suffix to differentiate between the actual released version and diff --git a/tutor/env.py b/tutor/env.py index 5e668b8cf2..470f9dc5a3 100644 --- a/tutor/env.py +++ b/tutor/env.py @@ -7,13 +7,13 @@ from copy import deepcopy import jinja2 -import pkg_resources +import importlib_resources from tutor import exceptions, fmt, hooks, plugins, utils from tutor.__about__ import __app__, __version__ from tutor.types import Config, ConfigValue -TEMPLATES_ROOT = pkg_resources.resource_filename("tutor", "templates") +TEMPLATES_ROOT = str(importlib_resources.files("tutor") / "templates") VERSION_FILENAME = "version" BIN_FILE_EXTENSIONS = [ ".ico", diff --git a/tutor/plugins/v0.py b/tutor/plugins/v0.py index 16e67704e0..b1cf0f76d5 100644 --- a/tutor/plugins/v0.py +++ b/tutor/plugins/v0.py @@ -5,7 +5,7 @@ from glob import glob import click -import pkg_resources +import importlib_metadata from tutor import env, exceptions, fmt, hooks, serialize from tutor.__about__ import __app__ @@ -246,12 +246,12 @@ class EntrypointPlugin(BasePlugin): ENTRYPOINT = "tutor.plugin.v0" - def __init__(self, entrypoint: pkg_resources.EntryPoint) -> None: - self.loader: pkg_resources.EntryPoint + def __init__(self, entrypoint: importlib_metadata.EntryPoint) -> None: + self.loader: importlib_metadata.EntryPoint = entrypoint super().__init__(entrypoint.name, entrypoint) def _load_obj(self) -> None: - self.obj = self.loader.load() + self.obj = importlib.import_module(self.loader.value) def _version(self) -> t.Optional[str]: if not self.loader.dist: @@ -260,12 +260,11 @@ def _version(self) -> t.Optional[str]: @classmethod def discover_all(cls) -> None: - for entrypoint in pkg_resources.iter_entry_points(cls.ENTRYPOINT): + entrypoints = importlib_metadata.entry_points(group=cls.ENTRYPOINT) + for entrypoint in entrypoints: try: error: t.Optional[str] = None cls(entrypoint) - except pkg_resources.VersionConflict as e: - error = e.report() except Exception as e: # pylint: disable=broad-except error = str(e) if error: diff --git a/tutor/plugins/v1.py b/tutor/plugins/v1.py index cf24bf0f00..88982b6f67 100644 --- a/tutor/plugins/v1.py +++ b/tutor/plugins/v1.py @@ -2,7 +2,7 @@ import os from glob import glob -import pkg_resources +import importlib_metadata from tutor import hooks @@ -26,7 +26,7 @@ def _discover_entrypoint_plugins() -> None: """ with hooks.Contexts.PLUGINS.enter(): if "TUTOR_IGNORE_ENTRYPOINT_PLUGINS" not in os.environ: - for entrypoint in pkg_resources.iter_entry_points("tutor.plugin.v1"): + for entrypoint in importlib_metadata.entry_points(group="tutor.plugin.v1"): discover_package(entrypoint) @@ -56,7 +56,7 @@ def load(plugin_name: str) -> None: spec.loader.exec_module(module) -def discover_package(entrypoint: pkg_resources.EntryPoint) -> None: +def discover_package(entrypoint: importlib_metadata.EntryPoint) -> None: """ Install a plugin from a python package. """ @@ -68,10 +68,11 @@ def discover_package(entrypoint: pkg_resources.EntryPoint) -> None: # Add plugin information if entrypoint.dist is None: raise ValueError(f"Could not read plugin version: {name}") - hooks.Filters.PLUGINS_INFO.add_item((name, entrypoint.dist.version)) + dist_version = entrypoint.dist.version if entrypoint.dist else "Unknown" + hooks.Filters.PLUGINS_INFO.add_item((name, dist_version)) # Import module on enable @hooks.Actions.PLUGIN_LOADED.add() def load(plugin_name: str) -> None: if name == plugin_name: - entrypoint.load() + importlib.import_module(entrypoint.value) diff --git a/tutor/templates/config/defaults.yml b/tutor/templates/config/defaults.yml index d49ce62d88..ea68b54a02 100644 --- a/tutor/templates/config/defaults.yml +++ b/tutor/templates/config/defaults.yml @@ -62,7 +62,7 @@ OPENEDX_LMS_UWSGI_WORKERS: 2 OPENEDX_MYSQL_DATABASE: "openedx" OPENEDX_MYSQL_USERNAME: "openedx" # the common version will be automatically set to "master" in the nightly branch -OPENEDX_COMMON_VERSION: "open-release/quince.1" +OPENEDX_COMMON_VERSION: "open-release/quince.2" OPENEDX_EXTRA_PIP_REQUIREMENTS: [] MYSQL_HOST: "mysql" MYSQL_PORT: 3306