Skip to content
This repository has been archived by the owner on Oct 5, 2023. It is now read-only.

Deprecate the repository and pip package #231

Merged
merged 7 commits into from
Oct 4, 2023
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,26 @@ Purpose
These are a collection of utility functions, base test classes and
documentation that are useful for any XBlocks.


⚠️ Deprecation Notice ⚠️
************************

**Effective Date:** September 26, 2023

**Repository Migration:**
This `xblock-utils` repository has been `deprecated <https://github.com/openedx/xblock-utils/issues/197>`_ as of September 26, 2023, and the code and documentation have been migrated to the `Xblock <https://github.com/openedx/XBlock>`_ repository.

This decision was made to streamline and consolidate our codebase.

The migration process was completed through this Pull Request: `PR #669 <https://github.com/openedx/XBlock/pull/669>`_

**Archival**: We are going to archive the `xblock-utils` repository. This means that it will become read-only, and no further updates or changes will be accepted.

We appreciate your understanding and cooperation during this transition. If you have any questions or concerns, please don't hesitate to reach out to us through the `XBlock` repository's issue tracker.

Thank you for your continued support and contributions to the Open edX community.


Getting Started
***************

Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ def get_version(*file_paths):
'xblockutils',
],
install_requires=load_requirements('requirements/base.in'),
package_data=package_data("xblockutils", ["public", "templates", "templatetags"]),
package_data=package_data("xblockutils", ["deprecation", "public", "templates", "templatetags"]),
url='https://github.com/openedx/xblock-utils',
classifiers=[
'Framework :: Django :: 3.2',
Expand Down
6 changes: 3 additions & 3 deletions tests/unit/test_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ def test_theme_files_are_loaded_from_correct_package(self, xblock_class):
package_name = 'some_package'
theme_config = {xblock_class.theme_key: {'package': package_name, 'locations': ['lms.css']}}
self.service_mock.get_settings_bucket = Mock(return_value=theme_config)
with patch("xblockutils.settings.ResourceLoader") as patched_resource_loader:
with patch("xblock.utils.settings.ResourceLoader") as patched_resource_loader:
xblock.include_theme_files(fragment)
patched_resource_loader.assert_called_with(package_name)

Expand All @@ -133,7 +133,7 @@ def test_theme_files_are_added_to_fragment(self, package_name, locations):
fragment = MagicMock()
theme_config = {DummyXBlockWithSettings.theme_key: {'package': package_name, 'locations': locations}}
self.service_mock.get_settings_bucket = Mock(return_value=theme_config)
with patch("xblockutils.settings.ResourceLoader.load_unicode") as patched_load_unicode:
with patch("xblock.utils.settings.ResourceLoader.load_unicode") as patched_load_unicode:
xblock.include_theme_files(fragment)
for location in locations:
patched_load_unicode.assert_any_call(location)
Expand All @@ -146,6 +146,6 @@ def test_invalid_default_theme_config(self, theme_config):
xblock.default_theme_config = theme_config
self.service_mock.get_settings_bucket = Mock(return_value={})
fragment = MagicMock()
with patch("xblockutils.settings.ResourceLoader.load_unicode") as patched_load_unicode:
with patch("xblock.utils.settings.ResourceLoader.load_unicode") as patched_load_unicode:
xblock.include_theme_files(fragment)
patched_load_unicode.assert_not_called()
2 changes: 1 addition & 1 deletion xblockutils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
Useful classes and functionality for building and testing XBlocks
"""

__version__ = '3.4.1'
__version__ = '4.0.0'
Empty file.
34 changes: 34 additions & 0 deletions xblockutils/deprecation/warn.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
"""
Utilities for warning about the use of deprecated package

See https://github.com/openedx/xblock-utils/issues/197 for details.
"""

import warnings


class DeprecatedPackageWarning(DeprecationWarning):
"""
A warning that a deprecated package is being used.
"""

def __init__(self, old_import, new_import):
super().__init__()
self.old_import = old_import
self.new_import = new_import

def __str__(self):
return (
"Please use import {self.new_import} instead of {self.old_import} as "
"'xblock-utils' package has been deprecated and migrated within 'xblock' package. "
farhan marked this conversation as resolved.
Show resolved Hide resolved
).format(self=self)


def warn_deprecated_package(old_import, new_import):
"""
Warn that a package has been deprecated
"""
warnings.warn(
DeprecatedPackageWarning(old_import, new_import),
stacklevel=3, # Should surface the line that is doing the importing.
)
31 changes: 9 additions & 22 deletions xblockutils/helpers.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,12 @@
"""
Useful helper methods
"""
"""Deprecated package support."""
# pylint: disable=useless-suppression,line-too-long,redefined-builtin,wildcard-import,
# pylint: disable=wrong-import-position,wrong-import-order

from xblockutils.deprecation.warn import warn_deprecated_package

def child_isinstance(block, child_id, block_class_or_mixin):
"""
Efficiently check if a child of an XBlock is an instance of the given class.
warn_deprecated_package(
'xblockutils.helpers',
'xblock.utils.helpers'
)

Arguments:
block -- the parent (or ancestor) of the child block in question
child_id -- the usage key of the child block we are wondering about
block_class_or_mixin -- We return true if block's child indentified by child_id is an
instance of this.

This method is equivalent to

isinstance(block.runtime.get_block(child_id), block_class_or_mixin)

but is far more efficient, as it avoids the need to instantiate the child.
"""
def_id = block.runtime.id_reader.get_definition_id(child_id)
type_name = block.runtime.id_reader.get_block_type(def_id)
child_class = block.runtime.load_block_type(type_name)
return issubclass(child_class, block_class_or_mixin)
from xblock.utils.helpers import *
62 changes: 9 additions & 53 deletions xblockutils/publish_event.py
Original file line number Diff line number Diff line change
@@ -1,56 +1,12 @@
#
# Copyright (C) 2014-2015 edX
#
# This software's license gives you freedom; you can copy, convey,
# propagate, redistribute and/or modify this program under the terms of
# the GNU Affero General Public License (AGPL) as published by the Free
# Software Foundation (FSF), either version 3 of the License, or (at your
# option) any later version of the AGPL published by the FSF.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
# General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program in a file in the toplevel directory called
# "AGPLv3". If not, see <http://www.gnu.org/licenses/>.
#
"""
PublishEventMixin: A mixin for publishing events from an XBlock
"""
"""Deprecated package support."""
# pylint: disable=useless-suppression,line-too-long,redefined-builtin,wildcard-import,
# pylint: disable=wrong-import-position,wrong-import-order

from xblock.core import XBlock
from xblockutils.deprecation.warn import warn_deprecated_package

warn_deprecated_package(
'xblockutils.publish_event',
'xblock.utils.publish_event'
)

class PublishEventMixin:
"""
A mixin for publishing events from an XBlock

Requires the object to have a runtime.publish method.
"""
additional_publish_event_data = {}

@XBlock.json_handler
def publish_event(self, data, suffix=''): # pylint: disable=unused-argument
"""
AJAX handler to allow client-side code to publish a server-side event
"""
try:
event_type = data.pop('event_type')
except KeyError:
return {'result': 'error', 'message': 'Missing event_type in JSON data'}

return self.publish_event_from_dict(event_type, data)

def publish_event_from_dict(self, event_type, data):
"""
Combine 'data' with self.additional_publish_event_data and publish an event
"""
for key, value in self.additional_publish_event_data.items():
if key in data:
return {'result': 'error', 'message': f'Key should not be in publish_event data: {key}'}
data[key] = value

self.runtime.publish(self, event_type, data)
return {'result': 'success'}
from xblock.utils.publish_event import *
127 changes: 9 additions & 118 deletions xblockutils/resources.py
Original file line number Diff line number Diff line change
@@ -1,121 +1,12 @@
#
# Copyright (C) 2014-2015 Harvard, edX, OpenCraft
#
# This software's license gives you freedom; you can copy, convey,
# propagate, redistribute and/or modify this program under the terms of
# the GNU Affero General Public License (AGPL) as published by the Free
# Software Foundation (FSF), either version 3 of the License, or (at your
# option) any later version of the AGPL published by the FSF.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
# General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program in a file in the toplevel directory called
# "AGPLv3". If not, see <http://www.gnu.org/licenses/>.
#
"""
Helper class (ResourceLoader) for loading resources used by an XBlock
"""
"""Deprecated package support."""
# pylint: disable=useless-suppression,line-too-long,redefined-builtin,wildcard-import,
# pylint: disable=wrong-import-position,wrong-import-order

import os
import sys
import warnings
from xblockutils.deprecation.warn import warn_deprecated_package

import pkg_resources
warn_deprecated_package(
'xblockutils.resources',
'xblock.utils.resources'
)

from django.template import Context, Template, Engine
from django.template.backends.django import get_installed_libraries

from mako.template import Template as MakoTemplate
from mako.lookup import TemplateLookup as MakoTemplateLookup


class ResourceLoader:
"""Loads resources relative to the module named by the module_name parameter."""
def __init__(self, module_name):
self.module_name = module_name

def load_unicode(self, resource_path):
"""
Gets the content of a resource
"""
resource_content = pkg_resources.resource_string(self.module_name, resource_path)
return resource_content.decode('utf-8')

def render_django_template(self, template_path, context=None, i18n_service=None):
"""
Evaluate a django template by resource path, applying the provided context.
"""
context = context or {}
context['_i18n_service'] = i18n_service
libraries = {
'i18n': 'xblockutils.templatetags.i18n',
}

installed_libraries = get_installed_libraries()
installed_libraries.update(libraries)
engine = Engine(libraries=installed_libraries)

template_str = self.load_unicode(template_path)
template = Template(template_str, engine=engine)
rendered = template.render(Context(context))

return rendered

def render_mako_template(self, template_path, context=None):
"""
Evaluate a mako template by resource path, applying the provided context
"""
context = context or {}
template_str = self.load_unicode(template_path)
lookup = MakoTemplateLookup(directories=[pkg_resources.resource_filename(self.module_name, '')])
template = MakoTemplate(template_str, lookup=lookup)
return template.render(**context)

def render_template(self, template_path, context=None):
"""
This function has been deprecated. It calls render_django_template to support backwards compatibility.
"""
warnings.warn(
"ResourceLoader.render_template has been deprecated in favor of ResourceLoader.render_django_template"
)
return self.render_django_template(template_path, context)

def render_js_template(self, template_path, element_id, context=None, i18n_service=None):
"""
Render a js template.
"""
context = context or {}
return "<script type='text/template' id='{}'>\n{}\n</script>".format(
element_id,
self.render_django_template(template_path, context, i18n_service)
)

def load_scenarios_from_path(self, relative_scenario_dir, include_identifier=False):
"""
Returns an array of (title, xmlcontent) from files contained in a specified directory,
formatted as expected for the return value of the workbench_scenarios() method.

If `include_identifier` is True, returns an array of (identifier, title, xmlcontent).
"""
base_dir = os.path.dirname(os.path.realpath(sys.modules[self.module_name].__file__))
scenario_dir = os.path.join(base_dir, relative_scenario_dir)

scenarios = []
if os.path.isdir(scenario_dir):
for template in sorted(os.listdir(scenario_dir)):
if not template.endswith('.xml'):
continue
identifier = template[:-4]
title = identifier.replace('_', ' ').title()
template_path = os.path.join(relative_scenario_dir, template)
scenario = str(self.render_django_template(template_path, {"url_name": identifier}))
if not include_identifier:
scenarios.append((title, scenario))
else:
scenarios.append((identifier, title, scenario))

return scenarios
from xblock.utils.resources import *
Loading