Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: annotate types #761

Draft
wants to merge 5 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all 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
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
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ help: ## display this help message

quality: ## check coding style with pycodestyle and pylint
pycodestyle
mypy
pylint xblock

validate: test
Expand Down
2 changes: 1 addition & 1 deletion docs/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ clean:
-rm -rf $(BUILDDIR)/*

html:
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
$(SPHINXBUILD) --keep-going -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."

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
12 changes: 12 additions & 0 deletions mypy.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[mypy]
follow_imports = normal
ignore_missing_imports = False
allow_untyped_globals = False
files =
xblock
exclude =
xblock.test

# Ignore web_fragments typing until it has hints.
[mypy-web_fragments.*]
ignore_missing_imports = True
34 changes: 34 additions & 0 deletions requirements/dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ asgiref==3.8.1
# via
# -r requirements/test.txt
# django
# django-stubs
astroid==3.3.6
# via
# -r requirements/test.txt
Expand Down Expand Up @@ -88,7 +89,15 @@ django==4.2.17
# via
# -c https://raw.githubusercontent.com/edx/edx-lint/master/edx_lint/files/common_constraints.txt
# -r requirements/test.txt
# django-stubs
# django-stubs-ext
# openedx-django-pyfs
django-stubs==5.1.1
# via -r requirements/test.txt
django-stubs-ext==5.1.1
# via
# -r requirements/test.txt
# django-stubs
dnspython==2.7.0
# via
# -r requirements/test.txt
Expand Down Expand Up @@ -136,6 +145,8 @@ lazy==1.6
# via -r requirements/test.txt
lxml==5.3.0
# via -r requirements/test.txt
lxml-stubs==0.5.1
# via -r requirements/test.txt
mako==1.3.8
# via -r requirements/test.txt
markupsafe==3.0.2
Expand All @@ -149,6 +160,12 @@ mccabe==0.7.0
# pylint
mock==5.1.0
# via -r requirements/test.txt
mypy==1.13.0
# via -r requirements/test.txt
mypy-extensions==1.0.0
# via
# -r requirements/test.txt
# mypy
openedx-django-pyfs==3.7.0
# via -r requirements/test.txt
packaging==24.2
Expand Down Expand Up @@ -283,10 +300,27 @@ tox==4.23.2
# via
# -r requirements/ci.txt
# -r requirements/test.txt
types-python-dateutil==2.9.0.20241206
# via -r requirements/test.txt
types-pytz==2024.2.0.20241003
# via -r requirements/test.txt
types-pyyaml==6.0.12.20240917
# via
# -r requirements/test.txt
# django-stubs
types-setuptools==75.6.0.20241126
# via -r requirements/test.txt
types-simplejson==3.19.0.20240801
# via -r requirements/test.txt
types-webob==1.8.0.20241205
# via -r requirements/test.txt
typing-extensions==4.12.2
# via
# -r requirements/test.txt
# django-stubs
# django-stubs-ext
# edx-opaque-keys
# mypy
urllib3==2.2.3
# via
# -r requirements/test.txt
Expand Down
9 changes: 9 additions & 0 deletions requirements/test.in
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,22 @@ astroid
coverage
ddt
diff-cover >= 0.2.1
django-stubs
edx_lint
hypothesis
lxml-stubs
mock
mypy
path
pycodestyle
pylint
pytest
pytest-cov
pytest-django
tox
types-python-dateutil
types-pytz
types-PyYAML
types-setuptools
types-simplejson
types-WebOb
30 changes: 30 additions & 0 deletions requirements/test.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ asgiref==3.8.1
# via
# -r requirements/django.txt
# django
# django-stubs
astroid==3.3.6
# via
# -r requirements/test.in
Expand Down Expand Up @@ -60,7 +61,13 @@ distlib==0.3.9
# via
# -c https://raw.githubusercontent.com/edx/edx-lint/master/edx_lint/files/common_constraints.txt
# -r requirements/django.txt
# django-stubs
# django-stubs-ext
# openedx-django-pyfs
django-stubs==5.1.1
# via -r requirements/test.in
django-stubs-ext==5.1.1
# via django-stubs
dnspython==2.7.0
# via
# -r requirements/django.txt
Expand Down Expand Up @@ -101,6 +108,8 @@ lazy==1.6
# via -r requirements/django.txt
lxml==5.3.0
# via -r requirements/django.txt
lxml-stubs==0.5.1
# via -r requirements/test.in
mako==1.3.8
# via -r requirements/django.txt
markupsafe==3.0.2
Expand All @@ -112,6 +121,10 @@ mccabe==0.7.0
# via pylint
mock==5.1.0
# via -r requirements/test.in
mypy==1.13.0
# via -r requirements/test.in
mypy-extensions==1.0.0
# via mypy
openedx-django-pyfs==3.7.0
# via -r requirements/django.txt
packaging==24.2
Expand Down Expand Up @@ -211,10 +224,27 @@ tomlkit==0.13.2
# via pylint
tox==4.23.2
# via -r requirements/test.in
types-python-dateutil==2.9.0.20241206
# via -r requirements/test.in
types-pytz==2024.2.0.20241003
# via -r requirements/test.in
types-pyyaml==6.0.12.20240917
# via
# -r requirements/test.in
# django-stubs
types-setuptools==75.6.0.20241126
# via -r requirements/test.in
types-simplejson==3.19.0.20240801
# via -r requirements/test.in
types-webob==1.8.0.20241205
# via -r requirements/test.in
typing-extensions==4.12.2
# via
# -r requirements/django.txt
# django-stubs
# django-stubs-ext
# edx-opaque-keys
# mypy
urllib3==2.2.3
# via
# -r requirements/django.txt
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
Loading