diff --git a/README.rst b/README.rst index 653c7fc..9a233ac 100644 --- a/README.rst +++ b/README.rst @@ -1,24 +1,26 @@ LTI 1.3 Advantage Tool implementation in Python =============================================== -.. image:: https://img.shields.io/pypi/v/PyLTI1p3 +This is a fork of the `pylti1p3 `_ package, originally written by Dmitry Viskov. + +.. image:: https://img.shields.io/pypi/v/pylti1p3next :scale: 100% - :target: https://pypi.python.org/pypi/PyLTI1p3 + :target: https://pypi.python.org/pypi/pylti1p3next :alt: PyPI -.. image:: https://img.shields.io/pypi/pyversions/PyLTI1p3 +.. image:: https://img.shields.io/pypi/pyversions/pylti1p3next :scale: 100% :target: https://www.python.org/ :alt: Python -.. image:: https://github.com/dmitry-viskov/pylti1.3/actions/workflows/tox.yml/badge.svg +.. image:: https://github.com/pymsglobal/pylti1p3next/actions/workflows/tox.yml/badge.svg :scale: 100% - :target: https://github.com/dmitry-viskov/pylti1.3/actions + :target: https://github.com/pymsglobal/pylti1p3next/actions :alt: Build Status -.. image:: https://img.shields.io/github/license/dmitry-viskov/pylti1.3 +.. image:: https://img.shields.io/github/license/pymsglobal/pylti1p3next :scale: 100% - :target: https://raw.githubusercontent.com/dmitry-viskov/pylti1.3/master/LICENSE + :target: https://raw.githubusercontent.com/pymsglobal/pylti1p3next/master/LICENSE :alt: MIT @@ -30,9 +32,9 @@ This library contains adapters for use with the Django and Flask web frameworks. Usage Examples ================= -Django: https://github.com/dmitry-viskov/pylti1.3-django-example +Django: https://github.com/pymsglobal/pylti1p3next-django-example -Flask: https://github.com/dmitry-viskov/pylti1.3-flask-example +Flask: https://github.com/pymsglobal/pylti1p3next-flask-example Configuration ============= @@ -581,7 +583,7 @@ After this, the special JS code will try to write and then read test cookie inst `special page`_ that will ask them to open the current URL in the new window if cookies are unavailable. If cookies are allowed, the user will be transparently redirected to the next page. All texts are configurable with passing arguments: -.. _special page: https://raw.githubusercontent.com/dmitry-viskov/repos-assets/master/pylti1p3/examples/cookies-check/001.png +.. _special page: https://raw.githubusercontent.com/pymsglobal/repos-assets/master/pylti1p3/examples/cookies-check/001.png .. code-block:: python diff --git a/pylintrc b/pylintrc index 948bbe4..c1e3810 100644 --- a/pylintrc +++ b/pylintrc @@ -42,13 +42,13 @@ fail-under=10 #from-stdin= # Files or directories to be skipped. They should be base names, not paths. -ignore=CVS +ignore= # Add files or directories matching the regular expressions patterns to the # ignore-list. The regex matches against paths and can be in Posix or Windows # format. Because '\' represents the directory delimiter on Windows systems, it # can't be used as an escape character. -ignore-paths= +ignore-paths=^.*/migrations/.*$ # Files or directories matching the regular expression patterns are skipped. # The regex matches against base names, not paths. The default value ignores @@ -77,7 +77,7 @@ limit-inference-results=100 # List of plugins (as comma separated values of python module names) to load, # usually to register additional checkers. -load-plugins= +load-plugins=pylint_django # Pickle collected data for later comparisons. persistent=yes @@ -307,8 +307,8 @@ min-public-methods=2 [EXCEPTIONS] # Exceptions that will emit a warning when caught. -overgeneral-exceptions=BaseException, - Exception +overgeneral-exceptions=builtins.BaseException, + builtins.Exception [FORMAT] @@ -428,6 +428,7 @@ disable=bad-inline-option, use-symbolic-message-instead, useless-option-value, useless-suppression, + django-not-configured, # Enable the message, report, category or checker with the given id(s). You can diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..ac92321 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,72 @@ +[project] +name = "pylti1p3next" +dynamic = ["version"] +authors = [ + { name="Christian Lawson-Perfect", email="christian.perfect@ncl.ac.uk"}, + { name="Dmitry Viskov", email="dmitry.viskov@webenterprise.ru"} +] +maintainers = [ + { name="Sébastien Philippot", email="sebastien@philippot.co" }, + { name="Christian Lawson-Perfect", email="christian.perfect@ncl.ac.uk"} +] +description = "LTI 1.3 Advantage Tool implementation in Python" +readme = "README.rst" +license = "MIT" +license-files = ["LICENSE"] +requires-python = ">=3.8" +keywords = [ + "pylti", + "pylti1p3", + "lti", + "lti1.3", + "lti1p3", + "django", + "flask" +] +classifiers = [ + "Development Status :: 5 - Production/Stable", + "Environment :: Web Environment", + "Framework :: Django", + "Framework :: Flask", + "Intended Audience :: Developers", + "Intended Audience :: Information Technology", + "Intended Audience :: Education", + "Operating System :: OS Independent", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3 :: Only", + "Topic :: Internet :: WWW/HTTP", + "Topic :: Internet :: WWW/HTTP :: WSGI", + "Topic :: Security", + "Topic :: Software Development :: Libraries :: Application Frameworks", + "Topic :: Internet :: WWW/HTTP :: Dynamic Content", + "Topic :: Software Development :: Libraries :: Python Modules", +] +dependencies = [ + "jwcrypto~=1.5", + "pyjwt~=1.5", + "requests~=2.32", + "typing_extensions~=4.2", +] + +[project.urls] +Homepage = "https://github.com/pymsglobal/pylti1p3next#readme" +Issues = "https://github.com/pymsglobal/pylti1p3next/issues" +Source = "https://github.com/pymsglobal/pylti1p3next" + +[build-system] +requires = ["setuptools>=61.0"] +build-backend = "setuptools.build_meta" + +[tool.setuptools.dynamic] +version = {attr = "pylti1p3.__version__"} + +[tool.coverage.paths] +pylti1p3next = ["pylti1p3"] +tests = ["tests"] diff --git a/setup.cfg b/setup.cfg index 2cd4d0c..769f74c 100644 --- a/setup.cfg +++ b/setup.cfg @@ -7,7 +7,7 @@ license_file = LICENSE [flake8] max_line_length=120 -exclude=.git,.idea,.tox,build,venv,venv3,env +exclude=.git,.idea,.tox,build,venv,venv3,env,migrations extend-ignore=E203 [coverage:run] diff --git a/setup.py b/setup.py deleted file mode 100644 index 8758cb4..0000000 --- a/setup.py +++ /dev/null @@ -1,72 +0,0 @@ -from __future__ import print_function - -import sys - -from setuptools import find_packages, setup - -from pylti1p3 import __version__ - -if sys.version_info < (3, 6): - error = "ERROR: PyLTI1p3 requires Python 3.6+ ... exiting." - print(error, file=sys.stderr) - sys.exit(1) - - -install_requires = [ - "jwcrypto", - "pyjwt>=1.5", - "requests", - "typing_extensions", -] - -with open("README.rst", "rt") as readme: - long_description = readme.read().strip() - -packages = find_packages(exclude=["examples", "tests"]) - -setup( - name="PyLTI1p3", - version=__version__, - description="LTI 1.3 Advantage Tool implementation in Python", - keywords="pylti,pylti1p3,lti,lti1.3,lti1p3,django,flask", - author="Dmitry Viskov", - author_email="dmitry.viskov@webenterprise.ru", - maintainer="Dmitry Viskov", - long_description=long_description, - install_requires=install_requires, - license="MIT", - url="https://github.com/dmitry-viskov/pylti1.3", - packages=packages, - zip_safe=False, - include_package_data=True, - classifiers=[ - "Development Status :: 5 - Production/Stable", - "Environment :: Web Environment", - "Framework :: Django", - "Framework :: Flask", - "Intended Audience :: Developers", - "Intended Audience :: Information Technology", - "License :: OSI Approved :: MIT License", - "Operating System :: OS Independent", - "Programming Language :: Python", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.6", - "Programming Language :: Python :: 3.7", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3 :: Only", - "Topic :: Internet :: WWW/HTTP", - "Topic :: Internet :: WWW/HTTP :: WSGI", - "Topic :: Security", - "Topic :: Software Development :: Libraries :: Application Frameworks", - "Topic :: Internet :: WWW/HTTP :: Dynamic Content", - "Topic :: Software Development :: Libraries :: Python Modules", - ], - package_data={ - "pylti1p3": ["py.typed"], - "pylti1p3.tool_config": ["py.typed"], - "pylti1p3.contrib": ["py.typed"], - }, -) diff --git a/pylti1p3/__init__.py b/src/pylti1p3/__init__.py similarity index 100% rename from pylti1p3/__init__.py rename to src/pylti1p3/__init__.py diff --git a/pylti1p3/actions.py b/src/pylti1p3/actions.py similarity index 100% rename from pylti1p3/actions.py rename to src/pylti1p3/actions.py diff --git a/pylti1p3/assignments_grades.py b/src/pylti1p3/assignments_grades.py similarity index 97% rename from pylti1p3/assignments_grades.py rename to src/pylti1p3/assignments_grades.py index d0db759..c7ad738 100644 --- a/pylti1p3/assignments_grades.py +++ b/src/pylti1p3/assignments_grades.py @@ -249,17 +249,17 @@ def find_or_create_lineitem( if not self.can_create_lineitem(): raise LtiException("Can't create lineitem: Missing required scope") - created_lineitem = self._service_connector.make_service_request( - self._service_data["scope"], - self._service_data["lineitems"], + response = self._service_connector.make_service_request( + scopes=self._service_data["scope"], + url=self._service_data["lineitems"], is_post=True, data=new_lineitem.get_value(), content_type="application/vnd.ims.lis.v2.lineitem+json", accept="application/vnd.ims.lis.v2.lineitem+json", ) - if not isinstance(created_lineitem["body"], dict): + if not isinstance(response["body"], dict): raise LtiException("Unknown response type received for create line item") - return LineItem(t.cast(TLineItem, created_lineitem["body"])) + return LineItem(t.cast(TLineItem, response["body"])) def get_grades(self, lineitem: t.Optional[LineItem] = None) -> list: """ diff --git a/pylti1p3/contrib/__init__.py b/src/pylti1p3/contrib/__init__.py similarity index 100% rename from pylti1p3/contrib/__init__.py rename to src/pylti1p3/contrib/__init__.py diff --git a/pylti1p3/contrib/django/__init__.py b/src/pylti1p3/contrib/django/__init__.py similarity index 100% rename from pylti1p3/contrib/django/__init__.py rename to src/pylti1p3/contrib/django/__init__.py diff --git a/pylti1p3/contrib/django/cookie.py b/src/pylti1p3/contrib/django/cookie.py similarity index 100% rename from pylti1p3/contrib/django/cookie.py rename to src/pylti1p3/contrib/django/cookie.py diff --git a/pylti1p3/contrib/django/launch_data_storage/__init__.py b/src/pylti1p3/contrib/django/launch_data_storage/__init__.py similarity index 100% rename from pylti1p3/contrib/django/launch_data_storage/__init__.py rename to src/pylti1p3/contrib/django/launch_data_storage/__init__.py diff --git a/pylti1p3/contrib/django/launch_data_storage/cache.py b/src/pylti1p3/contrib/django/launch_data_storage/cache.py similarity index 100% rename from pylti1p3/contrib/django/launch_data_storage/cache.py rename to src/pylti1p3/contrib/django/launch_data_storage/cache.py diff --git a/pylti1p3/contrib/django/lti1p3_tool_config/__init__.py b/src/pylti1p3/contrib/django/lti1p3_tool_config/__init__.py similarity index 99% rename from pylti1p3/contrib/django/lti1p3_tool_config/__init__.py rename to src/pylti1p3/contrib/django/lti1p3_tool_config/__init__.py index ed5a3bc..0754f45 100644 --- a/pylti1p3/contrib/django/lti1p3_tool_config/__init__.py +++ b/src/pylti1p3/contrib/django/lti1p3_tool_config/__init__.py @@ -1,5 +1,4 @@ import json - from pylti1p3.deployment import Deployment from pylti1p3.exception import LtiException from pylti1p3.registration import Registration diff --git a/pylti1p3/contrib/django/lti1p3_tool_config/admin.py b/src/pylti1p3/contrib/django/lti1p3_tool_config/admin.py similarity index 100% rename from pylti1p3/contrib/django/lti1p3_tool_config/admin.py rename to src/pylti1p3/contrib/django/lti1p3_tool_config/admin.py diff --git a/pylti1p3/contrib/django/lti1p3_tool_config/apps.py b/src/pylti1p3/contrib/django/lti1p3_tool_config/apps.py similarity index 100% rename from pylti1p3/contrib/django/lti1p3_tool_config/apps.py rename to src/pylti1p3/contrib/django/lti1p3_tool_config/apps.py diff --git a/pylti1p3/contrib/django/lti1p3_tool_config/migrations/0001_initial.py b/src/pylti1p3/contrib/django/lti1p3_tool_config/migrations/0001_initial.py similarity index 100% rename from pylti1p3/contrib/django/lti1p3_tool_config/migrations/0001_initial.py rename to src/pylti1p3/contrib/django/lti1p3_tool_config/migrations/0001_initial.py diff --git a/pylti1p3/contrib/django/lti1p3_tool_config/migrations/__init__.py b/src/pylti1p3/contrib/django/lti1p3_tool_config/migrations/__init__.py similarity index 100% rename from pylti1p3/contrib/django/lti1p3_tool_config/migrations/__init__.py rename to src/pylti1p3/contrib/django/lti1p3_tool_config/migrations/__init__.py diff --git a/pylti1p3/contrib/django/lti1p3_tool_config/models.py b/src/pylti1p3/contrib/django/lti1p3_tool_config/models.py similarity index 97% rename from pylti1p3/contrib/django/lti1p3_tool_config/models.py rename to src/pylti1p3/contrib/django/lti1p3_tool_config/models.py index 6e5ff73..617768c 100644 --- a/pylti1p3/contrib/django/lti1p3_tool_config/models.py +++ b/src/pylti1p3/contrib/django/lti1p3_tool_config/models.py @@ -170,9 +170,9 @@ def to_dict(self): "auth_audience": self.auth_audience, "key_set_url": self.key_set_url, "key_set": json.loads(self.key_set) if self.key_set else None, - "deployment_ids": json.loads(self.deployment_ids) - if self.deployment_ids - else [], + "deployment_ids": ( + json.loads(self.deployment_ids) if self.deployment_ids else [] + ), } return data diff --git a/pylti1p3/contrib/django/message_launch.py b/src/pylti1p3/contrib/django/message_launch.py similarity index 78% rename from pylti1p3/contrib/django/message_launch.py rename to src/pylti1p3/contrib/django/message_launch.py index 06c4cd1..c6dda08 100644 --- a/pylti1p3/contrib/django/message_launch.py +++ b/src/pylti1p3/contrib/django/message_launch.py @@ -10,6 +10,7 @@ def __init__( self, request, tool_config, + *, session_service=None, cookie_service=None, launch_data_storage=None, @@ -27,12 +28,12 @@ def __init__( session_service if session_service else DjangoSessionService(request) ) super().__init__( - django_request, - tool_config, - session_service, - cookie_service, - launch_data_storage, - requests_session, + request=django_request, + tool_config=tool_config, + session_service=session_service, + cookie_service=cookie_service, + launch_data_storage=launch_data_storage, + requests_session=requests_session, ) def _get_request_param(self, key): diff --git a/pylti1p3/contrib/django/oidc_login.py b/src/pylti1p3/contrib/django/oidc_login.py similarity index 82% rename from pylti1p3/contrib/django/oidc_login.py rename to src/pylti1p3/contrib/django/oidc_login.py index fd6a828..b75546c 100644 --- a/pylti1p3/contrib/django/oidc_login.py +++ b/src/pylti1p3/contrib/django/oidc_login.py @@ -13,6 +13,7 @@ def __init__( self, request, tool_config, + *, session_service=None, cookie_service=None, launch_data_storage=None, @@ -27,11 +28,11 @@ def __init__( session_service if session_service else DjangoSessionService(request) ) super().__init__( - django_request, - tool_config, - session_service, - cookie_service, - launch_data_storage, + request=django_request, + tool_config=tool_config, + session_service=session_service, + cookie_service=cookie_service, + launch_data_storage=launch_data_storage, ) def get_redirect(self, url): diff --git a/pylti1p3/contrib/django/redirect.py b/src/pylti1p3/contrib/django/redirect.py similarity index 100% rename from pylti1p3/contrib/django/redirect.py rename to src/pylti1p3/contrib/django/redirect.py diff --git a/pylti1p3/contrib/django/request.py b/src/pylti1p3/contrib/django/request.py similarity index 51% rename from pylti1p3/contrib/django/request.py rename to src/pylti1p3/contrib/django/request.py index bc314c3..28763a6 100644 --- a/pylti1p3/contrib/django/request.py +++ b/src/pylti1p3/contrib/django/request.py @@ -2,31 +2,28 @@ class DjangoRequest(Request): - _request = None + request = None _post_only = False _default_params = None @property def session(self): - return self._request.session + return self.request.session def __init__(self, request, post_only=False, default_params=None): - self.set_request(request) + self.request = request self._post_only = post_only self._default_params = default_params if default_params else {} - def set_request(self, request): - self._request = request - def get_param(self, key): if self._post_only: - return self._request.POST.get(key, self._default_params.get(key)) - return self._request.GET.get( - key, self._request.POST.get(key, self._default_params.get(key)) + return self.request.POST.get(key, self._default_params.get(key)) + return self.request.GET.get( + key, self.request.POST.get(key, self._default_params.get(key)) ) def get_cookie(self, key): - return self._request.COOKIES.get(key) + return self.request.COOKIES.get(key) def is_secure(self): - return self._request.is_secure() + return self.request.is_secure() diff --git a/pylti1p3/contrib/django/session.py b/src/pylti1p3/contrib/django/session.py similarity index 100% rename from pylti1p3/contrib/django/session.py rename to src/pylti1p3/contrib/django/session.py diff --git a/pylti1p3/contrib/flask/__init__.py b/src/pylti1p3/contrib/flask/__init__.py similarity index 100% rename from pylti1p3/contrib/flask/__init__.py rename to src/pylti1p3/contrib/flask/__init__.py diff --git a/pylti1p3/contrib/flask/cookie.py b/src/pylti1p3/contrib/flask/cookie.py similarity index 73% rename from pylti1p3/contrib/flask/cookie.py rename to src/pylti1p3/contrib/flask/cookie.py index 775855f..b9973fc 100644 --- a/pylti1p3/contrib/flask/cookie.py +++ b/src/pylti1p3/contrib/flask/cookie.py @@ -20,14 +20,14 @@ def set_cookie(self, name, value, exp=3600): def update_response(self, response): for key, cookie_data in self._cookie_data_to_set.items(): - cookie_kwargs = dict( - key=key, - value=cookie_data["value"], - max_age=cookie_data["exp"], - secure=self._request.is_secure(), - path="/", - httponly=True, - ) + cookie_kwargs = { + "key": key, + "value": cookie_data["value"], + "max_age": cookie_data["exp"], + "secure": self._request.is_secure(), + "path": "/", + "httponly": True, + } if self._request.is_secure(): cookie_kwargs["samesite"] = "None" diff --git a/pylti1p3/contrib/flask/launch_data_storage/__init__.py b/src/pylti1p3/contrib/flask/launch_data_storage/__init__.py similarity index 100% rename from pylti1p3/contrib/flask/launch_data_storage/__init__.py rename to src/pylti1p3/contrib/flask/launch_data_storage/__init__.py diff --git a/pylti1p3/contrib/flask/launch_data_storage/cache.py b/src/pylti1p3/contrib/flask/launch_data_storage/cache.py similarity index 100% rename from pylti1p3/contrib/flask/launch_data_storage/cache.py rename to src/pylti1p3/contrib/flask/launch_data_storage/cache.py diff --git a/pylti1p3/contrib/flask/message_launch.py b/src/pylti1p3/contrib/flask/message_launch.py similarity index 73% rename from pylti1p3/contrib/flask/message_launch.py rename to src/pylti1p3/contrib/flask/message_launch.py index b811995..427a8b0 100644 --- a/pylti1p3/contrib/flask/message_launch.py +++ b/src/pylti1p3/contrib/flask/message_launch.py @@ -8,6 +8,7 @@ def __init__( self, request, tool_config, + *, session_service=None, cookie_service=None, launch_data_storage=None, @@ -20,12 +21,12 @@ def __init__( session_service if session_service else FlaskSessionService(request) ) super().__init__( - request, - tool_config, - session_service, - cookie_service, - launch_data_storage, - requests_session, + request=request, + tool_config=tool_config, + session_service=session_service, + cookie_service=cookie_service, + launch_data_storage=launch_data_storage, + requests_session=requests_session, ) def _get_request_param(self, key): diff --git a/pylti1p3/contrib/flask/oidc_login.py b/src/pylti1p3/contrib/flask/oidc_login.py similarity index 79% rename from pylti1p3/contrib/flask/oidc_login.py rename to src/pylti1p3/contrib/flask/oidc_login.py index b0fee37..f0a2a94 100644 --- a/pylti1p3/contrib/flask/oidc_login.py +++ b/src/pylti1p3/contrib/flask/oidc_login.py @@ -10,6 +10,7 @@ def __init__( self, request, tool_config, + *, session_service=None, cookie_service=None, launch_data_storage=None, @@ -21,7 +22,11 @@ def __init__( session_service if session_service else FlaskSessionService(request) ) super().__init__( - request, tool_config, session_service, cookie_service, launch_data_storage + request=request, + tool_config=tool_config, + session_service=session_service, + cookie_service=cookie_service, + launch_data_storage=launch_data_storage, ) def get_redirect(self, url): diff --git a/pylti1p3/contrib/flask/redirect.py b/src/pylti1p3/contrib/flask/redirect.py similarity index 100% rename from pylti1p3/contrib/flask/redirect.py rename to src/pylti1p3/contrib/flask/redirect.py diff --git a/pylti1p3/contrib/flask/request.py b/src/pylti1p3/contrib/flask/request.py similarity index 100% rename from pylti1p3/contrib/flask/request.py rename to src/pylti1p3/contrib/flask/request.py diff --git a/pylti1p3/contrib/flask/session.py b/src/pylti1p3/contrib/flask/session.py similarity index 100% rename from pylti1p3/contrib/flask/session.py rename to src/pylti1p3/contrib/flask/session.py diff --git a/pylti1p3/contrib/py.typed b/src/pylti1p3/contrib/py.typed similarity index 100% rename from pylti1p3/contrib/py.typed rename to src/pylti1p3/contrib/py.typed diff --git a/pylti1p3/cookie.py b/src/pylti1p3/cookie.py similarity index 100% rename from pylti1p3/cookie.py rename to src/pylti1p3/cookie.py diff --git a/pylti1p3/cookies_allowed_check.py b/src/pylti1p3/cookies_allowed_check.py similarity index 99% rename from pylti1p3/cookies_allowed_check.py rename to src/pylti1p3/cookies_allowed_check.py index 388d924..5b75e0c 100644 --- a/pylti1p3/cookies_allowed_check.py +++ b/src/pylti1p3/cookies_allowed_check.py @@ -18,7 +18,7 @@ def __init__( click_text: str, loading_text: str, *args, - **kwargs + **kwargs, ): # pylint: disable=unused-argument self._params = params diff --git a/pylti1p3/course_groups.py b/src/pylti1p3/course_groups.py similarity index 100% rename from pylti1p3/course_groups.py rename to src/pylti1p3/course_groups.py diff --git a/pylti1p3/deep_link.py b/src/pylti1p3/deep_link.py similarity index 96% rename from pylti1p3/deep_link.py rename to src/pylti1p3/deep_link.py index fdce52c..8a4498a 100644 --- a/pylti1p3/deep_link.py +++ b/src/pylti1p3/deep_link.py @@ -66,6 +66,9 @@ def get_message_jwt( } return message_jwt + def get_accept_types(self) -> t.Sequence[str]: + return self._deep_link_settings.get("accept_types", []) + def encode_jwt(self, message): headers = None kid = self._registration.get_kid() diff --git a/pylti1p3/deep_link_resource.py b/src/pylti1p3/deep_link_resource.py similarity index 90% rename from pylti1p3/deep_link_resource.py rename to src/pylti1p3/deep_link_resource.py index 131606f..9ec6a4b 100644 --- a/pylti1p3/deep_link_resource.py +++ b/src/pylti1p3/deep_link_resource.py @@ -7,6 +7,7 @@ class DeepLinkResource: _title: t.Optional[str] = None _url: t.Optional[str] = None _lineitem: t.Optional[LineItem] = None + _html: t.Optional[str] = None _custom_params: t.Mapping[str, str] = {} _target: str = "iframe" _icon_url: t.Optional[str] = None @@ -60,6 +61,13 @@ def set_icon_url(self, value: str) -> "DeepLinkResource": self._icon_url = value return self + def set_html(self, value: str) -> "DeepLinkResource": + self._html = value + return self + + def get_html(self) -> t.Optional[str]: + return self._html + def to_dict(self) -> t.Dict[str, object]: res: t.Dict[str, object] = { "type": self._type, @@ -90,6 +98,9 @@ def to_dict(self) -> t.Dict[str, object]: res["lineItem"] = line_item + if self._html: + res["html"] = self._html + if self._icon_url: res["icon"] = {"url": self._icon_url} diff --git a/pylti1p3/deployment.py b/src/pylti1p3/deployment.py similarity index 100% rename from pylti1p3/deployment.py rename to src/pylti1p3/deployment.py diff --git a/pylti1p3/exception.py b/src/pylti1p3/exception.py similarity index 100% rename from pylti1p3/exception.py rename to src/pylti1p3/exception.py diff --git a/pylti1p3/grade.py b/src/pylti1p3/grade.py similarity index 100% rename from pylti1p3/grade.py rename to src/pylti1p3/grade.py diff --git a/pylti1p3/launch_data_storage/__init__.py b/src/pylti1p3/launch_data_storage/__init__.py similarity index 100% rename from pylti1p3/launch_data_storage/__init__.py rename to src/pylti1p3/launch_data_storage/__init__.py diff --git a/pylti1p3/launch_data_storage/base.py b/src/pylti1p3/launch_data_storage/base.py similarity index 100% rename from pylti1p3/launch_data_storage/base.py rename to src/pylti1p3/launch_data_storage/base.py diff --git a/pylti1p3/launch_data_storage/cache.py b/src/pylti1p3/launch_data_storage/cache.py similarity index 100% rename from pylti1p3/launch_data_storage/cache.py rename to src/pylti1p3/launch_data_storage/cache.py diff --git a/pylti1p3/launch_data_storage/session.py b/src/pylti1p3/launch_data_storage/session.py similarity index 100% rename from pylti1p3/launch_data_storage/session.py rename to src/pylti1p3/launch_data_storage/session.py diff --git a/pylti1p3/lineitem.py b/src/pylti1p3/lineitem.py similarity index 100% rename from pylti1p3/lineitem.py rename to src/pylti1p3/lineitem.py diff --git a/pylti1p3/message_launch.py b/src/pylti1p3/message_launch.py similarity index 99% rename from pylti1p3/message_launch.py rename to src/pylti1p3/message_launch.py index 4143900..a5f2c04 100644 --- a/pylti1p3/message_launch.py +++ b/src/pylti1p3/message_launch.py @@ -204,6 +204,7 @@ def __init__( self, request: REQ, tool_config: TCONF, + *, session_service: t.Optional[SES] = None, cookie_service: t.Optional[COOK] = None, launch_data_storage: t.Optional[LaunchDataStorage[t.Any]] = None, @@ -279,6 +280,7 @@ def from_cache( launch_id: str, request: REQ, tool_config: TCONF, + *, session_service: t.Optional[SES] = None, cookie_service: t.Optional[COOK] = None, launch_data_storage: t.Optional[LaunchDataStorage[t.Any]] = None, diff --git a/pylti1p3/message_validators/__init__.py b/src/pylti1p3/message_validators/__init__.py similarity index 100% rename from pylti1p3/message_validators/__init__.py rename to src/pylti1p3/message_validators/__init__.py diff --git a/pylti1p3/message_validators/abstract.py b/src/pylti1p3/message_validators/abstract.py similarity index 100% rename from pylti1p3/message_validators/abstract.py rename to src/pylti1p3/message_validators/abstract.py diff --git a/pylti1p3/message_validators/deep_link.py b/src/pylti1p3/message_validators/deep_link.py similarity index 100% rename from pylti1p3/message_validators/deep_link.py rename to src/pylti1p3/message_validators/deep_link.py diff --git a/pylti1p3/message_validators/privacy_launch.py b/src/pylti1p3/message_validators/privacy_launch.py similarity index 100% rename from pylti1p3/message_validators/privacy_launch.py rename to src/pylti1p3/message_validators/privacy_launch.py diff --git a/pylti1p3/message_validators/resource_message.py b/src/pylti1p3/message_validators/resource_message.py similarity index 100% rename from pylti1p3/message_validators/resource_message.py rename to src/pylti1p3/message_validators/resource_message.py diff --git a/pylti1p3/message_validators/submission_review.py b/src/pylti1p3/message_validators/submission_review.py similarity index 100% rename from pylti1p3/message_validators/submission_review.py rename to src/pylti1p3/message_validators/submission_review.py diff --git a/pylti1p3/names_roles.py b/src/pylti1p3/names_roles.py similarity index 100% rename from pylti1p3/names_roles.py rename to src/pylti1p3/names_roles.py diff --git a/pylti1p3/oidc_login.py b/src/pylti1p3/oidc_login.py similarity index 99% rename from pylti1p3/oidc_login.py rename to src/pylti1p3/oidc_login.py index 6d23e01..3496176 100644 --- a/pylti1p3/oidc_login.py +++ b/src/pylti1p3/oidc_login.py @@ -45,6 +45,7 @@ def __init__( self, request: REQ, tool_config: TCONF, + *, session_service: SES, cookie_service: COOK, launch_data_storage: t.Optional[LaunchDataStorage[t.Any]] = None, @@ -204,7 +205,7 @@ def enable_check_cookies( main_msg: t.Optional[str] = None, click_msg: t.Optional[str] = None, loading_msg: t.Optional[str] = None, - **kwargs + **kwargs, ) -> "OIDCLogin": # pylint: disable=unused-argument self._cookies_check = True diff --git a/pylti1p3/py.typed b/src/pylti1p3/py.typed similarity index 100% rename from pylti1p3/py.typed rename to src/pylti1p3/py.typed diff --git a/pylti1p3/redirect.py b/src/pylti1p3/redirect.py similarity index 100% rename from pylti1p3/redirect.py rename to src/pylti1p3/redirect.py diff --git a/pylti1p3/registration.py b/src/pylti1p3/registration.py similarity index 100% rename from pylti1p3/registration.py rename to src/pylti1p3/registration.py diff --git a/pylti1p3/request.py b/src/pylti1p3/request.py similarity index 100% rename from pylti1p3/request.py rename to src/pylti1p3/request.py diff --git a/pylti1p3/roles.py b/src/pylti1p3/roles.py similarity index 100% rename from pylti1p3/roles.py rename to src/pylti1p3/roles.py diff --git a/pylti1p3/service_connector.py b/src/pylti1p3/service_connector.py similarity index 99% rename from pylti1p3/service_connector.py rename to src/pylti1p3/service_connector.py index d50149f..c391039 100644 --- a/pylti1p3/service_connector.py +++ b/src/pylti1p3/service_connector.py @@ -108,6 +108,7 @@ def make_service_request( self, scopes: t.Sequence[str], url: str, + *, is_post: bool = False, data: t.Optional[str] = None, content_type: str = "application/json", diff --git a/pylti1p3/session.py b/src/pylti1p3/session.py similarity index 97% rename from pylti1p3/session.py rename to src/pylti1p3/session.py index e28078b..75a26ab 100644 --- a/pylti1p3/session.py +++ b/src/pylti1p3/session.py @@ -66,5 +66,5 @@ def set_launch_data_lifetime(self, time_sec: int): else: raise Exception( f"{self.data_storage.__class__.__name__} launch storage doesn't support " - f"manual change expiration of the keys" + f"changing the expiration time of keys" ) diff --git a/pylti1p3/tool_config/__init__.py b/src/pylti1p3/tool_config/__init__.py similarity index 100% rename from pylti1p3/tool_config/__init__.py rename to src/pylti1p3/tool_config/__init__.py diff --git a/pylti1p3/tool_config/abstract.py b/src/pylti1p3/tool_config/abstract.py similarity index 95% rename from pylti1p3/tool_config/abstract.py rename to src/pylti1p3/tool_config/abstract.py index e97d07e..84847fc 100644 --- a/pylti1p3/tool_config/abstract.py +++ b/src/pylti1p3/tool_config/abstract.py @@ -39,14 +39,14 @@ def check_iss_has_many_clients(self, iss: str) -> bool: return iss_type == IssuerToClientRelation.MANY_CLIENTS_IDS_PER_ISSUER def set_iss_has_one_client(self, iss: str): - self.issuers_relation_types[ - iss - ] = IssuerToClientRelation.ONE_CLIENT_ID_PER_ISSUER + self.issuers_relation_types[iss] = ( + IssuerToClientRelation.ONE_CLIENT_ID_PER_ISSUER + ) def set_iss_has_many_clients(self, iss: str): - self.issuers_relation_types[ - iss - ] = IssuerToClientRelation.MANY_CLIENTS_IDS_PER_ISSUER + self.issuers_relation_types[iss] = ( + IssuerToClientRelation.MANY_CLIENTS_IDS_PER_ISSUER + ) def find_registration(self, iss: str, *args, **kwargs) -> Registration: """ diff --git a/pylti1p3/tool_config/dict.py b/src/pylti1p3/tool_config/dict.py similarity index 100% rename from pylti1p3/tool_config/dict.py rename to src/pylti1p3/tool_config/dict.py diff --git a/pylti1p3/tool_config/json_file.py b/src/pylti1p3/tool_config/json_file.py similarity index 100% rename from pylti1p3/tool_config/json_file.py rename to src/pylti1p3/tool_config/json_file.py diff --git a/pylti1p3/tool_config/py.typed b/src/pylti1p3/tool_config/py.typed similarity index 100% rename from pylti1p3/tool_config/py.typed rename to src/pylti1p3/tool_config/py.typed diff --git a/pylti1p3/utils.py b/src/pylti1p3/utils.py similarity index 100% rename from pylti1p3/utils.py rename to src/pylti1p3/utils.py diff --git a/tests/base.py b/tests/base.py index f9ea55d..5456f75 100644 --- a/tests/base.py +++ b/tests/base.py @@ -20,6 +20,7 @@ def _launch( self, request, tool_conf, + *, key_set_url_response=None, force_validation=False, cache=False, diff --git a/tests/django_mixin.py b/tests/django_mixin.py index aa6d7c9..7fba0a7 100644 --- a/tests/django_mixin.py +++ b/tests/django_mixin.py @@ -10,6 +10,7 @@ class DjangoMixin: def _get_request( self, + *, login_request, login_response, request_is_secure=False, @@ -29,6 +30,7 @@ def _get_request( def _make_oidc_login( self, + *, uuid_val=None, tool_conf_cls=None, secure=False, diff --git a/tests/flask_mixin.py b/tests/flask_mixin.py index b2bb514..7bd3beb 100644 --- a/tests/flask_mixin.py +++ b/tests/flask_mixin.py @@ -18,6 +18,7 @@ def _get_request( self, login_request, login_response, + *, request_is_secure=False, post_data=None, empty_session=False, @@ -37,6 +38,7 @@ def _get_request( def _make_oidc_login( self, + *, uuid_val=None, tool_conf_cls=None, secure=False, diff --git a/tests/request.py b/tests/request.py index a2e1076..e5bdaaa 100644 --- a/tests/request.py +++ b/tests/request.py @@ -8,7 +8,9 @@ class FakeRequest: session = None secure = False - def __init__(self, get=None, post=None, cookies=None, session=None, secure=False): + def __init__( + self, *, get=None, post=None, cookies=None, session=None, secure=False + ): self.GET = get if get else {} self.POST = post if post else {} self.COOKIES = cookies if cookies else {} diff --git a/tests/test_grades.py b/tests/test_grades.py index e83a0d4..a16fae1 100644 --- a/tests/test_grades.py +++ b/tests/test_grades.py @@ -91,7 +91,7 @@ def test_get_grades( line_item = ags.find_or_create_lineitem(score_line_item) self.assertIsNotNone(line_item) - scores = ags.get_grades(line_item) + scores = list(ags.get_grades(line_item)) self.assertEqual(len(scores), 1) self.assertDictEqual( scores[0], diff --git a/tests/test_resource_link.py b/tests/test_resource_link.py index cb669c4..e402e78 100644 --- a/tests/test_resource_link.py +++ b/tests/test_resource_link.py @@ -176,6 +176,7 @@ class ResourceLinkBase(TestLinkBase): def _launch_success( self, + *, tool_conf_cls=None, secure=False, tool_conf_extended=False, @@ -204,9 +205,11 @@ def _launch_success( ] ) def test_res_link_launch_success( - self, name, secure, tool_conf_extended # pylint: disable=unused-argument - ): - self._launch_success(None, secure, tool_conf_extended) + self, name, secure, tool_conf_extended + ): # pylint: disable=unused-argument,too-many-positional-arguments,too-many-function-args + self._launch_success( + tool_conf_cls=None, secure=secure, tool_conf_extended=tool_conf_extended + ) def test_res_link_check_cookies_page(self): self._launch_success(enable_check_cookies=True) @@ -219,7 +222,9 @@ def test_res_link_launch_invalid_public_key(self): launch_request = self._get_request(login_request, login_response) with self.assertRaisesRegex(LtiException, "Invalid response"): - self._launch(launch_request, tool_conf, "invalid_key_set") + self._launch( + launch_request, tool_conf, key_set_url_response="invalid_key_set" + ) def test_res_link_launch_invalid_state(self): tool_conf, login_request, login_response = self._make_oidc_login() @@ -293,9 +298,9 @@ def _get_data_with_invalid_deployment( def _get_data_with_invalid_message(self, *args): # pylint: disable=unused-argument message_launch_data = self.expected_message_launch_data.copy() - message_launch_data[ - "https://purl.imsglobal.org/spec/lti/claim/version" - ] = "1.2.0" + message_launch_data["https://purl.imsglobal.org/spec/lti/claim/version"] = ( + "1.2.0" + ) return message_launch_data def test_res_link_launch_invalid_nonce(self): diff --git a/tox.ini b/tox.ini index 51058bd..3e066eb 100644 --- a/tox.ini +++ b/tox.ini @@ -5,8 +5,8 @@ envlist = py36, py37, py38, py39, py310, py311 commands = flake8 . pylint --rcfile=pylintrc pylti1p3 tests - mypy pylti1p3 - black . --check --diff + mypy src + black src --check --diff --extend-exclude ".*/migrations/.*" coverage run -m unittest -v tests coverage report -m deps = @@ -24,3 +24,5 @@ deps = requests requests-mock types-requests + django-types + pylint-django