diff --git a/.isort.cfg b/.isort.cfg index 453b0a3..6bd0db9 100644 --- a/.isort.cfg +++ b/.isort.cfg @@ -2,9 +2,8 @@ line_length=120 multi_line_output=0 lines_after_imports=2 -add_imports=from __future__ import unicode_literals sections=FUTURE,STDLIB,THIRDPARTY,INDICO,FIRSTPARTY,LOCALFOLDER known_third_party=flask_multipass,flask_pluginengine +known_first_party=indico_* known_indico=indico skip_glob=20??????????_*_*.py -not_skip=__init__.py diff --git a/_meta/setup.py b/_meta/setup.py index 30eb4f7..67c5373 100644 --- a/_meta/setup.py +++ b/_meta/setup.py @@ -5,7 +5,6 @@ # them and/or modify them under the terms of the MIT License; # see the LICENSE file for more details. -from __future__ import unicode_literals from setuptools import setup diff --git a/livesync/indico_livesync/__init__.py b/livesync/indico_livesync/__init__.py index a086bbc..2895b17 100644 --- a/livesync/indico_livesync/__init__.py +++ b/livesync/indico_livesync/__init__.py @@ -5,7 +5,6 @@ # them and/or modify them under the terms of the MIT License; # see the LICENSE file for more details. -from __future__ import unicode_literals from indico.core import signals from indico.util.i18n import make_bound_gettext diff --git a/livesync/indico_livesync/base.py b/livesync/indico_livesync/base.py index 9004cc3..e8fca16 100644 --- a/livesync/indico_livesync/base.py +++ b/livesync/indico_livesync/base.py @@ -5,7 +5,6 @@ # them and/or modify them under the terms of the MIT License; # see the LICENSE file for more details. -from __future__ import unicode_literals from flask_pluginengine import depends, trim_docstring @@ -27,14 +26,14 @@ class LiveSyncPluginBase(IndicoPlugin): # pragma: no cover category = PluginCategory.synchronization def init(self): - super(LiveSyncPluginBase, self).init() - for name, backend_class in self.backend_classes.iteritems(): + super().init() + for name, backend_class in self.backend_classes.items(): assert backend_class.plugin is None backend_class.plugin = type(self) LiveSyncPlugin.instance.register_backend_class(name, backend_class) -class LiveSyncBackendBase(object): +class LiveSyncBackendBase: """Base class for livesync backends""" #: the plugin containing the agent diff --git a/livesync/indico_livesync/blueprint.py b/livesync/indico_livesync/blueprint.py index a70a8b4..598b284 100644 --- a/livesync/indico_livesync/blueprint.py +++ b/livesync/indico_livesync/blueprint.py @@ -5,7 +5,6 @@ # them and/or modify them under the terms of the MIT License; # see the LICENSE file for more details. -from __future__ import unicode_literals from indico.core.plugins import IndicoPluginBlueprint diff --git a/livesync/indico_livesync/cli.py b/livesync/indico_livesync/cli.py index 1885ace..aaa6c31 100644 --- a/livesync/indico_livesync/cli.py +++ b/livesync/indico_livesync/cli.py @@ -5,7 +5,6 @@ # them and/or modify them under the terms of the MIT License; # see the LICENSE file for more details. -from __future__ import unicode_literals import click from flask_pluginengine import current_plugin @@ -27,15 +26,15 @@ def cli(): @cli.command() def available_backends(): """Lists the currently available backend types""" - print 'The following LiveSync agents are available:' - for name, backend in current_plugin.backend_classes.iteritems(): - print cformat(' - %{white!}{}%{reset}: {} ({})').format(name, backend.title, backend.description) + print('The following LiveSync agents are available:') + for name, backend in current_plugin.backend_classes.items(): + print(cformat(' - %{white!}{}%{reset}: {} ({})').format(name, backend.title, backend.description)) @cli.command() def agents(): """Lists the currently active agents""" - print 'The following LiveSync agents are active:' + print('The following LiveSync agents are active:') agent_list = LiveSyncAgent.find().order_by(LiveSyncAgent.backend_name, db.func.lower(LiveSyncAgent.name)).all() table_data = [['ID', 'Name', 'Backend', 'Initial Export', 'Queue']] for agent in agent_list: @@ -45,16 +44,16 @@ def agents(): backend_title = cformat('%{red!}invalid backend ({})%{reset}').format(agent.backend_name) else: backend_title = agent.backend.title - table_data.append([unicode(agent.id), agent.name, backend_title, initial, - unicode(agent.queue.filter_by(processed=False).count())]) + table_data.append([str(agent.id), agent.name, backend_title, initial, + str(agent.queue.filter_by(processed=False).count())]) table = AsciiTable(table_data) table.justify_columns[4] = 'right' - print table.table + print(table.table) if not all(a.initial_data_exported for a in agent_list): - print - print "You need to perform the initial data export for some agents." - print cformat("To do so, run " - "%{yellow!}indico livesync initial-export %{reset}%{yellow}%{reset} for those agents.") + print() + print("You need to perform the initial data export for some agents.") + print(cformat("To do so, run " + "%{yellow!}indico livesync initial-export %{reset}%{yellow}%{reset} for those agents.")) @cli.command() @@ -64,15 +63,15 @@ def initial_export(agent_id, force): """Performs the initial data export for an agent""" agent = LiveSyncAgent.find_first(id=agent_id) if agent is None: - print 'No such agent' + print('No such agent') return if agent.backend is None: - print cformat('Cannot run agent %{red!}{}%{reset} (backend not found)').format(agent.name) + print(cformat('Cannot run agent %{red!}{}%{reset} (backend not found)').format(agent.name)) return - print cformat('Selected agent: %{white!}{}%{reset} ({})').format(agent.name, agent.backend.title) + print(cformat('Selected agent: %{white!}{}%{reset} ({})').format(agent.name, agent.backend.title)) if agent.initial_data_exported and not force: - print 'The initial export has already been performed for this agent.' - print cformat('To re-run it, use %{yellow!}--force%{reset}') + print('The initial export has already been performed for this agent.') + print(cformat('To re-run it, use %{yellow!}--force%{reset}')) return agent.create_backend().run_initial_export(Event.find(is_deleted=False)) @@ -90,18 +89,18 @@ def run(agent_id, force=False): else: agent = LiveSyncAgent.find_first(id=agent_id) if agent is None: - print 'No such agent' + print('No such agent') return agent_list = [agent] for agent in agent_list: if agent.backend is None: - print cformat('Skipping agent: %{red!}{}%{reset} (backend not found)').format(agent.name) + print(cformat('Skipping agent: %{red!}{}%{reset} (backend not found)').format(agent.name)) continue if not agent.initial_data_exported and not force: - print cformat('Skipping agent: %{red!}{}%{reset} (initial export not performed)').format(agent.name) + print(cformat('Skipping agent: %{red!}{}%{reset} (initial export not performed)').format(agent.name)) continue - print cformat('Running agent: %{white!}{}%{reset}').format(agent.name) + print(cformat('Running agent: %{white!}{}%{reset}').format(agent.name)) try: agent.create_backend().run() db.session.commit() diff --git a/livesync/indico_livesync/controllers.py b/livesync/indico_livesync/controllers.py index db8663e..c822c93 100644 --- a/livesync/indico_livesync/controllers.py +++ b/livesync/indico_livesync/controllers.py @@ -5,7 +5,6 @@ # them and/or modify them under the terms of the MIT License; # see the LICENSE file for more details. -from __future__ import unicode_literals from flask import flash, redirect, request from flask_pluginengine import current_plugin, render_plugin_template diff --git a/livesync/indico_livesync/forms.py b/livesync/indico_livesync/forms.py index d9563d7..5b9638f 100644 --- a/livesync/indico_livesync/forms.py +++ b/livesync/indico_livesync/forms.py @@ -5,7 +5,6 @@ # them and/or modify them under the terms of the MIT License; # see the LICENSE file for more details. -from __future__ import unicode_literals from wtforms.fields.core import StringField from wtforms.validators import DataRequired diff --git a/livesync/indico_livesync/handler.py b/livesync/indico_livesync/handler.py index 2c37237..fc333d7 100644 --- a/livesync/indico_livesync/handler.py +++ b/livesync/indico_livesync/handler.py @@ -5,7 +5,6 @@ # them and/or modify them under the terms of the MIT License; # see the LICENSE file for more details. -from __future__ import unicode_literals from collections import defaultdict @@ -159,7 +158,7 @@ def _apply_changes(sender, **kwargs): if not hasattr(g, 'livesync_changes'): return excluded_categories = get_excluded_categories() - for ref, changes in g.livesync_changes.iteritems(): + for ref, changes in g.livesync_changes.items(): LiveSyncQueueEntry.create(changes, ref, excluded_categories=excluded_categories) diff --git a/livesync/indico_livesync/marcxml.py b/livesync/indico_livesync/marcxml.py index 81d91a6..e481dda 100644 --- a/livesync/indico_livesync/marcxml.py +++ b/livesync/indico_livesync/marcxml.py @@ -5,7 +5,6 @@ # them and/or modify them under the terms of the MIT License; # see the LICENSE file for more details. -from __future__ import unicode_literals from flask_pluginengine import current_plugin @@ -27,7 +26,7 @@ class MARCXMLGenerator: @classmethod def records_to_xml(cls, records): mg = MARCXMLGenerator() - for entry, change in records.iteritems(): + for entry, change in records.items(): mg.safe_add_object(entry, bool(change & SimpleChange.deleted)) return mg.get_xml() @@ -79,7 +78,7 @@ class MARCXMLGenerator: elif isinstance(obj, Category): pass # we don't send category updates else: - raise ValueError('unknown object ref: {}'.format(obj)) + raise ValueError(f'unknown object ref: {obj}') return self.xml_generator.getXml() def get_xml(self): diff --git a/livesync/indico_livesync/models/agents.py b/livesync/indico_livesync/models/agents.py index 892e54a..2c7b214 100644 --- a/livesync/indico_livesync/models/agents.py +++ b/livesync/indico_livesync/models/agents.py @@ -5,7 +5,6 @@ # them and/or modify them under the terms of the MIT License; # see the LICENSE file for more details. -from __future__ import unicode_literals from sqlalchemy.dialects.postgresql import JSON @@ -73,4 +72,4 @@ class LiveSyncAgent(db.Model): @return_ascii def __repr__(self): - return ''.format(self.id, self.backend_name, self.name) + return f'' diff --git a/livesync/indico_livesync/models/queue.py b/livesync/indico_livesync/models/queue.py index 6097cb0..dc1dc7f 100644 --- a/livesync/indico_livesync/models/queue.py +++ b/livesync/indico_livesync/models/queue.py @@ -5,7 +5,6 @@ # them and/or modify them under the terms of the MIT License; # see the LICENSE file for more details. -from __future__ import unicode_literals from flask import g from werkzeug.datastructures import ImmutableDict @@ -47,14 +46,14 @@ _column_for_types = { def _make_checks(): - available_columns = set(_column_for_types.viewvalues()) + available_columns = set(_column_for_types.values()) for link_type in EntryType: required_col = _column_for_types[link_type] forbidden_cols = available_columns - {required_col} - criteria = ['{} IS NULL'.format(col) for col in sorted(forbidden_cols)] - criteria += ['{} IS NOT NULL'.format(required_col)] + criteria = [f'{col} IS NULL' for col in sorted(forbidden_cols)] + criteria += [f'{required_col} IS NOT NULL'] condition = 'type != {} OR ({})'.format(link_type, ' AND '.join(criteria)) - yield db.CheckConstraint(condition, 'valid_{}_entry'.format(link_type.name)) + yield db.CheckConstraint(condition, f'valid_{link_type.name}_entry') class LiveSyncQueueEntry(db.Model): diff --git a/livesync/indico_livesync/plugin.py b/livesync/indico_livesync/plugin.py index 9912e92..646988a 100644 --- a/livesync/indico_livesync/plugin.py +++ b/livesync/indico_livesync/plugin.py @@ -5,7 +5,6 @@ # them and/or modify them under the terms of the MIT License; # see the LICENSE file for more details. -from __future__ import unicode_literals from wtforms.fields.html5 import IntegerField from wtforms.validators import NumberRange @@ -48,7 +47,7 @@ class LiveSyncPlugin(IndicoPlugin): category = PluginCategory.synchronization def init(self): - super(LiveSyncPlugin, self).init() + super().init() self.backend_classes = {} connect_signals(self) self.connect(signals.plugin.cli, self._extend_indico_cli) @@ -62,7 +61,7 @@ class LiveSyncPlugin(IndicoPlugin): def register_backend_class(self, name, backend_class): if name in self.backend_classes: - raise RuntimeError('Duplicate livesync backend: {}'.format(name)) + raise RuntimeError(f'Duplicate livesync backend: {name}') self.backend_classes[name] = backend_class def _extend_plugin_details(self, plugin, **kwargs): diff --git a/livesync/indico_livesync/simplify.py b/livesync/indico_livesync/simplify.py index b1db9fe..080b9f4 100644 --- a/livesync/indico_livesync/simplify.py +++ b/livesync/indico_livesync/simplify.py @@ -5,7 +5,6 @@ # them and/or modify them under the terms of the MIT License; # see the LICENSE file for more details. -from __future__ import unicode_literals import itertools from collections import defaultdict @@ -98,8 +97,7 @@ def _process_cascaded_category_contents(records): if category_move_records: changed_events.update(Event.find(Event.category_chain_overlaps(category_move_records))) - for elem in _process_cascaded_event_contents(records, additional_events=changed_events): - yield elem + yield from _process_cascaded_event_contents(records, additional_events=changed_events) def _process_cascaded_event_contents(records, additional_events=None): @@ -124,8 +122,7 @@ def _process_cascaded_event_contents(records, additional_events=None): if event_records: changed_events.update(Event.find(Event.id.in_(event_records))) - for event in changed_events: - yield event + yield from changed_events # Sessions are added (explicitly changed only, since they don't need to be sent anywhere) if session_records: @@ -147,5 +144,4 @@ def _process_cascaded_event_contents(records, additional_events=None): # Same for subcontributions if subcontribution_records: changed_subcontributions.update(SubContribution.find(SubContribution.id.in_(subcontribution_records))) - for subcontrib in changed_subcontributions: - yield subcontrib + yield from changed_subcontributions diff --git a/livesync/indico_livesync/task.py b/livesync/indico_livesync/task.py index 65a477f..8c91b92 100644 --- a/livesync/indico_livesync/task.py +++ b/livesync/indico_livesync/task.py @@ -5,7 +5,6 @@ # them and/or modify them under the terms of the MIT License; # see the LICENSE file for more details. -from __future__ import unicode_literals from celery.schedules import crontab diff --git a/livesync/indico_livesync/uploader.py b/livesync/indico_livesync/uploader.py index bc9b744..cdbdeee 100644 --- a/livesync/indico_livesync/uploader.py +++ b/livesync/indico_livesync/uploader.py @@ -5,7 +5,6 @@ # them and/or modify them under the terms of the MIT License; # see the LICENSE file for more details. -from __future__ import unicode_literals from indico.core.db import db from indico.util.struct.iterables import grouper @@ -13,7 +12,7 @@ from indico.util.struct.iterables import grouper from indico_livesync import MARCXMLGenerator, process_records -class Uploader(object): +class Uploader: """Handles batch data upload to a remote service.""" #: Number of queue entries to process at a time @@ -35,7 +34,7 @@ class Uploader(object): self.logger.info('%s processing batch %d', self_name, i) try: for j, proc_batch in enumerate(grouper( - process_records(batch).iteritems(), self.BATCH_SIZE, skip_missing=True), 1): + process_records(batch).items(), self.BATCH_SIZE, skip_missing=True), 1): self.logger.info('%s uploading chunk #%d (batch %d)', self_name, j, i) self.upload_records({k: v for k, v in proc_batch}, from_queue=True) except Exception: diff --git a/livesync/indico_livesync/util.py b/livesync/indico_livesync/util.py index 09b090c..3f2ddbf 100644 --- a/livesync/indico_livesync/util.py +++ b/livesync/indico_livesync/util.py @@ -5,7 +5,6 @@ # them and/or modify them under the terms of the MIT License; # see the LICENSE file for more details. -from __future__ import unicode_literals from datetime import timedelta @@ -34,7 +33,7 @@ def obj_ref(obj): elif isinstance(obj, SubContribution): ref = {'type': EntryType.subcontribution, 'subcontrib_id': obj.id} else: - raise ValueError('Unexpected object: {}'.format(obj.__class__.__name__)) + raise ValueError(f'Unexpected object: {obj.__class__.__name__}') return ImmutableDict(ref) @@ -58,8 +57,8 @@ def obj_deref(ref): def clean_old_entries(): """Deletes obsolete entries from the queues""" - from indico_livesync.plugin import LiveSyncPlugin from indico_livesync.models.queue import LiveSyncQueueEntry + from indico_livesync.plugin import LiveSyncPlugin queue_entry_ttl = LiveSyncPlugin.settings.get('queue_entry_ttl') if not queue_entry_ttl: @@ -81,8 +80,8 @@ def compound_id(obj): if isinstance(obj, (Category, Session)): raise TypeError('Compound IDs are not supported for this entry type') elif isinstance(obj, Event): - return unicode(obj.id) + return str(obj.id) elif isinstance(obj, Contribution): - return '{}.{}'.format(obj.event_id, obj.id) + return f'{obj.event_id}.{obj.id}' elif isinstance(obj, SubContribution): - return '{}.{}.{}'.format(obj.contribution.event_id, obj.contribution_id, obj.id) + return f'{obj.contribution.event_id}.{obj.contribution_id}.{obj.id}' diff --git a/livesync/setup.py b/livesync/setup.py index ac7dde3..597c946 100644 --- a/livesync/setup.py +++ b/livesync/setup.py @@ -5,7 +5,6 @@ # them and/or modify them under the terms of the MIT License; # see the LICENSE file for more details. -from __future__ import unicode_literals from setuptools import find_packages, setup diff --git a/livesync/tests/agent_test.py b/livesync/tests/agent_test.py index ea506f3..66dd21b 100644 --- a/livesync/tests/agent_test.py +++ b/livesync/tests/agent_test.py @@ -5,7 +5,7 @@ # them and/or modify them under the terms of the MIT License; # see the LICENSE file for more details. -from mock import MagicMock +from unittest.mock import MagicMock from indico_livesync.base import LiveSyncBackendBase from indico_livesync.models.queue import ChangeType, EntryType, LiveSyncQueueEntry diff --git a/livesync/tests/queue_test.py b/livesync/tests/queue_test.py index 398b714..0413631 100644 --- a/livesync/tests/queue_test.py +++ b/livesync/tests/queue_test.py @@ -31,7 +31,7 @@ def test_excluded_categories(mocker, monkeypatch, db, create_category): categories = {} with db.session.no_autoflush: - for cat_id in xrange(6): + for cat_id in range(6): category = (create_category(cat_id, title=str(cat_id), protection_mode=0, parent=categories[CATEGORY_PARENTS[cat_id]]) if cat_id else Category.get_root()) @@ -41,7 +41,7 @@ def test_excluded_categories(mocker, monkeypatch, db, create_category): db.session.flush() - for cat in categories.viewvalues(): + for cat in categories.values(): db = mocker.patch('indico_livesync.models.queue.db') LiveSyncQueueEntry.create({ChangeType.created}, obj_ref(cat), excluded_categories=get_excluded_categories()) assert db.session.add.called == (cat.id not in {2, 3, 4, 5}) diff --git a/livesync/tests/simplify_test.py b/livesync/tests/simplify_test.py index c30e233..8f48877 100644 --- a/livesync/tests/simplify_test.py +++ b/livesync/tests/simplify_test.py @@ -13,7 +13,7 @@ from indico_livesync import SimpleChange, process_records from indico_livesync.models.queue import ChangeType, EntryType, LiveSyncQueueEntry -class Dummy(object): +class Dummy: pass @@ -41,7 +41,7 @@ def test_process_records_category_ignored(mocker, change, invalid): else: result = process_records(records) assert len(result) == 1 - assert result.values()[0] == SimpleChange.updated + assert list(result.values())[0] == SimpleChange.updated @pytest.mark.parametrize(('change', 'cascade'), ( @@ -96,7 +96,7 @@ def test_process_records_simplify(changes, mocker, db, create_event, dummy_agent assert result == process_records(reversed(queue)) # queue order shouldn't matter assert len(result) == sum(1 for x in expected if x) - result_refs = {obj.id: change for obj, change in result.viewitems()} + result_refs = {obj.id: change for obj, change in result.items()} for i, ref in enumerate(refs): assert (ref['event_id'] in list(result_refs)) == bool(expected[i]) assert result_refs.get(ref['event_id'], 0) == expected[i] diff --git a/livesync/tests/uploader_test.py b/livesync/tests/uploader_test.py index 2caa0a1..c8c36b2 100644 --- a/livesync/tests/uploader_test.py +++ b/livesync/tests/uploader_test.py @@ -5,7 +5,7 @@ # them and/or modify them under the terms of the MIT License; # see the LICENSE file for more details. -from mock import MagicMock, Mock +from unittest.mock import MagicMock, Mock from indico.modules.events import Event @@ -17,13 +17,13 @@ from indico_livesync.uploader import MARCXMLUploader, Uploader class RecordingUploader(Uploader): """An uploader which logs each 'upload'""" def __init__(self, *args, **kwargs): - super(RecordingUploader, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self._uploaded = [] self.logger = MagicMock() def upload_records(self, records, from_queue): if from_queue: - recs = set(records.viewitems()) + recs = set(records.items()) self._uploaded.append((recs, from_queue)) else: self._uploaded.append((set(records), from_queue)) @@ -36,11 +36,11 @@ class RecordingUploader(Uploader): class FailingUploader(RecordingUploader): """An uploader where the second batch fails""" def __init__(self, *args, **kwargs): - super(FailingUploader, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self._n = 0 def upload_records(self, records, from_queue): - super(FailingUploader, self).upload_records(records, from_queue) + super().upload_records(records, from_queue) self._n += 1 if self._n == 2: raise Exception('All your data are belong to us!') @@ -51,7 +51,7 @@ def test_run_initial(mocker): mocker.patch.object(Uploader, 'processed_records', autospec=True) uploader = RecordingUploader(MagicMock()) uploader.INITIAL_BATCH_SIZE = 3 - records = tuple(Mock(spec=Event, id=evt_id) for evt_id in xrange(4)) + records = tuple(Mock(spec=Event, id=evt_id) for evt_id in range(4)) uploader.run_initial(records) # We expect two batches, with the second one being smaller (i.e. no None padding, just the events) batches = set(records[:3]), set(records[3:]) @@ -65,7 +65,7 @@ def test_run(mocker, db, create_event, dummy_agent): uploader = RecordingUploader(MagicMock()) uploader.BATCH_SIZE = 3 - events = tuple(create_event(id_=evt_id) for evt_id in xrange(4)) + events = tuple(create_event(id_=evt_id) for evt_id in range(4)) records = tuple(LiveSyncQueueEntry(change=ChangeType.created, type=EntryType.event, event_id=evt.id, agent=dummy_agent) for evt in events) @@ -91,7 +91,7 @@ def test_run_failing(mocker, db, create_event, dummy_agent): uploader = FailingUploader(MagicMock()) uploader.BATCH_SIZE = 3 - events = tuple(create_event(id_=evt_id) for evt_id in xrange(10)) + events = tuple(create_event(id_=evt_id) for evt_id in range(10)) records = tuple(LiveSyncQueueEntry(change=ChangeType.created, type=EntryType.event, event_id=evt.id, agent=dummy_agent) for evt in events) diff --git a/livesync_debug/indico_livesync_debug/backend.py b/livesync_debug/indico_livesync_debug/backend.py index 40fd974..0f7d154 100644 --- a/livesync_debug/indico_livesync_debug/backend.py +++ b/livesync_debug/indico_livesync_debug/backend.py @@ -5,7 +5,6 @@ # them and/or modify them under the terms of the MIT License; # see the LICENSE file for more details. -from __future__ import unicode_literals from indico.util.console import cformat from indico.util.struct.iterables import grouper @@ -24,7 +23,7 @@ class LiveSyncDebugBackend(LiveSyncBackendBase): """ def _print(self, msg=''): - print msg + print(msg) def run(self): records = self.fetch_records() @@ -38,7 +37,7 @@ class LiveSyncDebugBackend(LiveSyncBackendBase): self._print() self._print(cformat('%{white!}Simplified/cascaded changes:%{reset}')) - for obj, change in process_records(records).iteritems(): + for obj, change in process_records(records).items(): self._print(cformat('%{white!}{}%{reset}: {}').format(_change_str(change), obj)) self._print() @@ -51,17 +50,17 @@ class LiveSyncDebugBackend(LiveSyncBackendBase): uploader = DebugUploader(self) uploader.run_initial(events) for i, batch in enumerate(grouper(events, 10, skip_missing=True), 1): - print - print cformat('%{white!}Batch {}:%{reset}').format(i) - print MARCXMLGenerator.objects_to_xml(event for event in batch if event is not None) - print + print() + print(cformat('%{white!}Batch {}:%{reset}').format(i)) + print(MARCXMLGenerator.objects_to_xml(event for event in batch if event is not None)) + print() class DebugUploader(Uploader): BATCH_SIZE = 5 def __init__(self, *args, **kwargs): - super(DebugUploader, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self.n = 0 def upload_records(self, records, from_queue): diff --git a/livesync_debug/indico_livesync_debug/plugin.py b/livesync_debug/indico_livesync_debug/plugin.py index 5446d83..bc17d12 100644 --- a/livesync_debug/indico_livesync_debug/plugin.py +++ b/livesync_debug/indico_livesync_debug/plugin.py @@ -5,7 +5,6 @@ # them and/or modify them under the terms of the MIT License; # see the LICENSE file for more details. -from __future__ import unicode_literals from indico_livesync import LiveSyncPluginBase from indico_livesync_debug.backend import LiveSyncDebugBackend diff --git a/livesync_debug/setup.py b/livesync_debug/setup.py index 13766f0..c020751 100644 --- a/livesync_debug/setup.py +++ b/livesync_debug/setup.py @@ -5,7 +5,6 @@ # them and/or modify them under the terms of the MIT License; # see the LICENSE file for more details. -from __future__ import unicode_literals from setuptools import find_packages, setup diff --git a/payment_manual/indico_payment_manual/__init__.py b/payment_manual/indico_payment_manual/__init__.py index 04f67c2..40dfbcd 100644 --- a/payment_manual/indico_payment_manual/__init__.py +++ b/payment_manual/indico_payment_manual/__init__.py @@ -5,7 +5,6 @@ # them and/or modify them under the terms of the MIT License; # see the LICENSE file for more details. -from __future__ import unicode_literals from indico.util.i18n import make_bound_gettext diff --git a/payment_manual/indico_payment_manual/placeholders.py b/payment_manual/indico_payment_manual/placeholders.py index fb85c53..739b586 100644 --- a/payment_manual/indico_payment_manual/placeholders.py +++ b/payment_manual/indico_payment_manual/placeholders.py @@ -5,10 +5,10 @@ # them and/or modify them under the terms of the MIT License; # see the LICENSE file for more details. -from __future__ import unicode_literals from decimal import Decimal -from urllib import quote_plus + +from six.moves.urllib.parse import quote_plus from indico.modules.events.registration.placeholders.registrations import IDPlaceholder from indico.util.placeholders import ParametrizedPlaceholder, Placeholder @@ -74,11 +74,11 @@ class PricePlaceholder(ParametrizedPlaceholder): @classmethod def render(cls, param, regform, registration): if param == 'int': - return unicode(int(registration.price * 100)) + return str(int(registration.price * 100)) elif param == 'short' and int(registration.price) == registration.price: - return unicode(int(registration.price)) + return str(int(registration.price)) else: - return unicode(registration.price.quantize(Decimal('.01'))) + return str(registration.price.quantize(Decimal('.01'))) @classmethod def iter_param_info(cls, regform, registration): diff --git a/payment_manual/indico_payment_manual/plugin.py b/payment_manual/indico_payment_manual/plugin.py index 58fe235..cf65e3e 100644 --- a/payment_manual/indico_payment_manual/plugin.py +++ b/payment_manual/indico_payment_manual/plugin.py @@ -5,7 +5,6 @@ # them and/or modify them under the terms of the MIT License; # see the LICENSE file for more details. -from __future__ import unicode_literals from wtforms.fields.simple import TextAreaField from wtforms.validators import DataRequired @@ -29,7 +28,7 @@ class PluginSettingsForm(PaymentPluginSettingsFormBase): details = TextAreaField(_('Payment details'), []) def __init__(self, *args, **kwargs): - super(PluginSettingsForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self.details.description = DETAILS_DESC + '\n' + render_placeholder_info('manual-payment-details', regform=None, registration=None) @@ -38,7 +37,7 @@ class EventSettingsForm(PaymentEventSettingsFormBase): details = TextAreaField(_('Payment details'), [UsedIf(lambda form, _: form.enabled.data), DataRequired()]) def __init__(self, *args, **kwargs): - super(EventSettingsForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self.details.description = DETAILS_DESC + '\n' + render_placeholder_info('manual-payment-details', regform=None, registration=None) @@ -60,13 +59,13 @@ class ManualPaymentPlugin(PaymentPluginMixin, IndicoPlugin): 'details': ''} def init(self): - super(ManualPaymentPlugin, self).init() + super().init() self.connect(signals.get_placeholders, self._get_details_placeholders, sender='manual-payment-details') def _get_details_placeholders(self, sender, regform, registration, **kwargs): - from indico_payment_manual.placeholders import (FirstNamePlaceholder, LastNamePlaceholder, EmailPlaceholder, - RegistrationIDPlaceholder, EventIDPlaceholder, PricePlaceholder, - CurrencyPlaceholder) + from indico_payment_manual.placeholders import (CurrencyPlaceholder, EmailPlaceholder, EventIDPlaceholder, + FirstNamePlaceholder, LastNamePlaceholder, PricePlaceholder, + RegistrationIDPlaceholder) yield FirstNamePlaceholder yield LastNamePlaceholder yield EmailPlaceholder diff --git a/payment_manual/setup.py b/payment_manual/setup.py index 5169d97..a6e74fd 100644 --- a/payment_manual/setup.py +++ b/payment_manual/setup.py @@ -5,7 +5,6 @@ # them and/or modify them under the terms of the MIT License; # see the LICENSE file for more details. -from __future__ import unicode_literals from setuptools import find_packages, setup diff --git a/payment_paypal/indico_payment_paypal/__init__.py b/payment_paypal/indico_payment_paypal/__init__.py index 9d2be1a..df3349a 100644 --- a/payment_paypal/indico_payment_paypal/__init__.py +++ b/payment_paypal/indico_payment_paypal/__init__.py @@ -5,7 +5,6 @@ # them and/or modify them under the terms of the MIT License; # see the LICENSE file for more details. -from __future__ import unicode_literals from indico.util.i18n import make_bound_gettext diff --git a/payment_paypal/indico_payment_paypal/blueprint.py b/payment_paypal/indico_payment_paypal/blueprint.py index e00da1a..05f2bcf 100644 --- a/payment_paypal/indico_payment_paypal/blueprint.py +++ b/payment_paypal/indico_payment_paypal/blueprint.py @@ -5,7 +5,6 @@ # them and/or modify them under the terms of the MIT License; # see the LICENSE file for more details. -from __future__ import unicode_literals from indico.core.plugins import IndicoPluginBlueprint diff --git a/payment_paypal/indico_payment_paypal/controllers.py b/payment_paypal/indico_payment_paypal/controllers.py index f40b1b9..1d124bc 100644 --- a/payment_paypal/indico_payment_paypal/controllers.py +++ b/payment_paypal/indico_payment_paypal/controllers.py @@ -5,7 +5,6 @@ # them and/or modify them under the terms of the MIT License; # see the LICENSE file for more details. -from __future__ import unicode_literals from itertools import chain @@ -45,7 +44,7 @@ class RHPaypalIPN(RH): def _process(self): self._verify_business() - verify_params = list(chain(IPN_VERIFY_EXTRA_PARAMS, request.form.iteritems())) + verify_params = list(chain(IPN_VERIFY_EXTRA_PARAMS, request.form.items())) result = requests.post(current_plugin.settings.get('url'), data=verify_params).text if result != 'VERIFIED': current_plugin.logger.warning("Paypal IPN string %s did not validate (%s)", verify_params, result) diff --git a/payment_paypal/indico_payment_paypal/plugin.py b/payment_paypal/indico_payment_paypal/plugin.py index 2dd06de..2b5e3e4 100644 --- a/payment_paypal/indico_payment_paypal/plugin.py +++ b/payment_paypal/indico_payment_paypal/plugin.py @@ -5,10 +5,8 @@ # them and/or modify them under the terms of the MIT License; # see the LICENSE file for more details. -from __future__ import unicode_literals from flask_pluginengine import render_plugin_template - from wtforms.fields.core import StringField from wtforms.fields.html5 import URLField from wtforms.validators import DataRequired, Optional @@ -53,7 +51,7 @@ class PaypalPaymentPlugin(PaymentPluginMixin, IndicoPlugin): 'business': None} def init(self): - super(PaypalPaymentPlugin, self).init() + super().init() self.template_hook('event-manage-payment-plugin-before-form', self._get_encoding_warning) @property diff --git a/payment_paypal/indico_payment_paypal/util.py b/payment_paypal/indico_payment_paypal/util.py index 2352737..7c2cd2d 100644 --- a/payment_paypal/indico_payment_paypal/util.py +++ b/payment_paypal/indico_payment_paypal/util.py @@ -5,7 +5,6 @@ # them and/or modify them under the terms of the MIT License; # see the LICENSE file for more details. -from __future__ import unicode_literals import re diff --git a/payment_paypal/setup.py b/payment_paypal/setup.py index 9e5a118..bae50e4 100644 --- a/payment_paypal/setup.py +++ b/payment_paypal/setup.py @@ -5,7 +5,6 @@ # them and/or modify them under the terms of the MIT License; # see the LICENSE file for more details. -from __future__ import unicode_literals from setuptools import find_packages, setup diff --git a/payment_paypal/tests/controllers_test.py b/payment_paypal/tests/controllers_test.py index f083eda..523d5fc 100644 --- a/payment_paypal/tests/controllers_test.py +++ b/payment_paypal/tests/controllers_test.py @@ -5,9 +5,10 @@ # them and/or modify them under the terms of the MIT License; # see the LICENSE file for more details. +from unittest.mock import MagicMock + import pytest from flask import request -from mock import MagicMock from indico.modules.events.payment.models.transactions import PaymentTransaction diff --git a/payment_paypal/tests/util_test.py b/payment_paypal/tests/util_test.py index 484c1ba..8cb381d 100644 --- a/payment_paypal/tests/util_test.py +++ b/payment_paypal/tests/util_test.py @@ -5,8 +5,9 @@ # them and/or modify them under the terms of the MIT License; # see the LICENSE file for more details. +from unittest.mock import MagicMock + import pytest -from mock import MagicMock from wtforms import ValidationError from indico_payment_paypal.util import validate_business diff --git a/piwik/indico_piwik/__init__.py b/piwik/indico_piwik/__init__.py index ee76d4b..8447fb5 100644 --- a/piwik/indico_piwik/__init__.py +++ b/piwik/indico_piwik/__init__.py @@ -5,7 +5,6 @@ # them and/or modify them under the terms of the MIT License; # see the LICENSE file for more details. -from __future__ import unicode_literals from indico.util.i18n import make_bound_gettext diff --git a/piwik/indico_piwik/controllers.py b/piwik/indico_piwik/controllers.py index 4e5f540..53adbd8 100644 --- a/piwik/indico_piwik/controllers.py +++ b/piwik/indico_piwik/controllers.py @@ -20,8 +20,7 @@ class WPPiwikStatistics(WPStatistics): @property def additional_bundles(self): return { - 'screen': map(lambda x: current_plugin.manifest[x], - ('main.js', 'main.css')), + 'screen': [current_plugin.manifest[x] for x in ('main.js', 'main.css')], 'print': () } diff --git a/piwik/indico_piwik/forms.py b/piwik/indico_piwik/forms.py index b57f15a..355e533 100644 --- a/piwik/indico_piwik/forms.py +++ b/piwik/indico_piwik/forms.py @@ -5,7 +5,6 @@ # them and/or modify them under the terms of the MIT License; # see the LICENSE file for more details. -from __future__ import unicode_literals from wtforms import BooleanField, IntegerField, StringField from wtforms.validators import ValidationError diff --git a/piwik/indico_piwik/piwik.py b/piwik/indico_piwik/piwik.py index 46291e6..caf3d30 100644 --- a/piwik/indico_piwik/piwik.py +++ b/piwik/indico_piwik/piwik.py @@ -6,13 +6,13 @@ # see the LICENSE file for more details. import socket -from urllib2 import urlparse import requests from flask_pluginengine import current_plugin +from urllib2 import urlparse -class PiwikRequest(object): +class PiwikRequest: """Wrapper for Piwik API requests""" def __init__(self, server_url, query_script, site_id, api_token=None): @@ -31,7 +31,7 @@ class PiwikRequest(object): def api_url(self): url = urlparse.urlparse(self.server_url) scheme = url.scheme if url.scheme else 'https' - return '{}://{}{}{}'.format(scheme, url.netloc, url.path, self.query_script) + return f'{scheme}://{url.netloc}{url.path}{self.query_script}' def call(self, default_response=None, **query_params): """Perform a query to the Piwik server and return the response. @@ -50,7 +50,7 @@ class PiwikRequest(object): query_params['idSite'] = self.site_id if self.api_token is not None: query_params['token_auth'] = self.api_token - for key, value in query_params.iteritems(): + for key, value in query_params.items(): if isinstance(value, list): value = ','.join(value) query += '{}={}&'.format(str(key), str(value)) diff --git a/piwik/indico_piwik/plugin.py b/piwik/indico_piwik/plugin.py index 173ac5e..bb2e289 100644 --- a/piwik/indico_piwik/plugin.py +++ b/piwik/indico_piwik/plugin.py @@ -5,10 +5,9 @@ # them and/or modify them under the terms of the MIT License; # see the LICENSE file for more details. -from urllib2 import urlparse - from flask import request, session from flask_pluginengine import render_plugin_template +from urllib2 import urlparse from indico.core import signals from indico.core.plugins import IndicoPlugin, IndicoPluginBlueprint, plugin_url_rule_to_js, url_for_plugin @@ -38,9 +37,9 @@ class PiwikPlugin(IndicoPlugin): 'enabled_for_events': True, 'enabled_for_downloads': True, 'cache_enabled': True, - 'server_url': u'//127.0.0.1/piwik/', - 'server_api_url': u'//127.0.0.1/piwik/', - 'server_token': u'', + 'server_url': '//127.0.0.1/piwik/', + 'server_api_url': '//127.0.0.1/piwik/', + 'server_token': '', 'site_id_general': 1, 'site_id_events': 2, 'cache_ttl': 3600, @@ -48,7 +47,7 @@ class PiwikPlugin(IndicoPlugin): } def init(self): - super(PiwikPlugin, self).init() + super().init() self.connect(signals.menu.items, self.add_sidemenu_item, sender='event-management-sidemenu') self.connect(signals.attachments.attachment_accessed, self.track_download) self.template_hook('html-head', self.inject_tracking) @@ -67,7 +66,7 @@ class PiwikPlugin(IndicoPlugin): def add_sidemenu_item(self, sender, event, **kwargs): if not event.can_manage(session.user) or not PiwikPlugin.settings.get('site_id_events'): return - return SideMenuItem(u'statistics', _(u"Statistics"), url_for_plugin(u'piwik.view', event), section=u'reports') + return SideMenuItem('statistics', _("Statistics"), url_for_plugin('piwik.view', event), section='reports') def get_blueprints(self): return blueprint @@ -84,10 +83,10 @@ class PiwikPlugin(IndicoPlugin): return if attachment.type == AttachmentType.link: resource_url = attachment.link_url - resource_title = u'Link - {0.title}'.format(attachment) + resource_title = f'Link - {attachment.title}' else: resource_url = request.base_url - resource_title = u'Download - {0.title}'.format(attachment) + resource_title = f'Download - {attachment.title}' track_download_request.delay(resource_url, resource_title) def _get_event_tracking_params(self): @@ -96,16 +95,16 @@ class PiwikPlugin(IndicoPlugin): return {} params = {'site_id_events': site_id_events} if request.blueprint in ('event', 'events', 'contributions') and 'confId' in request.view_args: - if not unicode(request.view_args['confId']).isdigit(): + if not str(request.view_args['confId']).isdigit(): return {} params['event_id'] = request.view_args['confId'] contrib_id = request.view_args.get('contrib_id') - if contrib_id is not None and unicode(contrib_id).isdigit(): + if contrib_id is not None and str(contrib_id).isdigit(): contribution = Contribution.find_first(event_id=params['event_id'], id=contrib_id) if contribution: cid = (contribution.legacy_mapping.legacy_contribution_id if contribution.legacy_mapping else contribution.id) - params['contrib_id'] = '{}t{}'.format(contribution.event_id, cid) + params['contrib_id'] = f'{contribution.event_id}t{cid}' return params def _get_tracking_url(self): diff --git a/piwik/indico_piwik/queries/base.py b/piwik/indico_piwik/queries/base.py index 4409f16..7bab12a 100644 --- a/piwik/indico_piwik/queries/base.py +++ b/piwik/indico_piwik/queries/base.py @@ -8,7 +8,7 @@ from indico_piwik.piwik import PiwikRequest -class PiwikQueryBase(object): +class PiwikQueryBase: """Base Piwik query""" def __init__(self, query_script): @@ -27,18 +27,18 @@ class PiwikQueryReportBase(PiwikQueryBase): def __init__(self): from indico_piwik.plugin import PiwikPlugin - super(PiwikQueryReportBase, self).__init__(query_script=PiwikPlugin.report_script) + super().__init__(query_script=PiwikPlugin.report_script) def call(self, date=('last7',), period='day', **query_params): - date = ','.join(map(unicode, date)) - return super(PiwikQueryReportBase, self).call(date=date, period=period, **query_params) + date = ','.join(map(str, date)) + return super().call(date=date, period=period, **query_params) class PiwikQueryReportEventBase(PiwikQueryReportBase): """Base Piwik query to request reports of events and contributions""" def __init__(self, event_id, start_date, end_date, contrib_id=None): - super(PiwikQueryReportEventBase, self).__init__() + super().__init__() self.event_id = event_id self.contrib_id = contrib_id self.start_date = start_date @@ -47,7 +47,7 @@ class PiwikQueryReportEventBase(PiwikQueryReportBase): def call(self, segmentation_enabled=True, **query_params): if segmentation_enabled: query_params['segment'] = self.get_segmentation() - return super(PiwikQueryReportEventBase, self).call(module='API', date=[self.start_date, self.end_date], + return super().call(module='API', date=[self.start_date, self.end_date], **query_params) def get_segmentation(self): @@ -59,8 +59,8 @@ class PiwikQueryReportEventBase(PiwikQueryReportBase): segmentation['customVariablePageValue2'] = ('==', self.contrib_id) segments = set() - for name, (equality, value) in segmentation.iteritems(): - segment = '{}{}{}'.format(name, equality, value) + for name, (equality, value) in segmentation.items(): + segment = f'{name}{equality}{value}' segments.add(segment) return ';'.join(segments) diff --git a/piwik/indico_piwik/queries/graphs.py b/piwik/indico_piwik/queries/graphs.py index eeeb543..2622d73 100644 --- a/piwik/indico_piwik/queries/graphs.py +++ b/piwik/indico_piwik/queries/graphs.py @@ -20,7 +20,7 @@ class PiwikQueryReportEventGraphBase(PiwikQueryReportEventBase): query_params['height'] = height if width is not None: query_params['width'] = width - return super(PiwikQueryReportEventGraphBase, self).call(method='ImageGraph.get', + return super().call(method='ImageGraph.get', apiModule=apiModule, apiAction=apiAction, aliasedGraph='1', graphType=graphType, **query_params) @@ -41,13 +41,13 @@ class PiwikQueryReportEventGraphBase(PiwikQueryReportEventBase): class PiwikQueryReportEventGraphCountries(PiwikQueryReportEventGraphBase): def call(self, **query_params): - return super(PiwikQueryReportEventGraphCountries, self).call(apiModule='UserCountry', apiAction='getCountry', + return super().call(apiModule='UserCountry', apiAction='getCountry', period='range', width=490, height=260, graphType='horizontalBar', **query_params) class PiwikQueryReportEventGraphDevices(PiwikQueryReportEventGraphBase): def call(self, **query_params): - return super(PiwikQueryReportEventGraphDevices, self).call(apiModule='UserSettings', apiAction='getOS', + return super().call(apiModule='UserSettings', apiAction='getOS', period='range', width=320, height=260, graphType='horizontalBar', **query_params) diff --git a/piwik/indico_piwik/queries/metrics.py b/piwik/indico_piwik/queries/metrics.py index fe6bd90..4e235a5 100644 --- a/piwik/indico_piwik/queries/metrics.py +++ b/piwik/indico_piwik/queries/metrics.py @@ -5,10 +5,10 @@ # them and/or modify them under the terms of the MIT License; # see the LICENSE file for more details. -from __future__ import division from operator import itemgetter -from urllib2 import quote + +from six.moves.urllib.parse import quote from indico_piwik.queries.base import PiwikQueryReportEventBase from indico_piwik.queries.utils import get_json_from_remote_server, reduce_json, stringify_seconds @@ -18,11 +18,10 @@ class PiwikQueryReportEventMetricBase(PiwikQueryReportEventBase): """Base Piwik query for retrieving metrics in JSON format""" def call(self, method, **query_params): - return super(PiwikQueryReportEventMetricBase, self).call(method=method, format='JSON', **query_params) + return super().call(method=method, format='JSON', **query_params) def get_result(self): """Perform the call and return the sum of all unique values""" - pass class PiwikQueryReportEventMetricVisitsBase(PiwikQueryReportEventMetricBase): @@ -35,7 +34,7 @@ class PiwikQueryReportEventMetricVisitsBase(PiwikQueryReportEventMetricBase): class PiwikQueryReportEventMetricDownloads(PiwikQueryReportEventMetricBase): def call(self, download_url, **query_params): - return super(PiwikQueryReportEventMetricDownloads, self).call(method='Actions.getDownload', + return super().call(method='Actions.getDownload', downloadUrl=quote(download_url), **query_params) @@ -47,7 +46,7 @@ class PiwikQueryReportEventMetricDownloads(PiwikQueryReportEventMetricBase): def _get_per_day_results(self, results): hits_calendar = {} - for date, hits in results.iteritems(): + for date, hits in results.items(): day_hits = {'total': 0, 'unique': 0} if hits: for metrics in hits: @@ -74,7 +73,7 @@ class PiwikQueryReportEventMetricDownloads(PiwikQueryReportEventMetricBase): class PiwikQueryReportEventMetricReferrers(PiwikQueryReportEventMetricBase): def call(self, **query_params): - return super(PiwikQueryReportEventMetricReferrers, self).call(method='Referrers.getReferrerType', + return super().call(method='Referrers.getReferrerType', period='range', **query_params) def get_result(self): @@ -88,18 +87,18 @@ class PiwikQueryReportEventMetricReferrers(PiwikQueryReportEventMetricBase): class PiwikQueryReportEventMetricUniqueVisits(PiwikQueryReportEventMetricVisitsBase): def call(self, **query_params): - return super(PiwikQueryReportEventMetricUniqueVisits, self).call(method='VisitsSummary.getUniqueVisitors', + return super().call(method='VisitsSummary.getUniqueVisitors', **query_params) class PiwikQueryReportEventMetricVisits(PiwikQueryReportEventMetricVisitsBase): def call(self, **query_params): - return super(PiwikQueryReportEventMetricVisits, self).call(method='VisitsSummary.getVisits', **query_params) + return super().call(method='VisitsSummary.getVisits', **query_params) class PiwikQueryReportEventMetricVisitDuration(PiwikQueryReportEventMetricBase): def call(self, **query_params): - return super(PiwikQueryReportEventMetricVisitDuration, self).call(method='VisitsSummary.get', **query_params) + return super().call(method='VisitsSummary.get', **query_params) def get_result(self): """Perform the call and return a string with the time in hh:mm:ss""" @@ -109,7 +108,7 @@ class PiwikQueryReportEventMetricVisitDuration(PiwikQueryReportEventMetricBase): def _get_average_duration(self, result): seconds = 0 - data = result.values() + data = list(result.values()) if not data: return seconds for day in data: @@ -120,14 +119,14 @@ class PiwikQueryReportEventMetricVisitDuration(PiwikQueryReportEventMetricBase): class PiwikQueryReportEventMetricPeakDateAndVisitors(PiwikQueryReportEventMetricBase): def call(self, **query_params): - return super(PiwikQueryReportEventMetricPeakDateAndVisitors, self).call(method='VisitsSummary.getVisits', + return super().call(method='VisitsSummary.getVisits', **query_params) def get_result(self): """Perform the call and return the peak date and how many users""" result = get_json_from_remote_server(self.call) if result: - date, value = max(result.iteritems(), key=itemgetter(1)) + date, value = max(result.items(), key=itemgetter(1)) return {'date': date, 'users': value} else: return {'date': "No Data", 'users': 0} diff --git a/piwik/indico_piwik/queries/tracking.py b/piwik/indico_piwik/queries/tracking.py index 5f35693..40019d8 100644 --- a/piwik/indico_piwik/queries/tracking.py +++ b/piwik/indico_piwik/queries/tracking.py @@ -6,7 +6,8 @@ # see the LICENSE file for more details. from datetime import datetime -from urllib2 import quote + +from six.moves.urllib.parse import quote from indico.core.celery import celery diff --git a/piwik/indico_piwik/queries/utils.py b/piwik/indico_piwik/queries/utils.py index ab0a7f0..ab180cd 100644 --- a/piwik/indico_piwik/queries/utils.py +++ b/piwik/indico_piwik/queries/utils.py @@ -6,6 +6,7 @@ # see the LICENSE file for more details. import json +from functools import reduce from flask_pluginengine import current_plugin @@ -33,7 +34,7 @@ def get_json_from_remote_server(func, **kwargs): def reduce_json(data): """Reduce a JSON object""" - return reduce(lambda x, y: int(x) + int(y), data.values()) + return reduce(lambda x, y: int(x) + int(y), list(data.values())) def stringify_seconds(seconds=0): diff --git a/piwik/indico_piwik/reports.py b/piwik/indico_piwik/reports.py index 1499dc9..07438c3 100644 --- a/piwik/indico_piwik/reports.py +++ b/piwik/indico_piwik/reports.py @@ -55,7 +55,7 @@ class ReportBase(Serializer): return cls(*args, **kwargs).to_serializable() cache = GenericCache('Piwik.Report') - key = u'{}-{}-{}'.format(cls.__name__, args, kwargs) + key = f'{cls.__name__}-{args}-{kwargs}' report = cache.get(key) if not report: @@ -65,7 +65,6 @@ class ReportBase(Serializer): def _build_report(self): """To be overriden""" - pass def _init_date_range(self, start_date=None, end_date=None): """Set date range defaults if no dates are passed""" @@ -112,7 +111,7 @@ class ReportGeneral(ReportBase): 'referrers': PiwikQueryReportEventMetricReferrers(**self.params), 'peak': PiwikQueryReportEventMetricPeakDateAndVisitors(**self.params)} - for query_name, query in queries.iteritems(): + for query_name, query in queries.items(): self.metrics[query_name] = query.get_result() self._fetch_contribution_info() @@ -129,8 +128,8 @@ class ReportGeneral(ReportBase): continue cid = (contribution.legacy_mapping.legacy_contribution_id if contribution.legacy_mapping else contribution.id) - key = '{}t{}'.format(contribution.event_id, cid) - self.contributions[key] = u'{} ({})'.format(contribution.title, format_time(contribution.start_dt)) + key = f'{contribution.event_id}t{cid}' + self.contributions[key] = '{} ({})'.format(contribution.title, format_time(contribution.start_dt)) class ReportMaterial(ReportBase): @@ -172,8 +171,8 @@ class ReportVisitsPerDay(ReportBase): def _reduce_metrics(self): reduced_metrics = defaultdict(dict) - for metric_type, records in self.metrics.iteritems(): - for date, hits in records.iteritems(): + for metric_type, records in self.metrics.items(): + for date, hits in records.items(): reduced_metrics[date][metric_type] = int(hits) self.metrics = reduced_metrics diff --git a/piwik/setup.py b/piwik/setup.py index 0391172..b61c925 100644 --- a/piwik/setup.py +++ b/piwik/setup.py @@ -5,7 +5,6 @@ # them and/or modify them under the terms of the MIT License; # see the LICENSE file for more details. -from __future__ import unicode_literals from setuptools import find_packages, setup diff --git a/previewer_code/indico_previewer_code/plugin.py b/previewer_code/indico_previewer_code/plugin.py index 32d7bb6..595b212 100644 --- a/previewer_code/indico_previewer_code/plugin.py +++ b/previewer_code/indico_previewer_code/plugin.py @@ -5,7 +5,6 @@ # them and/or modify them under the terms of the MIT License; # see the LICENSE file for more details. -from __future__ import unicode_literals import mimetypes @@ -67,7 +66,7 @@ class CodePreviewerPlugin(IndicoPlugin): configurable = False def init(self): - super(CodePreviewerPlugin, self).init() + super().init() self.connect(signals.attachments.get_file_previewers, self._get_file_previewers) def _get_file_previewers(self, sender, **kwargs): diff --git a/previewer_code/setup.py b/previewer_code/setup.py index 286483f..997f689 100644 --- a/previewer_code/setup.py +++ b/previewer_code/setup.py @@ -5,7 +5,6 @@ # them and/or modify them under the terms of the MIT License; # see the LICENSE file for more details. -from __future__ import unicode_literals from setuptools import find_packages, setup diff --git a/previewer_jupyter/indico_previewer_jupyter/controllers.py b/previewer_jupyter/indico_previewer_jupyter/controllers.py index e097351..6a313b4 100644 --- a/previewer_jupyter/indico_previewer_jupyter/controllers.py +++ b/previewer_jupyter/indico_previewer_jupyter/controllers.py @@ -45,7 +45,7 @@ class RHEventPreviewIPyNB(RH): response = current_app.response_class(html) # Use CSP to restrict access to possibly malicious scripts or inline JS - csp_header = "script-src cdn.mathjax.org 'nonce-{}';".format(nonce) + csp_header = f"script-src cdn.mathjax.org 'nonce-{nonce}';" response.headers['Content-Security-Policy'] = csp_header response.headers['X-Webkit-CSP'] = csp_header # IE10 doesn't have proper CSP support, so we need to be more strict diff --git a/previewer_jupyter/indico_previewer_jupyter/cpp_highlighter.py b/previewer_jupyter/indico_previewer_jupyter/cpp_highlighter.py index ae52666..b2f7283 100644 --- a/previewer_jupyter/indico_previewer_jupyter/cpp_highlighter.py +++ b/previewer_jupyter/indico_previewer_jupyter/cpp_highlighter.py @@ -30,12 +30,12 @@ class CppHighlighter(Preprocessor): python = 'python' def __init__(self, config=None, **kw): - super(CppHighlighter, self).__init__(config=config, **kw) + super().__init__(config=config, **kw) # Build regular expressions to catch language extensions or switches and choose # an adequate pygments lexer any_magic = "|".join(self.magics) - self.re_magic_language = re.compile(r"^\s*({0}).*".format(any_magic), re.DOTALL) + self.re_magic_language = re.compile(fr"^\s*({any_magic}).*", re.DOTALL) def matches(self, source, reg_expr): return bool(reg_expr.match(source)) @@ -62,4 +62,4 @@ class CppHighlighter(Preprocessor): except: # if no language metadata, keep python as default pass - return super(CppHighlighter, self).preprocess(nb, resources) + return super().preprocess(nb, resources) diff --git a/previewer_jupyter/indico_previewer_jupyter/plugin.py b/previewer_jupyter/indico_previewer_jupyter/plugin.py index 0e93001..0700216 100644 --- a/previewer_jupyter/indico_previewer_jupyter/plugin.py +++ b/previewer_jupyter/indico_previewer_jupyter/plugin.py @@ -5,7 +5,6 @@ # them and/or modify them under the terms of the MIT License; # see the LICENSE file for more details. -from __future__ import unicode_literals import mimetypes import re @@ -42,7 +41,7 @@ class JupyterPreviewerPlugin(IndicoPlugin): configurable = False def init(self): - super(JupyterPreviewerPlugin, self).init() + super().init() self.connect(signals.attachments.get_file_previewers, self._get_file_previewers) def get_blueprints(self): diff --git a/previewer_jupyter/setup.py b/previewer_jupyter/setup.py index f77c3a6..b769169 100644 --- a/previewer_jupyter/setup.py +++ b/previewer_jupyter/setup.py @@ -5,7 +5,6 @@ # them and/or modify them under the terms of the MIT License; # see the LICENSE file for more details. -from __future__ import unicode_literals from setuptools import find_packages, setup diff --git a/storage_s3/indico_storage_s3/__init__.py b/storage_s3/indico_storage_s3/__init__.py index c0e9cbe..aaf64d8 100644 --- a/storage_s3/indico_storage_s3/__init__.py +++ b/storage_s3/indico_storage_s3/__init__.py @@ -5,7 +5,6 @@ # them and/or modify them under the terms of the MIT License; # see the LICENSE file for more details. -from __future__ import unicode_literals from indico.core import signals from indico.util.i18n import make_bound_gettext diff --git a/storage_s3/indico_storage_s3/blueprint.py b/storage_s3/indico_storage_s3/blueprint.py index 469c52a..1bc7876 100644 --- a/storage_s3/indico_storage_s3/blueprint.py +++ b/storage_s3/indico_storage_s3/blueprint.py @@ -5,7 +5,6 @@ # them and/or modify them under the terms of the MIT License; # see the LICENSE file for more details. -from __future__ import unicode_literals from indico.core.plugins import IndicoPluginBlueprint diff --git a/storage_s3/indico_storage_s3/controllers.py b/storage_s3/indico_storage_s3/controllers.py index 64e9cbf..f0e9a72 100644 --- a/storage_s3/indico_storage_s3/controllers.py +++ b/storage_s3/indico_storage_s3/controllers.py @@ -5,7 +5,6 @@ # them and/or modify them under the terms of the MIT License; # see the LICENSE file for more details. -from __future__ import unicode_literals from flask import current_app, jsonify, request from werkzeug.exceptions import NotFound, Unauthorized diff --git a/storage_s3/indico_storage_s3/migrate.py b/storage_s3/indico_storage_s3/migrate.py index a409ac3..2170e94 100644 --- a/storage_s3/indico_storage_s3/migrate.py +++ b/storage_s3/indico_storage_s3/migrate.py @@ -5,7 +5,6 @@ # them and/or modify them under the terms of the MIT License; # see the LICENSE file for more details. -from __future__ import unicode_literals import errno import json @@ -88,11 +87,11 @@ def rmlinktree(path): elif stat.S_ISLNK(mode): os.remove(fullname) else: - raise Exception('Tried to delete {} (not a directory/symlink)'.format(fullname)) + raise Exception(f'Tried to delete {fullname} (not a directory/symlink)') os.rmdir(path) -class S3Importer(object): +class S3Importer: def __init__(self, get_bucket_name, static_bucket_name, output_file, source_backend_names, rclone_remote, s3_endpoint, s3_profile, s3_bucket_policy): self.get_bucket_name = get_bucket_name @@ -133,12 +132,12 @@ class S3Importer(object): def run(self): models = {model: self.make_query(model).count() for model in StoredFileMixin.__subclasses__()} - models = {model: total for model, total in models.iteritems() if total} + models = {model: total for model, total in models.items() if total} labels = {model: cformat('Processing %{blue!}{}%{reset} (%{cyan}{}%{reset} rows)').format(model.__name__, total) - for model, total in models.iteritems()} - max_length = max(len(x) for x in labels.itervalues()) - labels = {model: label.ljust(max_length) for model, label in labels.iteritems()} - for model, total in sorted(models.items(), key=itemgetter(1)): + for model, total in models.items()} + max_length = max(len(x) for x in labels.values()) + labels = {model: label.ljust(max_length) for model, label in labels.items()} + for model, total in sorted(list(models.items()), key=itemgetter(1)): with click.progressbar(self.query_chunked(model, 1000), length=total, label=labels[model], show_percent=True, show_pos=True) as objects: for obj in self.flush_rclone_iterator(objects, 1000): @@ -149,14 +148,14 @@ class S3Importer(object): .format(obj, exc)) click.secho('All done!', fg='green') click.echo('Add the following entries to your STORAGE_BACKENDS:') - for bucket, data in sorted(self.buckets.viewitems(), key=itemgetter(0)): + for bucket, data in sorted(self.buckets.items(), key=itemgetter(0)): click.echo("'{}': 's3-readonly:host={},bucket={}',".format( data['backend'], self.s3_endpoint.replace('https://', ''), bucket)) def process_obj(self, obj): new_storage_path, new_filename = self.build_storage_path(obj) if new_storage_path in self.used_storage_paths: - raise Exception('Non-unique storage path: {}'.format(new_storage_path)) + raise Exception(f'Non-unique storage path: {new_storage_path}') self.used_storage_paths.add(new_storage_path) bucket_name, backend = self.get_bucket_name(self.get_object_dt(obj)) bucket_info = self.get_bucket_info(bucket_name, backend, create=(not self.rclone_remote)) @@ -202,7 +201,7 @@ class S3Importer(object): with obj.get_local_path() as file_path: pass while not os.path.exists(file_path): - raw_input(cformat('\n%{red}File not found on disk: %{yellow}{}').format(file_path)) + input(cformat('\n%{red}File not found on disk: %{yellow}{}').format(file_path)) try: queue_entry = self.rclone_queue[bucket] except KeyError: @@ -235,18 +234,18 @@ class S3Importer(object): if not self.rclone_remote or not self.rclone_queue: return click.echo() - for name, data in self.buckets.viewitems(): + for name, data in self.buckets.items(): if not data['exists']: self.create_bucket(name) data['exists'] = True - for bucket, data in self.rclone_queue.viewitems(): + for bucket, data in self.rclone_queue.items(): click.echo(cformat('Copying %{cyan}{}%{reset} files (%{cyan}{}%{reset}) to %{cyan}{}%{reset} via rclone') .format(data['files'], do_filesizeformat(data['bytes']), bucket)) start = datetime.now() try: subprocess.check_call([ 'rclone', 'copy', '--copy-links', - data['path'], '{}:{}'.format(self.rclone_remote, bucket) + data['path'], f'{self.rclone_remote}:{bucket}' ]) except subprocess.CalledProcessError: click.secho('\nError while running rclone', fg='red') @@ -335,17 +334,17 @@ def apply_changes(data_file, revert=False): data = defaultdict(list) for line in data_file: line_data = json.loads(line) - converted = {mapping[k]: v for k, v in line_data.viewitems() if k in mapping} + converted = {mapping[k]: v for k, v in line_data.items() if k in mapping} data[line_data['m']].append(converted) models = {model: len(data[model.__name__]) for model in StoredFileMixin.__subclasses__() if model.__name__ in data and len(data[model.__name__])} labels = {model: cformat('Processing %{blue!}{}%{reset} (%{cyan}{}%{reset} rows)').format(model.__name__, total) - for model, total in models.iteritems()} - max_length = max(len(x) for x in labels.itervalues()) - labels = {model: label.ljust(max_length) for model, label in labels.iteritems()} - for model, total in sorted(models.items(), key=itemgetter(1)): + for model, total in models.items()} + max_length = max(len(x) for x in labels.values()) + labels = {model: label.ljust(max_length) for model, label in labels.items()} + for model, total in sorted(list(models.items()), key=itemgetter(1)): pks = inspect(model).primary_key with click.progressbar(data[model.__name__], length=total, label=labels[model], show_percent=True, show_pos=True) as entries: @@ -368,7 +367,7 @@ def monkeypatch_registration_file_time(): # here we want reliable filenames from indico.modules.events.registration.models import registrations - class FakeTime(object): + class FakeTime: def time(self): return 0 @@ -472,7 +471,7 @@ def copy(source_backend_names, bucket_names, static_bucket_name, s3_endpoint, s3 code.append(' backend = backend.replace("", dt.strftime("%W"))') code.append(' return bucket, backend') d = {} - exec '\n'.join(code) in d + exec('\n'.join(code), d) if not source_backend_names: source_backend_names = [x for x in config.STORAGE_BACKENDS if not isinstance(get_storage(x), S3StorageBase)] if rclone: diff --git a/storage_s3/indico_storage_s3/plugin.py b/storage_s3/indico_storage_s3/plugin.py index 914462f..f61a376 100644 --- a/storage_s3/indico_storage_s3/plugin.py +++ b/storage_s3/indico_storage_s3/plugin.py @@ -5,7 +5,6 @@ # them and/or modify them under the terms of the MIT License; # see the LICENSE file for more details. -from __future__ import unicode_literals import sys @@ -41,7 +40,7 @@ class SettingsForm(IndicoForm): description=_("The password to access the S3 bucket info endpoint")) def __init__(self, *args, **kwargs): - super(SettingsForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) url = Markup('{}').format(url_for_plugin('storage_s3.buckets')) self.bucket_info_enabled.description = _("Enables an API on {url} that returns information on all S3 buckets " "currently in use, including dynamically-named ones.").format(url=url) @@ -62,7 +61,7 @@ class S3StoragePlugin(IndicoPlugin): } def init(self): - super(S3StoragePlugin, self).init() + super().init() self.connect(signals.get_storage_backends, self._get_storage_backends) self.connect(signals.plugin.cli, self._extend_indico_cli) @@ -90,25 +89,25 @@ class S3StoragePlugin(IndicoPlugin): storage_instance = get_storage(key) except RuntimeError: if storage: - click.echo('Storage {} does not exist'.format(key)) + click.echo(f'Storage {key} does not exist') sys.exit(1) continue if isinstance(storage_instance, ReadOnlyStorageMixin): if storage: - click.echo('Storage {} is read-only'.format(key)) + click.echo(f'Storage {key} is read-only') sys.exit(1) continue if isinstance(storage_instance, S3StorageBase): bucket_name = storage_instance._get_current_bucket_name() if storage_instance._bucket_exists(bucket_name): - click.echo('Storage {}: bucket {} already exists'.format(key, bucket_name)) + click.echo(f'Storage {key}: bucket {bucket_name} already exists') continue storage_instance._create_bucket(bucket_name) - click.echo('Storage {}: bucket {} created'.format(key, bucket_name)) + click.echo(f'Storage {key}: bucket {bucket_name} created') elif storage: - click.echo('Storage {} is not an s3 storage'.format(key)) + click.echo(f'Storage {key} is not an s3 storage') sys.exit(1) s3.add_command(migrate_cli, name='migrate') diff --git a/storage_s3/indico_storage_s3/storage.py b/storage_s3/indico_storage_s3/storage.py index 2b96149..01c11fe 100644 --- a/storage_s3/indico_storage_s3/storage.py +++ b/storage_s3/indico_storage_s3/storage.py @@ -5,7 +5,6 @@ # them and/or modify them under the terms of the MIT License; # see the LICENSE file for more details. -from __future__ import unicode_literals import hashlib import hmac @@ -71,7 +70,7 @@ class S3StorageBase(Storage): @cached_property def session(self): - key = '__'.join('{}_{}'.format(k, v) for k, v in sorted(self.session_kwargs.viewitems())) + key = '__'.join(f'{k}_{v}' for k, v in sorted(self.session_kwargs.items())) try: return getattr(s3_session_cache, key) except AttributeError: @@ -95,7 +94,7 @@ class S3StorageBase(Storage): s3_object = self.client.get_object(Bucket=bucket, Key=id_)['Body'] return BytesIO(s3_object.read()) except Exception as e: - raise StorageError('Could not open "{}": {}'.format(file_id, e)), None, sys.exc_info()[2] + raise None.with_traceback(sys.exc_info()[2]) @contextmanager def get_local_path(self, file_id): @@ -128,14 +127,14 @@ class S3StorageBase(Storage): try: self.client.delete_object(Bucket=bucket, Key=id_) except Exception as e: - raise StorageError('Could not delete "{}": {}'.format(file_id, e)), None, sys.exc_info()[2] + raise None.with_traceback(sys.exc_info()[2]) def getsize(self, file_id): bucket, id_ = self._parse_file_id(file_id) try: return self.client.head_object(Bucket=bucket, Key=id_)['ContentLength'] except Exception as e: - raise StorageError('Could not get size of "{}": {}'.format(file_id, e)), None, sys.exc_info()[2] + raise None.with_traceback(sys.exc_info()[2]) def send_file(self, file_id, content_type, filename, inline=True): if self.proxy_downloads == ProxyDownloadsMode.local: @@ -159,7 +158,7 @@ class S3StorageBase(Storage): response.headers['X-Accel-Redirect'] = '/.xsf/s3/' + url.replace('://', '/', 1) return response except Exception as e: - raise StorageError('Could not send file "{}": {}'.format(file_id, e)), None, sys.exc_info()[2] + raise None.with_traceback(sys.exc_info()[2]) def _create_bucket(self, name): from indico_storage_s3.plugin import S3StoragePlugin @@ -190,7 +189,7 @@ class S3Storage(S3StorageBase): name = 's3' def __init__(self, data): - super(S3Storage, self).__init__(data) + super().__init__(data) self.bucket_name = self.parsed_data['bucket'] if len(self.bucket_name) > 63: raise StorageError('Bucket name cannot be longer than 63 chars') @@ -211,14 +210,14 @@ class S3Storage(S3StorageBase): checksum = self._save(bucket, name, content_type, fileobj) return name, checksum except Exception as e: - raise StorageError('Could not save "{}": {}'.format(name, e)), None, sys.exc_info()[2] + raise None.with_traceback(sys.exc_info()[2]) class DynamicS3Storage(S3StorageBase): name = 's3-dynamic' def __init__(self, data): - super(DynamicS3Storage, self).__init__(data) + super().__init__(data) self.bucket_name_template = self.parsed_data['bucket_template'] self.bucket_secret = (self.parsed_data.get('bucket_secret', '') or self.session._session.get_scoped_config().get('indico_bucket_secret', '')) @@ -245,7 +244,7 @@ class DynamicS3Storage(S3StorageBase): def _get_bucket_name(self, date): name = self._replace_bucket_placeholders(self.bucket_name_template, date) token = hmac.new(self.bucket_secret.encode('utf-8'), name, hashlib.md5).hexdigest() - return '{}-{}'.format(name, token)[:63] + return f'{name}-{token}'[:63] def _replace_bucket_placeholders(self, name, date): name = name.replace('', date.strftime('%Y')) @@ -257,10 +256,10 @@ class DynamicS3Storage(S3StorageBase): try: bucket = self._get_current_bucket_name() checksum = self._save(bucket, name, content_type, fileobj) - file_id = '{}//{}'.format(bucket, name) + file_id = f'{bucket}//{name}' return file_id, checksum except Exception as e: - raise StorageError('Could not save "{}": {}'.format(name, e)), None, sys.exc_info()[2] + raise None.with_traceback(sys.exc_info()[2]) class ReadOnlyS3Storage(ReadOnlyStorageMixin, S3Storage): diff --git a/storage_s3/indico_storage_s3/task.py b/storage_s3/indico_storage_s3/task.py index 1c27916..0955815 100644 --- a/storage_s3/indico_storage_s3/task.py +++ b/storage_s3/indico_storage_s3/task.py @@ -5,7 +5,6 @@ # them and/or modify them under the terms of the MIT License; # see the LICENSE file for more details. -from __future__ import unicode_literals import re from datetime import date diff --git a/storage_s3/setup.py b/storage_s3/setup.py index c678fb5..a0cb8d8 100644 --- a/storage_s3/setup.py +++ b/storage_s3/setup.py @@ -5,7 +5,6 @@ # them and/or modify them under the terms of the MIT License; # see the LICENSE file for more details. -from __future__ import unicode_literals from setuptools import find_packages, setup diff --git a/storage_s3/tests/plugin_test.py b/storage_s3/tests/plugin_test.py index 1133e2c..a22f058 100644 --- a/storage_s3/tests/plugin_test.py +++ b/storage_s3/tests/plugin_test.py @@ -5,7 +5,6 @@ # them and/or modify them under the terms of the MIT License; # see the LICENSE file for more details. -from __future__ import unicode_literals import hashlib import hmac @@ -39,13 +38,13 @@ def test_resolve_bucket_name_static(): )) def test_resolve_bucket_name_dynamic(freeze_time, date, name_template, expected_name): freeze_time(date) - storage = plugin.DynamicS3Storage('bucket_template={},bucket_secret=secret'.format(name_template)) + storage = plugin.DynamicS3Storage(f'bucket_template={name_template},bucket_secret=secret') name, token = storage._get_current_bucket_name().rsplit('-', 1) assert name == expected_name assert token == hmac.new(b'secret', expected_name, hashlib.md5).hexdigest() -class MockConfig(object): +class MockConfig: def __init__(self): self.STORAGE_BACKENDS = {'s3': None} @@ -68,9 +67,9 @@ def test_dynamic_bucket_creation_task(freeze_time, mocker, date, name_template, expected_error): freeze_time(date) if '<' in name_template: - storage = plugin.DynamicS3Storage('bucket_template={},bucket_secret=secret'.format(name_template)) + storage = plugin.DynamicS3Storage(f'bucket_template={name_template},bucket_secret=secret') else: - storage = plugin.S3Storage('bucket={}'.format(name_template)) + storage = plugin.S3Storage(f'bucket={name_template}') mocker.patch('indico_storage_s3.task.config', MockConfig()) mocker.patch('indico_storage_s3.task.get_storage', return_value=storage) create_bucket_call = mocker.patch.object(plugin.DynamicS3Storage, '_create_bucket') @@ -81,7 +80,7 @@ def test_dynamic_bucket_creation_task(freeze_time, mocker, date, name_template, create_bucket() if bucket_created: token = hmac.new(b'secret', expected_name, hashlib.md5).hexdigest() - create_bucket_call.assert_called_with('{}-{}'.format(expected_name, token)) + create_bucket_call.assert_called_with(f'{expected_name}-{token}') else: assert not create_bucket_call.called diff --git a/update-meta.py b/update-meta.py index ab25fca..d02c1e1 100644 --- a/update-meta.py +++ b/update-meta.py @@ -5,7 +5,6 @@ # them and/or modify them under the terms of the MIT License; # see the LICENSE file for more details. -from __future__ import unicode_literals import errno import os @@ -24,8 +23,8 @@ END_MARKER = '# END GENERATED REQUIREMENTS' def _find_plugins(): - subdirs = sorted((x for x in os.walk('.').next()[1] - if x[0] != '.' and x != '_meta' and os.path.exists(os.path.join(x, 'setup.py')))) + subdirs = sorted(x for x in os.walk('.').next()[1] + if x[0] != '.' and x != '_meta' and os.path.exists(os.path.join(x, 'setup.py'))) for subdir in subdirs: path = os.path.join(subdir, 'setup.py') with open(path) as f: @@ -34,7 +33,7 @@ def _find_plugins(): name = re.search(r'''name=(['"])(.+)\1''', setup_py) version = re.search(r'''version=(['"])(.+)\1''', setup_py) if name is None or version is None: - click.secho('Could not extract name/version from {}'.format(path), fg='red', bold=True) + click.secho(f'Could not extract name/version from {path}', fg='red', bold=True) continue minver = str(Version(version.group(2))) yield name.group(2), minver @@ -44,7 +43,7 @@ def _get_config(): rv = {'extras': {}, 'skip': []} try: f = open('_meta/meta.yaml') - except IOError as exc: + except OSError as exc: if exc.errno != errno.ENOENT: raise else: @@ -83,7 +82,7 @@ def cli(nextver): for name, minver in sorted(_find_plugins()): if name in config['skip']: continue - pkgspec = '{}>={},<{}'.format(name, minver, nextver) + pkgspec = f'{name}>={minver},<{nextver}' if name in config['extras']: extras_require[config['extras'][name]].append(pkgspec) else: @@ -101,8 +100,8 @@ def cli(nextver): output.append('extras_require = {}') else: output.append('extras_require = {') - for extra, pkgspecs in sorted(extras_require.iteritems()): - output.append(' {!r}: {!r},'.format(extra, map(str, sorted(pkgspecs)))) + for extra, pkgspecs in sorted(extras_require.items()): + output.append(' {!r}: {!r},'.format(extra, list(map(str, sorted(pkgspecs))))) output.append('}') if _update_meta('\n'.join(output)): diff --git a/ursh/indico_ursh/__init__.py b/ursh/indico_ursh/__init__.py index 2128765..043cd78 100644 --- a/ursh/indico_ursh/__init__.py +++ b/ursh/indico_ursh/__init__.py @@ -5,7 +5,6 @@ # them and/or modify them under the terms of the MIT License; # see the LICENSE file for more details. -from __future__ import unicode_literals from indico.util.i18n import make_bound_gettext diff --git a/ursh/indico_ursh/blueprint.py b/ursh/indico_ursh/blueprint.py index e1ddcef..793c00c 100644 --- a/ursh/indico_ursh/blueprint.py +++ b/ursh/indico_ursh/blueprint.py @@ -5,7 +5,6 @@ # them and/or modify them under the terms of the MIT License; # see the LICENSE file for more details. -from __future__ import unicode_literals from indico.core.plugins import IndicoPluginBlueprint diff --git a/ursh/indico_ursh/controllers.py b/ursh/indico_ursh/controllers.py index 6ff5e02..5a331fb 100644 --- a/ursh/indico_ursh/controllers.py +++ b/ursh/indico_ursh/controllers.py @@ -5,7 +5,6 @@ # them and/or modify them under the terms of the MIT License; # see the LICENSE file for more details. -from __future__ import unicode_literals import posixpath @@ -74,7 +73,7 @@ class RHCustomShortURLPage(RHManageEventBase): def _process_args(self): from indico_ursh.plugin import UrshPlugin - super(RHCustomShortURLPage, self)._process_args() + super()._process_args() api_host = url_parse(UrshPlugin.settings.get('api_host')) self.ursh_host = strip_end(api_host.to_url(), api_host.path[1:]).rstrip('/') + '/' diff --git a/ursh/indico_ursh/plugin.py b/ursh/indico_ursh/plugin.py index 77e6fdb..2c18d5b 100644 --- a/ursh/indico_ursh/plugin.py +++ b/ursh/indico_ursh/plugin.py @@ -5,7 +5,6 @@ # them and/or modify them under the terms of the MIT License; # see the LICENSE file for more details. -from __future__ import unicode_literals from flask_pluginengine import render_plugin_template from wtforms.fields.core import StringField @@ -42,7 +41,7 @@ class UrshPlugin(IndicoPlugin): } def init(self): - super(UrshPlugin, self).init() + super().init() self.template_hook('url-shortener', self._inject_ursh_link) self.template_hook('page-footer', self._inject_ursh_footer) self.inject_bundle('main.js', WPBase) diff --git a/ursh/indico_ursh/util.py b/ursh/indico_ursh/util.py index 08daa7c..df0952c 100644 --- a/ursh/indico_ursh/util.py +++ b/ursh/indico_ursh/util.py @@ -5,7 +5,6 @@ # them and/or modify them under the terms of the MIT License; # see the LICENSE file for more details. -from __future__ import unicode_literals import json import posixpath @@ -36,7 +35,7 @@ def is_configured(): def request_short_url(original_url): from indico_ursh.plugin import UrshPlugin api_key, api_host = _get_settings() - headers = {'Authorization': 'Bearer {api_key}'.format(api_key=api_key), 'Content-Type': 'application/json'} + headers = {'Authorization': f'Bearer {api_key}', 'Content-Type': 'application/json'} url = posixpath.join(api_host, 'api/urls/') response = requests.post(url, data=json.dumps({'url': original_url, 'allow_reuse': True}), headers=headers) @@ -49,7 +48,7 @@ def request_short_url(original_url): def register_shortcut(original_url, shortcut, user): from indico_ursh.plugin import UrshPlugin api_key, api_host = _get_settings() - headers = {'Authorization': 'Bearer {api_key}'.format(api_key=api_key), 'Content-Type': 'application/json'} + headers = {'Authorization': f'Bearer {api_key}', 'Content-Type': 'application/json'} url = posixpath.join(api_host, 'api/urls', shortcut) data = {'url': original_url, 'allow_reuse': True, 'meta': {'indico.user': user.id}} diff --git a/ursh/indico_ursh/views.py b/ursh/indico_ursh/views.py index a44b2f6..efedffc 100644 --- a/ursh/indico_ursh/views.py +++ b/ursh/indico_ursh/views.py @@ -5,7 +5,6 @@ # them and/or modify them under the terms of the MIT License; # see the LICENSE file for more details. -from __future__ import unicode_literals from indico.core.plugins import WPJinjaMixinPlugin from indico.web.views import WPDecorated diff --git a/ursh/setup.py b/ursh/setup.py index 3ed53b0..8c46c91 100644 --- a/ursh/setup.py +++ b/ursh/setup.py @@ -5,7 +5,6 @@ # them and/or modify them under the terms of the MIT License; # see the LICENSE file for more details. -from __future__ import unicode_literals from setuptools import find_packages, setup diff --git a/vc_dummy/indico_vc_dummy/plugin.py b/vc_dummy/indico_vc_dummy/plugin.py index 87aa077..6a8542d 100644 --- a/vc_dummy/indico_vc_dummy/plugin.py +++ b/vc_dummy/indico_vc_dummy/plugin.py @@ -5,7 +5,6 @@ # them and/or modify them under the terms of the MIT License; # see the LICENSE file for more details. -from __future__ import unicode_literals from sqlalchemy.orm.attributes import flag_modified from wtforms.fields.core import BooleanField @@ -62,7 +61,7 @@ class DummyPlugin(VCPluginMixin, IndicoPlugin): pass def update_data_association(self, event, vc_room, event_vc_room, data): - super(DummyPlugin, self).update_data_association(event, vc_room, event_vc_room, data) + super().update_data_association(event, vc_room, event_vc_room, data) event_vc_room.data.update({key: data.pop(key) for key in [ 'show_phone_numbers' ]}) diff --git a/vc_dummy/setup.py b/vc_dummy/setup.py index ec36429..8d50fc6 100644 --- a/vc_dummy/setup.py +++ b/vc_dummy/setup.py @@ -5,7 +5,6 @@ # them and/or modify them under the terms of the MIT License; # see the LICENSE file for more details. -from __future__ import unicode_literals from setuptools import find_packages, setup diff --git a/vc_vidyo/indico_vc_vidyo/__init__.py b/vc_vidyo/indico_vc_vidyo/__init__.py index 12347a6..876fdd8 100644 --- a/vc_vidyo/indico_vc_vidyo/__init__.py +++ b/vc_vidyo/indico_vc_vidyo/__init__.py @@ -5,7 +5,6 @@ # them and/or modify them under the terms of the MIT License; # see the LICENSE file for more details. -from __future__ import unicode_literals from indico.core import signals from indico.util.i18n import make_bound_gettext diff --git a/vc_vidyo/indico_vc_vidyo/api/client.py b/vc_vidyo/indico_vc_vidyo/api/client.py index fc16f72..51ff954 100644 --- a/vc_vidyo/indico_vc_vidyo/api/client.py +++ b/vc_vidyo/indico_vc_vidyo/api/client.py @@ -39,7 +39,7 @@ def raises_api_error(wrapped): return _wrapper -class ClientBase(object): +class ClientBase: def __init__(self, wsdl, settings): session = Session() transport = Transport(session=session, cache=ZeepCache()) @@ -54,12 +54,12 @@ class ClientBase(object): class UserClient(ClientBase): def __init__(self, settings): - super(UserClient, self).__init__(settings.get('user_api_wsdl'), settings) + super().__init__(settings.get('user_api_wsdl'), settings) class AdminClient(ClientBase): def __init__(self, settings): - super(AdminClient, self).__init__(settings.get('admin_api_wsdl'), settings) + super().__init__(settings.get('admin_api_wsdl'), settings) def create_room_object(self, **kwargs): return self.factory.Room(**kwargs) diff --git a/vc_vidyo/indico_vc_vidyo/blueprint.py b/vc_vidyo/indico_vc_vidyo/blueprint.py index 32036eb..ed7b891 100644 --- a/vc_vidyo/indico_vc_vidyo/blueprint.py +++ b/vc_vidyo/indico_vc_vidyo/blueprint.py @@ -5,7 +5,6 @@ # them and/or modify them under the terms of the MIT License; # see the LICENSE file for more details. -from __future__ import unicode_literals from indico.core.plugins import IndicoPluginBlueprint diff --git a/vc_vidyo/indico_vc_vidyo/cli.py b/vc_vidyo/indico_vc_vidyo/cli.py index 083f276..d99541c 100644 --- a/vc_vidyo/indico_vc_vidyo/cli.py +++ b/vc_vidyo/indico_vc_vidyo/cli.py @@ -5,7 +5,6 @@ # them and/or modify them under the terms of the MIT License; # see the LICENSE file for more details. -from __future__ import unicode_literals import click from terminaltables import AsciiTable @@ -31,10 +30,10 @@ def rooms(status=None): room_query = room_query.filter(VCRoom.status == VCRoomStatus.get(status)) for room in room_query: - table_data.append([unicode(room.id), room.name, room.status.name, - unicode(room.data['vidyo_id']), unicode(room.vidyo_extension.extension)]) + table_data.append([str(room.id), room.name, room.status.name, + str(room.data['vidyo_id']), str(room.vidyo_extension.extension)]) table = AsciiTable(table_data) for col in (0, 3, 4): table.justify_columns[col] = 'right' - print table.table + print(table.table) diff --git a/vc_vidyo/indico_vc_vidyo/controllers.py b/vc_vidyo/indico_vc_vidyo/controllers.py index 618d173..0ef8ae1 100644 --- a/vc_vidyo/indico_vc_vidyo/controllers.py +++ b/vc_vidyo/indico_vc_vidyo/controllers.py @@ -5,7 +5,6 @@ # them and/or modify them under the terms of the MIT License; # see the LICENSE file for more details. -from __future__ import unicode_literals from flask import flash, jsonify, session @@ -26,6 +25,6 @@ class RHVidyoRoomOwner(RHVCSystemEventBase): result['success'] = False db.session.rollback() else: - flash(_("You are now the owner of the room '{room.name}'".format(room=self.vc_room)), 'success') + flash(_(f"You are now the owner of the room '{self.vc_room.name}'"), 'success') result['success'] = True return jsonify(result) diff --git a/vc_vidyo/indico_vc_vidyo/forms.py b/vc_vidyo/indico_vc_vidyo/forms.py index 4d8ae31..bc9e391 100644 --- a/vc_vidyo/indico_vc_vidyo/forms.py +++ b/vc_vidyo/indico_vc_vidyo/forms.py @@ -5,7 +5,6 @@ # them and/or modify them under the terms of the MIT License; # see the LICENSE file for more details. -from __future__ import unicode_literals from wtforms.fields.core import BooleanField from wtforms.fields.simple import TextAreaField @@ -23,7 +22,7 @@ from indico_vc_vidyo.util import iter_user_identities, retrieve_principal PIN_VALIDATORS = [Optional(), Length(min=3, max=10), Regexp(r'^\d+$', message=_("The PIN must be a number"))] -class VidyoAdvancedFormMixin(object): +class VidyoAdvancedFormMixin: # Advanced options (per event) show_pin = BooleanField(_('Show PIN'), widget=SwitchWidget(), @@ -62,7 +61,7 @@ class VCRoomForm(VCRoomFormBase, VidyoAdvancedFormMixin): defaults = kwargs['obj'] if defaults.owner_user is None and defaults.owner is not None: defaults.owner_user = retrieve_principal(defaults.owner) - super(VCRoomForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) @generated_data def owner(self): diff --git a/vc_vidyo/indico_vc_vidyo/http_api.py b/vc_vidyo/indico_vc_vidyo/http_api.py index ec74191..f7ad592 100644 --- a/vc_vidyo/indico_vc_vidyo/http_api.py +++ b/vc_vidyo/indico_vc_vidyo/http_api.py @@ -25,12 +25,12 @@ class DeleteVCRoomAPI(HTTPAPIHook): return user in VidyoPlugin.settings.acls.get('managers') def _getParams(self): - super(DeleteVCRoomAPI, self)._getParams() - self._room_ids = map(int, request.form.getlist('rid')) + super()._getParams() + self._room_ids = list(map(int, request.form.getlist('rid'))) def api_deletevcroom(self, user): - from indico_vc_vidyo.plugin import VidyoPlugin from indico_vc_vidyo.api import APIException + from indico_vc_vidyo.plugin import VidyoPlugin success = [] failed = [] diff --git a/vc_vidyo/indico_vc_vidyo/models/vidyo_extensions.py b/vc_vidyo/indico_vc_vidyo/models/vidyo_extensions.py index 882f3a7..c8b49a9 100644 --- a/vc_vidyo/indico_vc_vidyo/models/vidyo_extensions.py +++ b/vc_vidyo/indico_vc_vidyo/models/vidyo_extensions.py @@ -5,10 +5,10 @@ # them and/or modify them under the terms of the MIT License; # see the LICENSE file for more details. -from __future__ import unicode_literals - -import urllib +import six.moves.urllib.error +import six.moves.urllib.parse +import six.moves.urllib.request from sqlalchemy.event import listens_for from sqlalchemy.orm.attributes import flag_modified @@ -63,12 +63,12 @@ class VidyoExtension(db.Model): url = self.vc_room.data['url'] custom_url_tpl = VidyoPlugin.settings.get('client_chooser_url') if custom_url_tpl: - return custom_url_tpl + '?' + urllib.urlencode({'url': url}) + return custom_url_tpl + '?' + six.moves.urllib.parse.urlencode({'url': url}) return url @return_ascii def __repr__(self): - return ''.format(self.vc_room, self.extension, self.owned_by_user) + return f'' @listens_for(VidyoExtension.owned_by_user, 'set') diff --git a/vc_vidyo/indico_vc_vidyo/plugin.py b/vc_vidyo/indico_vc_vidyo/plugin.py index bb28a3b..36b6224 100644 --- a/vc_vidyo/indico_vc_vidyo/plugin.py +++ b/vc_vidyo/indico_vc_vidyo/plugin.py @@ -5,7 +5,6 @@ # them and/or modify them under the terms of the MIT License; # see the LICENSE file for more details. -from __future__ import unicode_literals from flask import session from sqlalchemy.orm.attributes import flag_modified @@ -75,7 +74,7 @@ class VidyoPlugin(VCPluginMixin, IndicoPlugin): friendly_name = 'Vidyo' def init(self): - super(VidyoPlugin, self).init() + super().init() self.connect(signals.plugin.cli, self._extend_indico_cli) self.inject_bundle('main.js', WPSimpleEventDisplay) self.inject_bundle('main.js', WPVCEventPage) @@ -95,7 +94,7 @@ class VidyoPlugin(VCPluginMixin, IndicoPlugin): # we skip identity providers in the default list if they don't support get_identity. # these providers (local accounts, oauth) are unlikely be the correct ones to integrate # with the vidyo infrastructure. - 'authenticators': ', '.join(p.name for p in multipass.identity_providers.itervalues() if p.supports_get), + 'authenticators': ', '.join(p.name for p in multipass.identity_providers.values() if p.supports_get), 'num_days_old': 365, 'max_rooms_warning': 5000, 'vidyo_phone_link': None, @@ -115,7 +114,7 @@ class VidyoPlugin(VCPluginMixin, IndicoPlugin): return cli def update_data_association(self, event, vc_room, event_vc_room, data): - super(VidyoPlugin, self).update_data_association(event, vc_room, event_vc_room, data) + super().update_data_association(event, vc_room, event_vc_room, data) event_vc_room.data.update({key: data.pop(key) for key in [ 'show_pin', @@ -126,7 +125,7 @@ class VidyoPlugin(VCPluginMixin, IndicoPlugin): flag_modified(event_vc_room, 'data') def update_data_vc_room(self, vc_room, data, is_new=False): - super(VidyoPlugin, self).update_data_vc_room(vc_room, data) + super().update_data_vc_room(vc_room, data, is_new=is_new) for key in ['description', 'owner', 'room_pin', 'moderation_pin', 'auto_mute']: if key in data: @@ -203,7 +202,7 @@ class VidyoPlugin(VCPluginMixin, IndicoPlugin): if not created_room: raise VCRoomNotFoundError(_("Could not find newly created room in Vidyo")) vc_room.data.update({ - 'vidyo_id': unicode(created_room.roomID), + 'vidyo_id': str(created_room.roomID), 'url': created_room.RoomMode.roomURL, 'owner_identity': created_room.ownerName }) @@ -299,7 +298,7 @@ class VidyoPlugin(VCPluginMixin, IndicoPlugin): return blueprint def get_vc_room_form_defaults(self, event): - defaults = super(VidyoPlugin, self).get_vc_room_form_defaults(event) + defaults = super().get_vc_room_form_defaults(event) defaults.update({ 'auto_mute': True, 'show_pin': False, @@ -311,7 +310,7 @@ class VidyoPlugin(VCPluginMixin, IndicoPlugin): return defaults def get_vc_room_attach_form_defaults(self, event): - defaults = super(VidyoPlugin, self).get_vc_room_attach_form_defaults(event) + defaults = super().get_vc_room_attach_form_defaults(event) defaults.update({ 'show_pin': False, 'show_autojoin': True, @@ -320,10 +319,10 @@ class VidyoPlugin(VCPluginMixin, IndicoPlugin): return defaults def can_manage_vc_room(self, user, room): - return user == room.vidyo_extension.owned_by_user or super(VidyoPlugin, self).can_manage_vc_room(user, room) + return user == room.vidyo_extension.owned_by_user or super().can_manage_vc_room(user, room) def _merge_users(self, target, source, **kwargs): - super(VidyoPlugin, self)._merge_users(target, source, **kwargs) + super()._merge_users(target, source, **kwargs) for ext in VidyoExtension.find(owned_by_user=source): ext.owned_by_user = target flag_modified(ext.vc_room, 'data') diff --git a/vc_vidyo/indico_vc_vidyo/task.py b/vc_vidyo/indico_vc_vidyo/task.py index 18e4fdc..71f0469 100644 --- a/vc_vidyo/indico_vc_vidyo/task.py +++ b/vc_vidyo/indico_vc_vidyo/task.py @@ -5,7 +5,6 @@ # them and/or modify them under the terms of the MIT License; # see the LICENSE file for more details. -from __future__ import unicode_literals from datetime import timedelta diff --git a/vc_vidyo/indico_vc_vidyo/util.py b/vc_vidyo/indico_vc_vidyo/util.py index 882b4f1..2954fc8 100644 --- a/vc_vidyo/indico_vc_vidyo/util.py +++ b/vc_vidyo/indico_vc_vidyo/util.py @@ -5,7 +5,6 @@ # them and/or modify them under the terms of the MIT License; # see the LICENSE file for more details. -from __future__ import unicode_literals import re @@ -60,11 +59,11 @@ def iter_extensions(prefix, event_id): """Return extension (prefix + event_id) with an optional suffix which is incremented step by step in case of collision """ - extension = '{prefix}{event_id}'.format(prefix=prefix, event_id=event_id) + extension = f'{prefix}{event_id}' yield extension suffix = 1 while True: - yield '{extension}{suffix}'.format(extension=extension, suffix=suffix) + yield f'{extension}{suffix}' suffix += 1 @@ -77,7 +76,7 @@ def update_room_from_obj(settings, vc_room, room_obj): vc_room.data.update({ 'description': room_obj.description, - 'vidyo_id': unicode(room_obj.roomID), + 'vidyo_id': str(room_obj.roomID), 'url': room_obj.RoomMode.roomURL, 'owner_identity': room_obj.ownerName, 'room_pin': room_obj.RoomMode.roomPIN if room_obj.RoomMode.hasPIN else "", @@ -92,4 +91,4 @@ def retrieve_principal(principal): if type_ in {'Avatar', 'User'}: return User.get(int(id_)) else: - raise ValueError('Unexpected type: {}'.format(type_)) + raise ValueError(f'Unexpected type: {type_}') diff --git a/vc_vidyo/setup.py b/vc_vidyo/setup.py index 15a5b1f..602d167 100644 --- a/vc_vidyo/setup.py +++ b/vc_vidyo/setup.py @@ -5,7 +5,6 @@ # them and/or modify them under the terms of the MIT License; # see the LICENSE file for more details. -from __future__ import unicode_literals from setuptools import find_packages, setup diff --git a/vc_vidyo/tests/task_test.py b/vc_vidyo/tests/task_test.py index 1b62e93..679ff4d 100644 --- a/vc_vidyo/tests/task_test.py +++ b/vc_vidyo/tests/task_test.py @@ -56,7 +56,7 @@ def test_room_cleanup(create_event, create_dummy_room, freeze_time, db): (1235, 5679, (2,)), (1236, 5670, (4,)), (1237, 5671, ())), start=1): - room = create_dummy_room('test_room_{}'.format(id_), extension, { + room = create_dummy_room(f'test_room_{id_}', extension, { 'vidyo_id': vidyo_id }) for evt_id in evt_ids: