diff --git a/profiling/construct_api_calls.py b/profiling/construct_api_calls.py index c33930ac..8979e2f3 100755 --- a/profiling/construct_api_calls.py +++ b/profiling/construct_api_calls.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -#-*- coding:utf-8 -*- import os import sys diff --git a/profiling/extract_api_calls.py b/profiling/extract_api_calls.py index f88e373a..a38721bc 100755 --- a/profiling/extract_api_calls.py +++ b/profiling/extract_api_calls.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -#-*- coding:utf-8 -*- import os import sys diff --git a/requirements-to-freeze.txt b/requirements-to-freeze.txt index 805e0335..fc6198ab 100644 --- a/requirements-to-freeze.txt +++ b/requirements-to-freeze.txt @@ -82,7 +82,6 @@ requests nose>=1.3.7 django-nose -mock mock_django responses django-debug-toolbar>3 diff --git a/requirements.txt b/requirements.txt index 74752506..341e346f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -39,7 +39,6 @@ importlib-metadata==4.13.0 jmespath==1.0.1 kombu==5.2.4 Markdown==2.6.11 -mock==5.0.1 mock-django==0.6.10 nose==1.3.7 oauthlib==3.2.2 diff --git a/src/project/__init__.py b/src/project/__init__.py index 03c60eb5..0e11489d 100644 --- a/src/project/__init__.py +++ b/src/project/__init__.py @@ -1,5 +1,3 @@ - - # This will make sure the app is always imported when # Django starts so that shared_task will use this app. from .celery import app as celery_app \ No newline at end of file diff --git a/src/project/celery.py b/src/project/celery.py index 8627f396..20f4d924 100644 --- a/src/project/celery.py +++ b/src/project/celery.py @@ -1,5 +1,3 @@ - -from __future__ import absolute_import, unicode_literals import os import sys from os.path import abspath, join diff --git a/src/project/twinkie.py b/src/project/twinkie.py index 4ff9ed4b..ecc52496 100644 --- a/src/project/twinkie.py +++ b/src/project/twinkie.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - from django.conf import settings from wsgiref.headers import Headers from wsgiref.handlers import format_date_time @@ -9,7 +7,7 @@ log = getLogger(__name__) -class ExpiresMiddleware (object): +class ExpiresMiddleware: """WSGI middleware that intercepts calls to the static files directory, as defined by the STATIC_URL setting, and serves those files. """ diff --git a/src/remote_client_user/middleware.py b/src/remote_client_user/middleware.py index ba5cc096..df8ae52f 100644 --- a/src/remote_client_user/middleware.py +++ b/src/remote_client_user/middleware.py @@ -52,7 +52,7 @@ def get_authed_user(request): return user -class RemoteClientMiddleware(object): +class RemoteClientMiddleware: def process_request(self, request): user = get_authed_user(request) diff --git a/src/sa_api_v2/admin.py b/src/sa_api_v2/admin.py index 88eab534..a67339d5 100644 --- a/src/sa_api_v2/admin.py +++ b/src/sa_api_v2/admin.py @@ -78,7 +78,7 @@ def render(self, name, value, attrs=None): except ValueError: # If we cannot, then we should still display the value pass - return super(PrettyAceWidget, self).render(name, value, attrs=attrs) + return super().render(name, value, attrs=attrs) BaseGeoAdmin = admin.OSMGeoAdmin if settings.USE_GEODB else admin.ModelAdmin @@ -98,14 +98,14 @@ def submitter_name(self, obj): return obj.submitter.username if obj.submitter else None def get_queryset(self, request): - qs = super(SubmittedThingAdmin, self).get_queryset(request) + qs = super().get_queryset(request) user = request.user if not user.is_superuser: qs = qs.filter(dataset__owner=user) return qs def get_form(self, request, obj=None, **kwargs): - FormWithJSONCleaning = super(SubmittedThingAdmin, self).get_form(request, obj=obj, **kwargs) + FormWithJSONCleaning = super().get_form(request, obj=obj, **kwargs) def clean_json_blob(form): data = form.cleaned_data['data'] @@ -223,7 +223,7 @@ class WebhookAdmin(admin.ModelAdmin): # list_filter = ('name',) def get_queryset(self, request): - qs = super(WebhookAdmin, self).get_queryset(request) + qs = super().get_queryset(request) user = request.user if not user.is_superuser: qs = qs.filter(dataset__owner=user) @@ -246,7 +246,7 @@ def clear_cache(self, request, obj): def clone_dataset(self, request, obj): siblings = models.DataSet.objects.filter(owner=obj.owner) - slugs = set([ds.slug for ds in siblings]) + slugs = {ds.slug for ds in siblings} for uniquifier in itertools.count(2): unique_slug = '-'.join([obj.slug, str(uniquifier)]) @@ -275,7 +275,7 @@ def api_path(self, instance): api_path.allow_tags = True def get_queryset(self, request): - qs = super(DataSetAdmin, self).get_queryset(request) + qs = super().get_queryset(request) user = request.user if not user.is_superuser: qs = qs.filter(owner=user) @@ -286,7 +286,7 @@ def get_form(self, request, obj=None, **kwargs): # user should be assumed to be owned by themselves. if not request.user.is_superuser: self.exclude = (self.exclude or ()) + ('owner',) - return super(DataSetAdmin, self).get_form(request, obj, **kwargs) + return super().get_form(request, obj, **kwargs) def save_model(self, request, obj, form, change): # Set the current user as the owner if the object has no owner and the @@ -295,7 +295,7 @@ def save_model(self, request, obj, form, change): if not user.is_superuser: if obj.owner_id is None: obj.owner = user - super(DataSetAdmin, self).save_model(request, obj, form, change) + super().save_model(request, obj, form, change) class PlaceAdmin(SubmittedThingAdmin): @@ -338,7 +338,7 @@ class ActionAdmin(admin.ModelAdmin): # Pre-Django 1.6 def queryset(self, request): - qs = super(ActionAdmin, self).queryset(request) + qs = super().queryset(request) user = request.user if not user.is_superuser: qs = qs.filter(thing__dataset__owner=user) @@ -346,7 +346,7 @@ def queryset(self, request): # Django 1.6+ def get_queryset(self, request): - qs = super(ActionAdmin, self).get_queryset(request) + qs = super().get_queryset(request) user = request.user if not user.is_superuser: qs = qs.filter(thing__dataset__owner=user) @@ -380,7 +380,7 @@ class Media: ) def get_queryset(self, request): - qs = super(GroupAdmin, self).get_queryset(request) + qs = super().get_queryset(request) user = request.user if not user.is_superuser: qs = qs.filter(dataset__owner=user) @@ -401,7 +401,7 @@ class UserAdmin(BaseUserAdmin): ) def get_queryset(self, request): - qs = super(UserAdmin, self).get_queryset(request) + qs = super().get_queryset(request) user = request.user if not user.is_superuser: # Only show users that have contributed to the owner's datasets diff --git a/src/sa_api_v2/apikey/admin.py b/src/sa_api_v2/apikey/admin.py index 692706ce..0c15f7d8 100644 --- a/src/sa_api_v2/apikey/admin.py +++ b/src/sa_api_v2/apikey/admin.py @@ -25,7 +25,7 @@ class Media: ) def get_queryset(self, request): - qs = super(ApiKeyAdmin, self).get_queryset(request) + qs = super().get_queryset(request) user = request.user if not user.is_superuser: qs = qs.filter(dataset__owner=user) diff --git a/src/sa_api_v2/apikey/auth.py b/src/sa_api_v2/apikey/auth.py index 879d20ec..e073d6aa 100644 --- a/src/sa_api_v2/apikey/auth.py +++ b/src/sa_api_v2/apikey/auth.py @@ -6,7 +6,7 @@ KEY_HEADER = 'HTTP_X_SHAREABOUTS_KEY' -class APIKeyBackend(object): +class APIKeyBackend: """ Django authentication backend purely by API key. """ diff --git a/src/sa_api_v2/apikey/models.py b/src/sa_api_v2/apikey/models.py index 193a3ac8..86680244 100644 --- a/src/sa_api_v2/apikey/models.py +++ b/src/sa_api_v2/apikey/models.py @@ -78,7 +78,7 @@ def clone_related(self, onto): permission.clone(overrides={'key': onto}) def get_ignore_fields(self, ModelClass): - fields = super(ApiKey, self).get_ignore_fields(ModelClass) + fields = super().get_ignore_fields(ModelClass) # Do not copy over the actual key value if ModelClass == ApiKey: fields.add('key') @@ -87,7 +87,7 @@ def get_ignore_fields(self, ModelClass): def save(self, *args, **kwargs): if self.logged_ip == '': self.logged_ip = None - return super(ApiKey, self).save(*args, **kwargs) + return super().save(*args, **kwargs) def create_data_permissions(sender, instance, created, **kwargs): diff --git a/src/sa_api_v2/auth_backends.py b/src/sa_api_v2/auth_backends.py index 5f3d9635..fcb4bb63 100644 --- a/src/sa_api_v2/auth_backends.py +++ b/src/sa_api_v2/auth_backends.py @@ -5,6 +5,6 @@ class CachedModelBackend (ModelBackend): def get_user(self, user_id): user = UserCache.get_instance(user_id=user_id) if user is None: - user = super(CachedModelBackend, self).get_user(user_id) + user = super().get_user(user_id) UserCache.set_instance(user, user_id=user_id) return user diff --git a/src/sa_api_v2/cache.py b/src/sa_api_v2/cache.py index ebf85e1c..18c99f91 100644 --- a/src/sa_api_v2/cache.py +++ b/src/sa_api_v2/cache.py @@ -12,7 +12,7 @@ # A sentinel object to differentiate from None Undefined = object() -class CacheBuffer (object): +class CacheBuffer: def __init__(self, initial_buffer=None): # When we get a value from the remote cache, it goes in to the buffer # so that we can retrieve it quickly on our next try. @@ -45,7 +45,7 @@ def get_many(self, keys): # TODO: Is this what's supposed to happen? get_many returns None for # each key that wasn't found? - all_results = dict([(key, Undefined) for key in set(keys) - set(results.keys())]) + all_results = {key: Undefined for key in set(keys) - set(results.keys())} self.buffer.update(all_results) return results @@ -165,7 +165,7 @@ def reset(self): cache_buffer = CacheBuffer() -class Cache (object): +class Cache: """ The base class for objects responsible for caching Shareabouts data structure information @@ -285,7 +285,7 @@ def get_serialized_data_keys(self, inst_key): meta_key = self.get_serialized_data_meta_key(inst_key) if meta_key is not None: keys = cache_buffer.get(meta_key) - return (keys or set()) | set([meta_key]) + return (keys or set()) | {meta_key} else: return set() @@ -301,7 +301,7 @@ def clear_instance(self, obj): #Serialized data keys data_keys = self.get_serialized_data_keys(obj) # Collect other related keys - other_keys = self.get_other_keys(**params) | set([self.get_instance_params_key(obj.pk)]) + other_keys = self.get_other_keys(**params) | {self.get_instance_params_key(obj.pk)} # Clear all the keys self.clear_keys(*(prefixed_keys | data_keys | other_keys)) @@ -336,7 +336,7 @@ def get_request_prefixes(cls, **params): @classmethod def get_other_keys(cls, **params): - return set([cls.get_instance_key(**params)]) + return {cls.get_instance_key(**params)} class DataSetCache (Cache): @@ -384,7 +384,7 @@ def get_instance_params(self, dataset_obj): # == Cache invalidation def get_request_prefixes(self, **params): owner, dataset = list(map(params.get, ('owner_username', 'dataset_slug'))) - prefixes = super(DataSetCache, self).get_request_prefixes(**params) + prefixes = super().get_request_prefixes(**params) instance_path = reverse('dataset-detail', args=[owner, dataset]) collection_path = reverse('dataset-list', args=[owner]) @@ -393,7 +393,7 @@ def get_request_prefixes(self, **params): return prefixes def get_other_keys(self, **params): - return set([self.get_instance_key(**params), self.get_permissions_key(**params)]) + return {self.get_instance_key(**params), self.get_permissions_key(**params)} class PlaceCache (Cache): @@ -411,7 +411,7 @@ def get_instance_params(self, place_obj): def get_request_prefixes(self, **params): owner, dataset, place = list(map(params.get, ('owner_username', 'dataset_slug', 'place_id'))) - prefixes = super(PlaceCache, self).get_request_prefixes(**params) + prefixes = super().get_request_prefixes(**params) instance_path = reverse('place-detail', args=[owner, dataset, place]) collection_path = reverse('place-list', args=[owner, dataset]) @@ -447,7 +447,7 @@ def get_other_keys(self, **params): def get_request_prefixes(self, **params): owner, dataset, place, submission_set_name, submission = list(map(params.get, ['owner_username', 'dataset_slug', 'place_id', 'submission_set_name', 'submission_id'])) - prefixes = super(SubmissionCache, self).get_request_prefixes(**params) + prefixes = super().get_request_prefixes(**params) # TODO: it's pretty clear that a developer should be able to register # a URL with or cache key or prefix or something to be cleared. How @@ -516,7 +516,7 @@ def get_instance_params(self, attachment_obj): return params def get_request_prefixes(self, **params): - prefixes = super(AttachmentCache, self).get_request_prefixes(**params) + prefixes = super().get_request_prefixes(**params) if params['thing_type'] == 'submission': return prefixes | self.get_submission_attachment_request_prefixes(**params) else: @@ -563,4 +563,4 @@ def get_other_keys(self, **params): thing_serialized_data_keys = self.place_cache.get_serialized_data_keys(thing_id) # Union the two sets - return set([thing_attachments_key]) | thing_serialized_data_keys + return {thing_attachments_key} | thing_serialized_data_keys diff --git a/src/sa_api_v2/cors/admin.py b/src/sa_api_v2/cors/admin.py index 657326fd..b5a0bb08 100644 --- a/src/sa_api_v2/cors/admin.py +++ b/src/sa_api_v2/cors/admin.py @@ -22,7 +22,7 @@ class Media: ) def get_queryset(self, request): - qs = super(OriginAdmin, self).get_queryset(request) + qs = super().get_queryset(request) user = request.user if not user.is_superuser: qs = qs.filter(dataset__owner=user) @@ -31,6 +31,6 @@ def get_queryset(self, request): def save_model(self, request, obj, form, change): if obj.logged_ip == '': obj.logged_ip = None - super(OriginAdmin, self).save_model(request, obj, form, change) + super().save_model(request, obj, form, change) admin.site.register(Origin, OriginAdmin) diff --git a/src/sa_api_v2/cors/models.py b/src/sa_api_v2/cors/models.py index f82555e6..380fa1bd 100644 --- a/src/sa_api_v2/cors/models.py +++ b/src/sa_api_v2/cors/models.py @@ -86,7 +86,7 @@ def clone_related(self, onto): def save(self, *args, **kwargs): if self.logged_ip == '': self.logged_ip = None - return super(Origin, self).save(*args, **kwargs) + return super().save(*args, **kwargs) def create_data_permissions(sender, instance, created, **kwargs): diff --git a/src/sa_api_v2/migrations/0001_initial.py b/src/sa_api_v2/migrations/0001_initial.py index 778a5297..6b3dcb42 100644 --- a/src/sa_api_v2/migrations/0001_initial.py +++ b/src/sa_api_v2/migrations/0001_initial.py @@ -1,6 +1,3 @@ -# -*- coding: utf-8 -*- - - from django.db import models, migrations import sa_api_v2.models.caching import django.contrib.gis.db.models.fields @@ -324,7 +321,7 @@ class Migration(migrations.Migration): ), migrations.AlterUniqueTogether( name='group', - unique_together=set([('name', 'dataset')]), + unique_together={('name', 'dataset')}, ), migrations.AddField( model_name='datasnapshot', @@ -334,7 +331,7 @@ class Migration(migrations.Migration): ), migrations.AlterUniqueTogether( name='dataset', - unique_together=set([('owner', 'slug')]), + unique_together={('owner', 'slug')}, ), migrations.AddField( model_name='dataindex', diff --git a/src/sa_api_v2/migrations/0002_auto__add_protected_access_flag.py b/src/sa_api_v2/migrations/0002_auto__add_protected_access_flag.py index 6794dbd0..ba30d795 100644 --- a/src/sa_api_v2/migrations/0002_auto__add_protected_access_flag.py +++ b/src/sa_api_v2/migrations/0002_auto__add_protected_access_flag.py @@ -1,6 +1,3 @@ -# -*- coding: utf-8 -*- - - from django.db import models, migrations diff --git a/src/sa_api_v2/migrations/0003_django18_upgrade.py b/src/sa_api_v2/migrations/0003_django18_upgrade.py index 3beeec30..dfc2d1d4 100644 --- a/src/sa_api_v2/migrations/0003_django18_upgrade.py +++ b/src/sa_api_v2/migrations/0003_django18_upgrade.py @@ -1,6 +1,3 @@ -# -*- coding: utf-8 -*- - - from django.db import migrations, models import sa_api_v2.models.profiles import django.core.validators diff --git a/src/sa_api_v2/migrations/0004_django_19_updates.py b/src/sa_api_v2/migrations/0004_django_19_updates.py index 79748792..2a68b777 100644 --- a/src/sa_api_v2/migrations/0004_django_19_updates.py +++ b/src/sa_api_v2/migrations/0004_django_19_updates.py @@ -1,6 +1,4 @@ -# -*- coding: utf-8 -*- # Generated by Django 1.9.13 on 2019-06-07 22:56 -from __future__ import unicode_literals import django.core.validators from django.db import migrations, models diff --git a/src/sa_api_v2/migrations/0005_add_dimensions_to_attachments.py b/src/sa_api_v2/migrations/0005_add_dimensions_to_attachments.py index db8b3072..fdc487dc 100644 --- a/src/sa_api_v2/migrations/0005_add_dimensions_to_attachments.py +++ b/src/sa_api_v2/migrations/0005_add_dimensions_to_attachments.py @@ -1,6 +1,4 @@ -# -*- coding: utf-8 -*- # Generated by Django 1.11.28 on 2020-02-29 16:58 -from __future__ import unicode_literals import django.contrib.auth.validators from django.db import migrations, models diff --git a/src/sa_api_v2/models/caching.py b/src/sa_api_v2/models/caching.py index 7ef2aaec..82468dfd 100644 --- a/src/sa_api_v2/models/caching.py +++ b/src/sa_api_v2/models/caching.py @@ -2,7 +2,7 @@ from importlib import import_module -class CacheClearingModel (object): +class CacheClearingModel: @classmethod def resolve_attr(cls, attr): if hasattr(cls, attr): @@ -43,7 +43,7 @@ def clear_instance_cache(self): pass def save(self, clear_cache=True, *args, **kwargs): - result = super(CacheClearingModel, self).save(*args, **kwargs) + result = super().save(*args, **kwargs) if clear_cache: self.clear_instance_cache() return result @@ -51,4 +51,4 @@ def save(self, clear_cache=True, *args, **kwargs): def delete(self, clear_cache=True, *args, **kwargs): if clear_cache: self.clear_instance_cache() - return super(CacheClearingModel, self).delete(*args, **kwargs) + return super().delete(*args, **kwargs) diff --git a/src/sa_api_v2/models/core.py b/src/sa_api_v2/models/core.py index 90263f3d..6beb8acc 100644 --- a/src/sa_api_v2/models/core.py +++ b/src/sa_api_v2/models/core.py @@ -97,7 +97,7 @@ def emit_action(self, source='', is_new=None): def save(self, silent=False, source='', reindex=True, *args, **kwargs): is_new = (self.id == None) - ret = super(SubmittedThing, self).save(*args, **kwargs) + ret = super().save(*args, **kwargs) if reindex: self.index_values() @@ -327,7 +327,7 @@ def apply_image_dimensions(self): def save(self, *args, **kwargs): if self.width is None or self.height is None: self.apply_image_dimensions() - super(Attachment, self).save(*args, **kwargs) + super().save(*args, **kwargs) class Meta: diff --git a/src/sa_api_v2/models/data_indexes.py b/src/sa_api_v2/models/data_indexes.py index 243a0a58..988c9106 100644 --- a/src/sa_api_v2/models/data_indexes.py +++ b/src/sa_api_v2/models/data_indexes.py @@ -29,7 +29,7 @@ def get_clone_save_kwargs(self): return {'reindex': False} def save(self, reindex=True, *args, **kwargs): - ret = super(DataIndex, self).save(*args, **kwargs) + ret = super().save(*args, **kwargs) if reindex: self.index_things() return ret @@ -85,7 +85,7 @@ def get(self): raise KeyError('The thing %s has no data attribute %s' % (self.thing, self.index.attr_name)) -class FilterByIndexMixin (object): +class FilterByIndexMixin: """ Mixin for model managers of indexed models. """ diff --git a/src/sa_api_v2/models/data_permissions.py b/src/sa_api_v2/models/data_permissions.py index b0d761aa..97149976 100644 --- a/src/sa_api_v2/models/data_permissions.py +++ b/src/sa_api_v2/models/data_permissions.py @@ -84,7 +84,7 @@ def save(self, *args, **kwargs): except IndexError: self.priority = 0 - return super(DataPermission, self).save(*args, **kwargs) + return super().save(*args, **kwargs) class DataSetPermission (DataPermission): diff --git a/src/sa_api_v2/models/mixins.py b/src/sa_api_v2/models/mixins.py index 06b681e5..b4137ba4 100644 --- a/src/sa_api_v2/models/mixins.py +++ b/src/sa_api_v2/models/mixins.py @@ -1,7 +1,7 @@ from django.db import models -class CloneableModelMixin (object): +class CloneableModelMixin: """ Mixin providing a clone method that copies all of a models instance's fields to a new instance of the model, allowing overrides. @@ -11,7 +11,7 @@ def get_ignore_fields(self, ModelClass): pk_fld = ModelClass._meta.pk pk_name = pk_fld.name - ignore_field_names = set([pk_name]) + ignore_field_names = {pk_name} # For OneToOneFields, ignore the reverse relation field if isinstance(pk_fld, models.OneToOneField): diff --git a/src/sa_api_v2/models/profiles.py b/src/sa_api_v2/models/profiles.py index bfd26661..a3f43cd6 100644 --- a/src/sa_api_v2/models/profiles.py +++ b/src/sa_api_v2/models/profiles.py @@ -8,7 +8,7 @@ class ShareaboutsUserManager (UserManager): def get_queryset(self): - return super(ShareaboutsUserManager, self).get_queryset().prefetch_related('_groups') + return super().get_queryset().prefetch_related('_groups') def get_twitter_access_token(self): from django.conf import settings diff --git a/src/sa_api_v2/parsers.py b/src/sa_api_v2/parsers.py index 0657a70c..46738b45 100644 --- a/src/sa_api_v2/parsers.py +++ b/src/sa_api_v2/parsers.py @@ -6,7 +6,7 @@ class GeoJSONParser (JSONParser): renderer_class = GeoJSONRenderer def parse(self, stream, media_type, parser_context): - data = super(GeoJSONParser, self).parse(stream, media_type, parser_context) + data = super().parse(stream, media_type, parser_context) if isinstance(data, dict): data = self.process_object(data) diff --git a/src/sa_api_v2/renderers.py b/src/sa_api_v2/renderers.py index 75d0e0d6..80c7d472 100644 --- a/src/sa_api_v2/renderers.py +++ b/src/sa_api_v2/renderers.py @@ -34,7 +34,7 @@ def render(self, data, accepted_media_type=None, renderer_context=None): """ renderer_context = renderer_context or {} callback = self.get_callback(renderer_context) - json = super(JSONPRenderer, self).render(data, accepted_media_type, renderer_context) + json = super().render(data, accepted_media_type, renderer_context) return callback.encode(self.charset) + b'(' + json + b');' @@ -42,7 +42,7 @@ class PaginatedCSVRenderer (CSVRenderer): def render(self, data, media_type=None, renderer_context=None): if not isinstance(data, list): data = data.get('results') or data.get('features') - return super(PaginatedCSVRenderer, self).render(data, media_type, renderer_context) + return super().render(data, media_type, renderer_context) class GeoJSONRenderer(JSONRenderer): @@ -62,7 +62,7 @@ def render(self, data, media_type=None, renderer_context=None): # Let error codes slip through to the super class method. response = (renderer_context or {}).get('response') if response and response.status_code >= 400: - return super(GeoJSONRenderer, self).render(data, media_type, renderer_context) + return super().render(data, media_type, renderer_context) # Assume everything else is a successful geometry. if isinstance(data, list): @@ -78,7 +78,7 @@ def render(self, data, media_type=None, renderer_context=None): else: new_data = self.get_feature(data) or data - return super(GeoJSONRenderer, self).render(new_data, media_type, renderer_context) + return super().render(new_data, media_type, renderer_context) def get_feature(self, data): if 'geometry' not in data: @@ -121,8 +121,8 @@ class NullJSONRenderer(JSONRenderer): """ def render(self, data, media_type=None, renderer_context=None): if data is None: - return bytes('null'.encode('utf-8')) - return super(NullJSONRenderer, self).render(data, media_type, renderer_context) + return bytes(b'null') + return super().render(data, media_type, renderer_context) class NullJSONPRenderer(JSONPRenderer, NullJSONRenderer): diff --git a/src/sa_api_v2/serializers.py b/src/sa_api_v2/serializers.py index f632c96a..9e393a18 100644 --- a/src/sa_api_v2/serializers.py +++ b/src/sa_api_v2/serializers.py @@ -41,7 +41,7 @@ def __init__(self, format='dict', *args, **kwargs): if self.format not in ('json', 'wkt', 'dict'): raise ValueError('Invalid format: %s' % self.format) - super(GeometryField, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) def to_representation(self, obj): if self.format == 'json': @@ -68,7 +68,7 @@ def to_internal_value(self, data): # --------------------------- # -class ShareaboutsFieldMixin (object): +class ShareaboutsFieldMixin: # These names should match the names of the cache parameters, and should be # in the same order as the corresponding URL arguments. @@ -123,7 +123,7 @@ def api_reverse(view_name, kwargs={}, request=None, format=None): except KeyError: raise ValueError('No API route named {} formatted.'.format(view_name)) - url_params = dict([(key, urlquote_plus(val)) for key,val in kwargs.items()]) + url_params = {key: urlquote_plus(val) for key,val in kwargs.items()} url += route_template_string.format(**url_params) if format is not None: @@ -142,7 +142,7 @@ def __init__(self, *args, **kwargs): if self.view_name is not None: kwargs['view_name'] = self.view_name kwargs.setdefault('read_only', self.read_only) - super(ShareaboutsRelatedField, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) def use_pk_only_optimization(self): return False @@ -188,7 +188,7 @@ class ShareaboutsIdentityField (ShareaboutsFieldMixin, serializers.HyperlinkedId def __init__(self, *args, **kwargs): view_name = kwargs.pop('view_name', None) or getattr(self, 'view_name', None) - super(ShareaboutsIdentityField, self).__init__(view_name=view_name, *args, **kwargs) + super().__init__(view_name=view_name, *args, **kwargs) def get_url(self, obj, view_name, request, format): # Unsaved objects will not yet have a valid URL. @@ -241,7 +241,7 @@ def to_representation(self, obj): # -class ActivityGenerator (object): +class ActivityGenerator: def _set_silent_flag(self, attrs): request = self.context['request'] silent_header = request.META.get('HTTP_X_SHAREABOUTS_SILENT', 'False') @@ -253,7 +253,7 @@ def validate(self, attrs): return attrs -class EmptyModelSerializer (object): +class EmptyModelSerializer: """ A simple mixin that constructs an in-memory model when None is passed in as the object to to_representation. @@ -299,7 +299,7 @@ def to_internal_value(self, data): if not field.read_only: structured_attrs.setdefault(field_name, field.default) - return super(DataBlobProcessor, self).to_internal_value(structured_attrs) + return super().to_internal_value(structured_attrs) def explode_data_blob(self, data): """ @@ -319,7 +319,7 @@ def explode_data_blob(self, data): def to_representation(self, obj): obj = self.ensure_obj(obj) - data = super(DataBlobProcessor, self).to_representation(obj) + data = super().to_representation(obj) self.explode_data_blob(data) return data @@ -332,7 +332,7 @@ def to_representation(self, obj): # objects. # -class DefaultUserDataStrategy (object): +class DefaultUserDataStrategy: def extract_avatar_url(self, user_info): return '' @@ -343,11 +343,11 @@ def extract_bio(self, user_info): return '' -class TwitterUserDataStrategy (object): +class TwitterUserDataStrategy: def extract_avatar_url(self, user_info): url = user_info['profile_image_url'] - url_pattern = '^(?P.*?)(?:_normal|_mini|_bigger|)(?P\.[^\.]*)$' + url_pattern = r'^(?P.*?)(?:_normal|_mini|_bigger|)(?P\.[^\.]*)$' match = re.match(url_pattern, url) if match: return match.group('path') + '_bigger' + match.group('ext') @@ -361,7 +361,7 @@ def extract_bio(self, user_info): return user_info['description'] -class FacebookUserDataStrategy (object): +class FacebookUserDataStrategy: def extract_avatar_url(self, user_info): url = user_info['picture']['data']['url'] return url @@ -373,7 +373,7 @@ def extract_bio(self, user_info): return user_info['about'] -class ShareaboutsUserDataStrategy (object): +class ShareaboutsUserDataStrategy: """ This strategy exists so that we can add avatars and full names to users that already exist in the system without them creating a Twitter or @@ -578,7 +578,7 @@ class Meta (BaseUserSerializer.Meta): pass def to_representation(self, obj): - data = super(FullUserSerializer, self).to_representation(obj) + data = super().to_representation(obj) if obj: group_field = self.fields['groups'] data['groups'] = group_field.to_representation(obj._groups) @@ -626,7 +626,7 @@ def get_place_counts(self, obj): def to_representation(self, obj): place_count_map = self.get_place_counts(obj) obj.places_length = place_count_map.get(obj.pk, 0) - data = super(DataSetPlaceSetSummarySerializer, self).to_representation(obj) + data = super().to_representation(obj) return data @@ -673,7 +673,7 @@ def to_representation(self, obj): obj.submission_set_name = set_name obj.submission_set_length = len(submission_set) - summaries[set_name] = super(DataSetSubmissionSetSummarySerializer, self).to_representation(obj) + summaries[set_name] = super().to_representation(obj) return summaries @@ -727,11 +727,11 @@ def _patch_submitter(self, instance=None, data={}): def create(self, validated_data): validated_data = self._patch_submitter(data=validated_data) - return super(SubmittedThingSerializer, self).create(validated_data) + return super().create(validated_data) def update(self, instance, validated_data): validated_data = self._patch_submitter(instance=instance, data=validated_data) - return super(SubmittedThingSerializer, self).update(instance, validated_data) + return super().update(instance, validated_data) # Place serializers @@ -991,12 +991,12 @@ def validate_load_from_url(self, attrs, source): return attrs def create(self, validated_data): - instance = super(DataSetSerializer, self).create(validated_data) + instance = super().create(validated_data) self.post_save(instance) return instance def update(self, instance, validated_data): - instance = super(DataSetSerializer, self).update(instance, validated_data) + instance = super().update(instance, validated_data) self.post_save(instance) return instance @@ -1014,7 +1014,7 @@ def to_internal_value(self, data): self.load_url = data.pop('load_from_url') if self.load_url and isinstance(self.load_url, list): self.load_url = str(self.load_url[0]) - return super(DataSetSerializer, self).to_internal_value(data) + return super().to_internal_value(data) # Action serializer @@ -1054,7 +1054,7 @@ def get_target(self, obj): # ---------------------- # -class PaginatedMetadataMixin (object): +class PaginatedMetadataMixin: page_size_query_param = 'page_size' def get_pagination_metadata(self, data): diff --git a/src/sa_api_v2/tasks.py b/src/sa_api_v2/tasks.py index 0fbde350..c4de4e9e 100644 --- a/src/sa_api_v2/tasks.py +++ b/src/sa_api_v2/tasks.py @@ -1,5 +1,3 @@ - - import requests import ujson as json from celery import shared_task @@ -196,7 +194,7 @@ def collect_username(data): collect_username(submission_data) users = User.objects.filter(username__in=usernames) - users_map = dict([(user.username, user) for user in users]) + users_map = {user.username: user for user in users} return users_map def list_errors(errors): @@ -234,7 +232,8 @@ def load_dataset_archive(dataset_id, archive_url): serializer.save() # Create a stub view object to use in serializer contexts. - class Stub (object): pass + class Stub: + pass view = Stub() view.request = Stub() view.request.META = {'HTTP_X_SHAREABOUTS_SILENT': 'True'} diff --git a/src/sa_api_v2/tests/test_authauth.py b/src/sa_api_v2/tests/test_authauth.py index a350bec7..10d38b40 100644 --- a/src/sa_api_v2/tests/test_authauth.py +++ b/src/sa_api_v2/tests/test_authauth.py @@ -4,7 +4,7 @@ from ..views import CurrentUserInstanceView -class APITestMixin (object): +class APITestMixin: def assertStatusCode(self, response, *expected): if hasattr(response, 'rendered_content'): content = response.rendered_content diff --git a/src/sa_api_v2/tests/test_celery_tasks.py b/src/sa_api_v2/tests/test_celery_tasks.py index eb0eef44..d9f391cf 100644 --- a/src/sa_api_v2/tests/test_celery_tasks.py +++ b/src/sa_api_v2/tests/test_celery_tasks.py @@ -1,7 +1,7 @@ from django.test import TestCase from ..models import DataSet, User from ..tasks import load_dataset_archive -from mock import patch +from unittest.mock import patch from .. import tasks diff --git a/src/sa_api_v2/tests/test_models.py b/src/sa_api_v2/tests/test_models.py index 18aa0755..214428b7 100644 --- a/src/sa_api_v2/tests/test_models.py +++ b/src/sa_api_v2/tests/test_models.py @@ -16,8 +16,8 @@ # from ..views import ApiKeyCollectionView # from ..views import OwnerPasswordView # import json -import mock -from mock import patch +from unittest import mock +from unittest.mock import patch import os.path @@ -127,7 +127,7 @@ def test_indexed_values_are_indexed_when_thing_is_saved(self): indexed_values = IndexedValue.objects.filter(index__dataset=self.dataset) self.assertEqual(indexed_values.count(), 2) - self.assertEqual(set([value.value for value in indexed_values]), set(['value1', '2'])) + self.assertEqual({value.value for value in indexed_values}, {'value1', '2'}) def test_indexed_values_are_indexed_when_index_is_saved(self): st1 = SubmittedThing(dataset=self.dataset) @@ -139,7 +139,7 @@ def test_indexed_values_are_indexed_when_index_is_saved(self): indexed_values = IndexedValue.objects.filter(index__dataset=self.dataset) self.assertEqual(indexed_values.count(), 2) - self.assertEqual(set([value.value for value in indexed_values]), set(['value1', '2'])) + self.assertEqual({value.value for value in indexed_values}, {'value1', '2'}) def test_user_can_query_by_indexed_value(self): st1 = SubmittedThing(dataset=self.dataset) @@ -183,7 +183,7 @@ def test_get_returns_the_true_value_of_an_indexed_value(self): indexed_values = IndexedValue.objects.filter(index__dataset=self.dataset) self.assertEqual(indexed_values.count(), 2) - self.assertEqual(set([value.get() for value in indexed_values]), set(['value1', 2])) + self.assertEqual({value.get() for value in indexed_values}, {'value1', 2}) def test_indexed_value_get_raises_KeyError_if_value_is_not_found(self): st = SubmittedThing(dataset=self.dataset) @@ -209,7 +209,7 @@ def test_data_values_are_updated_when_saved(self): indexed_values = IndexedValue.objects.filter(index__dataset=self.dataset) self.assertEqual(indexed_values.count(), 1) - self.assertEqual(set([value.value for value in indexed_values]), set(['value2'])) + self.assertEqual({value.value for value in indexed_values}, {'value2'}) def test_data_values_are_deleted_when_removed(self): st1 = SubmittedThing(dataset=self.dataset) @@ -309,8 +309,8 @@ def test_place_can_be_cloned(self): place_submission_set_names = sorted([s.set_name for s in place_submissions]) self.assertEqual(clone_submission_set_names, place_submission_set_names) - clone_submission_ids = set([s.id for s in clone_submissions]) - place_submission_ids = set([s.id for s in place_submissions]) + clone_submission_ids = {s.id for s in clone_submissions} + place_submission_ids = {s.id for s in place_submissions} self.assertEqual(clone_submission_ids & place_submission_ids, set()) # Change a property on the clone and make sure that they're different @@ -359,15 +359,15 @@ def test_dataset_can_be_cloned(self): orgnl_submission_set_names = sorted([s.full_submission.set_name for s in orgnl_submissions]) self.assertEqual(clone_submission_set_names, orgnl_submission_set_names) - clone_submission_ids = set([s.id for s in clone_submissions]) - orgnl_submission_ids = set([s.id for s in orgnl_submissions]) + clone_submission_ids = {s.id for s in clone_submissions} + orgnl_submission_ids = {s.id for s in orgnl_submissions} self.assertEqual(clone_submission_ids & orgnl_submission_ids, set()) clone_places = clone.things.filter(full_place__isnull=False) orgnl_places = dataset.things.filter(full_place__isnull=False) - clone_place_ids = set([s.id for s in clone_places]) - orgnl_place_ids = set([s.id for s in orgnl_places]) + clone_place_ids = {s.id for s in clone_places} + orgnl_place_ids = {s.id for s in orgnl_places} self.assertEqual(clone_place_ids & orgnl_place_ids, set()) # Make sure the clone and the original have the same values (but not @@ -376,12 +376,12 @@ def test_dataset_can_be_cloned(self): self.assertEqual(dataset.keys.count(), clone.keys.count()) self.assertEqual(dataset.origins.count(), clone.origins.count()) - clone_key_ids = set([k.id for k in clone.keys.all()]) - orgnl_key_ids = set([k.id for k in dataset.keys.all()]) + clone_key_ids = {k.id for k in clone.keys.all()} + orgnl_key_ids = {k.id for k in dataset.keys.all()} self.assertEqual(clone_key_ids & orgnl_key_ids, set()) - clone_orgn_ids = set([o.id for o in clone.origins.all()]) - orgnl_orgn_ids = set([o.id for o in dataset.origins.all()]) + clone_orgn_ids = {o.id for o in clone.origins.all()} + orgnl_orgn_ids = {o.id for o in dataset.origins.all()} self.assertEqual(clone_orgn_ids & orgnl_orgn_ids, set()) clonekey = dataset.keys.get(key=apikey.key) @@ -407,8 +407,8 @@ def test_group_can_be_cloned(self): clone_submitters = clone.submitters.all() group_submitters = group.submitters.all() - clone_submitter_ids = set([s.id for s in clone_submitters]) - group_submitter_ids = set([s.id for s in group_submitters]) + clone_submitter_ids = {s.id for s in clone_submitters} + group_submitter_ids = {s.id for s in group_submitters} self.assertEqual(clone_submitter_ids, group_submitter_ids) def test_apikey_can_be_cloned(self): diff --git a/src/sa_api_v2/tests/test_renderers.py b/src/sa_api_v2/tests/test_renderers.py index 06604f16..47d59cf3 100644 --- a/src/sa_api_v2/tests/test_renderers.py +++ b/src/sa_api_v2/tests/test_renderers.py @@ -1,5 +1,3 @@ -#-*- coding:utf-8 -*- - from django.test import TestCase from nose.tools import istest from sa_api_v2.renderers import GeoJSONRenderer diff --git a/src/sa_api_v2/tests/test_serializers.py b/src/sa_api_v2/tests/test_serializers.py index ed6a5165..15380b2d 100644 --- a/src/sa_api_v2/tests/test_serializers.py +++ b/src/sa_api_v2/tests/test_serializers.py @@ -1,5 +1,3 @@ -#-*- coding:utf-8 -*- - from django.test import TestCase from django.test.client import RequestFactory from django.contrib.gis.geos import GEOSGeometry @@ -13,7 +11,7 @@ from social_django.models import UserSocialAuth import json from os import path -from mock import patch +from unittest.mock import patch class TestAttachmentSerializer (TestCase): diff --git a/src/sa_api_v2/tests/test_views.py b/src/sa_api_v2/tests/test_views.py index 3b92e05d..854a2073 100644 --- a/src/sa_api_v2/tests/test_views.py +++ b/src/sa_api_v2/tests/test_views.py @@ -9,7 +9,7 @@ import base64 import csv import json -import mock +from unittest import mock import unittest from io import StringIO from ..models import User, DataSet, Place, Submission, Attachment, Action, Group, DataIndex @@ -23,7 +23,7 @@ from ..serializers import FeatureCollectionPagination -class APITestMixin (object): +class APITestMixin: def assertStatusCode(self, response, *expected): self.assertIn(response.status_code, expected, 'Status code not in %s response: (%s) %s' % @@ -45,7 +45,7 @@ def setUp(self): 'private-secrets': 42 }), ) - f = StringIO(u'This is test content in a "file"') + f = StringIO('This is test content in a "file"') f.name = 'myfile.txt' f.size = 20 self.attachments = Attachment.objects.create( @@ -1042,8 +1042,8 @@ def test_GET_response_for_multiple_specific_objects(self): # Check that the pks are correct self.assertEqual( - set([f['id'] for f in data['features']]), - set([p.pk for p in places[::2]]) + {f['id'] for f in data['features']}, + {p.pk for p in places[::2]} ) def test_GET_csv_response(self): @@ -1875,7 +1875,7 @@ def setUp(self): Submission.objects.create(place=self.place, set_name='likes', dataset=self.dataset, data='{"bar": 3}', visible=False), ] - f = StringIO(u'This is test content in a "file"') + f = StringIO('This is test content in a "file"') f.name = 'myfile.txt' f.size = 20 self.attachments = Attachment.objects.create( @@ -2318,8 +2318,8 @@ def test_GET_response_for_multiple_specific_objects(self): # Check that the pks are correct self.assertEqual( - set([r['id'] for r in data['results']]), - set([s.pk for s in submissions[::2]]) + {r['id'] for r in data['results']}, + {s.pk for s in submissions[::2]} ) def test_GET_csv_response(self): @@ -3916,7 +3916,7 @@ def setUp(self): }), ) - self.file = StringIO(u'This is test content in a "file"') + self.file = StringIO('This is test content in a "file"') self.file.name = 'myfile.txt' self.file.size = 20 # self.attachments = Attachment.objects.create( @@ -3972,7 +3972,7 @@ def test_POST_attachment_to_place(self): # # Can't write if not authenticated # - f = StringIO(u'This is test content in a "file"') + f = StringIO('This is test content in a "file"') f.name = 'myfile.txt' request = self.factory.post(self.path, data={'file': f, 'name': 'my-file'}) response = self.view(request, **self.request_kwargs) @@ -3983,7 +3983,7 @@ def test_POST_attachment_to_place(self): # # Can write with the API key. # - f = StringIO(u'This is test content in a "file"') + f = StringIO('This is test content in a "file"') f.name = 'myfile.txt' request = self.factory.post(self.path, data={'file': f, 'name': 'my-file'}) request.META[KEY_HEADER] = self.apikey.key @@ -4003,7 +4003,7 @@ def test_POST_attachment_to_place(self): # # Can not write when logged in as not owner. # - f = StringIO(u'This is test content in a "file"') + f = StringIO('This is test content in a "file"') f.name = 'myfile.txt' request = self.factory.post(self.path, data={'file': f, 'name': 'my-file'}) User.objects.create_user(username='new_user', password='password') @@ -4019,7 +4019,7 @@ def test_POST_attachment_to_place(self): # # Can write when logged in as owner. # - f = StringIO(u'This is test content in a "file"') + f = StringIO('This is test content in a "file"') f.name = 'myfile.txt' request = self.factory.post(self.path, data={'file': f, 'name': 'my-file'}) credentials = ':'.join([self.owner.username, '123']).encode() @@ -4033,7 +4033,7 @@ def test_POST_attachment_to_invisible_place(self): # # Can't write if not authenticated # - f = StringIO(u'This is test content in a "file"') + f = StringIO('This is test content in a "file"') f.name = 'myfile.txt' request = self.factory.post(self.invisible_path + '?include_invisible', data={'file': f, 'name': 'my-file'}) response = self.view(request, **self.invisible_request_kwargs) @@ -4044,7 +4044,7 @@ def test_POST_attachment_to_invisible_place(self): # # Can't write with the API key/include_invisible. (400) # - f = StringIO(u'This is test content in a "file"') + f = StringIO('This is test content in a "file"') f.name = 'myfile.txt' request = self.factory.post(self.invisible_path, data={'file': f, 'name': 'my-file'}) request.META[KEY_HEADER] = self.apikey.key @@ -4057,7 +4057,7 @@ def test_POST_attachment_to_invisible_place(self): # # Can't write with the API key (403). # - f = StringIO(u'This is test content in a "file"') + f = StringIO('This is test content in a "file"') f.name = 'myfile.txt' request = self.factory.post(self.invisible_path + '?include_invisible', data={'file': f, 'name': 'my-file'}) request.META[KEY_HEADER] = self.apikey.key @@ -4069,7 +4069,7 @@ def test_POST_attachment_to_invisible_place(self): # # Can not write when logged in as not owner. # - f = StringIO(u'This is test content in a "file"') + f = StringIO('This is test content in a "file"') f.name = 'myfile.txt' request = self.factory.post(self.invisible_path + '?include_invisible', data={'file': f, 'name': 'my-file'}) User.objects.create_user(username='new_user', password='password') @@ -4085,7 +4085,7 @@ def test_POST_attachment_to_invisible_place(self): # # Can't write when logged in as owner without include_invisible (400). # - f = StringIO(u'This is test content in a "file"') + f = StringIO('This is test content in a "file"') f.name = 'myfile.txt' request = self.factory.post(self.invisible_path, data={'file': f, 'name': 'my-file'}) credentials = ':'.join([self.owner.username, '123']).encode() @@ -4098,7 +4098,7 @@ def test_POST_attachment_to_invisible_place(self): # # Can write when logged in as owner. # - f = StringIO(u'This is test content in a "file"') + f = StringIO('This is test content in a "file"') f.name = 'myfile.txt' request = self.factory.post(self.invisible_path + '?include_invisible', data={'file': f, 'name': 'my-file'}) credentials = ':'.join([self.owner.username, '123']).encode() @@ -4232,7 +4232,7 @@ def setUp(self): Submission.objects.create(place=self.place, set_name='comments', dataset=self.dataset, data='{"foo": 3}', visible=False), ] - self.file = StringIO(u'This is test content in a "file"') + self.file = StringIO('This is test content in a "file"') self.file.name = 'myfile.txt' self.file.size = 20 @@ -4379,7 +4379,7 @@ def test_POST_attachment_to_submission(self): # # Can't write if not authenticated # - f = StringIO(u'This is test content in a "file"') + f = StringIO('This is test content in a "file"') f.name = 'myfile.txt' request = self.factory.post(self.path, data={'file': f, 'name': 'my-file'}) response = self.view(request, **self.request_kwargs) @@ -4390,7 +4390,7 @@ def test_POST_attachment_to_submission(self): # # Can write with the API key. # - f = StringIO(u'This is test content in a "file"') + f = StringIO('This is test content in a "file"') f.name = 'myfile.txt' request = self.factory.post(self.path, data={'file': f, 'name': 'my-file'}) request.META[KEY_HEADER] = self.apikey.key @@ -4410,7 +4410,7 @@ def test_POST_attachment_to_submission(self): # # Can not write when logged in as not owner. # - f = StringIO(u'This is test content in a "file"') + f = StringIO('This is test content in a "file"') f.name = 'myfile.txt' request = self.factory.post(self.path, data={'file': f, 'name': 'my-file'}) User.objects.create_user(username='new_user', password='password') @@ -4426,7 +4426,7 @@ def test_POST_attachment_to_submission(self): # # Can write when logged in as owner. # - f = StringIO(u'This is test content in a "file"') + f = StringIO('This is test content in a "file"') f.name = 'myfile.txt' request = self.factory.post(self.path, data={'file': f, 'name': 'my-file'}) credentials = ':'.join([self.owner.username, '123']).encode() @@ -4440,7 +4440,7 @@ def test_POST_attachment_to_invisible_submission(self): # # Can't write if not authenticated # - f = StringIO(u'This is test content in a "file"') + f = StringIO('This is test content in a "file"') f.name = 'myfile.txt' request = self.factory.post(self.invisible_path + '?include_invisible', data={'file': f, 'name': 'my-file'}) response = self.view(request, **self.invisible_request_kwargs) @@ -4451,7 +4451,7 @@ def test_POST_attachment_to_invisible_submission(self): # # Can't write with the API key/include_invisible. (400) # - f = StringIO(u'This is test content in a "file"') + f = StringIO('This is test content in a "file"') f.name = 'myfile.txt' request = self.factory.post(self.invisible_path, data={'file': f, 'name': 'my-file'}) request.META[KEY_HEADER] = self.apikey.key @@ -4464,7 +4464,7 @@ def test_POST_attachment_to_invisible_submission(self): # # Can't write with the API key (403). # - f = StringIO(u'This is test content in a "file"') + f = StringIO('This is test content in a "file"') f.name = 'myfile.txt' request = self.factory.post(self.invisible_path + '?include_invisible', data={'file': f, 'name': 'my-file'}) request.META[KEY_HEADER] = self.apikey.key @@ -4476,7 +4476,7 @@ def test_POST_attachment_to_invisible_submission(self): # # Can not write when logged in as not owner. # - f = StringIO(u'This is test content in a "file"') + f = StringIO('This is test content in a "file"') f.name = 'myfile.txt' request = self.factory.post(self.invisible_path + '?include_invisible', data={'file': f, 'name': 'my-file'}) User.objects.create_user(username='new_user', password='password') @@ -4492,7 +4492,7 @@ def test_POST_attachment_to_invisible_submission(self): # # Can't write when logged in as owner without include_invisible (400). # - f = StringIO(u'This is test content in a "file"') + f = StringIO('This is test content in a "file"') f.name = 'myfile.txt' request = self.factory.post(self.invisible_path, data={'file': f, 'name': 'my-file'}) credentials = ':'.join([self.owner.username, '123']).encode() @@ -4505,7 +4505,7 @@ def test_POST_attachment_to_invisible_submission(self): # # Can write when logged in as owner. # - f = StringIO(u'This is test content in a "file"') + f = StringIO('This is test content in a "file"') f.name = 'myfile.txt' request = self.factory.post(self.invisible_path + '?include_invisible', data={'file': f, 'name': 'my-file'}) credentials = ':'.join([self.owner.username, '123']).encode() diff --git a/src/sa_api_v2/tests/test_webhooks.py b/src/sa_api_v2/tests/test_webhooks.py index 26d6ed2d..b94ad9d7 100644 --- a/src/sa_api_v2/tests/test_webhooks.py +++ b/src/sa_api_v2/tests/test_webhooks.py @@ -6,7 +6,7 @@ from django.contrib.auth.models import AnonymousUser import base64 import json -import mock +from unittest import mock import responses from io import StringIO from ..models import User, DataSet, Place, Submission, Attachment, Group, Webhook @@ -19,7 +19,7 @@ DataSetListView, AttachmentListView, ActionListView, requests) -class APITestMixin (object): +class APITestMixin: def assertStatusCode(self, response, *expected): self.assertIn(response.status_code, expected, 'Status code not in %s response: (%s) %s' % diff --git a/src/sa_api_v2/views/base_views.py b/src/sa_api_v2/views/base_views.py index 3ee8e633..51276e2c 100644 --- a/src/sa_api_v2/views/base_views.py +++ b/src/sa_api_v2/views/base_views.py @@ -21,7 +21,7 @@ from rest_framework.exceptions import APIException from rest_framework_bulk import generics as bulk_generics from social_django import views as social_views -from mock import patch +from unittest.mock import patch from ..apikey import auth as apikey_auth from ..cors import auth as cors_auth from .. import models @@ -72,7 +72,7 @@ class JSONPCallbackNegotiation (DefaultContentNegotiation): def select_renderer(self, request, renderers, format_suffix=None): if 'callback' in request.query_params: format_suffix = 'jsonp' - return super(JSONPCallbackNegotiation, self).select_renderer(request, renderers, format_suffix) + return super().select_renderer(request, renderers, format_suffix) class XDomainRequestCompatNegotiation (DefaultContentNegotiation): @@ -92,7 +92,7 @@ def select_parser(self, request, parsers): # Also set this semi-hidden variable on the request, as it has # already been set and needs to be calculated again. request._content_type = 'application/json' - return super(XDomainRequestCompatNegotiation, self).select_parser(request, parsers) + return super().select_parser(request, parsers) class ShareaboutsContentNegotiation (JSONPCallbackNegotiation, XDomainRequestCompatNegotiation): @@ -316,7 +316,7 @@ class ShareaboutsAPIRequest (Request): def __init__(self, request, parsers=None, authenticators=None, client_authenticators=None, negotiator=None, parser_context=None): - super(ShareaboutsAPIRequest, self).__init__(request, + super().__init__(request, parsers=parsers, authenticators=authenticators, negotiator=negotiator, parser_context=parser_context) self.client_authenticators = client_authenticators @@ -362,7 +362,7 @@ def successful_authenticator(self): Return the instance of the authentication instance class that was used to authenticate the request, or `None`. """ - authenticator = super(ShareaboutsAPIRequest, self).successful_authenticator + authenticator = super().successful_authenticator if not authenticator: if not hasattr(self, '_client_authenticator'): @@ -401,7 +401,7 @@ def _client_not_authenticated(self): self._client_auth = None -class ClientAuthenticationMixin (object): +class ClientAuthenticationMixin: """ A view mixin that uses a ShareaboutsAPIRequest instead of a conventional DRF Request object. @@ -428,7 +428,7 @@ def initialize_request(self, request, *args, **kwargs): parser_context=parser_context) -class CorsEnabledMixin (object): +class CorsEnabledMixin: """ A view that puts Access-Control headers on the response. """ @@ -436,7 +436,7 @@ class CorsEnabledMixin (object): SAFE_CORS_METHODS = ('GET', 'HEAD', 'TRACE') def finalize_response(self, request, response, *args, **kwargs): - response = super(CorsEnabledMixin, self).finalize_response(request, response, *args, **kwargs) + response = super().finalize_response(request, response, *args, **kwargs) # Allow AJAX requests from anywhere for safe methods. Though OPTIONS # is also a safe method in that it does not modify data on the server, @@ -466,13 +466,13 @@ def finalize_response(self, request, response, *args, **kwargs): return response -class FilteredResourceMixin (object): +class FilteredResourceMixin: """ A view mixin that filters queryset of ModelWithDataBlob results based on the URL query parameters. """ def get_queryset(self): - queryset = super(FilteredResourceMixin, self).get_queryset() + queryset = super().get_queryset() # Filter by any provided primary keys pk_list = self.kwargs.get('pk_list', None) @@ -481,10 +481,10 @@ def get_queryset(self): queryset = queryset.filter(pk__in=pk_list) # These filters will have been applied when constructing the queryset - special_filters = set([FORMAT_PARAM, PAGE_PARAM, PAGE_SIZE_PARAM(), + special_filters = {FORMAT_PARAM, PAGE_PARAM, PAGE_SIZE_PARAM(), INCLUDE_SUBMISSIONS_PARAM, INCLUDE_PRIVATE_PARAM, INCLUDE_INVISIBLE_PARAM, NEAR_PARAM, DISTANCE_PARAM, - TEXTSEARCH_PARAM, BBOX_PARAM, CALLBACK_PARAM(self)]) + TEXTSEARCH_PARAM, BBOX_PARAM, CALLBACK_PARAM(self)} # Filter by full-text search textsearch_filter = self.request.GET.get(TEXTSEARCH_PARAM, None) @@ -516,13 +516,13 @@ def get_queryset(self): return queryset -class LocatedResourceMixin (object): +class LocatedResourceMixin: """ A view mixin that orders queryset results by distance from a geometry, if requested. """ def get_queryset(self): - queryset = super(LocatedResourceMixin, self).get_queryset() + queryset = super().get_queryset() if NEAR_PARAM in self.request.GET: try: @@ -586,7 +586,7 @@ def dispatch(self, request, *args, **kwargs): # authentication must check against it. request.get_dataset = self.get_dataset - return super(OwnedResourceMixin, self).dispatch(request, *args, **kwargs) + return super().dispatch(request, *args, **kwargs) def get_submitter(self): user = self.request.user @@ -689,7 +689,7 @@ class ProtectedOwnedResourceMixin (OwnedResourceMixin): permission_classes = (IsLoggedInOwnerOrPublicDataOnly,) + OwnedResourceMixin.permission_classes -class CachedResourceMixin (object): +class CachedResourceMixin: @property def cache_prefix(self): return self.request.path @@ -705,7 +705,7 @@ def get_cache_metakey(self): def dispatch(self, request, *args, **kwargs): # Only do the cache for GET, OPTIONS, or HEAD method. if request.method.upper() not in permissions.SAFE_METHODS: - return super(CachedResourceMixin, self).dispatch(request, *args, **kwargs) + return super().dispatch(request, *args, **kwargs) self.request = request @@ -729,9 +729,9 @@ def cached_handler(*args, **kwargs): # Patch the HTTP method with patch.object(self, handler_name, new=cached_handler): - response = super(CachedResourceMixin, self).dispatch(request, *args, **kwargs) + response = super().dispatch(request, *args, **kwargs) else: - response = super(CachedResourceMixin, self).dispatch(request, *args, **kwargs) + response = super().dispatch(request, *args, **kwargs) # Only cache on OK resposne if response.status_code == 200: @@ -798,7 +798,7 @@ def cache_response(self, key, response): return response -class SerializerParamsMixin (object): +class SerializerParamsMixin: def get_serializer_defaults(self): return {} @@ -808,7 +808,7 @@ def get_serializer_overrides(self): def get_serializer(self, *args, **kwargs): defaults = self.get_serializer_defaults() overrides = self.get_serializer_overrides() - serializer = super(SerializerParamsMixin, self).get_serializer(*args, **kwargs) + serializer = super().get_serializer(*args, **kwargs) # List serializers won't have any `fields`, but will instead have a # `child` serializer with fields. @@ -976,11 +976,11 @@ def get(self, request, *args, **kwargs): include_submissions=(INCLUDE_SUBMISSIONS_PARAM in request.GET), include_private=(INCLUDE_PRIVATE_PARAM in request.GET), include_invisible=(INCLUDE_INVISIBLE_PARAM in request.GET)) - result = super(CompletePlaceListRequestView, self).get(request, *args, **kwargs) + result = super().get(request, *args, **kwargs) return result -class PlaceListMixin (object): +class PlaceListMixin: pass @@ -1068,7 +1068,7 @@ def get_serializer_overrides(self): return {'dataset': self.get_dataset()} def post_save(self, obj, created): - super(PlaceListView, self).post_save(obj) + super().post_save(obj) # Get all place/add webhooks since we just added a place. if not created: @@ -1081,7 +1081,7 @@ def post_save(self, obj, created): def get_queryset(self): dataset = self.get_dataset() - queryset = super(PlaceListView, self).get_queryset() + queryset = super().get_queryset() # If the user is not allowed to request invisible data then we won't # be here in the first place. @@ -1275,7 +1275,7 @@ def get_queryset(self): dataset = self.get_dataset() place = self.get_place(dataset) submission_set_name = self.kwargs[self.submission_set_name_kwarg] - queryset = super(SubmissionListView, self).get_queryset() + queryset = super().get_queryset() if submission_set_name != 'submissions': queryset = queryset.filter(set_name=submission_set_name) @@ -1352,7 +1352,7 @@ def get_cache_metakey(self): def get_queryset(self): dataset = self.get_dataset() submission_set_name = self.kwargs[self.submission_set_name_kwarg] - queryset = super(DataSetSubmissionListView, self).get_queryset() + queryset = super().get_queryset() if submission_set_name != 'submissions': queryset = queryset.filter(set_name=submission_set_name) @@ -1420,7 +1420,7 @@ def get_object_or_404(self, owner_username, dataset_slug): raise Http404 def get_serializer_context(self): - context = super(DataSetInstanceView, self).get_serializer_context() + context = super().get_serializer_context() include_invisible = INCLUDE_INVISIBLE_PARAM in self.request.GET return context @@ -1433,7 +1433,7 @@ def get_object(self, queryset=None): return obj def put(self, request, owner_username, dataset_slug): - response = super(DataSetInstanceView, self).put(request, owner_username=owner_username, dataset_slug=dataset_slug) + response = super().put(request, owner_username=owner_username, dataset_slug=dataset_slug) if 'slug' in response.data and response.data['slug'] != dataset_slug: response.status_code = 301 response['Location'] = response.data['url'] @@ -1496,11 +1496,11 @@ class DataSetKeyListView (ProtectedOwnedResourceMixin, generics.ListAPIView): def get_queryset(self): dataset = self.get_dataset() - queryset = super(DataSetKeyListView, self).get_queryset() + queryset = super().get_queryset() return queryset.filter(dataset=dataset) -class DataSetListMixin (object): +class DataSetListMixin: """ Common aspects for dataset list views. """ @@ -1595,14 +1595,14 @@ def post_save(self, obj, created=False): def get_queryset(self): owner = self.get_owner() - queryset = super(DataSetListView, self).get_queryset() + queryset = super().get_queryset() return queryset.filter(owner=owner).order_by('id') def create(self, request, owner_username): if 'HTTP_X_SHAREABOUTS_CLONE' in request.META or 'clone' in request.GET: return self.clone(request, owner_username=owner_username) else: - return super(DataSetListView, self).create(request, owner_username=owner_username) + return super().create(request, owner_username=owner_username) def get_object_to_clone(self, clone_header): try: @@ -1672,7 +1672,7 @@ def clone(self, request, owner_username): else: return Response({'errors': {'slug': 'DataSet with this slug already exists'}}, status=409) else: - slugs = set([ds.slug for ds in queryset]) + slugs = {ds.slug for ds in queryset} unique_slug = original.slug for uniquifier in count(2): if unique_slug not in slugs: break @@ -1755,7 +1755,7 @@ def get_thing(self): def get_queryset(self): thing = self.get_thing() - queryset = super(AttachmentListView, self).get_queryset() + queryset = super().get_queryset() return queryset.filter(thing=thing) def get_serializer_overrides(self): @@ -1780,7 +1780,7 @@ class ActionListView (CachedResourceMixin, OwnedResourceMixin, generics.ListAPIV def get_queryset(self): dataset = self.get_dataset() - queryset = super(ActionListView, self).get_queryset()\ + queryset = super().get_queryset()\ .filter(thing__dataset=dataset)\ .select_related( 'thing', @@ -1822,7 +1822,7 @@ class ClientAuthListView (OwnedResourceMixin, generics.ListCreateAPIView): permission_classes = (IsLoggedInOwner,) def get_queryset(self): - qs = super(ClientAuthListView, self).get_queryset() + qs = super().get_queryset() dataset = self.get_dataset() return qs.filter(dataset=dataset) @@ -1832,7 +1832,7 @@ def get_serializer(self, instance=None, data=None, dataset = self.get_dataset() data = data.copy() data['dataset'] = dataset.id - return super(ClientAuthListView, self).get_serializer( + return super().get_serializer( instance=instance, data=data, files=files, many=many, partial=partial) diff --git a/src/sa_api_v2/views/bulk_data_views.py b/src/sa_api_v2/views/bulk_data_views.py index 78d280b3..f3a7bc6e 100644 --- a/src/sa_api_v2/views/bulk_data_views.py +++ b/src/sa_api_v2/views/bulk_data_views.py @@ -1,6 +1,6 @@ from django.urls import reverse from django.http import HttpResponse -from mock import patch +from unittest.mock import patch from rest_framework import views, permissions from rest_framework.negotiation import DefaultContentNegotiation from rest_framework.parsers import JSONParser, FormParser, MultiPartParser @@ -26,7 +26,7 @@ # -------------- # -class DataSnapshotMixin (object): +class DataSnapshotMixin: response_messages = { 'pending': 'You can download the data at the given URL when it is done being generated.', 'success': 'You can download the data at the given URL.',