From 344cc1c66acde209344e77ff7cd6ecc57fd78057 Mon Sep 17 00:00:00 2001 From: Oleh Paduchak Date: Tue, 6 Feb 2024 13:42:10 +0200 Subject: [PATCH 001/274] updated base image to python 3.10 --- Dockerfile | 39 +++++++++++++++++++++------------------ requirements.txt | 6 +++--- 2 files changed, 24 insertions(+), 21 deletions(-) diff --git a/Dockerfile b/Dockerfile index ace0492965a..b7e634ea994 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,8 +1,7 @@ -FROM node:8-alpine3.9 +FROM node:18-alpine3.17 # Source: https://github.com/docker-library/httpd/blob/7976cabe162268bd5ad2d233d61e340447bfc371/2.4/alpine/Dockerfile#L3 RUN set -x \ - && addgroup -g 82 -S www-data \ && adduser -h /var/www -u 82 -D -S -G www-data www-data RUN apk add --no-cache --virtual .run-deps \ @@ -26,8 +25,9 @@ RUN apk add --no-cache --virtual .run-deps \ libevent \ && yarn global add bower +# Setuptools lower than 58.0.0 are needed for rdflib-jsonld on python 3.10 RUN python3 -m ensurepip && \ - pip3 install --upgrade pip==21.0 + pip3 install --upgrade pip==21.0 "setuptools<58.0.0" WORKDIR /code @@ -78,7 +78,7 @@ RUN set -ex \ && (pip3 uninstall uritemplate.py --yes || true) \ && pip3 install --no-cache-dir uritemplate.py==0.3.0 \ # Fix: https://github.com/CenterForOpenScience/osf.io/pull/6783 - && python3 -m compileall /usr/lib/python3.6 || true \ + && python3 -m compileall /usr/lib/python3.10 || true \ && apk del .build-deps # Settings @@ -156,19 +156,22 @@ ENV GIT_COMMIT ${GIT_COMMIT} # TODO: Admin/API should fully specify their bower static deps, and not include ./website/static in their defaults.py. # (this adds an additional 300+mb to the build image) -RUN for module in \ - api.base.settings \ - admin.base.settings \ - ; do \ - export DJANGO_SETTINGS_MODULE=$module \ - && python3 manage.py collectstatic --noinput --no-init-app \ - ; done \ - && for file in \ - ./website/templates/_log_templates.mako \ - ./website/static/built/nodeCategories.json \ - ; do \ - touch $file && chmod o+w $file \ - ; done \ - && rm ./website/settings/local.py ./api/base/settings/local.py + +# TODO: Uncomment following RUN when python dependencies are ready + +# RUN for module in \ +# api.base.settings \ +# admin.base.settings \ +# ; do \ +# export DJANGO_SETTINGS_MODULE=$module \ +# && python3 manage.py collectstatic --noinput --no-init-app \ +# ; done \ +# && for file in \ +# ./website/templates/_log_templates.mako \ +# ./website/static/built/nodeCategories.json \ +# ; do \ +# touch $file && chmod o+w $file \ +# ; done \ +# && rm ./website/settings/local.py ./api/base/settings/local.py CMD ["su-exec", "nobody", "invoke", "--list"] diff --git a/requirements.txt b/requirements.txt index 84747393ec6..46fca40d0a3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,12 +4,12 @@ # To install addon requirements: inv requirements --addons # To install dev requirements: inv requirements --dev # To install release requirements: inv requirements --release - +setuptools<58.0.0 future==0.18.2 invoke==0.15.0 Werkzeug==1.0.0 Flask==1.0 -gevent==1.2.2 +gevent==21.12.0 Mako==1.0.7 Markdown==3.3.7 WTForms==1.0.4 @@ -82,7 +82,7 @@ django-storages==1.6.6 google-cloud-storage==0.22.0 # dependency of django-storages, hard-pin to version django-dirtyfields==1.3.1 django-extensions==3.2.0 -psycopg2==2.7.3 --no-binary psycopg2 +psycopg2==2.8.4 --no-binary psycopg2 django-bulk-update==2.2.0 # Reviews requirements From 4fdf6ae86bfb135db68b753569a74c6f46cb9d3e Mon Sep 17 00:00:00 2001 From: Oleh Paduchak Date: Tue, 6 Feb 2024 18:01:23 +0200 Subject: [PATCH 002/274] stated the reasoning behind not creating www-data group --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index b7e634ea994..5eab4efe371 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ FROM node:18-alpine3.17 -# Source: https://github.com/docker-library/httpd/blob/7976cabe162268bd5ad2d233d61e340447bfc371/2.4/alpine/Dockerfile#L3 +# Creation of www-data group was removed as it is created by default in alpine 3.14 and higher RUN set -x \ && adduser -h /var/www -u 82 -D -S -G www-data www-data From 631bef012454e4ed6486adc8ed3da34a31e9ddf0 Mon Sep 17 00:00:00 2001 From: Fitz Elliott Date: Tue, 6 Feb 2024 11:56:28 -0500 Subject: [PATCH 003/274] a little more docs about alpine & www-data --- Dockerfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Dockerfile b/Dockerfile index 5eab4efe371..77ec33363f9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,8 @@ FROM node:18-alpine3.17 # Creation of www-data group was removed as it is created by default in alpine 3.14 and higher +# Alpine does not create a www-data user, so we still need to create that. 82 is the standard +# uid/guid for www-data in Alpine. RUN set -x \ && adduser -h /var/www -u 82 -D -S -G www-data www-data From cee4034aa3bbbbbd2ddadb1d4f4f1615ff3992ab Mon Sep 17 00:00:00 2001 From: Oleh Paduchak Date: Wed, 7 Feb 2024 14:32:02 +0200 Subject: [PATCH 004/274] fixed all issues (errors and wornings) from compilation to bytecode on python 3.10 --- addons/owncloud/models.py | 2 +- addons/zotero/provider.py | 2 +- api_tests/registrations/views/test_registration_list.py | 4 ++-- scripts/remove_qa_test_items_from_elastic_and_share.py | 2 +- scripts/tests/test_user_system_tag_normalization.py | 4 ++-- website/citations/providers.py | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/addons/owncloud/models.py b/addons/owncloud/models.py index d39580c5da2..e90409687f3 100644 --- a/addons/owncloud/models.py +++ b/addons/owncloud/models.py @@ -172,7 +172,7 @@ def get_folders(self, **kwargs): ret = [] for item in c.list(path): - if item.file_type is 'dir': + if item.file_type == 'dir': ret.append({ 'addon': 'owncloud', 'path': item.path, diff --git a/addons/zotero/provider.py b/addons/zotero/provider.py index 6b1cc954d70..7921dad1fef 100644 --- a/addons/zotero/provider.py +++ b/addons/zotero/provider.py @@ -101,7 +101,7 @@ def citation_list(self, node_addon, user, list_id, show='all'): ancestor_id = folders[list_id].get('parent_list_id') while ancestor_id != attached_list_id: - if ancestor_id is '__': + if ancestor_id == '__': raise HTTPError(http_status.HTTP_403_FORBIDDEN) ancestor_id = folders[ancestor_id].get('parent_list_id') diff --git a/api_tests/registrations/views/test_registration_list.py b/api_tests/registrations/views/test_registration_list.py index bac1cf78e2f..997577dac76 100644 --- a/api_tests/registrations/views/test_registration_list.py +++ b/api_tests/registrations/views/test_registration_list.py @@ -1570,7 +1570,7 @@ def test_need_admin_perms_on_draft( payload_ver['data']['attributes']['draft_registration_id'] = draft_registration._id assert draft_registration.branched_from.is_admin_contributor(user) is False assert draft_registration.has_permission(user, permissions.ADMIN) is True - assert draft_registration.title is '' + assert draft_registration.title == '' draft_registration.title = 'test user generated title required' draft_registration.save() res = app.post_json_api(url_registrations_ver, payload_ver, auth=user.auth) @@ -1581,7 +1581,7 @@ def test_need_admin_perms_on_draft( assert draft_registration.branched_from.is_admin_contributor(user) is True assert draft_registration.has_permission(user, permissions.ADMIN) is True payload_ver['data']['attributes']['draft_registration_id'] = draft_registration._id - assert draft_registration.title is '' + assert draft_registration.title == '' draft_registration.title = 'test user generated title required' draft_registration.save() res = app.post_json_api(url_registrations_ver, payload_ver, auth=user.auth) diff --git a/scripts/remove_qa_test_items_from_elastic_and_share.py b/scripts/remove_qa_test_items_from_elastic_and_share.py index 123d4cc5f1a..c81796958eb 100644 --- a/scripts/remove_qa_test_items_from_elastic_and_share.py +++ b/scripts/remove_qa_test_items_from_elastic_and_share.py @@ -34,7 +34,7 @@ def remove_search_index(dry_run=True): logger.info('Removing {} with title \'{}\' from search index and SHARE.'.format(node._id, node.title)) else: for node in nodes: - update_node(node, bulk=False, async=True) + update_node(node, bulk=False) update_share(node) if __name__ == '__main__': diff --git a/scripts/tests/test_user_system_tag_normalization.py b/scripts/tests/test_user_system_tag_normalization.py index 968d45d9e42..689bffc72e0 100644 --- a/scripts/tests/test_user_system_tag_normalization.py +++ b/scripts/tests/test_user_system_tag_normalization.py @@ -26,14 +26,14 @@ def legacy_system_tags(self): @pytest.fixture() def prereg_challenge_user_created_before_cutoff(self): user = UserFactory() - user.date_registered = pytz.utc.localize(datetime(1998, 12, 01, 04, 48)) + user.date_registered = pytz.utc.localize(datetime(1998, 12, 1, 4, 48)) user.save() return user @pytest.fixture() def prereg_challenge_user_created_after_cutoff(self): user = UserFactory() - user.date_registered = pytz.utc.localize(datetime(2019, 12, 01, 04, 48)) + user.date_registered = pytz.utc.localize(datetime(2019, 12, 1, 4, 48)) user.save() return user diff --git a/website/citations/providers.py b/website/citations/providers.py index 786572302ea..110a00a49f4 100644 --- a/website/citations/providers.py +++ b/website/citations/providers.py @@ -221,7 +221,7 @@ def citation_list(self, node_addon, user, list_id, show='all'): ancestor_id = folders[list_id].get('parent_list_id') while ancestor_id != attached_list_id: - if ancestor_id is '__': + if ancestor_id == '__': raise HTTPError(http_status.HTTP_403_FORBIDDEN) ancestor_id = folders[ancestor_id].get('parent_list_id') From 162c91c098c4d52052045e650314084c084310ba Mon Sep 17 00:00:00 2001 From: Oleh Paduchak Date: Wed, 7 Feb 2024 16:07:55 +0200 Subject: [PATCH 005/274] bumped pycopg2 to 2.8.4 in start-build action and travis.yml --- .github/actions/start-build/action.yml | 2 +- .travis.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/actions/start-build/action.yml b/.github/actions/start-build/action.yml index 46dffc474c9..873ee2b46fa 100644 --- a/.github/actions/start-build/action.yml +++ b/.github/actions/start-build/action.yml @@ -21,7 +21,7 @@ runs: - name: Other installs shell: bash run: | - pip install psycopg2==2.7.3 --no-binary psycopg2 + pip install psycopg2==2.8.4 --no-binary psycopg2 invoke travis_addon_settings invoke requirements --dev --addons pip uninstall uritemplate.py --yes diff --git a/.travis.yml b/.travis.yml index c769b34141f..c4bfe2112d7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -145,7 +145,7 @@ install: fi - travis_retry invoke travis_addon_settings - - travis_retry pip install psycopg2==2.7.3 --no-binary psycopg2 + - travis_retry pip install psycopg2==2.8.4 --no-binary psycopg2 - travis_retry invoke requirements --dev --addons # Hack to fix package conflict between uritemplate and uritemplate.py (dependency of github3.py) - pip uninstall uritemplate.py --yes From f7495a1d66ded9e37e9d1fc21692d39807f20372 Mon Sep 17 00:00:00 2001 From: Oleh Paduchak Date: Wed, 7 Feb 2024 16:47:59 +0200 Subject: [PATCH 006/274] added comments to explain reasoning behind bumping psycopg2 --- .github/actions/start-build/action.yml | 1 + .travis.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/actions/start-build/action.yml b/.github/actions/start-build/action.yml index 873ee2b46fa..20c35761a22 100644 --- a/.github/actions/start-build/action.yml +++ b/.github/actions/start-build/action.yml @@ -21,6 +21,7 @@ runs: - name: Other installs shell: bash run: | + # bumped psycopg to match requirements.txt, as otherwise build would fail pip install psycopg2==2.8.4 --no-binary psycopg2 invoke travis_addon_settings invoke requirements --dev --addons diff --git a/.travis.yml b/.travis.yml index c4bfe2112d7..93e36dee1a2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -145,6 +145,7 @@ install: fi - travis_retry invoke travis_addon_settings + # bumped psycopg to match requirements.txt, as otherwise build would fail - travis_retry pip install psycopg2==2.8.4 --no-binary psycopg2 - travis_retry invoke requirements --dev --addons # Hack to fix package conflict between uritemplate and uritemplate.py (dependency of github3.py) From de3fb6bd770d7ca5bf4d4ffd2a0ed8f462ae9f1a Mon Sep 17 00:00:00 2001 From: Oleh Paduchak Date: Thu, 8 Feb 2024 19:25:44 +0200 Subject: [PATCH 007/274] upgraded code to use more modern syntax --- addons/base/apps.py | 10 +- addons/base/generic_views.py | 14 +- addons/base/logger.py | 4 +- addons/base/models.py | 68 ++-- addons/base/serializer.py | 22 +- addons/base/tests/base.py | 12 +- addons/base/tests/logger.py | 12 +- addons/base/tests/models.py | 20 +- addons/base/tests/serializers.py | 6 +- addons/base/tests/utils.py | 4 +- addons/base/tests/views.py | 52 +-- addons/base/utils.py | 10 +- addons/base/views.py | 30 +- addons/bitbucket/api.py | 2 +- addons/bitbucket/apps.py | 2 +- addons/bitbucket/migrations/0001_initial.py | 2 - .../migrations/0002_auto_20220817_1915.py | 2 - addons/bitbucket/models.py | 30 +- addons/bitbucket/routes.py | 2 - addons/bitbucket/serializer.py | 2 +- addons/bitbucket/tests/factories.py | 6 +- addons/bitbucket/tests/test_models.py | 10 +- addons/bitbucket/tests/test_serializer.py | 3 +- addons/bitbucket/tests/test_views.py | 21 +- addons/bitbucket/tests/utils.py | 10 +- addons/bitbucket/views.py | 1 - addons/boa/models.py | 4 +- addons/boa/serializer.py | 6 +- addons/boa/tests/factories.py | 6 +- addons/boa/tests/test_serializer.py | 6 +- addons/boa/tests/test_tasks.py | 16 +- addons/boa/tests/test_views.py | 26 +- addons/boa/tests/utils.py | 8 +- addons/boa/views.py | 2 +- addons/box/migrations/0001_initial.py | 2 - .../box/migrations/0002_auto_20220817_1915.py | 2 - addons/box/models.py | 10 +- addons/box/routes.py | 1 - addons/box/settings/local-dist.py | 1 - addons/box/tests/factories.py | 4 +- addons/box/tests/test_client.py | 3 +- addons/box/tests/test_models.py | 10 +- addons/box/tests/test_serializer.py | 7 +- addons/box/tests/test_views.py | 17 +- addons/box/tests/utils.py | 103 +++--- addons/box/views.py | 1 - addons/dataverse/migrations/0001_initial.py | 2 - .../migrations/0002_auto_20220817_1915.py | 2 - addons/dataverse/models.py | 9 +- addons/dataverse/serializer.py | 10 +- addons/dataverse/tests/factories.py | 5 +- addons/dataverse/tests/test_client.py | 4 +- addons/dataverse/tests/test_logger.py | 5 +- addons/dataverse/tests/test_model.py | 6 +- addons/dataverse/tests/test_serializer.py | 9 +- addons/dataverse/tests/test_utils.py | 6 +- addons/dataverse/tests/test_views.py | 13 +- addons/dataverse/tests/utils.py | 34 +- addons/dataverse/views.py | 3 +- addons/dropbox/migrations/0001_initial.py | 2 - .../migrations/0002_auto_20220817_1915.py | 2 - addons/dropbox/models.py | 12 +- addons/dropbox/routes.py | 1 - addons/dropbox/settings/local-dist.py | 1 - addons/dropbox/tests/factories.py | 4 +- addons/dropbox/tests/test_client.py | 2 +- addons/dropbox/tests/test_models.py | 4 +- addons/dropbox/tests/test_views.py | 12 +- addons/dropbox/tests/utils.py | 16 +- addons/dropbox/views.py | 1 - addons/figshare/client.py | 2 +- addons/figshare/migrations/0001_initial.py | 2 - .../migrations/0002_auto_20220817_1915.py | 2 - addons/figshare/models.py | 12 +- addons/figshare/routes.py | 1 - addons/figshare/tests/factories.py | 5 +- addons/figshare/tests/test_models.py | 4 +- addons/figshare/tests/test_serializer.py | 5 +- addons/figshare/tests/test_views.py | 11 +- addons/figshare/tests/utils.py | 8 +- addons/figshare/views.py | 2 - addons/forward/migrations/0001_initial.py | 2 - .../migrations/0002_nodesettings_owner.py | 2 - addons/forward/models.py | 3 +- addons/forward/routes.py | 1 - addons/forward/settings/defaults.py | 1 - addons/forward/settings/local-dist.py | 1 - addons/forward/tests/factories.py | 1 - addons/forward/tests/test_models.py | 5 +- addons/forward/tests/test_utils.py | 1 - addons/forward/tests/test_views.py | 4 +- addons/forward/tests/utils.py | 1 - addons/forward/views/config.py | 1 - addons/github/api.py | 2 +- addons/github/apps.py | 2 +- addons/github/migrations/0001_initial.py | 2 - .../migrations/0002_auto_20220817_1915.py | 2 - addons/github/models.py | 30 +- addons/github/routes.py | 2 - addons/github/serializer.py | 2 +- addons/github/tests/factories.py | 5 +- addons/github/tests/test_models.py | 12 +- addons/github/tests/test_serializer.py | 7 +- addons/github/tests/test_utils.py | 2 +- addons/github/tests/test_views.py | 37 ++- addons/github/tests/utils.py | 192 +++++------ addons/github/utils.py | 10 +- addons/github/views.py | 5 +- addons/gitlab/api.py | 8 +- addons/gitlab/apps.py | 4 +- addons/gitlab/migrations/0001_initial.py | 2 - .../migrations/0002_auto_20220817_1915.py | 2 - addons/gitlab/models.py | 35 +- addons/gitlab/routes.py | 2 - addons/gitlab/serializer.py | 4 +- addons/gitlab/tests/factories.py | 6 +- addons/gitlab/tests/test_models.py | 12 +- addons/gitlab/tests/test_serializer.py | 7 +- addons/gitlab/tests/test_utils.py | 2 +- addons/gitlab/tests/test_views.py | 37 ++- addons/gitlab/tests/utils.py | 132 ++++---- addons/gitlab/utils.py | 10 +- addons/gitlab/views.py | 5 +- addons/googledrive/client.py | 5 +- addons/googledrive/migrations/0001_initial.py | 2 - .../migrations/0002_auto_20220817_1915.py | 2 - addons/googledrive/models.py | 5 +- addons/googledrive/routes.py | 1 - addons/googledrive/serializer.py | 2 +- addons/googledrive/tests/factories.py | 7 +- addons/googledrive/tests/test_models.py | 11 +- addons/googledrive/tests/test_serializer.py | 3 +- addons/googledrive/tests/test_views.py | 9 +- addons/googledrive/tests/utils.py | 6 +- addons/googledrive/utils.py | 5 +- addons/googledrive/views.py | 1 - addons/mendeley/api.py | 2 +- addons/mendeley/migrations/0001_initial.py | 2 - .../migrations/0002_auto_20220817_1915.py | 2 - addons/mendeley/models.py | 2 - addons/mendeley/provider.py | 1 - addons/mendeley/routes.py | 2 - addons/mendeley/settings/local-dist.py | 1 - addons/mendeley/tests/factories.py | 7 +- addons/mendeley/tests/test_api.py | 4 +- addons/mendeley/tests/test_models.py | 3 +- addons/mendeley/tests/test_serializer.py | 1 - addons/mendeley/tests/test_views.py | 5 +- addons/mendeley/tests/utils.py | 4 +- addons/mendeley/views.py | 1 - addons/onedrive/client.py | 11 +- addons/onedrive/migrations/0001_initial.py | 2 - .../migrations/0002_auto_20220817_1915.py | 2 - addons/onedrive/models.py | 9 +- addons/onedrive/routes.py | 1 - addons/onedrive/settings/local-dist.py | 1 - addons/onedrive/tests/factories.py | 7 +- addons/onedrive/tests/test_client.py | 5 +- addons/onedrive/tests/test_models.py | 15 +- addons/onedrive/tests/test_serializer.py | 7 +- addons/onedrive/tests/test_views.py | 7 +- addons/onedrive/tests/utils.py | 6 +- addons/onedrive/views.py | 1 - addons/osfstorage/decorators.py | 2 +- addons/osfstorage/migrations/0001_initial.py | 2 - .../migrations/0002_auto_20220817_1915.py | 4 +- addons/osfstorage/models.py | 28 +- addons/osfstorage/routes.py | 2 - addons/osfstorage/settings/defaults.py | 5 +- addons/osfstorage/tests/factories.py | 1 - addons/osfstorage/tests/test_models.py | 130 ++++---- addons/osfstorage/tests/test_utils.py | 3 +- addons/osfstorage/tests/test_views.py | 39 ++- addons/osfstorage/tests/utils.py | 7 +- addons/osfstorage/utils.py | 7 +- addons/osfstorage/views.py | 6 +- addons/owncloud/migrations/0001_initial.py | 2 - .../migrations/0002_auto_20220817_1915.py | 2 - addons/owncloud/models.py | 11 +- addons/owncloud/routes.py | 1 - addons/owncloud/serializer.py | 6 +- addons/owncloud/tests/factories.py | 7 +- addons/owncloud/tests/test_serializer.py | 6 +- addons/owncloud/tests/test_views.py | 12 +- addons/owncloud/tests/utils.py | 8 +- addons/owncloud/views.py | 3 +- addons/s3/migrations/0001_initial.py | 2 - .../s3/migrations/0002_auto_20220817_1915.py | 2 - addons/s3/models.py | 10 +- addons/s3/provider.py | 4 +- addons/s3/tests/factories.py | 7 +- addons/s3/tests/test_model.py | 4 +- addons/s3/tests/test_serializer.py | 7 +- addons/s3/tests/test_view.py | 19 +- addons/s3/tests/utils.py | 1 - addons/s3/views.py | 2 +- addons/twofactor/migrations/0001_initial.py | 2 - .../migrations/0002_usersettings_owner.py | 2 - addons/twofactor/models.py | 6 +- addons/twofactor/tests/test_models.py | 8 +- addons/twofactor/tests/test_utils.py | 2 +- addons/twofactor/tests/test_views.py | 2 +- addons/twofactor/views.py | 1 - addons/wiki/migrations/0001_initial.py | 2 - .../migrations/0002_auto_20220817_1915.py | 2 - addons/wiki/models.py | 25 +- addons/wiki/settings/defaults.py | 4 +- addons/wiki/tests/test_models.py | 8 +- addons/wiki/tests/test_views.py | 11 +- addons/wiki/tests/test_wiki.py | 47 ++- addons/wiki/utils.py | 3 +- addons/wiki/views.py | 4 +- addons/zotero/migrations/0001_initial.py | 2 - .../migrations/0002_auto_20220817_1915.py | 2 - addons/zotero/models.py | 4 +- addons/zotero/provider.py | 7 +- addons/zotero/routes.py | 2 - addons/zotero/serializer.py | 6 +- addons/zotero/settings/local-dist.py | 1 - addons/zotero/tests/factories.py | 7 +- addons/zotero/tests/test_models.py | 9 +- addons/zotero/tests/test_serializer.py | 1 - addons/zotero/tests/test_views.py | 9 +- addons/zotero/tests/utils.py | 3 +- addons/zotero/views.py | 5 +- admin/banners/forms.py | 4 +- admin/banners/views.py | 6 +- admin/base/apps.py | 2 - admin/base/db/router.py | 2 +- admin/base/forms.py | 2 +- admin/base/settings/__init__.py | 1 - admin/base/utils.py | 2 +- admin/base/views.py | 2 - admin/brands/views.py | 4 +- admin/collection_providers/forms.py | 36 +-- admin/collection_providers/views.py | 36 +-- admin/comments/views.py | 2 - admin/common_auth/admin.py | 12 +- admin/common_auth/forms.py | 6 +- admin/common_auth/urls.py | 2 - admin/common_auth/views.py | 18 +- admin/institution_asset_files/forms.py | 4 +- admin/institution_asset_files/views.py | 2 +- admin/institutions/forms.py | 2 +- admin/institutions/views.py | 32 +- admin/maintenance/views.py | 4 +- admin/management/urls.py | 2 - admin/meetings/forms.py | 4 +- admin/meetings/urls.py | 2 - admin/meetings/views.py | 15 +- admin/metrics/apps.py | 2 - admin/metrics/views.py | 4 +- admin/osf_groups/views.py | 6 +- admin/preprint_providers/forms.py | 14 +- admin/preprint_providers/views.py | 44 ++- admin/provider_asset_files/forms.py | 4 +- admin/provider_asset_files/views.py | 2 +- admin/registration_providers/forms.py | 8 +- admin/registration_providers/views.py | 38 +-- admin/subjects/urls.py | 2 - admin/subjects/views.py | 4 +- admin/tasks.py | 12 +- admin/users/views.py | 2 - admin_tests/base/test_forms.py | 4 +- admin_tests/base/test_utils.py | 6 +- admin_tests/comments/test_views.py | 4 +- admin_tests/common_auth/test_views.py | 4 +- admin_tests/institutions/test_views.py | 14 +- admin_tests/meetings/test_forms.py | 2 +- admin_tests/meetings/test_serializers.py | 2 +- admin_tests/meetings/test_views.py | 6 +- admin_tests/mixins/providers.py | 8 +- admin_tests/nodes/test_views.py | 22 +- admin_tests/osf_groups/test_views.py | 6 +- admin_tests/preprint_providers/test_views.py | 12 +- admin_tests/preprints/test_views.py | 6 +- .../registration_providers/test_views.py | 2 +- admin_tests/users/test_views.py | 30 +- api/actions/permissions.py | 5 +- api/actions/serializers.py | 11 +- api/actions/views.py | 3 - api/addons/forward/test_views.py | 10 +- api/addons/views.py | 4 +- api/applications/views.py | 2 +- api/banners/serializers.py | 2 +- api/base/authentication/drf.py | 4 +- api/base/content_negotiation.py | 3 +- api/base/exceptions.py | 30 +- api/base/filters.py | 14 +- api/base/generic_bulk_views.py | 12 +- api/base/metrics.py | 12 +- api/base/middleware.py | 2 +- api/base/pagination.py | 22 +- api/base/parsers.py | 20 +- api/base/permissions.py | 4 +- api/base/renderers.py | 4 +- api/base/requests.py | 2 +- api/base/schemas/utils.py | 2 +- api/base/serializers.py | 86 ++--- api/base/settings/__init__.py | 3 +- api/base/storage.py | 4 +- api/base/throttling.py | 12 +- api/base/utils.py | 5 +- api/base/versioning.py | 8 +- api/base/views.py | 32 +- api/base/wsgi.py | 2 +- api/brands/views.py | 2 +- api/caching/tasks.py | 2 +- api/caching/tests/test_caching.py | 12 +- api/chronos/permissions.py | 1 - api/chronos/views.py | 2 - api/citations/utils.py | 4 +- api/collection_submissions/permissions.py | 3 - api/collections/permissions.py | 6 +- api/collections/serializers.py | 8 +- api/collections/views.py | 10 +- api/comments/permissions.py | 7 +- api/comments/serializers.py | 8 +- api/comments/views.py | 2 +- api/crossref/permissions.py | 1 - api/crossref/views.py | 6 +- api/draft_nodes/views.py | 2 +- api/draft_registrations/serializers.py | 4 +- api/draft_registrations/views.py | 2 +- api/files/permissions.py | 6 +- api/files/serializers.py | 10 +- api/files/views.py | 2 +- api/guids/serializers.py | 6 +- api/guids/views.py | 2 +- api/identifiers/serializers.py | 2 +- api/institutions/permissions.py | 1 - api/institutions/serializers.py | 2 +- api/institutions/views.py | 10 +- api/logs/permissions.py | 3 +- api/logs/views.py | 2 +- api/meetings/serializers.py | 2 +- api/meetings/views.py | 15 +- api/metrics/utils.py | 2 +- api/metrics/views.py | 2 +- api/nodes/filters.py | 6 +- api/nodes/permissions.py | 7 +- api/nodes/serializers.py | 36 +-- api/nodes/utils.py | 5 +- api/nodes/views.py | 54 ++-- api/osf_groups/serializers.py | 4 +- api/osf_groups/views.py | 12 +- api/preprints/permissions.py | 9 +- api/preprints/serializers.py | 4 +- api/preprints/views.py | 8 +- api/providers/permissions.py | 3 - api/providers/serializers.py | 2 +- api/providers/views.py | 18 +- api/providers/workflows.py | 2 - api/regions/urls.py | 1 - api/regions/views.py | 2 +- api/registrations/permissions.py | 1 - api/registrations/serializers.py | 2 +- api/requests/permissions.py | 7 +- api/requests/serializers.py | 4 +- api/requests/views.py | 4 +- api/schema_responses/schemas.py | 1 - api/scopes/permissions.py | 2 +- api/search/permissions.py | 2 +- api/search/views.py | 6 +- api/share/utils.py | 7 +- api/sparse/views.py | 2 +- api/subjects/serializers.py | 2 +- api/subjects/views.py | 2 +- api/subscriptions/permissions.py | 2 +- api/subscriptions/serializers.py | 2 +- api/taxonomies/serializers.py | 2 +- api/tokens/serializers.py | 6 +- api/users/permissions.py | 8 +- api/users/serializers.py | 14 +- api/users/views.py | 4 +- api/view_only_links/views.py | 2 +- api/waffle/serializers.py | 2 +- api/waffle/utils.py | 2 +- api/wb/views.py | 2 +- api/wikis/permissions.py | 9 +- api/wikis/serializers.py | 4 +- api/wikis/views.py | 2 +- api_tests/actions/views/test_action_detail.py | 4 +- api_tests/actions/views/test_action_list.py | 4 +- api_tests/addons_tests/test_addons_list.py | 6 +- api_tests/alerts/views/test_alerts_detail.py | 2 +- api_tests/alerts/views/test_alerts_list.py | 10 +- .../views/test_application_detail.py | 4 +- .../views/test_application_list.py | 4 +- .../views/test_application_reset.py | 6 +- api_tests/base/test_auth.py | 26 +- api_tests/base/test_filters.py | 13 +- api_tests/base/test_middleware.py | 5 +- api_tests/base/test_pagination.py | 5 +- api_tests/base/test_root.py | 13 +- api_tests/base/test_serializers.py | 31 +- api_tests/base/test_throttling.py | 16 +- api_tests/base/test_utils.py | 7 +- api_tests/base/test_versioning.py | 12 +- api_tests/base/test_views.py | 31 +- api_tests/brands/views/test_brands.py | 4 +- .../views/test_chronos_journal_detail.py | 2 +- .../views/test_chronos_journal_list.py | 6 +- .../views/test_chronos_submission_detail.py | 10 +- .../views/test_chronos_submission_list.py | 6 +- api_tests/collections/test_views.py | 122 +++---- .../comments/views/test_comment_detail.py | 78 ++--- .../views/test_comment_report_detail.py | 4 +- .../views/test_comment_report_list.py | 22 +- api_tests/conftest.py | 2 - .../views/test_crossref_email_response.py | 6 +- .../views/test_draft_node_detail.py | 6 +- ...est_draft_node_draft_registrations_list.py | 2 +- .../views/test_draft_node_files_lists.py | 66 ++-- ...t_draft_registration_contributor_detail.py | 4 +- ...est_draft_registration_contributor_list.py | 32 +- .../views/test_draft_registration_detail.py | 12 +- ...st_draft_registration_institutions_list.py | 4 +- .../views/test_draft_registration_list.py | 14 +- ..._registration_relationship_institutions.py | 2 +- ...raft_registration_relationship_subjects.py | 2 +- .../test_draft_registration_subjects_list.py | 2 +- .../files/serializers/test_file_serializer.py | 16 +- api_tests/files/views/test_file_detail.py | 38 ++- api_tests/files/views/test_file_list.py | 4 +- api_tests/guids/views/test_guid_detail.py | 18 +- .../views/test_identifier_detail.py | 18 +- .../identifiers/views/test_identifier_list.py | 20 +- .../views/test_institution_auth.py | 20 +- .../views/test_institution_list.py | 2 +- .../views/test_institution_nodes_list.py | 8 +- .../test_institution_registrations_list.py | 8 +- .../views/test_institution_users_list.py | 2 +- .../licenses/views/test_license_detail.py | 2 +- api_tests/licenses/views/test_license_list.py | 4 +- api_tests/logs/views/test_log_detail.py | 18 +- api_tests/logs/views/test_log_params.py | 6 +- .../meetings/views/test_meetings_detail.py | 6 +- .../meetings/views/test_meetings_list.py | 4 +- .../views/test_meetings_submissions_detail.py | 16 +- .../views/test_meetings_submissions_list.py | 10 +- api_tests/metrics/test_composite_query.py | 4 +- api_tests/metrics/test_parse_datetimes.py | 6 +- api_tests/metrics/test_preprint_metrics.py | 26 +- api_tests/metrics/test_raw_metrics.py | 16 +- api_tests/nodes/filters/test_filters.py | 44 +-- .../nodes/serializers/test_serializers.py | 14 +- api_tests/nodes/views/test_node_addons.py | 67 ++-- ...st_node_bibliographic_contributors_list.py | 8 +- .../nodes/views/test_node_children_list.py | 26 +- api_tests/nodes/views/test_node_citations.py | 8 +- .../nodes/views/test_node_comments_list.py | 25 +- ...ode_contributors_and_group_members_list.py | 12 +- .../views/test_node_contributors_detail.py | 60 ++-- .../views/test_node_contributors_list.py | 61 ++-- api_tests/nodes/views/test_node_detail.py | 79 +++-- .../test_node_draft_registration_list.py | 2 +- api_tests/nodes/views/test_node_embeds.py | 18 +- api_tests/nodes/views/test_node_exceptions.py | 8 +- api_tests/nodes/views/test_node_files_list.py | 62 ++-- api_tests/nodes/views/test_node_forks_list.py | 12 +- api_tests/nodes/views/test_node_groups.py | 16 +- .../test_node_implicit_contributors_list.py | 8 +- .../views/test_node_institutions_list.py | 2 +- .../nodes/views/test_node_linked_by_list.py | 4 +- .../nodes/views/test_node_linked_nodes.py | 12 +- .../views/test_node_linked_registrations.py | 18 +- .../nodes/views/test_node_links_detail.py | 6 +- api_tests/nodes/views/test_node_links_list.py | 28 +- api_tests/nodes/views/test_node_list.py | 144 ++++----- api_tests/nodes/views/test_node_preprints.py | 2 +- .../views/test_node_registrations_list.py | 8 +- .../test_node_relationship_institutions.py | 4 +- .../views/test_node_relationship_subjects.py | 2 +- api_tests/nodes/views/test_node_settings.py | 3 +- .../nodes/views/test_node_sparse_fieldsets.py | 62 ++-- api_tests/nodes/views/test_node_storage.py | 4 +- .../nodes/views/test_node_subjects_list.py | 2 +- .../views/test_node_view_only_links_detail.py | 2 +- .../views/test_node_view_only_links_list.py | 6 +- api_tests/nodes/views/test_node_wiki_list.py | 24 +- .../views/test_view_only_query_parameter.py | 10 +- .../osf_groups/views/test_osf_group_detail.py | 2 +- .../views/test_osf_group_members_detail.py | 30 +- .../views/test_osf_group_members_list.py | 46 +-- .../osf_groups/views/test_osf_groups_list.py | 4 +- api_tests/preprints/filters/test_filters.py | 60 ++-- .../preprints/views/test_preprint_actions.py | 4 +- ...reprint_bibliographic_contributors_list.py | 8 +- .../views/test_preprint_citations.py | 57 ++-- .../test_preprint_contributors_detail.py | 60 ++-- .../views/test_preprint_contributors_list.py | 55 ++-- .../preprints/views/test_preprint_detail.py | 32 +- .../views/test_preprint_files_list.py | 28 +- .../preprints/views/test_preprint_list.py | 58 ++-- .../views/test_preprint_list_mixin.py | 8 +- .../views/test_preprint_node_relationship.py | 4 +- .../views/test_preprint_subjects_list.py | 2 +- .../views/test_collection_provider_detail.py | 6 +- .../views/test_collection_provider_list.py | 2 +- .../test_collection_provider_subjects_list.py | 12 +- ...st_collection_provider_submissions_list.py | 2 +- ...est_collections_provider_moderator_list.py | 4 +- api_tests/providers/mixins.py | 54 ++-- .../views/test_preprint_provider_detail.py | 14 +- .../views/test_preprint_provider_list.py | 2 +- .../test_preprint_provider_moderator_list.py | 4 +- .../test_preprint_provider_preprints_list.py | 12 +- .../test_preprint_provider_request_list.py | 10 +- .../test_preprint_provider_subjects_list.py | 18 +- .../views/test_preprint_providers_list.py | 4 +- .../test_registration_provider_bulk_upload.py | 2 +- .../test_registration_provider_detail.py | 6 +- .../views/test_registration_provider_list.py | 2 +- ...est_registration_provider_subjects_list.py | 12 +- api_tests/providers/tasks/test_bulk_upload.py | 2 +- api_tests/regions/views/test_region_detail.py | 2 +- .../registrations/filters/test_filters.py | 19 +- ...tration_bibliographic_contributors_list.py | 2 +- .../views/test_registration_detail.py | 24 +- .../views/test_registration_embeds.py | 10 +- .../views/test_registration_files_list.py | 2 +- .../views/test_registration_forks.py | 12 +- .../views/test_registration_linked_nodes.py | 8 +- .../test_registration_linked_registrations.py | 12 +- .../views/test_registration_list.py | 56 ++-- ..._registration_relationship_institutions.py | 2 +- ...test_registration_relationship_subjects.py | 2 +- .../views/test_registration_resource_list.py | 2 +- .../views/test_registration_subjects_list.py | 2 +- .../test_registrations_childrens_list.py | 2 +- .../views/test_view_only_query_parameter.py | 12 +- .../views/test_withdrawn_registrations.py | 12 +- api_tests/requests/mixins.py | 4 +- .../views/test_request_action_list.py | 2 +- .../views/test_request_actions_create.py | 8 +- .../requests/views/test_request_detail.py | 4 +- .../views/test_request_list_create.py | 10 +- .../test_open_practice_badge_annotations.py | 10 +- api_tests/reviews/mixins/comment_settings.py | 6 +- api_tests/reviews/mixins/filter_mixins.py | 54 ++-- ...est_registrations_schema_responses_list.py | 6 +- .../views/test_file_metadata_schema_detail.py | 4 +- .../views/test_file_metadata_schema_list.py | 2 +- .../views/test_registration_schemas_detail.py | 20 +- .../views/test_registration_schemas_list.py | 6 +- api_tests/scopes/views/test_scope_detail.py | 4 +- api_tests/scopes/views/test_scope_list.py | 2 +- api_tests/search/views/test_views.py | 28 +- .../sparse/test_node_sparse_fieldsets.py | 8 +- api_tests/sparse/test_sparse_node_detail.py | 1 - api_tests/sparse/test_sparse_node_list.py | 2 +- api_tests/subjects/mixins.py | 59 ++-- .../subjects/views/test_subject_detail.py | 6 +- .../views/test_subscriptions_detail.py | 6 +- .../views/test_subscriptions_list.py | 12 +- .../taxonomies/views/test_taxonomy_list.py | 6 +- api_tests/test/views/test_throttling.py | 4 +- api_tests/tokens/views/test_token_detail.py | 10 +- api_tests/tokens/views/test_token_list.py | 8 +- api_tests/tokens/views/test_token_scopes.py | 2 +- api_tests/users/views/test_user_actions.py | 10 +- api_tests/users/views/test_user_addons.py | 9 +- api_tests/users/views/test_user_can_review.py | 2 +- api_tests/users/views/test_user_claim.py | 5 +- api_tests/users/views/test_user_detail.py | 95 +++--- .../test_user_draft_registration_list.py | 6 +- api_tests/users/views/test_user_exceptions.py | 3 +- .../views/test_user_external_identities.py | 9 +- .../users/views/test_user_institutions.py | 2 +- .../test_user_institutions_relationship.py | 1 - api_tests/users/views/test_user_list.py | 57 ++-- api_tests/users/views/test_user_nodes_list.py | 86 ++--- .../users/views/test_user_osf_groups_list.py | 4 +- .../users/views/test_user_preprints_list.py | 24 +- .../views/test_user_registrations_list.py | 15 +- api_tests/users/views/test_user_settings.py | 49 ++- .../users/views/test_user_settings_detail.py | 5 +- .../views/test_view_only_link_detail.py | 2 +- .../views/test_view_only_link_nodes.py | 6 +- api_tests/waffle/views/test_waffle_list.py | 2 +- api_tests/wb/views/test_wb_hooks.py | 14 +- api_tests/wikis/views/test_wiki_detail.py | 18 +- .../wikis/views/test_wiki_version_content.py | 8 +- .../wikis/views/test_wiki_version_detail.py | 32 +- .../wikis/views/test_wiki_versions_list.py | 26 +- conftest.py | 3 +- framework/addons/utils.py | 2 +- framework/analytics/__init__.py | 2 - framework/auth/__init__.py | 3 +- framework/auth/campaigns.py | 4 +- framework/auth/cas.py | 12 +- framework/auth/core.py | 8 +- framework/auth/decorators.py | 2 - framework/auth/exceptions.py | 4 +- framework/auth/forms.py | 40 ++- framework/auth/oauth_scopes.py | 4 +- framework/auth/signals.py | 2 - framework/auth/signing.py | 4 +- framework/auth/utils.py | 4 +- framework/auth/views.py | 11 +- framework/celery_tasks/__init__.py | 3 +- framework/celery_tasks/handlers.py | 1 - framework/celery_tasks/routers.py | 3 +- framework/celery_tasks/utils.py | 6 +- framework/csrf/handlers.py | 1 - framework/database/__init__.py | 10 +- framework/django/handlers.py | 1 - framework/email/tasks.py | 16 +- framework/exceptions/__init__.py | 7 +- framework/flask/__init__.py | 5 +- framework/forms/__init__.py | 10 +- framework/postcommit_tasks/handlers.py | 5 +- framework/routing/__init__.py | 16 +- framework/sentry/__init__.py | 1 - framework/sessions/__init__.py | 2 +- framework/status/__init__.py | 2 - framework/transactions/handlers.py | 2 - framework/utils.py | 1 - main.py | 1 - osf/admin.py | 4 +- osf/apps.py | 2 +- osf/exceptions.py | 8 +- osf/external/askismet/client.py | 2 +- osf/external/askismet/exceptions.py | 2 +- osf/external/chronos/chronos.py | 21 +- osf/external/oopspam/client.py | 2 +- osf/features.py | 2 +- .../commands/add_colon_delim_to_s3_buckets.py | 1 - .../commands/add_egap_registration_schema.py | 1 - .../commands/add_institution_perm_groups.py | 3 +- .../commands/add_notification_subscription.py | 12 +- osf/management/commands/addon_deleted_date.py | 10 +- .../approve_pending_schema_responses.py | 2 +- .../commands/backfill_date_retracted.py | 10 +- .../commands/backfill_domain_references.py | 1 - .../commands/check_crossref_dois.py | 8 +- osf/management/commands/check_ia_metadata.py | 2 +- osf/management/commands/check_spam.py | 8 +- osf/management/commands/checkmigrations.py | 2 +- ...ect_user_nodes_exceeding_storage_limits.py | 2 +- osf/management/commands/confirm_spam.py | 4 +- .../commands/count_preregistrations.py | 5 +- .../commands/create_fake_preprint_actions.py | 4 +- .../commands/cumulative_plos_metrics.py | 2 +- osf/management/commands/data_storage_usage.py | 67 ++-- .../commands/deactivate_requested_accounts.py | 6 +- osf/management/commands/email_all_users.py | 4 +- .../commands/export_user_account.py | 10 +- osf/management/commands/find_spammy_files.py | 8 +- .../commands/fix_registration_file_domains.py | 2 +- osf/management/commands/force_archive.py | 57 ++-- osf/management/commands/gdpr_delete_user.py | 4 +- .../commands/handle_duplicate_files.py | 18 +- osf/management/commands/import_EGAP.py | 29 +- .../make_dummy_pageviews_for_metrics.py | 4 +- .../commands/make_taxonomy_custom.py | 1 - .../commands/manage_switch_flags.py | 5 +- .../commands/metrics_backfill_pageviews.py | 4 +- .../commands/metrics_backfill_summaries.py | 10 +- .../commands/metrics_backfill_user_domains.py | 6 +- .../commands/migrate_deleted_date.py | 14 +- .../commands/migrate_pagecounter_data.py | 18 +- .../migrate_registration_responses.py | 20 +- .../migrate_user_institution_affiliation.py | 2 +- .../commands/move_egap_regs_to_provider.py | 3 +- osf/management/commands/osf_shell.py | 4 +- .../commands/populate_branched_from_node.py | 6 +- .../commands/populate_custom_taxonomies.py | 34 +- .../commands/populate_fake_providers.py | 27 +- .../populate_impact_institution_metrics.py | 2 +- .../populate_impact_preprint_metrics.py | 2 +- .../populate_initial_schema_responses.py | 2 +- ...ion_provider_notification_subscriptions.py | 2 +- osf/management/commands/purge_test_node.py | 13 +- .../commands/rebrand_preprint_provider.py | 33 +- .../commands/registration_schema_metrics.py | 14 +- osf/management/commands/reindex_es6.py | 2 +- osf/management/commands/reindex_provider.py | 8 +- .../commands/restore_deleted_root_folders.py | 9 +- .../send_storage_exceeded_announcement.py | 2 +- .../set_institution_storage_regions.py | 2 +- .../strip_trailing_subject_whitespace.py | 8 +- .../commands/sync_citation_styles.py | 3 +- .../sync_collection_provider_indices.py | 8 +- .../commands/sync_datacite_doi_metadata.py | 1 - osf/management/commands/sync_doi_metadata.py | 1 - ...gistration_creator_bibliographic_status.py | 4 +- .../transfer_quickfiles_to_projects.py | 2 +- osf/management/commands/update_auth_groups.py | 4 +- .../commands/update_bepress_version.py | 11 +- .../update_institution_sso_email_domain.py | 4 +- .../commands/update_preprint_share_dates.py | 3 +- .../commands/update_registration_schemas.py | 2 +- .../commands/update_storage_usage.py | 6 +- osf/management/commands/vacuum.py | 5 +- .../withdraw_all_preprints_from_provider.py | 2 +- osf/management/utils.py | 2 +- osf/metadata/gather/gatherer.py | 6 +- osf/metadata/serializers/_base.py | 2 +- osf/metadata/tools.py | 2 +- osf/metrics/metric_mixin.py | 6 +- osf/migrations/0001_initial.py | 82 +++-- osf/migrations/0002_adminlogentry.py | 2 - .../0003_aggregated_runsql_calls.py | 2 - .../0005_add_notabledomain_domainreference.py | 4 +- osf/migrations/__init__.py | 3 +- osf/models/action.py | 3 - osf/models/analytics.py | 4 +- osf/models/archive.py | 2 +- osf/models/banner.py | 2 +- osf/models/base.py | 22 +- osf/models/chronos.py | 1 - osf/models/collection.py | 30 +- osf/models/collection_submission.py | 8 +- osf/models/comment.py | 13 +- osf/models/conference.py | 5 +- osf/models/contributor.py | 6 +- osf/models/dismissed_alerts.py | 4 +- osf/models/draft_node.py | 2 - osf/models/external.py | 12 +- osf/models/files.py | 18 +- osf/models/institution.py | 14 +- osf/models/licenses.py | 3 +- osf/models/metadata.py | 1 - osf/models/metaschema.py | 9 +- osf/models/mixins.py | 44 +-- osf/models/node.py | 58 ++-- osf/models/nodelog.py | 2 +- osf/models/notifications.py | 2 +- osf/models/oauth.py | 10 +- osf/models/osf_group.py | 10 +- osf/models/osf_grouplog.py | 2 +- osf/models/preprint.py | 23 +- osf/models/preprintlog.py | 2 +- osf/models/provider.py | 17 +- osf/models/quickfiles.py | 12 +- osf/models/registrations.py | 24 +- osf/models/request.py | 3 - osf/models/sanctions.py | 16 +- osf/models/session.py | 2 +- osf/models/spam.py | 2 +- osf/models/storage.py | 1 - osf/models/subject.py | 11 +- osf/models/tag.py | 6 +- osf/models/user.py | 64 ++-- osf/models/validators.py | 29 +- osf/registrations/utils.py | 5 +- osf/utils/caching.py | 3 +- osf/utils/datetime_aware_jsonfield.py | 12 +- osf/utils/fields.py | 6 +- osf/utils/functional.py | 4 +- osf/utils/machines.py | 10 +- osf/utils/migrations.py | 12 +- osf/utils/order_apps.py | 2 +- osf/utils/permissions.py | 2 - osf/utils/registrations.py | 4 +- osf/utils/requests.py | 3 +- osf/utils/sanitize.py | 4 +- osf/utils/storage.py | 4 +- osf/utils/tokens/__init__.py | 4 +- osf/utils/tokens/handlers.py | 4 +- osf/utils/workflows.py | 3 - osf_tests/embargoes/test_embargoes.py | 2 +- osf_tests/external/akismet/test_akismet.py | 2 +- osf_tests/external/oopspam/test_oopspam.py | 2 +- osf_tests/factories.py | 69 ++-- .../management_commands/test_EGAP_import.py | 9 +- .../test_backfill_domain_references.py | 2 +- .../test_check_crossref_dois.py | 5 +- ..._correct_registration_moderation_states.py | 2 +- ..._withdrawn_or_failed_registration_files.py | 24 +- .../test_email_all_users.py | 2 +- .../test_manage_switch_flags.py | 1 - .../test_migrate_deleted_date.py | 3 +- .../test_move_egap_regs_to_provider.py | 1 - ...gistration_creator_bibliographic_status.py | 1 - .../test_update_registration_schemas.py | 2 +- osf_tests/metrics/test_metric_mixin.py | 2 +- osf_tests/test_analytics.py | 13 +- osf_tests/test_app.py | 1 - osf_tests/test_archiver.py | 23 +- osf_tests/test_collection.py | 2 +- osf_tests/test_collection_submission.py | 2 +- osf_tests/test_comment.py | 32 +- osf_tests/test_draft_registration.py | 4 +- osf_tests/test_elastic_search.py | 111 ++++--- osf_tests/test_external_accounts.py | 13 +- osf_tests/test_generate_sitemap.py | 12 +- osf_tests/test_guid.py | 94 +++--- osf_tests/test_institution.py | 5 +- osf_tests/test_management_commands.py | 35 +- osf_tests/test_node.py | 44 +-- osf_tests/test_node_license.py | 2 +- osf_tests/test_notable_domains.py | 26 +- osf_tests/test_oauth_application.py | 5 +- osf_tests/test_osfgroup.py | 14 +- osf_tests/test_outcomes.py | 2 +- osf_tests/test_pigeon.py | 2 +- osf_tests/test_project_decorators.py | 2 +- osf_tests/test_queued_mail.py | 2 +- osf_tests/test_region.py | 2 +- .../test_registration_bulk_upload_parser.py | 1 - ...t_registration_moderation_notifications.py | 4 +- osf_tests/test_registrations.py | 4 +- osf_tests/test_reviewable.py | 2 +- osf_tests/test_sanctions.py | 3 +- osf_tests/test_schema_responses.py | 2 +- osf_tests/test_schemas.py | 1 - osf_tests/test_search_views.py | 31 +- osf_tests/test_session.py | 4 +- osf_tests/test_user.py | 51 ++- osf_tests/users/test_last_login_date.py | 4 +- osf_tests/utils.py | 12 +- scripts/EGAP/create_EGAP_json.py | 20 +- scripts/EGAP/files_to_import_structure.py | 10 +- scripts/add_global_subscriptions.py | 6 +- .../add_missing_identifiers_to_preprints.py | 11 +- scripts/approve_embargo_terminations.py | 8 +- scripts/approve_registrations.py | 4 +- ...urce_tags_for_unregistered_contributors.py | 26 +- .../clear_chronos_user_id_and_submissions.py | 4 +- scripts/create_fakes.py | 10 +- scripts/embargo_registrations.py | 6 +- scripts/find_spammy_content.py | 4 +- scripts/fix_merged_user_quickfiles.py | 4 +- .../fix_nodes_templated_from_registration.py | 5 +- scripts/fix_nodes_with_no_creator.py | 4 +- scripts/fix_registration_unclaimed_records.py | 10 +- scripts/fix_root.py | 4 +- scripts/fix_user_mailchimp.py | 4 +- scripts/generate_sitemap.py | 25 +- scripts/meta/gatherer.py | 2 +- .../migration/migrate_share_preprint_data.py | 10 +- .../migrate_share_registration_data.py | 6 +- scripts/normalize_user_tags.py | 4 +- scripts/osfstorage/usage_audit.py | 16 +- scripts/parse_citation_styles.py | 5 +- scripts/populate_institutions.py | 15 +- .../populate_new_and_noteworthy_projects.py | 8 +- ...late_popular_projects_and_registrations.py | 8 +- scripts/populate_preprint_providers.py | 13 +- scripts/premigrate_created_modified.py | 4 +- scripts/refresh_addon_tokens.py | 9 +- scripts/register_oauth_scopes.py | 6 +- ...cation_subscriptions_from_registrations.py | 2 +- ...ve_qa_test_items_from_elastic_and_share.py | 2 +- scripts/retract_registrations.py | 6 +- scripts/send_queued_mails.py | 10 +- ...end_specific_registration_data_to_share.py | 6 +- scripts/stuck_registration_audit.py | 7 +- .../test_approve_embargo_terminations.py | 8 +- scripts/tests/test_approve_registrations.py | 4 +- .../test_deactivate_requested_accounts.py | 4 +- scripts/tests/test_embargo_registrations.py | 4 +- .../tests/test_files_to_import_structure.py | 3 +- .../tests/test_fix_draft_node_permissions.py | 2 - .../tests/test_populate_new_and_noteworthy.py | 4 +- ...late_popular_projects_and_registrations.py | 6 +- scripts/tests/test_refresh_addon_tokens.py | 8 +- scripts/tests/test_retract_registrations.py | 4 +- scripts/tests/test_send_queued_mails.py | 4 +- scripts/tests/test_triggered_mails.py | 4 +- scripts/triggered_mails.py | 2 +- scripts/update_taxonomies.py | 8 +- scripts/utils.py | 6 +- tasks/__init__.py | 87 +++-- tasks/local-dist.py | 2 +- tasks/utils.py | 6 +- tests/base.py | 37 ++- tests/exceptions.py | 2 +- tests/framework_tests/test_email.py | 3 +- tests/framework_tests/test_encryption.py | 11 +- .../framework_tests/test_modular_templates.py | 9 +- tests/framework_tests/test_oauth_scopes.py | 1 - tests/framework_tests/test_routing.py | 1 - tests/framework_tests/test_sentry.py | 3 +- tests/framework_tests/test_url_mapping.py | 1 - tests/identifiers/test_crossref.py | 9 +- tests/identifiers/test_datacite.py | 7 +- tests/identifiers/test_identifiers.py | 1 - tests/json_api_test_app.py | 6 +- tests/test_addons.py | 50 ++- tests/test_auth.py | 31 +- tests/test_auth_basic_auth.py | 4 +- tests/test_auth_forms.py | 1 - tests/test_campaigns.py | 10 +- tests/test_cas_authentication.py | 11 +- tests/test_citations.py | 5 +- tests/test_citeprocpy.py | 25 +- tests/test_conferences.py | 48 ++- tests/test_contributors_views.py | 6 +- tests/test_ember_osf_web.py | 5 +- tests/test_events.py | 304 +++++++++--------- .../test_affiliation_via_orcid.py | 2 +- tests/test_mailchimp.py | 5 +- tests/test_metadata.py | 1 - tests/test_node_licenses.py | 5 +- tests/test_notifications.py | 94 +++--- tests/test_oauth.py | 8 +- tests/test_permissions.py | 1 - tests/test_preprints.py | 45 ++- tests/test_registrations/base.py | 6 +- tests/test_registrations/test_embargoes.py | 22 +- .../test_registration_approvals.py | 4 +- tests/test_registrations/test_retractions.py | 14 +- tests/test_registrations/test_views.py | 4 +- tests/test_rubeus.py | 15 +- tests/test_search/__init__.py | 7 +- tests/test_security.py | 1 - tests/test_serializers.py | 12 +- tests/test_spam_mixin.py | 5 +- tests/test_subjects.py | 7 +- tests/test_test_utils.py | 5 +- tests/test_tokens.py | 6 +- tests/test_utils.py | 7 +- tests/test_views.py | 122 ++++--- tests/test_websitefiles.py | 28 +- tests/test_webtests.py | 65 ++-- tests/utils.py | 4 +- website/app.py | 5 +- website/archiver/tasks.py | 16 +- website/citations/providers.py | 4 +- website/citations/views.py | 17 +- website/closed_challenges/views.py | 2 - website/conferences/exceptions.py | 2 - website/conferences/message.py | 8 +- website/conferences/signals.py | 2 - website/conferences/utils.py | 1 - website/conferences/views.py | 4 +- website/ember_osf_web/views.py | 1 - website/identifiers/clients/base.py | 4 +- website/identifiers/clients/crossref.py | 5 +- website/identifiers/clients/datacite.py | 7 +- website/identifiers/clients/exceptions.py | 2 +- website/identifiers/utils.py | 4 +- website/identifiers/views.py | 1 - website/language.py | 39 ++- website/mailchimp_utils.py | 2 - website/mails/mails.py | 15 +- website/mails/presends.py | 1 - website/notifications/emails.py | 8 +- website/notifications/events/base.py | 3 +- website/notifications/events/files.py | 62 ++-- website/notifications/listeners.py | 12 +- website/notifications/utils.py | 6 +- website/notifications/views.py | 8 +- website/oauth/views.py | 2 - website/osf_groups/views.py | 4 +- website/preprints/views.py | 1 - website/profile/utils.py | 1 - website/profile/views.py | 7 +- website/project/__init__.py | 1 - website/project/commentable.py | 2 +- website/project/decorators.py | 3 +- website/project/forms.py | 7 +- website/project/licenses/__init__.py | 10 +- website/project/model.py | 3 +- website/project/utils.py | 9 +- website/project/views/comment.py | 2 - website/project/views/contributor.py | 2 - website/project/views/drafts.py | 4 +- website/project/views/node.py | 17 +- website/project/views/register.py | 1 - website/registries/views.py | 1 - website/reviews/listeners.py | 3 +- website/reviews/views.py | 2 - website/routes.py | 10 +- website/search/elastic_search.py | 28 +- website/search/util.py | 2 +- website/search/views.py | 3 +- website/search_migration/migrate.py | 56 ++-- website/security.py | 1 - website/settings/__init__.py | 3 +- website/settings/defaults.py | 5 +- website/settings/local-dist.py | 9 +- website/settings/local-travis.py | 1 - website/util/__init__.py | 12 +- website/util/client.py | 4 +- website/util/metrics.py | 13 +- website/util/paths.py | 4 +- website/util/rubeus.py | 15 +- website/util/sanitize.py | 1 - website/views.py | 6 +- 984 files changed, 5154 insertions(+), 5807 deletions(-) diff --git a/addons/base/apps.py b/addons/base/apps.py index b9e5a513b52..ec6a1734c91 100644 --- a/addons/base/apps.py +++ b/addons/base/apps.py @@ -45,7 +45,7 @@ def _root_folder(node_settings, auth, **kwargs): private_key=kwargs.get('view_only', None), ) return [root] - _root_folder.__name__ = '{0}_root_folder'.format(addon_short_name) + _root_folder.__name__ = f'{addon_short_name}_root_folder' return _root_folder @@ -74,7 +74,7 @@ class BaseAddonAppConfig(AppConfig): categories = [] def __init__(self, *args, **kwargs): - ret = super(BaseAddonAppConfig, self).__init__(*args, **kwargs).__init__() + ret = super().__init__(*args, **kwargs).__init__() # Build template lookup paths = [settings.TEMPLATES_PATH] if self.user_settings_template: @@ -82,13 +82,11 @@ def __init__(self, *args, **kwargs): if self.node_settings_template: paths.append(os.path.dirname(self.node_settings_template)) template_dirs = list( - set( - [ + { path for path in paths if os.path.exists(path) - ] - ) + } ) if template_dirs: self.template_lookup = TemplateLookup( diff --git a/addons/base/generic_views.py b/addons/base/generic_views.py index 18ef2590b28..950b017c9bc 100644 --- a/addons/base/generic_views.py +++ b/addons/base/generic_views.py @@ -39,7 +39,7 @@ def _import_auth(auth, node_addon, user_addon, **kwargs): 'result': Serializer().serialize_settings(node_addon, auth.user), 'message': 'Successfully imported credentials from profile.', } - _import_auth.__name__ = '{0}_import_auth'.format(addon_short_name) + _import_auth.__name__ = f'{addon_short_name}_import_auth' return _import_auth @@ -49,7 +49,7 @@ def _account_list(auth): user_settings = auth.user.get_addon(addon_short_name) serializer = Serializer(user_settings=user_settings) return serializer.serialized_user_settings - _account_list.__name__ = '{0}_account_list'.format(addon_short_name) + _account_list.__name__ = f'{addon_short_name}_account_list' return _account_list @@ -64,7 +64,7 @@ def _folder_list(node_addon, **kwargs): folder_id = request.args.get('folderId') return get_folders(node_addon, folder_id) - _folder_list.__name__ = '{0}_folder_list'.format(addon_short_name) + _folder_list.__name__ = f'{addon_short_name}_folder_list' return _folder_list @@ -81,7 +81,7 @@ def _get_config(node_addon, auth, **kwargs): auth.user ) } - _get_config.__name__ = '{0}_get_config'.format(addon_short_name) + _get_config.__name__ = f'{addon_short_name}_get_config' return _get_config @@ -100,7 +100,7 @@ def _set_config(node_addon, user_addon, auth, **kwargs): return { 'result': { 'folder': { - 'name': path.replace('All Files', '') if path != '/' else '/ (Full {0})'.format( + 'name': path.replace('All Files', '') if path != '/' else '/ (Full {})'.format( addon_full_name ), 'path': path, @@ -109,7 +109,7 @@ def _set_config(node_addon, user_addon, auth, **kwargs): }, 'message': 'Successfully updated settings.', } - _set_config.__name__ = '{0}_set_config'.format(addon_short_name) + _set_config.__name__ = f'{addon_short_name}_set_config' return _set_config @@ -120,5 +120,5 @@ def deauthorize_node(addon_short_name): def _deauthorize_node(auth, node_addon, **kwargs): node_addon.deauthorize(auth=auth) node_addon.save() - _deauthorize_node.__name__ = '{0}_deauthorize_node'.format(addon_short_name) + _deauthorize_node.__name__ = f'{addon_short_name}_deauthorize_node' return _deauthorize_node diff --git a/addons/base/logger.py b/addons/base/logger.py index a09abc15df0..5c87b4589f6 100644 --- a/addons/base/logger.py +++ b/addons/base/logger.py @@ -1,6 +1,6 @@ import abc -class AddonNodeLogger(object): +class AddonNodeLogger: """Helper class for adding correctly-formatted addon logs to nodes. :param Node node: The node to add logs to @@ -53,7 +53,7 @@ def log(self, action, extra=None, save=False): params.update(extra) self.node.add_log( - action='{0}_{1}'.format(self.addon_short_name, action), + action=f'{self.addon_short_name}_{action}', params=params, auth=self.auth ) diff --git a/addons/base/models.py b/addons/base/models.py index 5c16d5e072d..00a1d2aa3b7 100644 --- a/addons/base/models.py +++ b/addons/base/models.py @@ -119,7 +119,7 @@ def can_be_merged(self): return hasattr(self, 'merge') def to_json(self, user): - ret = super(BaseUserSettings, self).to_json(user) + ret = super().to_json(user) ret['has_auth'] = self.has_auth ret.update({ 'nodes': [ @@ -137,8 +137,8 @@ def to_json(self, user): def __repr__(self): if self.owner: - return '<{cls} owned by user {uid}>'.format(cls=self.__class__.__name__, uid=self.owner._id) - return '<{cls} with no owner>'.format(cls=self.__class__.__name__) + return f'<{self.__class__.__name__} owned by user {self.owner._id}>' + return f'<{self.__class__.__name__} with no owner>' @oauth_complete.connect @@ -186,7 +186,7 @@ def external_accounts(self): def delete(self, save=True): for account in self.external_accounts.filter(provider=self.config.short_name): self.revoke_oauth_access(account, save=False) - super(BaseOAuthUserSettings, self).delete(save=save) + super().delete(save=save) def grant_oauth_access(self, node, external_account, metadata=None): """Give a node permission to use an ``ExternalAccount`` instance.""" @@ -328,7 +328,7 @@ def merge(self, user_settings): self.save() def to_json(self, user): - ret = super(BaseOAuthUserSettings, self).to_json(user) + ret = super().to_json(user) ret['accounts'] = self.serializer( user_settings=self @@ -343,7 +343,7 @@ def to_json(self, user): def on_delete(self): """When the user deactivates the addon, clear auth for connected nodes. """ - super(BaseOAuthUserSettings, self).on_delete() + super().on_delete() nodes = [AbstractNode.load(node_id) for node_id in self.oauth_grants.keys()] for node in nodes: node_addon = node.get_addon(self.oauth_provider.short_name) @@ -378,7 +378,7 @@ def has_auth(self): return False def to_json(self, user): - ret = super(BaseNodeSettings, self).to_json(user) + ret = super().to_json(user) ret.update({ 'user': { 'permissions': self.owner.get_permissions(user) @@ -458,9 +458,9 @@ def before_fork(self, node, user): if hasattr(self, 'user_settings'): if self.user_settings is None: return ( - u'Because you have not configured the {addon} add-on, your authentication will not be ' - u'transferred to the forked {category}. You may authorize and configure the {addon} add-on ' - u'in the new fork on the settings page.' + 'Because you have not configured the {addon} add-on, your authentication will not be ' + 'transferred to the forked {category}. You may authorize and configure the {addon} add-on ' + 'in the new fork on the settings page.' ).format( addon=self.config.full_name, category=node.project_or_component, @@ -468,19 +468,19 @@ def before_fork(self, node, user): elif self.user_settings and self.user_settings.owner == user: return ( - u'Because you have authorized the {addon} add-on for this ' - u'{category}, forking it will also transfer your authentication to ' - u'the forked {category}.' + 'Because you have authorized the {addon} add-on for this ' + '{category}, forking it will also transfer your authentication to ' + 'the forked {category}.' ).format( addon=self.config.full_name, category=node.project_or_component, ) else: return ( - u'Because the {addon} add-on has been authorized by a different ' - u'user, forking it will not transfer authentication to the forked ' - u'{category}. You may authorize and configure the {addon} add-on ' - u'in the new fork on the settings page.' + 'Because the {addon} add-on has been authorized by a different ' + 'user, forking it will not transfer authentication to the forked ' + '{category}. You may authorize and configure the {addon} add-on ' + 'in the new fork on the settings page.' ).format( addon=self.config.full_name, category=node.project_or_component, @@ -541,12 +541,12 @@ def after_delete(self, user): # Archiver # ############ -class GenericRootNode(object): +class GenericRootNode: path = '/' name = '' -class BaseStorageAddon(object): +class BaseStorageAddon: """ Mixin class for traversing file trees of addons with files """ @@ -558,10 +558,10 @@ class Meta: @property def archive_folder_name(self): - name = 'Archive of {addon}'.format(addon=self.config.full_name) + name = f'Archive of {self.config.full_name}' folder_name = getattr(self, 'folder_name', '').lstrip('/').strip() if folder_name: - name = name + ': {folder}'.format(folder=folder_name) + name = name + f': {folder_name}' return name def _get_fileobj_child_metadata(self, filenode, user, cookie=None, version=None): @@ -670,7 +670,7 @@ def nodelogger(self): self, '_logger_class', type( - '{0}NodeLogger'.format(self.config.short_name.capitalize()), + f'{self.config.short_name.capitalize()}NodeLogger', (logger.AddonNodeLogger,), {'addon_short_name': self.config.short_name} ) @@ -762,9 +762,9 @@ def before_remove_contributor_message(self, node, removed): """ if self.has_auth and self.user_settings.owner == removed: return ( - u'The {addon} add-on for this {category} is authenticated by {name}. ' - u'Removing this user will also remove write access to {addon} ' - u'unless another contributor re-authenticates the add-on.' + 'The {addon} add-on for this {category} is authenticated by {name}. ' + 'Removing this user will also remove write access to {addon} ' + 'unless another contributor re-authenticates the add-on.' ).format( addon=self.config.full_name, category=node.project_or_component, @@ -785,8 +785,8 @@ def after_remove_contributor(self, node, removed, auth=None): self.user_settings.save() self.clear_auth() message = ( - u'Because the {addon} add-on for {category} "{title}" was authenticated ' - u'by {user}, authentication information has been deleted.' + 'Because the {addon} add-on for {category} "{title}" was authenticated ' + 'by {user}, authentication information has been deleted.' ).format( addon=self.config.full_name, category=markupsafe.escape(node.category_display), @@ -797,7 +797,7 @@ def after_remove_contributor(self, node, removed, auth=None): if not auth or auth.user != removed: url = node.web_url_for('node_addons') message += ( - u' You can re-authenticate on the add-ons page.' + ' You can re-authenticate on the add-ons page.' ).format(url=url) # return message @@ -808,7 +808,7 @@ def after_fork(self, node, fork, user, save=True): :return: the cloned settings """ - clone = super(BaseOAuthNodeSettings, self).after_fork( + clone = super().after_fork( node=node, fork=fork, user=user, @@ -834,9 +834,9 @@ def before_register_message(self, node, user): """ if self.has_auth: return ( - u'The contents of {addon} add-ons cannot be registered at this time; ' - u'the {addon} add-on linked to this {category} will not be included ' - u'as part of this registration.' + 'The contents of {addon} add-ons cannot be registered at this time; ' + 'the {addon} add-on linked to this {category} will not be included ' + 'as part of this registration.' ).format( addon=self.config.full_name, category=node.project_or_component, @@ -926,13 +926,13 @@ def set_auth(self, *args, **kwargs): self.list_id = None self.save() - return super(BaseCitationsNodeSettings, self).set_auth(*args, **kwargs) + return super().set_auth(*args, **kwargs) def deauthorize(self, auth=None, add_log=True): """Remove user authorization from this node and log the event.""" if add_log: self.owner.add_log( - '{0}_node_deauthorized'.format(self.provider_name), + f'{self.provider_name}_node_deauthorized', params={ 'project': self.owner.parent_id, 'node': self.owner._id, diff --git a/addons/base/serializer.py b/addons/base/serializer.py index 6187408df64..002167fab36 100644 --- a/addons/base/serializer.py +++ b/addons/base/serializer.py @@ -4,7 +4,7 @@ from website.util import api_url_for, web_url_for -class AddonSerializer(object): +class AddonSerializer: __metaclass__ = abc.ABCMeta # TODO take addon_node_settings, addon_user_settings @@ -80,7 +80,7 @@ def serialized_urls(self): ret = self.addon_serialized_urls # Make sure developer returns set of needed urls for url in self.REQUIRED_URLS: - msg = "addon_serialized_urls must include key '{0}'".format(url) + msg = f"addon_serialized_urls must include key '{url}'" assert url in ret, msg ret.update({'settings': web_url_for('user_addons')}) return ret @@ -94,7 +94,7 @@ def serialized_accounts(self): @property def serialized_user_settings(self): - retval = super(OAuthAddonSerializer, self).serialized_user_settings + retval = super().serialized_user_settings retval['accounts'] = [] if self.user_settings: retval['accounts'] = self.serialized_accounts @@ -202,12 +202,12 @@ def serialized_urls(self): if external_account and external_account.profile_url: ret['owner'] = external_account.profile_url - ret.update(super(CitationsAddonSerializer, self).serialized_urls) + ret.update(super().serialized_urls) return ret @property def serialized_node_settings(self): - result = super(CitationsAddonSerializer, self).serialized_node_settings + result = super().serialized_node_settings result['folder'] = { 'name': self.node_settings.fetch_folder_name } @@ -225,7 +225,7 @@ def serialize_folder(self, folder): 'id': folder['id'], 'urls': { 'fetch': self.node_settings.owner.api_url_for( - '{0}_citation_list'.format(self.addon_short_name), + f'{self.addon_short_name}_citation_list', list_id=folder['id'] ), }, @@ -235,11 +235,11 @@ def serialize_folder(self, folder): def addon_serialized_urls(self): node = self.node_settings.owner return { - 'importAuth': node.api_url_for('{0}_import_auth'.format(self.addon_short_name)), - 'folders': node.api_url_for('{0}_citation_list'.format(self.addon_short_name)), - 'config': node.api_url_for('{0}_set_config'.format(self.addon_short_name)), - 'deauthorize': node.api_url_for('{0}_deauthorize_node'.format(self.addon_short_name)), - 'accounts': node.api_url_for('{0}_account_list'.format(self.addon_short_name)), + 'importAuth': node.api_url_for(f'{self.addon_short_name}_import_auth'), + 'folders': node.api_url_for(f'{self.addon_short_name}_citation_list'), + 'config': node.api_url_for(f'{self.addon_short_name}_set_config'), + 'deauthorize': node.api_url_for(f'{self.addon_short_name}_deauthorize_node'), + 'accounts': node.api_url_for(f'{self.addon_short_name}_account_list'), } def serialize_citation(self, citation): diff --git a/addons/base/tests/base.py b/addons/base/tests/base.py index 86939652292..406cbe0a8a5 100644 --- a/addons/base/tests/base.py +++ b/addons/base/tests/base.py @@ -4,7 +4,7 @@ from osf_tests.factories import AuthUserFactory, ProjectFactory -class AddonTestCase(object): +class AddonTestCase: """General Addon TestCase that automatically sets up a user and node with an addon. @@ -31,7 +31,7 @@ class AddonTestCase(object): NODE_USER_FIELD = 'user_settings' def __init__(self, *args, **kwargs): - super(AddonTestCase,self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self.node_settings = None self.project = None self.user = None @@ -57,7 +57,7 @@ def create_user_settings(self): if 'user' not in self.OWNERS: return self.user.add_addon(self.ADDON_SHORT_NAME) - assert self.user.has_addon(self.ADDON_SHORT_NAME), '{0} is not enabled'.format(self.ADDON_SHORT_NAME) + assert self.user.has_addon(self.ADDON_SHORT_NAME), f'{self.ADDON_SHORT_NAME} is not enabled' self.user_settings = self.user.get_addon(self.ADDON_SHORT_NAME) self.set_user_settings(self.user_settings) self.user_settings.save() @@ -78,7 +78,7 @@ def create_node_settings(self): def setUp(self): - super(AddonTestCase, self).setUp() + super().setUp() self.user = self.create_user() if not self.ADDON_SHORT_NAME: @@ -90,14 +90,14 @@ def setUp(self): self.create_node_settings() -class OAuthAddonTestCaseMixin(object): +class OAuthAddonTestCaseMixin: @property def ExternalAccountFactory(self): raise NotImplementedError() def __init__(self, *args, **kwargs): - super(OAuthAddonTestCaseMixin, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self.auth = None self.external_account = None self.project = None diff --git a/addons/base/tests/logger.py b/addons/base/tests/logger.py index d7075322d44..ebbd4f8f37a 100644 --- a/addons/base/tests/logger.py +++ b/addons/base/tests/logger.py @@ -5,7 +5,7 @@ from framework.auth import Auth from osf_tests.factories import AuthUserFactory, ProjectFactory -class AddonNodeLoggerTestSuiteMixinBase(object): +class AddonNodeLoggerTestSuiteMixinBase: __metaclass__ = abc.ABCMeta @@ -18,7 +18,7 @@ def NodeLogger(self): pass def setUp(self): - super(AddonNodeLoggerTestSuiteMixinBase, self).setUp() + super().setUp() self.auth = Auth(AuthUserFactory()) self.node = ProjectFactory(creator=self.auth.user) self.path = None @@ -29,19 +29,19 @@ def setUp(self): class StorageAddonNodeLoggerTestSuiteMixin(AddonNodeLoggerTestSuiteMixinBase): def setUp(self): - super(StorageAddonNodeLoggerTestSuiteMixin, self).setUp() + super().setUp() def test_log_file_added(self): self.logger.log('file_added', save=True) last_log = self.node.logs.latest() - assert_equal(last_log.action, '{0}_{1}'.format(self.addon_short_name, 'file_added')) + assert_equal(last_log.action, '{}_{}'.format(self.addon_short_name, 'file_added')) def test_log_file_removed(self): self.logger.log('file_removed', save=True) last_log = self.node.logs.latest() - assert_equal(last_log.action, '{0}_{1}'.format(self.addon_short_name, 'file_removed')) + assert_equal(last_log.action, '{}_{}'.format(self.addon_short_name, 'file_removed')) def test_log_deauthorized_when_node_settings_are_deleted(self): node_settings = self.node.get_addon(self.addon_short_name) @@ -52,4 +52,4 @@ def test_log_deauthorized_when_node_settings_are_deleted(self): self.logger.log(action='node_deauthorized', save=True) last_log = self.node.logs.latest() - assert_equal(last_log.action, '{0}_node_deauthorized'.format(self.addon_short_name)) + assert_equal(last_log.action, f'{self.addon_short_name}_node_deauthorized') diff --git a/addons/base/tests/models.py b/addons/base/tests/models.py index de5a43ff3a8..3ea88cdcb68 100644 --- a/addons/base/tests/models.py +++ b/addons/base/tests/models.py @@ -1,6 +1,6 @@ import abc -import mock +from unittest import mock import pytest import pytz import datetime @@ -20,7 +20,7 @@ pytestmark = pytest.mark.django_db -class OAuthAddonModelTestSuiteMixinBase(object): +class OAuthAddonModelTestSuiteMixinBase: ___metaclass__ = abc.ABCMeta @@ -179,7 +179,7 @@ def _node_settings_class_kwargs(self, node, user_settings): } def setUp(self): - super(OAuthAddonNodeSettingsTestSuiteMixin, self).setUp() + super().setUp() self.node = ProjectFactory() self.user = self.node.creator self.external_account = self.ExternalAccountFactory() @@ -334,7 +334,7 @@ def test_deauthorize(self): assert_is(self.node_settings.folder_id, None) last_log = self.node.logs.first() - assert_equal(last_log.action, '{0}_node_deauthorized'.format(self.short_name)) + assert_equal(last_log.action, f'{self.short_name}_node_deauthorized') params = last_log.params assert_in('node', params) assert_in('project', params) @@ -347,7 +347,7 @@ def test_set_folder(self): assert_equal(self.node_settings.folder_id, folder_id) # Log was saved last_log = self.node.logs.first() - assert_equal(last_log.action, '{0}_folder_selected'.format(self.short_name)) + assert_equal(last_log.action, f'{self.short_name}_folder_selected') def test_set_user_auth(self): node_settings = self.NodeSettingsFactory() @@ -365,7 +365,7 @@ def test_set_user_auth(self): assert_equal(node_settings.user_settings, user_settings) # A log was saved last_log = node_settings.owner.logs.first() - assert_equal(last_log.action, '{0}_node_authorized'.format(self.short_name)) + assert_equal(last_log.action, f'{self.short_name}_node_authorized') log_params = last_log.params assert_equal(log_params['node'], node_settings.owner._id) assert_equal(last_log.user, user_settings.owner) @@ -408,7 +408,7 @@ def test_create_log(self): assert_equal(self.node.logs.count(), nlog + 1) assert_equal( self.node.logs.latest().action, - '{0}_{1}'.format(self.short_name, action), + f'{self.short_name}_{action}', ) assert_equal( self.node.logs.latest().params['path'], @@ -478,7 +478,7 @@ class OAuthCitationsNodeSettingsTestSuiteMixin( OAuthCitationsTestSuiteMixinBase): def setUp(self): - super(OAuthCitationsNodeSettingsTestSuiteMixin, self).setUp() + super().setUp() self.user_settings.grant_oauth_access( node=self.node, external_account=self.external_account, @@ -592,7 +592,7 @@ def test_set_folder(self): ) log = self.node.logs.latest() - assert_equal(log.action, '{}_folder_selected'.format(self.short_name)) + assert_equal(log.action, f'{self.short_name}_folder_selected') assert_equal(log.params['folder_id'], folder_id) assert_equal(log.params['folder_name'], folder_name) @@ -638,7 +638,7 @@ def ApiExceptionClass(self): pass def setUp(self): - super(CitationAddonProviderTestSuiteMixin, self).setUp() + super().setUp() self.provider = self.OAuthProviderClass() @abc.abstractmethod diff --git a/addons/base/tests/serializers.py b/addons/base/tests/serializers.py index 9c331bca343..c145d967fc1 100644 --- a/addons/base/tests/serializers.py +++ b/addons/base/tests/serializers.py @@ -1,6 +1,6 @@ import abc -import mock +from unittest import mock from framework.auth import Auth from nose.tools import (assert_equal, assert_false, assert_in, assert_is_not_none, assert_raises, assert_true) @@ -9,7 +9,7 @@ from website.util import web_url_for -class AddonSerializerTestSuiteMixin(object): +class AddonSerializerTestSuiteMixin: __metaclass__ = abc.ABCMeta @@ -38,7 +38,7 @@ def required_settings_authorized(self): pass def setUp(self): - super(AddonSerializerTestSuiteMixin, self).setUp() + super().setUp() self.user = AuthUserFactory() self.node = ProjectFactory(creator=self.user) self.set_user_settings(self.user) diff --git a/addons/base/tests/utils.py b/addons/base/tests/utils.py index 432371077f6..5ab0bbbf45c 100644 --- a/addons/base/tests/utils.py +++ b/addons/base/tests/utils.py @@ -7,7 +7,7 @@ from website.settings import MFR_SERVER_URL -class MockFolder(dict, object): +class MockFolder(dict): def __init__(self): self.name = 'Fake Folder' @@ -18,7 +18,7 @@ def __init__(self): self['id'] = 'Fake Key' -class MockLibrary(dict, object): +class MockLibrary(dict): def __init__(self): self.name = 'Fake Library' diff --git a/addons/base/tests/views.py b/addons/base/tests/views.py index 9a78ca49cdf..99d5b31bd97 100644 --- a/addons/base/tests/views.py +++ b/addons/base/tests/views.py @@ -1,5 +1,5 @@ from future.moves.urllib.parse import urlparse, parse_qs -import mock +from unittest import mock from nose.tools import * # noqa import responses from rest_framework import status as http_status @@ -79,7 +79,7 @@ def test_delete_external_account_not_owner(self): class OAuthAddonConfigViewsTestCaseMixin(OAuthAddonTestCaseMixin): def __init__(self, *args, **kwargs): - super(OAuthAddonConfigViewsTestCaseMixin,self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self.node_settings = None @property @@ -110,7 +110,7 @@ def test_import_auth(self): node = ProjectFactory(creator=self.user) node_settings = node.get_or_add_addon(self.ADDON_SHORT_NAME, auth=Auth(self.user)) node.save() - url = node.api_url_for('{0}_import_auth'.format(self.ADDON_SHORT_NAME)) + url = node.api_url_for(f'{self.ADDON_SHORT_NAME}_import_auth') res = self.app.put_json(url, { 'external_account_id': ea._id }, auth=self.user.auth) @@ -121,7 +121,7 @@ def test_import_auth(self): node.reload() last_log = node.logs.latest() - assert_equal(last_log.action, '{0}_node_authorized'.format(self.ADDON_SHORT_NAME)) + assert_equal(last_log.action, f'{self.ADDON_SHORT_NAME}_node_authorized') def test_import_auth_invalid_account(self): ea = self.ExternalAccountFactory() @@ -129,7 +129,7 @@ def test_import_auth_invalid_account(self): node = ProjectFactory(creator=self.user) node.add_addon(self.ADDON_SHORT_NAME, auth=self.auth) node.save() - url = node.api_url_for('{0}_import_auth'.format(self.ADDON_SHORT_NAME)) + url = node.api_url_for(f'{self.ADDON_SHORT_NAME}_import_auth') res = self.app.put_json(url, { 'external_account_id': ea._id }, auth=self.user.auth, expect_errors=True) @@ -146,7 +146,7 @@ def test_import_auth_cant_write_node(self): node.add_contributor(user, permissions=permissions.READ, auth=self.auth, save=True) node.add_addon(self.ADDON_SHORT_NAME, auth=self.auth) node.save() - url = node.api_url_for('{0}_import_auth'.format(self.ADDON_SHORT_NAME)) + url = node.api_url_for(f'{self.ADDON_SHORT_NAME}_import_auth') res = self.app.put_json(url, { 'external_account_id': ea._id }, auth=user.auth, expect_errors=True) @@ -154,7 +154,7 @@ def test_import_auth_cant_write_node(self): def test_set_config(self): self.node_settings.set_auth(self.external_account, self.user) - url = self.project.api_url_for('{0}_set_config'.format(self.ADDON_SHORT_NAME)) + url = self.project.api_url_for(f'{self.ADDON_SHORT_NAME}_set_config') res = self.app.put_json(url, { 'selected': self.folder }, auth=self.user.auth) @@ -162,12 +162,12 @@ def test_set_config(self): self.project.reload() assert_equal( self.project.logs.latest().action, - '{0}_folder_selected'.format(self.ADDON_SHORT_NAME) + f'{self.ADDON_SHORT_NAME}_folder_selected' ) assert_equal(res.json['result']['folder']['path'], self.folder['path']) def test_get_config(self): - url = self.project.api_url_for('{0}_get_config'.format(self.ADDON_SHORT_NAME)) + url = self.project.api_url_for(f'{self.ADDON_SHORT_NAME}_get_config') with mock.patch.object(type(self.Serializer()), 'credentials_are_valid', return_value=True): res = self.app.get(url, auth=self.user.auth) assert_equal(res.status_code, http_status.HTTP_200_OK) @@ -180,19 +180,19 @@ def test_get_config(self): assert_equal(serialized, res.json['result']) def test_get_config_unauthorized(self): - url = self.project.api_url_for('{0}_get_config'.format(self.ADDON_SHORT_NAME)) + url = self.project.api_url_for(f'{self.ADDON_SHORT_NAME}_get_config') user = AuthUserFactory() self.project.add_contributor(user, permissions=permissions.READ, auth=self.auth, save=True) res = self.app.get(url, auth=user.auth, expect_errors=True) assert_equal(res.status_code, http_status.HTTP_403_FORBIDDEN) def test_get_config_not_logged_in(self): - url = self.project.api_url_for('{0}_get_config'.format(self.ADDON_SHORT_NAME)) + url = self.project.api_url_for(f'{self.ADDON_SHORT_NAME}_get_config') res = self.app.get(url, auth=None, expect_errors=True) assert_equal(res.status_code, http_status.HTTP_302_FOUND) def test_account_list_single(self): - url = api_url_for('{0}_account_list'.format(self.ADDON_SHORT_NAME)) + url = api_url_for(f'{self.ADDON_SHORT_NAME}_account_list') res = self.app.get(url, auth=self.user.auth) assert_equal(res.status_code, http_status.HTTP_200_OK) assert_in('accounts', res.json) @@ -203,14 +203,14 @@ def test_account_list_multiple(self): self.user.external_accounts.add(ea) self.user.save() - url = api_url_for('{0}_account_list'.format(self.ADDON_SHORT_NAME)) + url = api_url_for(f'{self.ADDON_SHORT_NAME}_account_list') res = self.app.get(url, auth=self.user.auth) assert_equal(res.status_code, http_status.HTTP_200_OK) assert_in('accounts', res.json) assert_equal(len(res.json['accounts']), 2) def test_account_list_not_authorized(self): - url = api_url_for('{0}_account_list'.format(self.ADDON_SHORT_NAME)) + url = api_url_for(f'{self.ADDON_SHORT_NAME}_account_list') res = self.app.get(url, auth=None, expect_errors=True) assert_equal(res.status_code, http_status.HTTP_302_FOUND) @@ -220,13 +220,13 @@ def test_folder_list(self): # subclass, mock any API calls, and call super. self.node_settings.set_auth(self.external_account, self.user) self.node_settings.save() - url = self.project.api_url_for('{0}_folder_list'.format(self.ADDON_SHORT_NAME)) + url = self.project.api_url_for(f'{self.ADDON_SHORT_NAME}_folder_list') res = self.app.get(url, auth=self.user.auth) assert_equal(res.status_code, http_status.HTTP_200_OK) # TODO test result serialization? def test_deauthorize_node(self): - url = self.project.api_url_for('{0}_deauthorize_node'.format(self.ADDON_SHORT_NAME)) + url = self.project.api_url_for(f'{self.ADDON_SHORT_NAME}_deauthorize_node') res = self.app.delete(url, auth=self.user.auth) assert_equal(res.status_code, http_status.HTTP_200_OK) self.node_settings.reload() @@ -236,7 +236,7 @@ def test_deauthorize_node(self): # A log event was saved self.project.reload() last_log = self.project.logs.latest() - assert_equal(last_log.action, '{0}_node_deauthorized'.format(self.ADDON_SHORT_NAME)) + assert_equal(last_log.action, f'{self.ADDON_SHORT_NAME}_node_deauthorized') class OAuthCitationAddonConfigViewsTestCaseMixin(OAuthAddonConfigViewsTestCaseMixin): @@ -284,7 +284,7 @@ def mockResponses(self): raise NotImplementedError() def setUp(self): - super(OAuthCitationAddonConfigViewsTestCaseMixin, self).setUp() + super().setUp() self.mock_verify = mock.patch.object( self.client, '_verify_client_validity' @@ -293,12 +293,12 @@ def setUp(self): def tearDown(self): self.mock_verify.stop() - super(OAuthCitationAddonConfigViewsTestCaseMixin, self).tearDown() + super().tearDown() def test_set_config(self): with mock.patch.object(self.client, '_folder_metadata') as mock_metadata: mock_metadata.return_value = self.folder - url = self.project.api_url_for('{0}_set_config'.format(self.ADDON_SHORT_NAME)) + url = self.project.api_url_for(f'{self.ADDON_SHORT_NAME}_set_config') res = self.app.put_json(url, { 'external_list_id': self.folder.json['id'], 'external_list_name': self.folder.name, @@ -307,7 +307,7 @@ def test_set_config(self): self.project.reload() assert_equal( self.project.logs.latest().action, - '{0}_folder_selected'.format(self.ADDON_SHORT_NAME) + f'{self.ADDON_SHORT_NAME}_folder_selected' ) assert_equal(res.json['result']['folder']['name'], self.folder.name) @@ -316,7 +316,7 @@ def test_get_config(self): mock_metadata.return_value = self.folder self.node_settings.api._client = 'client' self.node_settings.save() - url = self.project.api_url_for('{0}_get_config'.format(self.ADDON_SHORT_NAME)) + url = self.project.api_url_for(f'{self.ADDON_SHORT_NAME}_get_config') res = self.app.get(url, auth=self.user.auth) assert_equal(res.status_code, http_status.HTTP_200_OK) assert_in('result', res.json) @@ -332,7 +332,7 @@ def test_folder_list(self): with mock.patch.object(self.client, '_get_folders'): self.node_settings.set_auth(self.external_account, self.user) self.node_settings.save() - url = self.project.api_url_for('{0}_citation_list'.format(self.ADDON_SHORT_NAME)) + url = self.project.api_url_for(f'{self.ADDON_SHORT_NAME}_citation_list') res = self.app.get(url, auth=self.user.auth) assert_equal(res.status_code, http_status.HTTP_200_OK) @@ -388,7 +388,7 @@ def test_citation_list_root(self): ) res = self.app.get( - self.project.api_url_for('{0}_citation_list'.format(self.ADDON_SHORT_NAME)), + self.project.api_url_for(f'{self.ADDON_SHORT_NAME}_citation_list'), auth=self.user.auth ) root = res.json['contents'][0] @@ -418,7 +418,7 @@ def test_citation_list_non_root(self): ) res = self.app.get( - self.project.api_url_for('{0}_citation_list'.format(self.ADDON_SHORT_NAME), list_id='ROOT'), + self.project.api_url_for(f'{self.ADDON_SHORT_NAME}_citation_list', list_id='ROOT'), auth=self.user.auth ) @@ -455,7 +455,7 @@ def test_citation_list_non_linked_or_child_non_authorizer(self): ) res = self.app.get( - self.project.api_url_for('{0}_citation_list'.format(self.ADDON_SHORT_NAME), list_id='ROOT'), + self.project.api_url_for(f'{self.ADDON_SHORT_NAME}_citation_list', list_id='ROOT'), auth=non_authorizing_user.auth, expect_errors=True ) diff --git a/addons/base/utils.py b/addons/base/utils.py index 92be45b3739..a22e6c30406 100644 --- a/addons/base/utils.py +++ b/addons/base/utils.py @@ -42,13 +42,13 @@ def format_last_known_metadata(auth, node, file, error_type): parts = [ """
This file was """ if last_seen or hashes or path or size else '', """last seen on {} UTC """.format(last_seen.strftime('%c')) if last_seen else '', - """and found at path {} """.format(markupsafe.escape(path)) if last_seen and path else '', - """last found at path {} """.format(markupsafe.escape(path)) if not last_seen and path else '', - """with a file size of {} bytes""".format(size) if size and (last_seen or path) else '', - """last seen with a file size of {} bytes""".format(size) if size and not (last_seen or path) else '', + f"""and found at path {markupsafe.escape(path)} """ if last_seen and path else '', + f"""last found at path {markupsafe.escape(path)} """ if not last_seen and path else '', + f"""with a file size of {size} bytes""" if size and (last_seen or path) else '', + f"""last seen with a file size of {size} bytes""" if size and not (last_seen or path) else '', """.

""" if last_seen or hashes or path or size else '', """Hashes of last seen version:

{}

""".format( - '
'.join(['{}: {}'.format(k, v) for k, v in hashes.items()]) + '
'.join([f'{k}: {v}' for k, v in hashes.items()]) ) if hashes else '', # TODO: Format better for UI msg ] diff --git a/addons/base/views.py b/addons/base/views.py index d3e5007166a..67fddda3aed 100644 --- a/addons/base/views.py +++ b/addons/base/views.py @@ -66,7 +66,7 @@ # import so that associated listener is instantiated and gets emails from website.notifications.events.files import FileEvent # noqa -ERROR_MESSAGES = {'FILE_GONE': u""" +ERROR_MESSAGES = {'FILE_GONE': """ @@ -77,7 +77,7 @@

It was deleted by {deleted_by} on {deleted_on}.

""", - 'FILE_GONE_ACTOR_UNKNOWN': u""" + 'FILE_GONE_ACTOR_UNKNOWN': """ @@ -88,7 +88,7 @@

It was deleted on {deleted_on}.

""", - 'DONT_KNOW': u""" + 'DONT_KNOW': """ @@ -96,7 +96,7 @@

File not found at {provider}.

""", - 'BLAME_PROVIDER': u""" + 'BLAME_PROVIDER': """ @@ -108,7 +108,7 @@

You may wish to verify this through {provider}'s website.

""", - 'FILE_SUSPENDED': u""" + 'FILE_SUSPENDED': """ @@ -224,7 +224,7 @@ def make_auth(user): if user is not None: return { 'id': user._id, - 'email': '{}@osf.io'.format(user._id), + 'email': f'{user._id}@osf.io', 'name': user.fullname, } return {} @@ -387,10 +387,10 @@ def get_auth(auth, **kwargs): 'create_folder': NodeLog.FOLDER_CREATED, } -DOWNLOAD_ACTIONS = set([ +DOWNLOAD_ACTIONS = { 'download_file', 'download_zip', -]) +} @must_be_signed @no_auto_transaction @@ -711,19 +711,19 @@ def addon_view_or_download_file(auth, path, provider, **kwargs): object_text = markupsafe.escape(getattr(target, 'project_or_component', 'this object')) raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data={ 'message_short': 'Bad Request', - 'message_long': 'The {} add-on containing {} is no longer connected to {}.'.format(provider_safe, path_safe, object_text) + 'message_long': f'The {provider_safe} add-on containing {path_safe} is no longer connected to {object_text}.' }) if not node_addon.has_auth: raise HTTPError(http_status.HTTP_401_UNAUTHORIZED, data={ 'message_short': 'Unauthorized', - 'message_long': 'The {} add-on containing {} is no longer authorized.'.format(provider_safe, path_safe) + 'message_long': f'The {provider_safe} add-on containing {path_safe} is no longer authorized.' }) if not node_addon.complete: raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data={ 'message_short': 'Bad Request', - 'message_long': 'The {} add-on containing {} is no longer configured.'.format(provider_safe, path_safe) + 'message_long': f'The {provider_safe} add-on containing {path_safe} is no longer configured.' }) savepoint_id = transaction.savepoint() @@ -809,7 +809,7 @@ def addon_view_or_download_file(auth, path, provider, **kwargs): format = extras.get('format') _, extension = os.path.splitext(file_node.name) # avoid rendering files with the same format type. - if format and '.{}'.format(format.lower()) != extension.lower(): + if format and f'.{format.lower()}' != extension.lower(): return redirect('{}/export?format={}&url={}'.format(get_mfr_url(target, provider), format, quote(file_node.generate_waterbutler_url( **dict(extras, direct=None, version=version.identifier, _internal=extras.get('mode') == 'render') )))) @@ -829,10 +829,10 @@ def addon_view_or_download_file(auth, path, provider, **kwargs): if len(request.path.strip('/').split('/')) > 1: guid = file_node.get_guid(create=True) - return redirect(furl.furl('/{}/'.format(guid._id)).set(args=extras).url) + return redirect(furl.furl(f'/{guid._id}/').set(args=extras).url) if isinstance(target, Preprint): # Redirecting preprint file guids to the preprint detail page - return redirect('/{}/'.format(target._id)) + return redirect(f'/{target._id}/') return addon_view_file(auth, target, file_node, version) @@ -877,7 +877,7 @@ def addon_view_or_download_quickfile(**kwargs): 'message_short': 'File Not Found', 'message_long': 'The requested file could not be found.' }) - return proxy_url('/project/{}/files/osfstorage/{}/'.format(file_.target._id, fid)) + return proxy_url(f'/project/{file_.target._id}/files/osfstorage/{fid}/') def addon_view_file(auth, node, file_node, version): # TODO: resolve circular import issue diff --git a/addons/bitbucket/api.py b/addons/bitbucket/api.py index e7176b6395b..c145c57fb23 100644 --- a/addons/bitbucket/api.py +++ b/addons/bitbucket/api.py @@ -15,7 +15,7 @@ def __init__(self, access_token=None): @property def _default_headers(self): if self.access_token: - return {'Authorization': 'Bearer {}'.format(self.access_token)} + return {'Authorization': f'Bearer {self.access_token}'} return {} @property diff --git a/addons/bitbucket/apps.py b/addons/bitbucket/apps.py index 12ed98d9222..d1f1946a42a 100644 --- a/addons/bitbucket/apps.py +++ b/addons/bitbucket/apps.py @@ -54,7 +54,7 @@ def bitbucket_hgrid_data(node_settings, auth, **kwargs): 'fetch': node_settings.owner.api_url + 'bitbucket/hgrid/' + (ref or ''), 'branch': node_settings.owner.api_url + 'bitbucket/hgrid/root/', 'zip': node_settings.owner.api_url + 'bitbucket/zipball/' + (ref or ''), - 'repo': 'https://bitbucket.com/{0}/{1}/branch/'.format(node_settings.user, node_settings.repo) + 'repo': f'https://bitbucket.com/{node_settings.user}/{node_settings.repo}/branch/' } branch_names = [each['name'] for each in branches] diff --git a/addons/bitbucket/migrations/0001_initial.py b/addons/bitbucket/migrations/0001_initial.py index dfde90f1fe5..e15bde518bd 100644 --- a/addons/bitbucket/migrations/0001_initial.py +++ b/addons/bitbucket/migrations/0001_initial.py @@ -1,6 +1,4 @@ -# -*- coding: utf-8 -*- # Generated by Django 1.11.29 on 2022-08-17 19:15 -from __future__ import unicode_literals import addons.base.models from django.db import migrations, models diff --git a/addons/bitbucket/migrations/0002_auto_20220817_1915.py b/addons/bitbucket/migrations/0002_auto_20220817_1915.py index 6646f72e6c5..d443615930b 100644 --- a/addons/bitbucket/migrations/0002_auto_20220817_1915.py +++ b/addons/bitbucket/migrations/0002_auto_20220817_1915.py @@ -1,6 +1,4 @@ -# -*- coding: utf-8 -*- # Generated by Django 1.11.29 on 2022-08-17 19:15 -from __future__ import unicode_literals from django.conf import settings from django.db import migrations, models diff --git a/addons/bitbucket/models.py b/addons/bitbucket/models.py index b9f22b79ce2..6da76009b11 100644 --- a/addons/bitbucket/models.py +++ b/addons/bitbucket/models.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - import markupsafe from django.db import models @@ -34,7 +32,7 @@ class BitbucketFile(BitbucketFileNode, File): def touch(self, auth_header, revision=None, commitSha=None, branch=None, **kwargs): revision = revision or commitSha or branch - return super(BitbucketFile, self).touch(auth_header, revision=revision, **kwargs) + return super().touch(auth_header, revision=revision, **kwargs) @property def _hashes(self): @@ -133,7 +131,7 @@ def folder_id(self): @property def folder_name(self): if self.complete: - return '{}/{}'.format(self.user, self.repo) + return f'{self.user}/{self.repo}' return None @property @@ -178,7 +176,7 @@ def deauthorize(self, auth=None, log=True): self.clear_auth() def delete(self, save=False): - super(NodeSettings, self).delete(save=False) + super().delete(save=False) self.deauthorize(log=False) if save: self.save() @@ -186,7 +184,7 @@ def delete(self, save=False): @property def repo_url(self): if self.user and self.repo: - return 'https://bitbucket.org/{0}/{1}/'.format( + return 'https://bitbucket.org/{}/{}/'.format( self.user, self.repo ) @@ -211,7 +209,7 @@ def fetch_access_token(self): # TODO: Delete me and replace with serialize_settings / Knockout def to_json(self, user): - ret = super(NodeSettings, self).to_json(user) + ret = super().to_json(user) user_settings = user.get_addon('bitbucket') ret.update({ 'user_has_auth': user_settings and user_settings.has_auth, @@ -238,7 +236,7 @@ def to_json(self, user): 'node_has_auth': True, 'bitbucket_user': self.user or '', 'bitbucket_repo': self.repo or '', - 'bitbucket_repo_full_name': '{0} / {1}'.format(self.user, self.repo) if (self.user and self.repo) else '', + 'bitbucket_repo_full_name': f'{self.user} / {self.repo}' if (self.user and self.repo) else '', 'auth_osf_name': owner.fullname, 'auth_osf_url': owner.url, 'auth_osf_id': owner._id, @@ -274,14 +272,14 @@ def create_waterbutler_log(self, auth, action, metadata): try: sha = metadata['extra']['commitSha'] urls = { - 'view': '{0}?commitSha={1}'.format(url, sha), - 'download': '{0}?action=download&commitSha={1}'.format(url, sha) + 'view': f'{url}?commitSha={sha}', + 'download': f'{url}?action=download&commitSha={sha}' } except KeyError: pass self.owner.add_log( - 'bitbucket_{0}'.format(action), + f'bitbucket_{action}', auth=auth, params={ 'project': self.owner.parent_id, @@ -371,7 +369,7 @@ def before_remove_contributor_message(self, node, removed): """ try: - message = (super(NodeSettings, self).before_remove_contributor_message(node, removed) + + message = (super().before_remove_contributor_message(node, removed) + 'You can download the contents of this repository before removing ' 'this contributor here.'.format( url=node.api_url + 'bitbucket/tarball/' @@ -397,8 +395,8 @@ def after_remove_contributor(self, node, removed, auth=None): self.user_settings = None self.save() message = ( - u'Because the Bitbucket add-on for {category} "{title}" was authenticated ' - u'by {user}, authentication information has been deleted.' + 'Because the Bitbucket add-on for {category} "{title}" was authenticated ' + 'by {user}, authentication information has been deleted.' ).format( category=markupsafe.escape(node.category_display), title=markupsafe.escape(node.title), @@ -408,7 +406,7 @@ def after_remove_contributor(self, node, removed, auth=None): if not auth or auth.user != removed: url = node.web_url_for('node_setting') message += ( - u' You can re-authenticate on the Settings page.' + ' You can re-authenticate on the Settings page.' ).format(url=url) # return message @@ -424,7 +422,7 @@ def after_fork(self, node, fork, user, save=True): :param bool save: Save settings after callback :return tuple: Tuple of cloned settings and alert message """ - clone = super(NodeSettings, self).after_fork( + clone = super().after_fork( node, fork, user, save=False ) diff --git a/addons/bitbucket/routes.py b/addons/bitbucket/routes.py index 07a28748446..9deb07f2428 100644 --- a/addons/bitbucket/routes.py +++ b/addons/bitbucket/routes.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - from framework.routing import Rule, json_renderer from addons.bitbucket import views diff --git a/addons/bitbucket/serializer.py b/addons/bitbucket/serializer.py index 6797ffd5749..65cc76d84b7 100644 --- a/addons/bitbucket/serializer.py +++ b/addons/bitbucket/serializer.py @@ -18,7 +18,7 @@ def credentials_are_valid(self, user_settings, client): def serialized_folder(self, node_settings): return { 'path': node_settings.repo, - 'name': '{0} / {1}'.format(node_settings.user, node_settings.repo), + 'name': f'{node_settings.user} / {node_settings.repo}', } @property diff --git a/addons/bitbucket/tests/factories.py b/addons/bitbucket/tests/factories.py index d5416c7c938..7220c54cbd7 100644 --- a/addons/bitbucket/tests/factories.py +++ b/addons/bitbucket/tests/factories.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - from factory import Sequence, SubFactory from factory.django import DjangoModelFactory from osf_tests.factories import ExternalAccountFactory, ProjectFactory, UserFactory @@ -9,8 +7,8 @@ class BitbucketAccountFactory(ExternalAccountFactory): provider = 'bitbucket' - provider_id = Sequence(lambda n: 'id-{0}'.format(n)) - oauth_key = Sequence(lambda n: 'key-{0}'.format(n)) + provider_id = Sequence(lambda n: f'id-{n}') + oauth_key = Sequence(lambda n: f'key-{n}') display_name = 'abc' diff --git a/addons/bitbucket/tests/test_models.py b/addons/bitbucket/tests/test_models.py index b2c38d0cd9f..3d7a722a7ab 100644 --- a/addons/bitbucket/tests/test_models.py +++ b/addons/bitbucket/tests/test_models.py @@ -1,6 +1,4 @@ -# -*- coding: utf-8 -*- - -import mock +from unittest import mock import pytest import unittest from nose.tools import * # noqa @@ -65,14 +63,14 @@ def test_serialize_settings(self): mock.PropertyMock() ) def test_complete_has_auth_not_verified(self): - super(TestNodeSettings, self).test_complete_has_auth_not_verified() + super().test_complete_has_auth_not_verified() @mock.patch('addons.bitbucket.api.BitbucketClient.repos') @mock.patch('addons.bitbucket.api.BitbucketClient.team_repos') def test_to_json(self, mock_repos, mock_team_repos): mock_repos.return_value = [] mock_team_repos.return_value = [] - super(TestNodeSettings, self).test_to_json() + super().test_to_json() @mock.patch('addons.bitbucket.api.BitbucketClient.repos') @mock.patch('addons.bitbucket.api.BitbucketClient.team_repos') @@ -114,7 +112,7 @@ class TestCallbacks(OsfTestCase): def setUp(self): - super(TestCallbacks, self).setUp() + super().setUp() self.project = ProjectFactory() self.consolidated_auth = Auth(self.project.creator) diff --git a/addons/bitbucket/tests/test_serializer.py b/addons/bitbucket/tests/test_serializer.py index 2f804ec6980..ae9e0c36aa4 100644 --- a/addons/bitbucket/tests/test_serializer.py +++ b/addons/bitbucket/tests/test_serializer.py @@ -1,7 +1,6 @@ -# -*- coding: utf-8 -*- """Serializer tests for the Bitbucket addon.""" -import mock +from unittest import mock from nose.tools import * # noqa (PEP8 asserts) import pytest diff --git a/addons/bitbucket/tests/test_views.py b/addons/bitbucket/tests/test_views.py index 13725b3f2c0..c17a213a9b8 100644 --- a/addons/bitbucket/tests/test_views.py +++ b/addons/bitbucket/tests/test_views.py @@ -1,7 +1,6 @@ -# -*- coding: utf-8 -*- from rest_framework import status as http_status -import mock +from unittest import mock import datetime import unittest import pytest @@ -38,7 +37,7 @@ class TestBitbucketAuthViews(BitbucketAddonTestCase, OAuthAddonAuthViewsTestCase mock.PropertyMock() ) def test_delete_external_account(self): - super(TestBitbucketAuthViews, self).test_delete_external_account() + super().test_delete_external_account() class TestBitbucketConfigViews(BitbucketAddonTestCase, OAuthAddonConfigViewsTestCaseMixin, OsfTestCase): @@ -49,14 +48,14 @@ class TestBitbucketConfigViews(BitbucketAddonTestCase, OAuthAddonConfigViewsTest ## Overrides ## def setUp(self): - super(TestBitbucketConfigViews, self).setUp() + super().setUp() self.mock_access_token = mock.patch('addons.bitbucket.models.BitbucketProvider.fetch_access_token') self.mock_access_token.return_value = mock.Mock() self.mock_access_token.start() def tearDown(self): self.mock_access_token.stop() - super(TestBitbucketConfigViews, self).tearDown() + super().tearDown() def test_folder_list(self): # BB only lists root folder (repos), this test is superfluous @@ -68,7 +67,7 @@ def test_set_config(self, mock_account, mock_repo): # BB selects repos, not folders, so this needs to be overriden mock_account.return_value = mock.Mock() mock_repo.return_value = 'repo_name' - url = self.project.api_url_for('{0}_set_config'.format(self.ADDON_SHORT_NAME)) + url = self.project.api_url_for(f'{self.ADDON_SHORT_NAME}_set_config') res = self.app.post_json(url, { 'bitbucket_user': 'octocat', 'bitbucket_repo': 'repo_name', @@ -77,14 +76,14 @@ def test_set_config(self, mock_account, mock_repo): self.project.reload() assert_equal( self.project.logs.latest().action, - '{0}_repo_linked'.format(self.ADDON_SHORT_NAME) + f'{self.ADDON_SHORT_NAME}_repo_linked' ) class TestBitbucketViews(OsfTestCase): def setUp(self): - super(TestBitbucketViews, self).setUp() + super().setUp() self.user = AuthUserFactory() self.consolidated_auth = Auth(user=self.user) @@ -192,8 +191,8 @@ def test_get_refs_sha_no_branch(self, mock_account): def check_hook_urls(self, urls, node, path, sha): url = node.web_url_for('addon_view_or_download_file', path=path, provider='bitbucket') expected_urls = { - 'view': '{0}?ref={1}'.format(url, sha), - 'download': '{0}?action=download&ref={1}'.format(url, sha) + 'view': f'{url}?ref={sha}', + 'download': f'{url}?action=download&ref={sha}' } assert_equal(urls['view'], expected_urls['view']) @@ -204,7 +203,7 @@ class TestBitbucketSettings(OsfTestCase): def setUp(self): - super(TestBitbucketSettings, self).setUp() + super().setUp() self.bitbucket = create_mock_bitbucket(user='fred', private=False) self.project = ProjectFactory() self.project.save() diff --git a/addons/bitbucket/tests/utils.py b/addons/bitbucket/tests/utils.py index 6f1312fae01..bfedc8d0ac6 100644 --- a/addons/bitbucket/tests/utils.py +++ b/addons/bitbucket/tests/utils.py @@ -1,4 +1,4 @@ -import mock +from unittest import mock from addons.bitbucket.api import BitbucketClient @@ -13,7 +13,7 @@ class BitbucketAddonTestCase(OAuthAddonTestCaseMixin, AddonTestCase): Provider = BitbucketProvider def set_node_settings(self, settings): - super(BitbucketAddonTestCase, self).set_node_settings(settings) + super().set_node_settings(settings) settings.repo = 'abc' settings.user = 'octo-cat' @@ -36,9 +36,9 @@ def create_mock_bitbucket(user='octo-cat', private=False): 'owner': {'username': user}, } bitbucket_mock.repos.return_value = [ - {'full_name': '{}/cow-problems-app'.format(user)}, - {'full_name': '{}/duck-problems-app'.format(user)}, - {'full_name': '{}/horse-problems-app'.format(user)}, + {'full_name': f'{user}/cow-problems-app'}, + {'full_name': f'{user}/duck-problems-app'}, + {'full_name': f'{user}/horse-problems-app'}, ] bitbucket_mock.team_repos.return_value = [ {'full_name': 'team-barn-devs/pig-problems-app'}, diff --git a/addons/bitbucket/views.py b/addons/bitbucket/views.py index df555703c34..0409595c9e1 100644 --- a/addons/bitbucket/views.py +++ b/addons/bitbucket/views.py @@ -1,5 +1,4 @@ """Views for the node settings page.""" -# -*- coding: utf-8 -*- from rest_framework import status as http_status import logging diff --git a/addons/boa/models.py b/addons/boa/models.py index bde51f59b2a..01578a89bdc 100644 --- a/addons/boa/models.py +++ b/addons/boa/models.py @@ -17,7 +17,7 @@ class BoaProvider(BasicAuthProviderMixin): def __init__(self, account=None, host=None, username=None, password=None): if username: username = username.lower() - super(BoaProvider, self).__init__(account=account, host=host, username=username, password=password) + super().__init__(account=account, host=host, username=username, password=password) def __repr__(self): return '<{name}: {status}>'.format( @@ -32,7 +32,7 @@ class UserSettings(BaseOAuthUserSettings): serializer = BoaSerializer def to_json(self, user): - ret = super(UserSettings, self).to_json(user) + ret = super().to_json(user) ret['hosts'] = DEFAULT_HOSTS return ret diff --git a/addons/boa/serializer.py b/addons/boa/serializer.py index 7a3275ff170..42c738e62a8 100644 --- a/addons/boa/serializer.py +++ b/addons/boa/serializer.py @@ -52,19 +52,19 @@ def addon_serialized_urls(self): @property def serialized_node_settings(self): - result = super(BoaSerializer, self).serialized_node_settings + result = super().serialized_node_settings result['hosts'] = DEFAULT_HOSTS return result @property def serialized_user_settings(self): - result = super(BoaSerializer, self).serialized_user_settings + result = super().serialized_user_settings result['hosts'] = DEFAULT_HOSTS return result def serialize_settings(self, node_settings, current_user, client=None): if client is not None: sentry.log_message('Client ignored for Boa Serializer in serialize_settings()') - ret = super(BoaSerializer, self).serialize_settings(node_settings, current_user, client=client) + ret = super().serialize_settings(node_settings, current_user, client=client) ret['hosts'] = DEFAULT_HOSTS return ret diff --git a/addons/boa/tests/factories.py b/addons/boa/tests/factories.py index c432eea1c24..7d08be56cac 100644 --- a/addons/boa/tests/factories.py +++ b/addons/boa/tests/factories.py @@ -12,9 +12,9 @@ class BoaAccountFactory(ExternalAccountFactory): provider = 'boa' provider_name = 'Fake Boa Provider' - provider_id = Sequence(lambda n: '{0}:{1}-{2}'.format(BOA_HOST, BOA_USERNAME, n)) - profile_url = Sequence(lambda n: 'http://localhost:9999/{0}/boa'.format(n)) - oauth_secret = Sequence(lambda n: 'secret-{0}'.format(n)) + provider_id = Sequence(lambda n: f'{BOA_HOST}:{BOA_USERNAME}-{n}') + profile_url = Sequence(lambda n: f'http://localhost:9999/{n}/boa') + oauth_secret = Sequence(lambda n: f'secret-{n}') oauth_key = BOA_PASSWORD display_name = 'Fake Boa' diff --git a/addons/boa/tests/test_serializer.py b/addons/boa/tests/test_serializer.py index 100c044797f..058ed018533 100644 --- a/addons/boa/tests/test_serializer.py +++ b/addons/boa/tests/test_serializer.py @@ -1,4 +1,4 @@ -import mock +from unittest import mock import pytest from tests.base import OsfTestCase @@ -18,11 +18,11 @@ def setUp(self): self.mock_credentials = mock.patch('addons.boa.serializer.BoaSerializer.credentials_are_valid') self.mock_credentials.return_value = True self.mock_credentials.start() - super(TestBoaSerializer, self).setUp() + super().setUp() def tearDown(self): self.mock_credentials.stop() - super(TestBoaSerializer, self).tearDown() + super().tearDown() def test_serialize_settings_authorized_folder_is_set(self): self.set_provider_id(pid='foo') diff --git a/addons/boa/tests/test_tasks.py b/addons/boa/tests/test_tasks.py index e0807823a9f..c79b7f5b55f 100644 --- a/addons/boa/tests/test_tasks.py +++ b/addons/boa/tests/test_tasks.py @@ -2,7 +2,7 @@ from boaapi.boa_client import BoaException from boaapi.status import CompilerStatus, ExecutionStatus from http.client import HTTPMessage -import mock +from unittest import mock import pytest from unittest.mock import ANY, MagicMock from urllib.error import HTTPError @@ -21,13 +21,13 @@ class AsyncMock(MagicMock): async def __call__(self, *args, **kwargs): - return super(AsyncMock, self).__call__(*args, **kwargs) + return super().__call__(*args, **kwargs) class TestBoaErrorHandling(OsfTestCase): def setUp(self): - super(TestBoaErrorHandling, self).setUp() + super().setUp() self.error_message = 'fake-error-message' self.user_username = 'fake-user-username' self.user_fullname = 'fake-user-fullname' @@ -40,7 +40,7 @@ def setUp(self): self.job_id = '1a2b3c4d5e6f7g8' def tearDown(self): - super(TestBoaErrorHandling, self).tearDown() + super().tearDown() def test_boa_error_code(self): assert BoaErrorCode.NO_ERROR == -1 @@ -95,7 +95,7 @@ def test_handle_boa_error(self): class TestSubmitToBoa(OsfTestCase): def setUp(self): - super(TestSubmitToBoa, self).setUp() + super().setUp() self.host = 'http://locahost:9999/boa/?q=boa/api' self.username = 'fake-boa-username' self.password = 'fake-boa-password' @@ -109,7 +109,7 @@ def setUp(self): self.output_upload_url = f'http://localhost:7777/v1/resources/{self.project_guid}/providers/osfstorage/?kind=file' def tearDown(self): - super(TestSubmitToBoa, self).tearDown() + super().tearDown() def test_submit_to_boa_async_called(self): with mock.patch( @@ -138,7 +138,7 @@ def test_submit_to_boa_async_called(self): class TestSubmitToBoaAsync(OsfTestCase, AsyncTestCase): def setUp(self): - super(TestSubmitToBoaAsync, self).setUp() + super().setUp() self.host = 'http://locahost:9999/boa/?q=boa/api' self.username = 'fake-boa-username' self.password = 'fake-boa-password' @@ -168,7 +168,7 @@ def setUp(self): boa_settings.MAX_JOB_WAITING_TIME = DEFAULT_MAX_JOB_WAITING_TIME def tearDown(self): - super(TestSubmitToBoaAsync, self).tearDown() + super().tearDown() async def test_submit_success(self): with mock.patch('osf.models.user.OSFUser.objects.get', return_value=self.user), \ diff --git a/addons/boa/tests/test_views.py b/addons/boa/tests/test_views.py index 1cd4516cf86..30d052c51fa 100644 --- a/addons/boa/tests/test_views.py +++ b/addons/boa/tests/test_views.py @@ -1,4 +1,4 @@ -import mock +from unittest import mock import pytest from rest_framework import status as http_status @@ -29,7 +29,7 @@ def test_oauth_finish(self): class TestConfigViews(BoaBasicAuthAddonTestCase, OAuthAddonConfigViewsTestCaseMixin, OsfTestCase): def setUp(self): - super(TestConfigViews, self).setUp() + super().setUp() self.mock_boa_client_login = mock.patch('boaapi.boa_client.BoaClient.login') self.mock_boa_client_close = mock.patch('boaapi.boa_client.BoaClient.close') self.mock_boa_client_login.start() @@ -38,7 +38,7 @@ def setUp(self): def tearDown(self): self.mock_boa_client_close.stop() self.mock_boa_client_login.stop() - super(TestConfigViews, self).tearDown() + super().tearDown() def test_folder_list(self): """Not applicable to remote computing add-ons.""" @@ -77,7 +77,7 @@ def test_get_config_owner_with_external_account(self): assert self.node_settings.external_account is not None assert serialized['validCredentials'] is True - url = self.project.api_url_for('{0}_get_config'.format(self.ADDON_SHORT_NAME)) + url = self.project.api_url_for(f'{self.ADDON_SHORT_NAME}_get_config') res = self.app.get(url, auth=self.user.auth) assert res.status_code == http_status.HTTP_200_OK assert 'result' in res.json @@ -93,7 +93,7 @@ def test_get_config_owner_without_external_account(self): assert self.node_settings.external_account is None assert serialized['validCredentials'] is False - url = self.project.api_url_for('{0}_get_config'.format(self.ADDON_SHORT_NAME)) + url = self.project.api_url_for(f'{self.ADDON_SHORT_NAME}_get_config') res = self.app.get(url, auth=self.user.auth) assert res.status_code == http_status.HTTP_200_OK assert 'result' in res.json @@ -112,7 +112,7 @@ def test_get_config_write_contrib_with_external_account(self): assert self.node_settings.external_account is not None assert serialized['validCredentials'] is True - url = self.project.api_url_for('{0}_get_config'.format(self.ADDON_SHORT_NAME)) + url = self.project.api_url_for(f'{self.ADDON_SHORT_NAME}_get_config') res = self.app.get(url, auth=user_write.auth) assert res.status_code == http_status.HTTP_200_OK assert 'result' in res.json @@ -130,7 +130,7 @@ def test_get_config_write_contrib_without_external_account(self): assert self.node_settings.external_account is None assert serialized['validCredentials'] is False - url = self.project.api_url_for('{0}_get_config'.format(self.ADDON_SHORT_NAME)) + url = self.project.api_url_for(f'{self.ADDON_SHORT_NAME}_get_config') res = self.app.get(url, auth=user_write.auth) assert res.status_code == http_status.HTTP_200_OK assert 'result' in res.json @@ -149,7 +149,7 @@ def test_get_config_admin_contrib_with_external_account(self): assert self.node_settings.external_account is not None assert serialized['validCredentials'] is True - url = self.project.api_url_for('{0}_get_config'.format(self.ADDON_SHORT_NAME)) + url = self.project.api_url_for(f'{self.ADDON_SHORT_NAME}_get_config') res = self.app.get(url, auth=user_admin.auth) assert res.status_code == http_status.HTTP_200_OK assert 'result' in res.json @@ -167,7 +167,7 @@ def test_get_config_admin_contrib_without_external_account(self): assert self.node_settings.external_account is None assert serialized['validCredentials'] is False - url = self.project.api_url_for('{0}_get_config'.format(self.ADDON_SHORT_NAME)) + url = self.project.api_url_for(f'{self.ADDON_SHORT_NAME}_get_config') res = self.app.get(url, auth=user_admin.auth) assert res.status_code == http_status.HTTP_200_OK assert 'result' in res.json @@ -178,7 +178,7 @@ def test_get_config_read_contrib_with_valid_credentials(self): user_read_only = AuthUserFactory() self.project.add_contributor(user_read_only, permissions=permissions.READ, auth=self.auth, save=True) - url = self.project.api_url_for('{0}_get_config'.format(self.ADDON_SHORT_NAME)) + url = self.project.api_url_for(f'{self.ADDON_SHORT_NAME}_get_config') with mock.patch.object(type(self.Serializer()), 'credentials_are_valid', return_value=True): res = self.app.get(url, auth=user_read_only.auth, expect_errors=True) assert res.status_code == http_status.HTTP_403_FORBIDDEN @@ -188,7 +188,7 @@ def test_get_config_read_contrib_without_valid_credentials(self): user_read_only = AuthUserFactory() self.project.add_contributor(user_read_only, permissions=permissions.READ, auth=self.auth, save=True) - url = self.project.api_url_for('{0}_get_config'.format(self.ADDON_SHORT_NAME)) + url = self.project.api_url_for(f'{self.ADDON_SHORT_NAME}_get_config') with mock.patch.object(type(self.Serializer()), 'credentials_are_valid', return_value=False): res = self.app.get(url, auth=user_read_only.auth, expect_errors=True) assert res.status_code == http_status.HTTP_403_FORBIDDEN @@ -197,7 +197,7 @@ def test_get_config_read_contrib_without_valid_credentials(self): class TestBoaSubmitViews(BoaBasicAuthAddonTestCase, OsfTestCase): def setUp(self): - super(TestBoaSubmitViews, self).setUp() + super().setUp() self.folder_name = 'fake_boa_folder' self.file_name = 'fake_boa_file.boa' self.file_size = 255 @@ -238,7 +238,7 @@ def setUp(self): } def tearDown(self): - super(TestBoaSubmitViews, self).tearDown() + super().tearDown() def test_boa_submit_job_from_addon_root(self): with mock.patch('addons.boa.tasks.submit_to_boa.s', return_value=BoaErrorCode.NO_ERROR) as mock_submit_s: diff --git a/addons/boa/tests/utils.py b/addons/boa/tests/utils.py index b03e9d803c9..c5ad1f474b0 100644 --- a/addons/boa/tests/utils.py +++ b/addons/boa/tests/utils.py @@ -3,7 +3,7 @@ from addons.boa.tests.factories import BoaAccountFactory, BoaNodeSettingsFactory, BoaUserSettingsFactory -class BoaAddonTestCaseBaseMixin(object): +class BoaAddonTestCaseBaseMixin: short_name = 'boa' full_name = 'Boa' @@ -22,12 +22,12 @@ class BoaAddonTestCaseBaseMixin(object): class BoaBasicAuthAddonTestCase(BoaAddonTestCaseBaseMixin, OAuthAddonTestCaseMixin, AddonTestCase): def __init__(self, *args, **kwargs): - super(BoaBasicAuthAddonTestCase, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self.auth = None self.external_account = None def set_user_settings(self, settings): - super(BoaBasicAuthAddonTestCase, self).set_user_settings(settings) + super().set_user_settings(settings) def set_node_settings(self, settings): - super(BoaBasicAuthAddonTestCase, self).set_node_settings(settings) + super().set_node_settings(settings) diff --git a/addons/boa/views.py b/addons/boa/views.py index 2b82d4c91bd..05cf37729c5 100644 --- a/addons/boa/views.py +++ b/addons/boa/views.py @@ -50,7 +50,7 @@ def boa_add_user_account(auth, **kwargs): except ValidationError: provider.account = ExternalAccount.objects.get( provider=provider.short_name, - provider_id='{}:{}'.format(BOA_API_ENDPOINT, username).lower() + provider_id=f'{BOA_API_ENDPOINT}:{username}'.lower() ) if provider.account.oauth_key != password: provider.account.oauth_key = password diff --git a/addons/box/migrations/0001_initial.py b/addons/box/migrations/0001_initial.py index f2053798e95..0b9a6c40caf 100644 --- a/addons/box/migrations/0001_initial.py +++ b/addons/box/migrations/0001_initial.py @@ -1,6 +1,4 @@ -# -*- coding: utf-8 -*- # Generated by Django 1.11.29 on 2022-08-17 19:15 -from __future__ import unicode_literals import addons.base.models from django.db import migrations, models diff --git a/addons/box/migrations/0002_auto_20220817_1915.py b/addons/box/migrations/0002_auto_20220817_1915.py index 58be50b8bea..3805ee039bc 100644 --- a/addons/box/migrations/0002_auto_20220817_1915.py +++ b/addons/box/migrations/0002_auto_20220817_1915.py @@ -1,6 +1,4 @@ -# -*- coding: utf-8 -*- # Generated by Django 1.11.29 on 2022-08-17 19:15 -from __future__ import unicode_literals from django.conf import settings from django.db import migrations, models diff --git a/addons/box/models.py b/addons/box/models.py index 88108a78bc1..8230d8a09bb 100644 --- a/addons/box/models.py +++ b/addons/box/models.py @@ -71,7 +71,7 @@ def handle_callback(self, response): return { 'provider_id': about['id'], 'display_name': about['name'], - 'profile_url': 'https://app.box.com/profile/{0}'.format(about['id']) + 'profile_url': 'https://app.box.com/profile/{}'.format(about['id']) } @@ -118,7 +118,7 @@ def api(self): @property def display_name(self): - return '{0}: {1}'.format(self.config.full_name, self.folder_id) + return f'{self.config.full_name}: {self.folder_id}' def fetch_full_folder_path(self): return self.folder_path @@ -134,7 +134,7 @@ def get_folders(self, **kwargs): 'name': '/ (Full Box)', 'urls': { # 'folders': node.api_url_for('box_folder_list', folderId=0), - 'folders': api_v2_url('nodes/{}/addons/box/folders/'.format(self.owner._id), + 'folders': api_v2_url(f'nodes/{self.owner._id}/addons/box/folders/', params={'id': '0'} ) } @@ -169,7 +169,7 @@ def get_folders(self, **kwargs): 'name': item['name'], 'path': os.path.join(folder_path, item['name']).replace('All Files', ''), 'urls': { - 'folders': api_v2_url('nodes/{}/addons/box/folders/'.format(self.owner._id), + 'folders': api_v2_url(f'nodes/{self.owner._id}/addons/box/folders/', params={'id': item['id']} ) } @@ -237,7 +237,7 @@ def serialize_waterbutler_settings(self): def create_waterbutler_log(self, auth, action, metadata): self.owner.add_log( - 'box_{0}'.format(action), + f'box_{action}', auth=auth, params={ 'path': metadata['materialized'], diff --git a/addons/box/routes.py b/addons/box/routes.py index a73302bd638..b29d650902d 100644 --- a/addons/box/routes.py +++ b/addons/box/routes.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """Box addon routes.""" from framework.routing import Rule, json_renderer diff --git a/addons/box/settings/local-dist.py b/addons/box/settings/local-dist.py index 492ed19ff45..38d245c2df6 100644 --- a/addons/box/settings/local-dist.py +++ b/addons/box/settings/local-dist.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """Example Box local settings file. Copy this file to local.py and change these settings. """ diff --git a/addons/box/tests/factories.py b/addons/box/tests/factories.py index bada66e3a54..17b1898e49d 100644 --- a/addons/box/tests/factories.py +++ b/addons/box/tests/factories.py @@ -14,8 +14,8 @@ class BoxAccountFactory(ExternalAccountFactory): provider = 'box' - provider_id = Sequence(lambda n: 'id-{0}'.format(n)) - oauth_key = Sequence(lambda n: 'key-{0}'.format(n)) + provider_id = Sequence(lambda n: f'id-{n}') + oauth_key = Sequence(lambda n: f'key-{n}') expires_at = timezone.now() + relativedelta(seconds=3600) diff --git a/addons/box/tests/test_client.py b/addons/box/tests/test_client.py index 97db6293bd4..3cad36a3fdd 100644 --- a/addons/box/tests/test_client.py +++ b/addons/box/tests/test_client.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- from nose.tools import assert_true import pytest import unittest @@ -13,7 +12,7 @@ class TestCore(unittest.TestCase): def setUp(self): - super(TestCore, self).setUp() + super().setUp() self.user = UserFactory() self.user.add_addon('box') diff --git a/addons/box/tests/test_models.py b/addons/box/tests/test_models.py index c3a9a78c903..6829b754d23 100644 --- a/addons/box/tests/test_models.py +++ b/addons/box/tests/test_models.py @@ -1,4 +1,4 @@ -import mock +from unittest import mock import unittest import pytest @@ -28,11 +28,11 @@ def setUp(self): return_value=('12235', '/Foo') ) self.mock_data.start() - super(TestBoxNodeSettings, self).setUp() + super().setUp() def tearDown(self): self.mock_data.stop() - super(TestBoxNodeSettings, self).tearDown() + super().tearDown() def test_folder_defaults_to_none(self): node_settings = NodeSettings(user_settings=self.user_settings, owner=factories.ProjectFactory()) @@ -42,11 +42,11 @@ def test_folder_defaults_to_none(self): @mock.patch('addons.box.models.Provider.refresh_oauth_key') def test_serialize_credentials(self, mock_refresh): mock_refresh.return_value = True - super(TestBoxNodeSettings, self).test_serialize_credentials() + super().test_serialize_credentials() @mock.patch('addons.box.models.UserSettings.revoke_remote_oauth_access', mock.PropertyMock()) def test_complete_has_auth_not_verified(self): - super(TestBoxNodeSettings, self).test_complete_has_auth_not_verified() + super().test_complete_has_auth_not_verified() class TestBoxUserSettings(OAuthAddonUserSettingTestSuiteMixin, unittest.TestCase): diff --git a/addons/box/tests/test_serializer.py b/addons/box/tests/test_serializer.py index 1ee4537d4ba..e32adec4faf 100644 --- a/addons/box/tests/test_serializer.py +++ b/addons/box/tests/test_serializer.py @@ -1,6 +1,5 @@ -# -*- coding: utf-8 -*- """Serializer tests for the Box addon.""" -import mock +from unittest import mock import pytest from addons.base.tests.serializers import StorageAddonSerializerTestSuiteMixin @@ -27,11 +26,11 @@ def setUp(self): return_value=True ) self.mock_valid.start() - super(TestBoxSerializer, self).setUp() + super().setUp() def tearDown(self): self.mock_valid.stop() - super(TestBoxSerializer, self).tearDown() + super().tearDown() def set_provider_id(self, pid): self.node_settings.folder_id = pid diff --git a/addons/box/tests/test_views.py b/addons/box/tests/test_views.py index af7d646ae77..c79b87949f6 100644 --- a/addons/box/tests/test_views.py +++ b/addons/box/tests/test_views.py @@ -1,9 +1,8 @@ -# -*- coding: utf-8 -*- """Views tests for the Box addon.""" from django.utils import timezone from rest_framework import status as http_status from nose.tools import * # noqa (PEP8 asserts) -import mock +from unittest import mock import pytest from urllib3.exceptions import MaxRetryError @@ -32,18 +31,18 @@ def setUp(self): self.mock_refresh = mock.patch('addons.box.models.Provider.refresh_oauth_key') self.mock_refresh.return_value = True self.mock_refresh.start() - super(TestAuthViews, self).setUp() + super().setUp() def tearDown(self): self.mock_refresh.stop() - super(TestAuthViews, self).tearDown() + super().tearDown() @mock.patch( 'addons.box.models.UserSettings.revoke_remote_oauth_access', mock.PropertyMock() ) def test_delete_external_account(self): - super(TestAuthViews, self).test_delete_external_account() + super().test_delete_external_account() class TestConfigViews(BoxAddonTestCase, views_testing.OAuthAddonConfigViewsTestCaseMixin, OsfTestCase): @@ -62,20 +61,20 @@ def setUp(self): return_value=(self.folder['id'], self.folder['path']) ) self.mock_data.start() - super(TestConfigViews, self).setUp() + super().setUp() def tearDown(self): self.mock_data.stop() - super(TestConfigViews, self).tearDown() + super().tearDown() @mock.patch.object(BoxSerializer, 'credentials_are_valid', return_value=True) def test_import_auth(self, *args): - super(TestConfigViews, self).test_import_auth() + super().test_import_auth() class TestFilebrowserViews(BoxAddonTestCase, OsfTestCase): def setUp(self): - super(TestFilebrowserViews, self).setUp() + super().setUp() self.user.add_addon('box') self.node_settings.external_account = self.user_settings.external_accounts[0] self.node_settings.save() diff --git a/addons/box/tests/utils.py b/addons/box/tests/utils.py index 793d16c5b1f..7b4c942258e 100644 --- a/addons/box/tests/utils.py +++ b/addons/box/tests/utils.py @@ -1,5 +1,4 @@ -# -*- coding: utf-8 -*- -import mock +from unittest import mock from contextlib import contextmanager from addons.base.tests.base import OAuthAddonTestCaseMixin, AddonTestCase @@ -14,7 +13,7 @@ class BoxAddonTestCase(OAuthAddonTestCaseMixin, AddonTestCase): Provider = Provider def set_node_settings(self, settings): - super(BoxAddonTestCase, self).set_node_settings(settings) + super().set_node_settings(settings) settings.folder_id = '1234567890' settings.folder_name = 'Foo' @@ -88,64 +87,64 @@ def set_node_settings(self, settings): 'revision': 220191 }, { - u'bytes': 0, - u'icon': u'folder', - u'is_dir': True, - u'modified': u'Sat, 22 Mar 2014 05:40:29 +0000', - u'path': u'/datasets/New Folder', - u'rev': u'3fed51f002c12fc', - u'revision': 67032351, - u'root': u'box', - u'size': u'0 bytes', - u'thumb_exists': False + 'bytes': 0, + 'icon': 'folder', + 'is_dir': True, + 'modified': 'Sat, 22 Mar 2014 05:40:29 +0000', + 'path': '/datasets/New Folder', + 'rev': '3fed51f002c12fc', + 'revision': 67032351, + 'root': 'box', + 'size': '0 bytes', + 'thumb_exists': False } ], 'revision': 29007 }, 'metadata_single': { - u'id': 'id', - u'bytes': 74, - u'client_mtime': u'Mon, 13 Jan 2014 20:24:15 +0000', - u'icon': u'page_white', - u'is_dir': False, - u'mime_type': u'text/csv', - u'modified': u'Fri, 21 Mar 2014 05:46:36 +0000', - u'path': '/datasets/foo.txt', - u'rev': u'a2149fb64', - u'revision': 10, - u'root': u'app_folder', - u'size': u'74 bytes', - u'thumb_exists': False + 'id': 'id', + 'bytes': 74, + 'client_mtime': 'Mon, 13 Jan 2014 20:24:15 +0000', + 'icon': 'page_white', + 'is_dir': False, + 'mime_type': 'text/csv', + 'modified': 'Fri, 21 Mar 2014 05:46:36 +0000', + 'path': '/datasets/foo.txt', + 'rev': 'a2149fb64', + 'revision': 10, + 'root': 'app_folder', + 'size': '74 bytes', + 'thumb_exists': False }, - 'revisions': [{u'bytes': 0, - u'client_mtime': u'Wed, 31 Dec 1969 23:59:59 +0000', - u'icon': u'page_white_picture', - u'is_deleted': True, - u'is_dir': False, - u'mime_type': u'image/png', - u'modified': u'Tue, 25 Mar 2014 03:39:13 +0000', - u'path': u'/svs-v-barks.png', - u'rev': u'3fed741002c12fc', - u'revision': 67032897, - u'root': u'box', - u'size': u'0 bytes', - u'thumb_exists': True}, - {u'bytes': 151164, - u'client_mtime': u'Sat, 13 Apr 2013 21:56:36 +0000', - u'icon': u'page_white_picture', - u'is_dir': False, - u'mime_type': u'image/png', - u'modified': u'Tue, 25 Mar 2014 01:45:51 +0000', - u'path': u'/svs-v-barks.png', - u'rev': u'3fed61a002c12fc', - u'revision': 67032602, - u'root': u'box', - u'size': u'147.6 KB', - u'thumb_exists': True}] + 'revisions': [{'bytes': 0, + 'client_mtime': 'Wed, 31 Dec 1969 23:59:59 +0000', + 'icon': 'page_white_picture', + 'is_deleted': True, + 'is_dir': False, + 'mime_type': 'image/png', + 'modified': 'Tue, 25 Mar 2014 03:39:13 +0000', + 'path': '/svs-v-barks.png', + 'rev': '3fed741002c12fc', + 'revision': 67032897, + 'root': 'box', + 'size': '0 bytes', + 'thumb_exists': True}, + {'bytes': 151164, + 'client_mtime': 'Sat, 13 Apr 2013 21:56:36 +0000', + 'icon': 'page_white_picture', + 'is_dir': False, + 'mime_type': 'image/png', + 'modified': 'Tue, 25 Mar 2014 01:45:51 +0000', + 'path': '/svs-v-barks.png', + 'rev': '3fed61a002c12fc', + 'revision': 67032602, + 'root': 'box', + 'size': '147.6 KB', + 'thumb_exists': True}] } -class MockBox(object): +class MockBox: def put_file(self, full_path, file_obj, overwrite=False, parent_rev=None): return mock_responses['put_file'] diff --git a/addons/box/views.py b/addons/box/views.py index e808b079536..7e3694f75ae 100644 --- a/addons/box/views.py +++ b/addons/box/views.py @@ -1,5 +1,4 @@ """Views for the node settings page.""" -# -*- coding: utf-8 -*- from flask import request from addons.base import generic_views diff --git a/addons/dataverse/migrations/0001_initial.py b/addons/dataverse/migrations/0001_initial.py index 8f0bdbe9345..d3a1ec3a73c 100644 --- a/addons/dataverse/migrations/0001_initial.py +++ b/addons/dataverse/migrations/0001_initial.py @@ -1,6 +1,4 @@ -# -*- coding: utf-8 -*- # Generated by Django 1.11.29 on 2022-08-17 19:15 -from __future__ import unicode_literals import addons.base.models from django.db import migrations, models diff --git a/addons/dataverse/migrations/0002_auto_20220817_1915.py b/addons/dataverse/migrations/0002_auto_20220817_1915.py index bb10ee76ec4..1ba459bd7a6 100644 --- a/addons/dataverse/migrations/0002_auto_20220817_1915.py +++ b/addons/dataverse/migrations/0002_auto_20220817_1915.py @@ -1,6 +1,4 @@ -# -*- coding: utf-8 -*- # Generated by Django 1.11.29 on 2022-08-17 19:15 -from __future__ import unicode_literals from django.conf import settings from django.db import migrations, models diff --git a/addons/dataverse/models.py b/addons/dataverse/models.py index f586a99a958..1b4753d7cfa 100644 --- a/addons/dataverse/models.py +++ b/addons/dataverse/models.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- from rest_framework import status as http_status from addons.base import exceptions as addon_errors @@ -71,14 +70,14 @@ def update(self, revision, data, save=True, user=None): return version -class DataverseProvider(object): +class DataverseProvider: """An alternative to `ExternalProvider` not tied to OAuth""" name = 'Dataverse' short_name = 'dataverse' serializer = DataverseSerializer def __init__(self, account=None): - super(DataverseProvider, self).__init__() # this does exactly nothing... + super().__init__() # this does exactly nothing... # provide an unauthenticated session by default self.account = account @@ -165,7 +164,7 @@ def set_folder(self, dataverse, dataset, auth=None): def _get_fileobj_child_metadata(self, filenode, user, cookie=None, version=None): try: - return super(NodeSettings, self)._get_fileobj_child_metadata(filenode, user, cookie=cookie, version=version) + return super()._get_fileobj_child_metadata(filenode, user, cookie=cookie, version=version) except HTTPError as e: # The Dataverse API returns a 404 if the dataset has no published files if e.code == http_status.HTTP_404_NOT_FOUND and version == 'latest-published': @@ -215,7 +214,7 @@ def serialize_waterbutler_settings(self): def create_waterbutler_log(self, auth, action, metadata): url = self.owner.web_url_for('addon_view_or_download_file', path=metadata['path'], provider='dataverse') self.owner.add_log( - 'dataverse_{0}'.format(action), + f'dataverse_{action}', auth=auth, params={ 'project': self.owner.parent_id, diff --git a/addons/dataverse/serializer.py b/addons/dataverse/serializer.py index 0535580827f..0b8d66abceb 100644 --- a/addons/dataverse/serializer.py +++ b/addons/dataverse/serializer.py @@ -12,11 +12,11 @@ class DataverseSerializer(OAuthAddonSerializer): # Include host information with more informative labels / formatting def serialize_account(self, external_account): - ret = super(DataverseSerializer, self).serialize_account(external_account) + ret = super().serialize_account(external_account) host = external_account.oauth_key ret.update({ 'host': host, - 'host_url': 'https://{0}'.format(host), + 'host_url': f'https://{host}', }) return ret @@ -38,7 +38,7 @@ def serialized_urls(self): addon_urls = self.addon_serialized_urls # Make sure developer returns set of needed urls for url in self.REQUIRED_URLS: - assert url in addon_urls, "addon_serilized_urls must include key '{0}'".format(url) + assert url in addon_urls, f"addon_serilized_urls must include key '{url}'" ret.update(addon_urls) return ret @@ -55,13 +55,13 @@ def addon_serialized_urls(self): 'deauthorize': node.api_url_for('dataverse_deauthorize_node'), 'getDatasets': node.api_url_for('dataverse_get_datasets'), 'datasetPrefix': 'https://doi.org/', - 'dataversePrefix': 'http://{0}/dataverse/'.format(host), + 'dataversePrefix': f'http://{host}/dataverse/', 'accounts': api_url_for('dataverse_account_list'), } @property def serialized_node_settings(self): - result = super(DataverseSerializer, self).serialized_node_settings + result = super().serialized_node_settings result['hosts'] = DEFAULT_HOSTS # Update with Dataverse specific fields diff --git a/addons/dataverse/tests/factories.py b/addons/dataverse/tests/factories.py index 2d13177d5fe..6a67eb8fd5c 100644 --- a/addons/dataverse/tests/factories.py +++ b/addons/dataverse/tests/factories.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """Factory boy factories for the Dataverse addon.""" import factory from factory.django import DjangoModelFactory @@ -10,8 +9,8 @@ class DataverseAccountFactory(ExternalAccountFactory): provider = 'dataverse' provider_name = 'Dataverse' - provider_id = factory.Sequence(lambda n: 'id-{0}'.format(n)) - oauth_key = factory.Sequence(lambda n: 'key-{0}'.format(n)) + provider_id = factory.Sequence(lambda n: f'id-{n}') + oauth_key = factory.Sequence(lambda n: f'key-{n}') display_name = 'foo.bar.baz' oauth_secret = 'doremi-abc-123' diff --git a/addons/dataverse/tests/test_client.py b/addons/dataverse/tests/test_client.py index 37835869435..59f3b1198d5 100644 --- a/addons/dataverse/tests/test_client.py +++ b/addons/dataverse/tests/test_client.py @@ -1,4 +1,4 @@ -import mock +from unittest import mock from nose.tools import ( assert_equal, assert_raises, assert_true, assert_false, assert_in, assert_is, assert_is_none @@ -25,7 +25,7 @@ class TestClient(DataverseAddonTestCase, unittest.TestCase): def setUp(self): - super(TestClient, self).setUp() + super().setUp() self.host = 'some.host.url' self.token = 'some-fancy-api-token-which-is-long' diff --git a/addons/dataverse/tests/test_logger.py b/addons/dataverse/tests/test_logger.py index 032ca9c0878..6ff9670e41e 100644 --- a/addons/dataverse/tests/test_logger.py +++ b/addons/dataverse/tests/test_logger.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """NodeLogger tests for the Dataverse addon.""" import pytest @@ -16,10 +15,10 @@ class TestDataverseNodeLogger(StorageAddonNodeLoggerTestSuiteMixin, OsfTestCase) NodeLogger = DataverseNodeLogger def setUp(self): - super(TestDataverseNodeLogger, self).setUp() + super().setUp() node_settings = self.node.get_addon(self.addon_short_name) node_settings.dataset = 'fake dataset' node_settings.save() def tearDown(self): - super(TestDataverseNodeLogger, self).tearDown() + super().tearDown() diff --git a/addons/dataverse/tests/test_model.py b/addons/dataverse/tests/test_model.py index 1c53b57ff5c..ee13d9426cc 100644 --- a/addons/dataverse/tests/test_model.py +++ b/addons/dataverse/tests/test_model.py @@ -1,5 +1,5 @@ from nose.tools import * # noqa -import mock +from unittest import mock import pytest import unittest @@ -62,7 +62,7 @@ def test_create_log(self): assert_equal(self.node.logs.count(), nlog + 1) assert_equal( self.node.logs.latest().action, - '{0}_{1}'.format(self.short_name, action), + f'{self.short_name}_{action}', ) assert_equal( self.node.logs.latest().params['filename'], @@ -77,7 +77,7 @@ def test_set_folder(self): assert_equal(self.node_settings.folder_id, dataset.id) # Log was saved last_log = self.node.logs.latest() - assert_equal(last_log.action, '{0}_dataset_linked'.format(self.short_name)) + assert_equal(last_log.action, f'{self.short_name}_dataset_linked') def test_serialize_credentials(self): credentials = self.node_settings.serialize_waterbutler_credentials() diff --git a/addons/dataverse/tests/test_serializer.py b/addons/dataverse/tests/test_serializer.py index a6b47bb5c13..e71f424c016 100644 --- a/addons/dataverse/tests/test_serializer.py +++ b/addons/dataverse/tests/test_serializer.py @@ -1,6 +1,5 @@ -# -*- coding: utf-8 -*- from nose.tools import * # noqa -import mock +from unittest import mock import pytest @@ -25,7 +24,7 @@ class TestDataverseSerializer(OAuthAddonSerializerTestSuiteMixin, OsfTestCase): required_settings_authorized = ('ownerName', ) def setUp(self): - super(TestDataverseSerializer, self).setUp() + super().setUp() self.ser = self.Serializer( user_settings=self.user_settings, node_settings=self.node_settings @@ -36,7 +35,7 @@ def setUp(self): def tearDown(self): self.mock_api.stop() - super(TestDataverseSerializer, self).tearDown() + super().tearDown() def test_serialize_acccount(self): ea = self.ExternalAccountFactory() @@ -49,6 +48,6 @@ def test_serialize_acccount(self): 'profile_url': ea.profile_url, 'nodes': [], 'host': ea.oauth_key, - 'host_url': 'https://{0}'.format(ea.oauth_key), + 'host_url': f'https://{ea.oauth_key}', } assert_equal(self.ser.serialize_account(ea), expected) diff --git a/addons/dataverse/tests/test_utils.py b/addons/dataverse/tests/test_utils.py index 35f47278b18..0c4171903d7 100644 --- a/addons/dataverse/tests/test_utils.py +++ b/addons/dataverse/tests/test_utils.py @@ -37,12 +37,12 @@ def test_mock_dataverse(self): def test_mock_dataset(self): dataset_id = 'DVN/23456' - doi = 'doi:12.3456/{0}'.format(dataset_id) + doi = f'doi:12.3456/{dataset_id}' mock_dataset = create_mock_dataset(dataset_id) assert_equal(mock_dataset.doi, doi) assert_equal(mock_dataset.citation, - 'Example Citation for {0}'.format(dataset_id)) - assert_equal(mock_dataset.title, 'Example ({0})'.format(dataset_id)) + f'Example Citation for {dataset_id}') + assert_equal(mock_dataset.title, f'Example ({dataset_id})') assert_equal(mock_dataset.doi, doi) assert_equal(mock_dataset.get_state(), 'DRAFT') assert_equal(len(mock_dataset.get_files()), 1) diff --git a/addons/dataverse/tests/test_views.py b/addons/dataverse/tests/test_views.py index bcf7e6ef1ca..ccb369c464d 100644 --- a/addons/dataverse/tests/test_views.py +++ b/addons/dataverse/tests/test_views.py @@ -1,6 +1,5 @@ -# -*- coding: utf-8 -*- from nose.tools import (assert_false, assert_equal, assert_in, assert_true) -import mock +from unittest import mock import pytest import unittest @@ -66,14 +65,14 @@ class TestConfigViews(DataverseAddonTestCase, OAuthAddonConfigViewsTestCaseMixin client = DataverseProvider def setUp(self): - super(TestConfigViews, self).setUp() + super().setUp() self.mock_ser_api = mock.patch('addons.dataverse.serializer.client.connect_from_settings') self.mock_ser_api.return_value = create_mock_connection() self.mock_ser_api.start() def tearDown(self): self.mock_ser_api.stop() - super(TestConfigViews, self).tearDown() + super().tearDown() @mock.patch('addons.dataverse.views.client.connect_from_settings') def test_folder_list(self, mock_connection): @@ -93,7 +92,7 @@ def test_folder_list(self, mock_connection): def test_set_config(self, mock_connection): mock_connection.return_value = self.connection - url = self.project.api_url_for('{0}_set_config'.format(self.ADDON_SHORT_NAME)) + url = self.project.api_url_for(f'{self.ADDON_SHORT_NAME}_set_config') res = self.app.post_json(url, { 'dataverse': {'alias': 'ALIAS3'}, 'dataset': {'doi': 'doi:12.3456/DVN/00003'}, @@ -102,14 +101,14 @@ def test_set_config(self, mock_connection): self.project.reload() assert_equal( self.project.logs.latest().action, - '{0}_dataset_linked'.format(self.ADDON_SHORT_NAME) + f'{self.ADDON_SHORT_NAME}_dataset_linked' ) assert_equal(res.json['dataverse'], self.connection.get_dataverse('ALIAS3').title) assert_equal(res.json['dataset'], self.connection.get_dataverse('ALIAS3').get_dataset_by_doi('doi:12.3456/DVN/00003').title) def test_get_config(self): - url = self.project.api_url_for('{0}_get_config'.format(self.ADDON_SHORT_NAME)) + url = self.project.api_url_for(f'{self.ADDON_SHORT_NAME}_get_config') res = self.app.get(url, auth=self.user.auth) assert_equal(res.status_code, http_status.HTTP_200_OK) assert_in('result', res.json) diff --git a/addons/dataverse/tests/utils.py b/addons/dataverse/tests/utils.py index af64882cc2f..349fdc25a57 100644 --- a/addons/dataverse/tests/utils.py +++ b/addons/dataverse/tests/utils.py @@ -1,4 +1,4 @@ -import mock +from unittest import mock from dataverse import Connection, Dataverse, Dataset, DataverseFile @@ -14,7 +14,7 @@ class DataverseAddonTestCase(OAuthAddonTestCaseMixin, AddonTestCase): Provider = DataverseProvider def set_node_settings(self, settings): - super(DataverseAddonTestCase, self).set_node_settings(settings) + super().set_node_settings(settings) settings.dataverse_alias = 'ALIAS2' settings.dataverse = 'Example 2' settings.dataset_doi = 'doi:12.3456/DVN/00001' @@ -85,7 +85,7 @@ def create_mock_dataverse(title='Example Dataverse 0'): type(mock_dataverse).title = mock.PropertyMock(return_value=title) type(mock_dataverse).is_published = mock.PropertyMock(return_value=True) type(mock_dataverse).alias = mock.PropertyMock( - return_value='ALIAS{}'.format(title[-1]) + return_value=f'ALIAS{title[-1]}' ) mock_dataverse.get_datasets.return_value = [ @@ -110,9 +110,9 @@ def _get_dataset_by_doi(doi, timeout=None): def create_mock_dataset(id='DVN/12345'): mock_dataset = mock.create_autospec(Dataset) - mock_dataset.citation = 'Example Citation for {0}'.format(id) - mock_dataset.title = 'Example ({0})'.format(id) - mock_dataset.doi = 'doi:12.3456/{0}'.format(id) + mock_dataset.citation = f'Example Citation for {id}' + mock_dataset.title = f'Example ({id})' + mock_dataset.doi = f'doi:12.3456/{id}' mock_dataset.id = '18' mock_dataset.get_state.return_value = 'DRAFT' @@ -150,16 +150,16 @@ def create_mock_published_file(id='54321'): mock_responses = { 'contents': { - u'kind': u'item', - u'name': u'file.txt', - u'ext': u'.txt', - u'file_id': u'54321', - u'urls': {u'download': u'/project/xxxxx/dataverse/file/54321/download/', - u'delete': u'/api/v1/project/xxxxx/dataverse/file/54321/', - u'view': u'/project/xxxxx/dataverse/file/54321/'}, - u'permissions': {u'edit': False, u'view': True}, - u'addon': u'dataverse', - u'hasPublishedFiles': True, - u'state': 'published', + 'kind': 'item', + 'name': 'file.txt', + 'ext': '.txt', + 'file_id': '54321', + 'urls': {'download': '/project/xxxxx/dataverse/file/54321/download/', + 'delete': '/api/v1/project/xxxxx/dataverse/file/54321/', + 'view': '/project/xxxxx/dataverse/file/54321/'}, + 'permissions': {'edit': False, 'view': True}, + 'addon': 'dataverse', + 'hasPublishedFiles': True, + 'state': 'published', } } diff --git a/addons/dataverse/views.py b/addons/dataverse/views.py index a3d5e24834f..9b549e6de9b 100644 --- a/addons/dataverse/views.py +++ b/addons/dataverse/views.py @@ -1,5 +1,4 @@ """Views for the node settings page.""" -# -*- coding: utf-8 -*- from rest_framework import status as http_status from django.utils import timezone @@ -312,7 +311,7 @@ def dataverse_get_widget_contents(node_addon, **kwargs): return {'data': data}, http_status.HTTP_400_BAD_REQUEST dataverse_host = node_addon.external_account.oauth_key - dataverse_url = 'http://{0}/dataverse/{1}'.format(dataverse_host, alias) + dataverse_url = f'http://{dataverse_host}/dataverse/{alias}' dataset_url = 'https://doi.org/' + doi data.update({ diff --git a/addons/dropbox/migrations/0001_initial.py b/addons/dropbox/migrations/0001_initial.py index f62db239046..43f59b49b24 100644 --- a/addons/dropbox/migrations/0001_initial.py +++ b/addons/dropbox/migrations/0001_initial.py @@ -1,6 +1,4 @@ -# -*- coding: utf-8 -*- # Generated by Django 1.11.29 on 2022-08-17 19:15 -from __future__ import unicode_literals import addons.base.models from django.db import migrations, models diff --git a/addons/dropbox/migrations/0002_auto_20220817_1915.py b/addons/dropbox/migrations/0002_auto_20220817_1915.py index 1de073e684c..bd2981d90e8 100644 --- a/addons/dropbox/migrations/0002_auto_20220817_1915.py +++ b/addons/dropbox/migrations/0002_auto_20220817_1915.py @@ -1,6 +1,4 @@ -# -*- coding: utf-8 -*- # Generated by Django 1.11.29 on 2022-08-17 19:15 -from __future__ import unicode_literals from django.conf import settings from django.db import migrations, models diff --git a/addons/dropbox/models.py b/addons/dropbox/models.py index 05a7e6797ad..242f60cd00e 100644 --- a/addons/dropbox/models.py +++ b/addons/dropbox/models.py @@ -55,7 +55,7 @@ class Provider(ExternalProvider): def auth_url(self): # Dropbox requires explicitly requesting refresh_tokens via `token_access_type` # https://developers.dropbox.com/oauth-guide#implementing-oauth - url = super(Provider, self).auth_url + url = super().auth_url return furl(url).add({'token_access_type': 'offline'}).url def handle_callback(self, response): @@ -122,7 +122,7 @@ def folder_path(self): @property def display_name(self): - return '{0}: {1}'.format(self.config.full_name, self.folder) + return f'{self.config.full_name}: {self.folder}' def fetch_access_token(self): return self.api.fetch_access_token() @@ -140,7 +140,7 @@ def get_folders(self, **kwargs): 'kind': 'folder', 'name': '/ (Full Dropbox)', 'urls': { - 'folders': api_v2_url('nodes/{}/addons/dropbox/folders/'.format(self.owner._id), + 'folders': api_v2_url(f'nodes/{self.owner._id}/addons/dropbox/folders/', params={'id': '/'} ) } @@ -171,7 +171,7 @@ def get_folders(self, **kwargs): 'name': item.path_display.split('/')[-1], 'path': item.path_display, 'urls': { - 'folders': api_v2_url('nodes/{}/addons/dropbox/folders/'.format(self.owner._id), + 'folders': api_v2_url(f'nodes/{self.owner._id}/addons/dropbox/folders/', params={'id': item.path_display} ) } @@ -212,7 +212,7 @@ def create_waterbutler_log(self, auth, action, metadata): provider='dropbox' ) self.owner.add_log( - 'dropbox_{0}'.format(action), + f'dropbox_{action}', auth=auth, params={ 'project': self.owner.parent_id, @@ -227,7 +227,7 @@ def create_waterbutler_log(self, auth, action, metadata): ) def __repr__(self): - return u''.format(self=self) + return f'' ##### Callback overrides ##### def after_delete(self, user): diff --git a/addons/dropbox/routes.py b/addons/dropbox/routes.py index db55622fcaf..91a51d784ae 100644 --- a/addons/dropbox/routes.py +++ b/addons/dropbox/routes.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """Dropbox addon routes.""" from framework.routing import Rule, json_renderer diff --git a/addons/dropbox/settings/local-dist.py b/addons/dropbox/settings/local-dist.py index 22bbbf1057a..1e0bfb434fb 100644 --- a/addons/dropbox/settings/local-dist.py +++ b/addons/dropbox/settings/local-dist.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """Example Dropbox local settings file. Copy this file to local.py and change these settings. """ diff --git a/addons/dropbox/tests/factories.py b/addons/dropbox/tests/factories.py index 760dc72f970..4962ccc586c 100644 --- a/addons/dropbox/tests/factories.py +++ b/addons/dropbox/tests/factories.py @@ -25,5 +25,5 @@ class Meta: class DropboxAccountFactory(ExternalAccountFactory): provider = 'dropbox' - provider_id = factory.Sequence(lambda n: 'id-{0}'.format(n)) - oauth_key = factory.Sequence(lambda n: 'key-{0}'.format(n)) + provider_id = factory.Sequence(lambda n: f'id-{n}') + oauth_key = factory.Sequence(lambda n: f'key-{n}') diff --git a/addons/dropbox/tests/test_client.py b/addons/dropbox/tests/test_client.py index bc92098f77a..43957c32ff4 100644 --- a/addons/dropbox/tests/test_client.py +++ b/addons/dropbox/tests/test_client.py @@ -12,7 +12,7 @@ class TestCore(unittest.TestCase): def setUp(self): - super(TestCore, self).setUp() + super().setUp() self.user = UserFactory() self.user.add_addon('dropbox') diff --git a/addons/dropbox/tests/test_models.py b/addons/dropbox/tests/test_models.py index 35b6337e3a3..669fc2cbab2 100644 --- a/addons/dropbox/tests/test_models.py +++ b/addons/dropbox/tests/test_models.py @@ -1,6 +1,6 @@ import unittest -import mock +from unittest import mock import pytest from addons.base.tests.models import (OAuthAddonNodeSettingsTestSuiteMixin, OAuthAddonUserSettingTestSuiteMixin) @@ -39,7 +39,7 @@ def test_folder_defaults_to_none(self): mock.PropertyMock() ) def test_complete_has_auth_not_verified(self): - super(TestDropboxNodeSettings, self).test_complete_has_auth_not_verified() + super().test_complete_has_auth_not_verified() class TestDropboxUserSettings(OAuthAddonUserSettingTestSuiteMixin, unittest.TestCase): diff --git a/addons/dropbox/tests/test_views.py b/addons/dropbox/tests/test_views.py index 4f76022e93d..3154f3e11d0 100644 --- a/addons/dropbox/tests/test_views.py +++ b/addons/dropbox/tests/test_views.py @@ -7,7 +7,7 @@ from tests.base import OsfTestCase from urllib3.exceptions import MaxRetryError -import mock +from unittest import mock import pytest from addons.base.tests import views as views_testing from addons.dropbox.tests.utils import ( @@ -34,11 +34,11 @@ class TestAuthViews(DropboxAddonTestCase, views_testing.OAuthAddonAuthViewsTestC mock.PropertyMock(return_value='http://api.foo.com') ) def test_oauth_start(self): - super(TestAuthViews, self).test_oauth_start() + super().test_oauth_start() @mock.patch('addons.dropbox.models.UserSettings.revoke_remote_oauth_access', mock.PropertyMock()) def test_delete_external_account(self): - super(TestAuthViews, self).test_delete_external_account() + super().test_delete_external_account() class TestConfigViews(DropboxAddonTestCase, views_testing.OAuthAddonConfigViewsTestCaseMixin, OsfTestCase): @@ -52,17 +52,17 @@ class TestConfigViews(DropboxAddonTestCase, views_testing.OAuthAddonConfigViewsT @mock.patch('addons.dropbox.models.Dropbox', return_value=mock_client) def test_folder_list(self, *args): - super(TestConfigViews, self).test_folder_list() + super().test_folder_list() @mock.patch.object(DropboxSerializer, 'credentials_are_valid', return_value=True) def test_import_auth(self, *args): - super(TestConfigViews, self).test_import_auth() + super().test_import_auth() class TestFilebrowserViews(DropboxAddonTestCase, OsfTestCase): def setUp(self): - super(TestFilebrowserViews, self).setUp() + super().setUp() self.user.add_addon('dropbox') self.node_settings.external_account = self.user_settings.external_accounts[0] self.node_settings.save() diff --git a/addons/dropbox/tests/utils.py b/addons/dropbox/tests/utils.py index 626aefae7fd..6ef97b047d8 100644 --- a/addons/dropbox/tests/utils.py +++ b/addons/dropbox/tests/utils.py @@ -1,6 +1,6 @@ from contextlib import contextmanager -import mock +from unittest import mock from addons.base.tests.base import AddonTestCase, OAuthAddonTestCaseMixin from addons.dropbox.models import Provider from addons.dropbox.tests.factories import DropboxAccountFactory @@ -13,41 +13,41 @@ class DropboxAddonTestCase(OAuthAddonTestCaseMixin, AddonTestCase): Provider = Provider def set_node_settings(self, settings): - super(DropboxAddonTestCase, self).set_node_settings(settings) + super().set_node_settings(settings) settings.folder = 'foo' settings.save() -class MockFileMetadata(object): +class MockFileMetadata: name = 'Prime_Numbers.txt' path_display = '/Homework/math/Prime_Numbers.txt' -class MockFolderMetadata(object): +class MockFolderMetadata: name = 'math' path_display = '/Homework/math' -class MockListFolderResult(object): +class MockListFolderResult: def __init__(self, has_more=False): self.entries = [MockFileMetadata(), MockFolderMetadata()] self.cursor = 'ZtkX9_EHj3x7PMkVuFIhwKYXEpwpLwyxp9vMKomUhllil9q7eWiAu' self.has_more = has_more -class MockName(object): +class MockName: display_name = 'Rain Drop, Drop Box' -class MockFullAccount(object): +class MockFullAccount: name = MockName() -class MockDropbox(object): +class MockDropbox: def files_list_folder(self, path, diff --git a/addons/dropbox/views.py b/addons/dropbox/views.py index b8246418eca..8518d6b1787 100644 --- a/addons/dropbox/views.py +++ b/addons/dropbox/views.py @@ -1,5 +1,4 @@ """Views fo the node settings page.""" -# -*- coding: utf-8 -*- from flask import request import logging diff --git a/addons/figshare/client.py b/addons/figshare/client.py index fb6db8ef3e7..1bce2c83d45 100644 --- a/addons/figshare/client.py +++ b/addons/figshare/client.py @@ -20,7 +20,7 @@ def from_account(cls, account): @property def _default_headers(self): if self.access_token: - return {'Authorization': 'token {}'.format(self.access_token)} + return {'Authorization': f'token {self.access_token}'} return {} @property diff --git a/addons/figshare/migrations/0001_initial.py b/addons/figshare/migrations/0001_initial.py index f2053798e95..0b9a6c40caf 100644 --- a/addons/figshare/migrations/0001_initial.py +++ b/addons/figshare/migrations/0001_initial.py @@ -1,6 +1,4 @@ -# -*- coding: utf-8 -*- # Generated by Django 1.11.29 on 2022-08-17 19:15 -from __future__ import unicode_literals import addons.base.models from django.db import migrations, models diff --git a/addons/figshare/migrations/0002_auto_20220817_1915.py b/addons/figshare/migrations/0002_auto_20220817_1915.py index 988ab59f118..468532cc84d 100644 --- a/addons/figshare/migrations/0002_auto_20220817_1915.py +++ b/addons/figshare/migrations/0002_auto_20220817_1915.py @@ -1,6 +1,4 @@ -# -*- coding: utf-8 -*- # Generated by Django 1.11.29 on 2022-08-17 19:15 -from __future__ import unicode_literals from django.conf import settings from django.db import migrations, models diff --git a/addons/figshare/models.py b/addons/figshare/models.py index 3f92d737222..8e7691e05c8 100644 --- a/addons/figshare/models.py +++ b/addons/figshare/models.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - import markupsafe from addons.base.models import (BaseOAuthNodeSettings, BaseOAuthUserSettings, BaseStorageAddon) @@ -35,11 +33,11 @@ def update(self, revision, data, user=None, save=True): Always pass revision as None to avoid conflict. Call super to update _history and last_touched anyway. """ - version = super(FigshareFile, self).update(None, data, user=user, save=save) + version = super().update(None, data, user=user, save=save) # Draft files are not renderable if data['extra']['status'] == 'drafts': - return (version, u""" + return (version, """