Skip to content

Commit

Permalink
feat!: annotate types
Browse files Browse the repository at this point in the history
WIP: annotate xblock/runtime.py
WIP: remove XBlock mixin from hierarchy, just use asserts, for backcompat?
WIP: DEPR non-string usage and definition keys?

BREAKING CHANGE: [Developers only]
Type-checked Python code using the XBlock API will likely need to be
updated in order to continue passing type-checking, since XBlock's
new annotations will trigger mypy (et al) to behave much more strictly.

NO BREAKING CHANGES for site operators, authors, learners, etc.
  • Loading branch information
kdmccormick committed Dec 18, 2024
1 parent d6fad53 commit ed5ffd3
Show file tree
Hide file tree
Showing 27 changed files with 970 additions and 630 deletions.
6 changes: 5 additions & 1 deletion CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,17 @@ Change history for XBlock
Unreleased
----------

6.0.0 - 2024-08-20
------------------

* added type hints to all public classes, methods, and functions

5.1.0 - 2024-08-07
------------------

* added ability to override an XBlock with the 'xblock.v1.overrides' entry point
* added ability to override an XBlock Aside with the 'xblock_asides.v1.overrides' entry point


5.0.0 - 2024-05-30
------------------

Expand Down
47 changes: 39 additions & 8 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,6 @@

import django

MOCK_MODULES = [
'webob',
'lxml'
]

for mod_name in MOCK_MODULES:
sys.modules[mod_name] = mock.Mock(class_that_is_extended=object)


# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
Expand Down Expand Up @@ -111,6 +103,11 @@
# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []

# Display the type hints in the docs as part of the function signature.
# This is the default, but it could be changed to "description" or "both".
# https://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html#confval-autodoc_typehints
autodoc_typehints = "signature"

# When auto-doc'ing a class, write the class' docstring and the __init__ docstring
# into the class docs.
autoclass_content = "both"
Expand All @@ -129,6 +126,40 @@
('py:class', 'aside_fn'),
('py:class', 'webob.Request'),
('py:class', 'webob.Response'),
('py:class', 'webob.request.Request'),
('py:class', 'webob.response.Response'),
('py:class', 'lxml.etree._Element'),
# As of Sphinx==8.0.2 and Python 3.11, its seems that Sphinx has bug(s) that make it
# unable to consistently recognize classes in otherwise-valid type annotations. So, since
# adding type hints to XBlock, we've had to add this big list of warning suppressions.
# If you're reading this in the future with newer versions of Sphinx and/or Python, feel
# free to try to whittle down this list:
('py:class', 'Blocklike'),
('py:class', 'BlocklikeSubclass'),
('py:class', 'DefinitionKey'),
('py:class', 'FieldValue'),
('py:class', 'InnerFieldValue'),
('py:class', 'Request'),
('py:class', 'Response'),
('py:class', 'UniqueIdPlaceholder'),
('py:class', 'Unset'),
('py:class', 'UsageKey'),
('py:class', 'etree._Element'),
('py:class', 'importlib.metadata.EntryPoint'),
('py:class', 'importlib.metadata.EntryPoint'),
('py:class', 'opaque_keys.edx.keys.DefinitionKey'),
('py:class', 'opaque_keys.edx.keys.LearningContextKey'),
('py:class', 'opaque_keys.edx.keys.UsageKey'),
('py:class', 't.Any'),
('py:class', 't.Callable'),
('py:class', 't.Iterable'),
('py:class', 'web_fragments.fragment.Fragment'),
('py:class', 'xblock.core.Blocklike'),
('py:class', 'xblock.fields.FieldValue'),
('py:class', 'xblock.fields.InnerFieldValue'),
('py:class', 'xblock.fields.UniqueIdPlaceholder'),
('py:class', 'xblock.fields.Unset'),
('py:class', 'xblock.validation.Validation'),
]

suppress_warnings = [
Expand Down
2 changes: 2 additions & 0 deletions mypy.ini
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ ignore_missing_imports = False
allow_untyped_globals = False
files =
xblock
exclude =
xblock.test

# Ignore web_fragments typing until it has hints.
[mypy-web_fragments.*]
Expand Down
2 changes: 1 addition & 1 deletion xblock/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
XBlock Courseware Components
"""

__version__ = '5.1.0'
__version__ = '6.0.0'
11 changes: 6 additions & 5 deletions xblock/completable.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""
This module defines CompletableXBlockMixin and completion mode enumeration.
"""
from xblock.core import Blocklike, XBlockMixin


class XBlockCompletionMode:
Expand All @@ -12,7 +13,7 @@ class XBlockCompletionMode:
EXCLUDED = "excluded"

@classmethod
def get_mode(cls, block_class):
def get_mode(cls, block_class: Blocklike | type[Blocklike]) -> str:
"""
Return the effective completion mode for a given block.
Expand All @@ -21,17 +22,17 @@ def get_mode(cls, block_class):
return getattr(block_class, 'completion_mode', cls.COMPLETABLE)


class CompletableXBlockMixin:
class CompletableXBlockMixin(XBlockMixin):
"""
This mixin sets attributes and provides helper method to integrate XBlock with Completion API.
"""

has_custom_completion = True
completion_mode = XBlockCompletionMode.COMPLETABLE
has_custom_completion: bool = True
completion_mode: str = XBlockCompletionMode.COMPLETABLE

# To read more on the debate about using the terms percent vs ratio, see:
# https://openedx.atlassian.net/wiki/spaces/OpenDev/pages/245465398/Naming+with+Percent+or+Ratio
def emit_completion(self, completion_percent):
def emit_completion(self, completion_percent: float) -> None:
"""
Emits completion event through Completion API.
Expand Down
Loading

0 comments on commit ed5ffd3

Please sign in to comment.