diff --git a/docs/api/schemas/generate-schemas.py b/docs/api/schemas/generate-schemas.py index 3a436a16..14b74147 100755 --- a/docs/api/schemas/generate-schemas.py +++ b/docs/api/schemas/generate-schemas.py @@ -22,12 +22,14 @@ def generate_schemas(): env = jinja2.Environment( loader=jinja2.FileSystemLoader(ROOT_DIR), trim_blocks=True, - lstrip_blocks=True) + lstrip_blocks=True, + ) template = env.get_template('patchwork.j2') for version in VERSIONS: version_dir = os.path.join( - ROOT_DIR, 'v%d.%d' % version if version else 'latest') + ROOT_DIR, 'v%d.%d' % version if version else 'latest' + ) if not os.path.exists(version_dir): os.mkdir(version_dir) @@ -37,8 +39,11 @@ def generate_schemas(): version = version or LATEST_VERSION with open(os.path.join(version_dir, 'patchwork.yaml'), 'wb') as fh: - template.stream(version=version, version_str=version_str, - version_url=version_url).dump(fh, encoding='utf-8') + template.stream( + version=version, + version_str=version_str, + version_url=version_url, + ).dump(fh, encoding='utf-8') fh.write(b'\n') print(f'Schemas written to {ROOT_DIR}.') diff --git a/docs/conf.py b/docs/conf.py index 646db889..30a6b006 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -20,11 +20,7 @@ # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. -extensions = [ - 'sphinx.ext.todo', - 'reno.sphinxext', - 'sphinxcontrib.openapi' -] +extensions = ['sphinx.ext.todo', 'reno.sphinxext', 'sphinxcontrib.openapi'] # The theme to use for HTML and HTML Help pages. html_theme = 'sphinx_rtd_theme' diff --git a/manage.py b/manage.py index e4266ade..033c8ae4 100755 --- a/manage.py +++ b/manage.py @@ -4,8 +4,8 @@ if __name__ == "__main__": os.environ.setdefault( - "DJANGO_SETTINGS_MODULE", - "patchwork.settings.production") + "DJANGO_SETTINGS_MODULE", "patchwork.settings.production" + ) from django.core.management import execute_from_command_line diff --git a/patchwork/admin.py b/patchwork/admin.py index 5823a00a..d3bf8d29 100644 --- a/patchwork/admin.py +++ b/patchwork/admin.py @@ -32,7 +32,7 @@ class UserProfileInline(admin.StackedInline): class UserAdmin(BaseUserAdmin): - inlines = (UserProfileInline, ) + inlines = (UserProfileInline,) admin.site.unregister(User) @@ -78,7 +78,7 @@ class StateAdmin(admin.ModelAdmin): class CoverAdmin(admin.ModelAdmin): list_display = ('name', 'submitter', 'project', 'date') - list_filter = ('project', ) + list_filter = ('project',) search_fields = ('name', 'submitter__name', 'submitter__email') date_hierarchy = 'date' @@ -87,8 +87,15 @@ class CoverAdmin(admin.ModelAdmin): class PatchAdmin(admin.ModelAdmin): - list_display = ('name', 'submitter', 'project', 'state', 'date', - 'archived', 'is_pull_request') + list_display = ( + 'name', + 'submitter', + 'project', + 'state', + 'date', + 'archived', + 'is_pull_request', + ) list_filter = ('project', 'submitter', 'state', 'archived') list_select_related = ('submitter', 'project', 'state') search_fields = ('name', 'submitter__name', 'submitter__email') @@ -129,23 +136,38 @@ class PatchInline(admin.StackedInline): class SeriesAdmin(admin.ModelAdmin): - list_display = ('name', 'submitter', 'project', 'date', 'version', 'total', - 'received_total', 'received_all') + list_display = ( + 'name', + 'submitter', + 'project', + 'date', + 'version', + 'total', + 'received_total', + 'received_all', + ) list_filter = ('project', 'submitter') list_select_related = ('submitter', 'project') readonly_fields = ('received_total', 'received_all') search_fields = ('submitter__name', 'submitter__email') - exclude = ('patches', ) - inlines = (PatchInline, ) + exclude = ('patches',) + inlines = (PatchInline,) def received_all(self, series): return series.received_all + received_all.boolean = True def get_queryset(self, request): qs = super(SeriesAdmin, self).get_queryset(request) - return qs.prefetch_related(Prefetch( - 'patches', Patch.objects.only('series',))) + return qs.prefetch_related( + Prefetch( + 'patches', + Patch.objects.only( + 'series', + ), + ) + ) admin.site.register(Series, SeriesAdmin) @@ -159,9 +181,15 @@ class SeriesReferenceAdmin(admin.ModelAdmin): class CheckAdmin(admin.ModelAdmin): - list_display = ('patch', 'user', 'state', 'target_url', - 'description', 'context') - exclude = ('date', ) + list_display = ( + 'patch', + 'user', + 'state', + 'target_url', + 'description', + 'context', + ) + exclude = ('date',) search_fields = ('patch__name', 'project__name') date_hierarchy = 'date' diff --git a/patchwork/api/__init__.py b/patchwork/api/__init__.py index efc0dd89..ff42c46a 100644 --- a/patchwork/api/__init__.py +++ b/patchwork/api/__init__.py @@ -15,6 +15,7 @@ # [1] https://github.com/encode/django-rest-framework/issues/7550 # [2] https://github.com/encode/django-rest-framework/pull/7574 + def _get_attribute(self, instance): # Can't have any relationships if not created if hasattr(instance, 'pk') and instance.pk is None: @@ -39,7 +40,7 @@ def _get_attribute(self, instance): field=self.field_name, serializer=self.parent.__class__.__name__, instance=instance.__class__.__name__, - exc=exc + exc=exc, ) ) raise type(exc)(msg) diff --git a/patchwork/api/base.py b/patchwork/api/base.py index 7baac275..6268f67d 100644 --- a/patchwork/api/base.py +++ b/patchwork/api/base.py @@ -21,6 +21,7 @@ if DRF_VERSION > (3, 11): + class CurrentPatchDefault(object): requires_context = True @@ -32,7 +33,9 @@ class CurrentCoverDefault(object): def __call__(self, serializer_field): return serializer_field.context['request'].cover + else: + class CurrentPatchDefault(object): def set_context(self, serializer_field): self.patch = serializer_field.context['request'].patch @@ -56,6 +59,7 @@ class LinkHeaderPagination(PageNumberPagination): https://tools.ietf.org/html/rfc5988#section-5 https://developer.github.com/guides/traversing-with-pagination """ + page_size = settings.REST_RESULTS_PER_PAGE max_page_size = settings.MAX_REST_RESULTS_PER_PAGE page_size_query_param = 'per_page' @@ -95,6 +99,7 @@ class PatchworkPermission(permissions.BasePermission): This permission works for Project, Patch, PatchComment and CoverComment model objects """ + def has_object_permission(self, request, view, obj): # read only for everyone if request.method in permissions.SAFE_METHODS: @@ -109,7 +114,8 @@ def get_object(self): queryset = self.filter_queryset(self.get_queryset()) filter_kwargs = {} for field_name, field in zip( - self.lookup_fields, self.lookup_url_kwargs): + self.lookup_fields, self.lookup_url_kwargs + ): if self.kwargs[field]: filter_kwargs[field_name] = self.kwargs[field] @@ -117,7 +123,6 @@ def get_object(self): class CheckHyperlinkedIdentityField(HyperlinkedIdentityField): - def get_url(self, obj, view_name, request, format): # Unsaved objects will not yet have a valid URL. if obj.pk is None: @@ -135,10 +140,10 @@ def get_url(self, obj, view_name, request, format): class BaseHyperlinkedModelSerializer(HyperlinkedModelSerializer): - def to_representation(self, instance): data = super(BaseHyperlinkedModelSerializer, self).to_representation( - instance) + instance + ) request = self.context.get('request') for version in getattr(self.Meta, 'versioned_fields', {}): diff --git a/patchwork/api/bundle.py b/patchwork/api/bundle.py index 93e32316..b6c7c9d2 100644 --- a/patchwork/api/bundle.py +++ b/patchwork/api/bundle.py @@ -27,6 +27,7 @@ class BundlePermission(permissions.BasePermission): Bundle creation/updating was only added in API v1.2 and we don't want to change behavior in older API versions. """ + def has_permission(self, request, view): # read-only permission for everything if request.method in permissions.SAFE_METHODS: @@ -36,16 +37,19 @@ def has_permission(self, request, view): raise exceptions.MethodNotAllowed(request.method) if request.method == 'POST' and ( - not request.user or not request.user.is_authenticated): + not request.user or not request.user.is_authenticated + ): return False # we have more to do but we can't do that until we have an object return True def has_object_permission(self, request, view, obj): - if (request.user and - request.user.is_authenticated and - request.user == obj.owner): + if ( + request.user + and request.user.is_authenticated + and request.user == obj.owner + ): return True if not obj.public: @@ -62,8 +66,9 @@ class BundleSerializer(BaseHyperlinkedModelSerializer): project = ProjectSerializer(read_only=True) mbox = SerializerMethodField() owner = UserSerializer(read_only=True) - patches = PatchSerializer(many=True, required=True, - style={'base_template': 'input.html'}) + patches = PatchSerializer( + many=True, required=True, style={'base_template': 'input.html'} + ) def get_web_url(self, instance): request = self.context.get('request') @@ -82,7 +87,8 @@ def create(self, validated_data): def update(self, instance, validated_data): patches = validated_data.pop('patches', None) instance = super(BundleSerializer, self).update( - instance, validated_data) + instance, validated_data + ) if patches: instance.overwrite_patches(patches) return instance @@ -92,8 +98,9 @@ def validate_patches(self, value): raise ValidationError('Bundles cannot be empty') if len(set([p.project.id for p in value])) > 1: - raise ValidationError('Bundle patches must belong to the same ' - 'project') + raise ValidationError( + 'Bundle patches must belong to the same ' 'project' + ) return value @@ -105,11 +112,20 @@ def validate(self, data): class Meta: model = Bundle - fields = ('id', 'url', 'web_url', 'project', 'name', 'owner', - 'patches', 'public', 'mbox') + fields = ( + 'id', + 'url', + 'web_url', + 'project', + 'name', + 'owner', + 'patches', + 'public', + 'mbox', + ) read_only_fields = ('project', 'owner', 'mbox') versioned_fields = { - '1.1': ('web_url', ), + '1.1': ('web_url',), } extra_kwargs = { 'url': {'view_name': 'api-bundle-detail'}, @@ -127,10 +143,13 @@ def get_queryset(self): else: bundle_filter = Q(public=True) - return Bundle.objects\ - .filter(bundle_filter)\ - .prefetch_related('patches',)\ + return ( + Bundle.objects.filter(bundle_filter) + .prefetch_related( + 'patches', + ) .select_related('owner', 'project') + ) class BundleList(BundleMixin, ListCreateAPIView): diff --git a/patchwork/api/check.py b/patchwork/api/check.py index aeb34889..c28d89f7 100644 --- a/patchwork/api/check.py +++ b/patchwork/api/check.py @@ -61,8 +61,17 @@ def to_representation(self, instance): class Meta: model = Check - fields = ('id', 'url', 'patch', 'user', 'date', 'state', 'target_url', - 'context', 'description') + fields = ( + 'id', + 'url', + 'patch', + 'user', + 'date', + 'state', + 'target_url', + 'context', + 'description', + ) read_only_fields = ('date',) extra_kwargs = { 'url': {'view_name': 'api-check-detail'}, diff --git a/patchwork/api/comment.py b/patchwork/api/comment.py index b150365e..13c116ee 100644 --- a/patchwork/api/comment.py +++ b/patchwork/api/comment.py @@ -35,8 +35,11 @@ def get_web_url(self, instance): return request.build_absolute_uri(instance.get_absolute_url()) def get_subject(self, comment): - return email.parser.Parser().parsestr(comment.headers, - True).get('Subject', '') + return ( + email.parser.Parser() + .parsestr(comment.headers, True) + .get('Subject', '') + ) def get_headers(self, comment): headers = {} @@ -53,13 +56,31 @@ def get_headers(self, comment): return headers class Meta: - fields = ('id', 'web_url', 'msgid', 'list_archive_url', 'date', - 'subject', 'submitter', 'content', 'headers', 'addressed') - read_only_fields = ('id', 'web_url', 'msgid', 'list_archive_url', - 'date', 'subject', 'submitter', 'content', - 'headers') + fields = ( + 'id', + 'web_url', + 'msgid', + 'list_archive_url', + 'date', + 'subject', + 'submitter', + 'content', + 'headers', + 'addressed', + ) + read_only_fields = ( + 'id', + 'web_url', + 'msgid', + 'list_archive_url', + 'date', + 'subject', + 'submitter', + 'content', + 'headers', + ) versioned_fields = { - '1.1': ('web_url', ), + '1.1': ('web_url',), '1.2': ('list_archive_url',), '1.3': ('addressed',), } @@ -71,14 +92,12 @@ class CoverCommentSerializer(BaseCommentListSerializer): class Meta: model = CoverComment - fields = BaseCommentListSerializer.Meta.fields + ( - 'cover', 'addressed') + fields = BaseCommentListSerializer.Meta.fields + ('cover', 'addressed') read_only_fields = BaseCommentListSerializer.Meta.read_only_fields + ( - 'cover', ) + 'cover', + ) versioned_fields = BaseCommentListSerializer.Meta.versioned_fields - extra_kwargs = { - 'url': {'view_name': 'api-cover-comment-detail'} - } + extra_kwargs = {'url': {'view_name': 'api-cover-comment-detail'}} class CoverCommentMixin(object): @@ -97,9 +116,9 @@ def get_queryset(self): cover_id = self.kwargs['cover_id'] get_object_or_404(Cover, pk=cover_id) - return CoverComment.objects.filter( - cover=cover_id - ).select_related('submitter') + return CoverComment.objects.filter(cover=cover_id).select_related( + 'submitter' + ) class PatchCommentSerializer(BaseCommentListSerializer): @@ -108,13 +127,12 @@ class PatchCommentSerializer(BaseCommentListSerializer): class Meta: model = PatchComment - fields = BaseCommentListSerializer.Meta.fields + ('patch', ) + fields = BaseCommentListSerializer.Meta.fields + ('patch',) read_only_fields = BaseCommentListSerializer.Meta.read_only_fields + ( - 'patch', ) + 'patch', + ) versioned_fields = BaseCommentListSerializer.Meta.versioned_fields - extra_kwargs = { - 'url': {'view_name': 'api-patch-comment-detail'} - } + extra_kwargs = {'url': {'view_name': 'api-patch-comment-detail'}} class PatchCommentMixin(object): @@ -133,9 +151,9 @@ def get_queryset(self): patch_id = self.kwargs['patch_id'] get_object_or_404(Patch, id=patch_id) - return PatchComment.objects.filter( - patch=patch_id - ).select_related('submitter') + return PatchComment.objects.filter(patch=patch_id).select_related( + 'submitter' + ) class CoverCommentList(CoverCommentMixin, ListAPIView): @@ -147,8 +165,9 @@ class CoverCommentList(CoverCommentMixin, ListAPIView): lookup_url_kwarg = 'cover_id' -class CoverCommentDetail(CoverCommentMixin, MultipleFieldLookupMixin, - RetrieveUpdateAPIView): +class CoverCommentDetail( + CoverCommentMixin, MultipleFieldLookupMixin, RetrieveUpdateAPIView +): """ get: Show a cover comment. @@ -159,6 +178,7 @@ class CoverCommentDetail(CoverCommentMixin, MultipleFieldLookupMixin, put: Update a cover comment. """ + lookup_url_kwargs = ('cover_id', 'comment_id') lookup_fields = ('cover_id', 'id') @@ -172,8 +192,9 @@ class PatchCommentList(PatchCommentMixin, ListAPIView): lookup_url_kwarg = 'patch_id' -class PatchCommentDetail(PatchCommentMixin, MultipleFieldLookupMixin, - RetrieveUpdateAPIView): +class PatchCommentDetail( + PatchCommentMixin, MultipleFieldLookupMixin, RetrieveUpdateAPIView +): """ get: Show a patch comment. @@ -184,5 +205,6 @@ class PatchCommentDetail(PatchCommentMixin, MultipleFieldLookupMixin, put: Update a patch comment. """ + lookup_url_kwargs = ('patch_id', 'comment_id') lookup_fields = ('patch_id', 'id') diff --git a/patchwork/api/cover.py b/patchwork/api/cover.py index c4355f6b..7645c711 100644 --- a/patchwork/api/cover.py +++ b/patchwork/api/cover.py @@ -37,22 +37,33 @@ def get_mbox(self, instance): def get_comments(self, cover): return self.context.get('request').build_absolute_uri( - reverse('api-cover-comment-list', kwargs={'cover_id': cover.id})) + reverse('api-cover-comment-list', kwargs={'cover_id': cover.id}) + ) def to_representation(self, instance): # NOTE(stephenfin): This is here to ensure our API looks the same even # after we changed the series-patch relationship from M:N to 1:N. It # will be removed in API v2 - data = super(CoverListSerializer, self).to_representation( - instance) + data = super(CoverListSerializer, self).to_representation(instance) data['series'] = [data['series']] if data['series'] else [] return data class Meta: model = Cover - fields = ('id', 'url', 'web_url', 'project', 'msgid', - 'list_archive_url', 'date', 'name', 'submitter', 'mbox', - 'series', 'comments') + fields = ( + 'id', + 'url', + 'web_url', + 'project', + 'msgid', + 'list_archive_url', + 'date', + 'name', + 'submitter', + 'mbox', + 'series', + 'comments', + ) read_only_fields = fields versioned_fields = { '1.1': ('web_url', 'mbox', 'comments'), @@ -83,8 +94,7 @@ def get_headers(self, instance): class Meta: model = Cover - fields = CoverListSerializer.Meta.fields + ( - 'headers', 'content') + fields = CoverListSerializer.Meta.fields + ('headers', 'content') read_only_fields = fields extra_kwargs = CoverListSerializer.Meta.extra_kwargs versioned_fields = CoverListSerializer.Meta.versioned_fields @@ -100,10 +110,12 @@ class CoverList(ListAPIView): ordering = 'id' def get_queryset(self): - return Cover.objects.all()\ - .prefetch_related('series__project')\ - .select_related('project', 'submitter', 'series')\ + return ( + Cover.objects.all() + .prefetch_related('series__project') + .select_related('project', 'submitter', 'series') .defer('content', 'headers') + ) class CoverDetail(RetrieveAPIView): @@ -112,5 +124,6 @@ class CoverDetail(RetrieveAPIView): serializer_class = CoverDetailSerializer def get_queryset(self): - return Cover.objects.all()\ - .select_related('project', 'submitter', 'series') + return Cover.objects.all().select_related( + 'project', 'submitter', 'series' + ) diff --git a/patchwork/api/embedded.py b/patchwork/api/embedded.py index 4ba80f4a..c41511fe 100644 --- a/patchwork/api/embedded.py +++ b/patchwork/api/embedded.py @@ -45,13 +45,9 @@ def get_choices(self, cutoff=None): if cutoff is not None: queryset = queryset[:cutoff] - return OrderedDict([ - ( - item.pk, - self.display_value(item) - ) - for item in queryset - ]) + return OrderedDict( + [(item.pk, self.display_value(item)) for item in queryset] + ) def to_representation(self, data): return self._Serializer(context=self.context).to_representation(data) @@ -82,14 +78,14 @@ def get_web_url(self, instance): class CheckSerializer(SerializedRelatedField): - class _Serializer(BaseHyperlinkedModelSerializer): url = CheckHyperlinkedIdentityField('api-check-detail') def to_representation(self, instance): data = super(CheckSerializer._Serializer, self).to_representation( - instance) + instance + ) data['state'] = instance.get_state_display() return data @@ -103,16 +99,25 @@ class Meta: class CoverSerializer(SerializedRelatedField): - class _Serializer(MboxMixin, WebURLMixin, BaseHyperlinkedModelSerializer): - class Meta: model = models.Cover - fields = ('id', 'url', 'web_url', 'msgid', 'list_archive_url', - 'date', 'name', 'mbox') + fields = ( + 'id', + 'url', + 'web_url', + 'msgid', + 'list_archive_url', + 'date', + 'name', + 'mbox', + ) read_only_fields = fields versioned_fields = { - '1.1': ('web_url', 'mbox', ), + '1.1': ( + 'web_url', + 'mbox', + ), '1.2': ('list_archive_url',), } extra_kwargs = { @@ -121,16 +126,24 @@ class Meta: class CoverCommentSerializer(SerializedRelatedField): - class _Serializer(MboxMixin, WebURLMixin, BaseHyperlinkedModelSerializer): - class Meta: model = models.CoverComment - fields = ('id', 'url', 'web_url', 'msgid', 'list_archive_url', - 'date', 'name') + fields = ( + 'id', + 'url', + 'web_url', + 'msgid', + 'list_archive_url', + 'date', + 'name', + ) read_only_fields = fields versioned_fields = { - '1.1': ('web_url', 'mbox', ), + '1.1': ( + 'web_url', + 'mbox', + ), '1.2': ('list_archive_url',), } extra_kwargs = { @@ -139,16 +152,22 @@ class Meta: class PatchSerializer(SerializedRelatedField): - class _Serializer(MboxMixin, WebURLMixin, BaseHyperlinkedModelSerializer): - class Meta: model = models.Patch - fields = ('id', 'url', 'web_url', 'msgid', 'list_archive_url', - 'date', 'name', 'mbox') + fields = ( + 'id', + 'url', + 'web_url', + 'msgid', + 'list_archive_url', + 'date', + 'name', + 'mbox', + ) read_only_fields = fields versioned_fields = { - '1.1': ('web_url', ), + '1.1': ('web_url',), '1.2': ('list_archive_url',), } extra_kwargs = { @@ -157,16 +176,24 @@ class Meta: class PatchCommentSerializer(SerializedRelatedField): - class _Serializer(MboxMixin, WebURLMixin, BaseHyperlinkedModelSerializer): - class Meta: model = models.PatchComment - fields = ('id', 'url', 'web_url', 'msgid', 'list_archive_url', - 'date', 'name') + fields = ( + 'id', + 'url', + 'web_url', + 'msgid', + 'list_archive_url', + 'date', + 'name', + ) read_only_fields = fields versioned_fields = { - '1.1': ('web_url', 'mbox', ), + '1.1': ( + 'web_url', + 'mbox', + ), '1.2': ('list_archive_url',), } extra_kwargs = { @@ -175,9 +202,7 @@ class Meta: class PersonSerializer(SerializedRelatedField): - class _Serializer(BaseHyperlinkedModelSerializer): - class Meta: model = models.Person fields = ('id', 'url', 'name', 'email') @@ -188,7 +213,6 @@ class Meta: class ProjectSerializer(SerializedRelatedField): - class _Serializer(BaseHyperlinkedModelSerializer): link_name = CharField(max_length=255, source='linkname') @@ -197,31 +221,49 @@ class _Serializer(BaseHyperlinkedModelSerializer): class Meta: model = models.Project - fields = ('id', 'url', 'name', 'link_name', 'list_id', - 'list_email', 'web_url', 'scm_url', 'webscm_url', - 'list_archive_url', 'list_archive_url_format', - 'commit_url_format') + fields = ( + 'id', + 'url', + 'name', + 'link_name', + 'list_id', + 'list_email', + 'web_url', + 'scm_url', + 'webscm_url', + 'list_archive_url', + 'list_archive_url_format', + 'commit_url_format', + ) read_only_fields = fields extra_kwargs = { 'url': {'view_name': 'api-project-detail'}, } versioned_fields = { - '1.2': ('list_archive_url', 'list_archive_url_format', - 'commit_url_format'), + '1.2': ( + 'list_archive_url', + 'list_archive_url_format', + 'commit_url_format', + ), } class SeriesSerializer(SerializedRelatedField): - class _Serializer(MboxMixin, WebURLMixin, BaseHyperlinkedModelSerializer): - class Meta: model = models.Series - fields = ('id', 'url', 'web_url', 'date', 'name', 'version', - 'mbox') + fields = ( + 'id', + 'url', + 'web_url', + 'date', + 'name', + 'version', + 'mbox', + ) read_only_fields = fields versioned_fields = { - '1.1': ('web_url', ), + '1.1': ('web_url',), } extra_kwargs = { 'url': {'view_name': 'api-series-detail'}, @@ -229,13 +271,17 @@ class Meta: class UserSerializer(SerializedRelatedField): - class _Serializer(BaseHyperlinkedModelSerializer): - class Meta: model = models.User - fields = ('id', 'url', 'username', 'first_name', 'last_name', - 'email') + fields = ( + 'id', + 'url', + 'username', + 'first_name', + 'last_name', + 'email', + ) read_only_fields = fields extra_kwargs = { 'url': {'view_name': 'api-user-detail'}, @@ -243,7 +289,6 @@ class Meta: class UserProfileSerializer(SerializedRelatedField): - class _Serializer(BaseHyperlinkedModelSerializer): username = CharField(source='user.username') @@ -253,8 +298,14 @@ class _Serializer(BaseHyperlinkedModelSerializer): class Meta: model = models.UserProfile - fields = ('id', 'url', 'username', 'first_name', 'last_name', - 'email') + fields = ( + 'id', + 'url', + 'username', + 'first_name', + 'last_name', + 'email', + ) read_only_fields = fields extra_kwargs = { 'url': {'view_name': 'api-user-detail'}, diff --git a/patchwork/api/filters.py b/patchwork/api/filters.py index d9b65a8f..edba3ccf 100644 --- a/patchwork/api/filters.py +++ b/patchwork/api/filters.py @@ -29,8 +29,8 @@ # custom backend -class DjangoFilterBackend(rest_framework.DjangoFilterBackend): +class DjangoFilterBackend(rest_framework.DjangoFilterBackend): def filter_queryset(self, request, queryset, view): try: return super().filter_queryset(request, queryset, view) @@ -40,8 +40,8 @@ def filter_queryset(self, request, queryset, view): # custom fields, filters -class ModelMultipleChoiceField(BaseMultipleChoiceField): +class ModelMultipleChoiceField(BaseMultipleChoiceField): def _get_filter(self, value): if not self.alternate_lookup: return 'pk', value @@ -124,7 +124,6 @@ class ProjectFilter(ModelMultipleChoiceFilter): class StateChoiceField(ModelMultipleChoiceField): - def _get_filter(self, value): try: return 'pk', int(value) @@ -151,7 +150,6 @@ class UserFilter(ModelMultipleChoiceFilter): class BaseFilterSet(FilterSet): - @property def form(self): form = super(BaseFilterSet, self).form @@ -193,8 +191,11 @@ class CoverFilterSet(TimestampMixin, BaseFilterSet): project = ProjectFilter(queryset=Project.objects.all(), distinct=False) # NOTE(stephenfin): We disable the select-based HTML widgets for these # filters as the resulting query is _huge_ - series = BaseFilter(queryset=Project.objects.all(), - widget=MultipleHiddenInput, distinct=False) + series = BaseFilter( + queryset=Project.objects.all(), + widget=MultipleHiddenInput, + distinct=False, + ) submitter = PersonFilter(queryset=Person.objects.all(), distinct=False) msgid = CharFilter(method=msgid_filter) @@ -208,8 +209,11 @@ class PatchFilterSet(TimestampMixin, BaseFilterSet): project = ProjectFilter(queryset=Project.objects.all(), distinct=False) # NOTE(stephenfin): We disable the select-based HTML widgets for these # filters as the resulting query is _huge_ - series = BaseFilter(queryset=Series.objects.all(), - widget=MultipleHiddenInput, distinct=False) + series = BaseFilter( + queryset=Series.objects.all(), + widget=MultipleHiddenInput, + distinct=False, + ) submitter = PersonFilter(queryset=Person.objects.all(), distinct=False) delegate = UserFilter(queryset=User.objects.all(), distinct=False) state = StateFilter(queryset=State.objects.all(), distinct=False) @@ -222,8 +226,16 @@ class Meta: # can't find a way to do that which is reliable and not extremely ugly. # The best I can come up with is manually working with request.GET # which seems to rather defeat the point of using django-filters. - fields = ('project', 'series', 'submitter', 'delegate', - 'state', 'archived', 'hash', 'msgid') + fields = ( + 'project', + 'series', + 'submitter', + 'delegate', + 'state', + 'archived', + 'hash', + 'msgid', + ) versioned_fields = { '1.2': ('hash', 'msgid'), } @@ -243,24 +255,32 @@ class EventFilterSet(TimestampMixin, BaseFilterSet): # NOTE(stephenfin): We disable the select-based HTML widgets for these # filters as the resulting query is _huge_ # TODO(stephenfin): We should really use an AJAX widget of some form here - project = ProjectFilter(queryset=Project.objects.all(), - widget=MultipleHiddenInput, - distinct=False) - series = BaseFilter(queryset=Series.objects.all(), - widget=MultipleHiddenInput, - distinct=False) - patch = BaseFilter(queryset=Patch.objects.all(), - widget=MultipleHiddenInput, - distinct=False) - cover = BaseFilter(queryset=Cover.objects.all(), - widget=MultipleHiddenInput, - distinct=False) + project = ProjectFilter( + queryset=Project.objects.all(), + widget=MultipleHiddenInput, + distinct=False, + ) + series = BaseFilter( + queryset=Series.objects.all(), + widget=MultipleHiddenInput, + distinct=False, + ) + patch = BaseFilter( + queryset=Patch.objects.all(), + widget=MultipleHiddenInput, + distinct=False, + ) + cover = BaseFilter( + queryset=Cover.objects.all(), + widget=MultipleHiddenInput, + distinct=False, + ) class Meta: model = Event fields = ('project', 'category', 'series', 'patch', 'cover', 'actor') versioned_fields = { - '1.2': ('actor', ), + '1.2': ('actor',), } diff --git a/patchwork/api/index.py b/patchwork/api/index.py index 45485c91..684dad27 100644 --- a/patchwork/api/index.py +++ b/patchwork/api/index.py @@ -9,16 +9,17 @@ class IndexView(APIView): - def get(self, request, *args, **kwargs): """List API resources.""" - return Response({ - 'projects': reverse('api-project-list', request=request), - 'users': reverse('api-user-list', request=request), - 'people': reverse('api-person-list', request=request), - 'patches': reverse('api-patch-list', request=request), - 'covers': reverse('api-cover-list', request=request), - 'series': reverse('api-series-list', request=request), - 'events': reverse('api-event-list', request=request), - 'bundles': reverse('api-bundle-list', request=request), - }) + return Response( + { + 'projects': reverse('api-project-list', request=request), + 'users': reverse('api-user-list', request=request), + 'people': reverse('api-person-list', request=request), + 'patches': reverse('api-patch-list', request=request), + 'covers': reverse('api-cover-list', request=request), + 'series': reverse('api-series-list', request=request), + 'events': reverse('api-event-list', request=request), + 'bundles': reverse('api-bundle-list', request=request), + } + ) diff --git a/patchwork/api/patch.py b/patchwork/api/patch.py index a97a8820..9fd10e06 100644 --- a/patchwork/api/patch.py +++ b/patchwork/api/patch.py @@ -38,12 +38,15 @@ class StateField(RelatedField): TODO(stephenfin): Consider switching to SlugRelatedField for the v2.0 API. """ + default_error_messages = { 'required': _('This field is required.'), - 'invalid_choice': _('Invalid state {name}. Expected one of: ' - '{choices}.'), - 'incorrect_type': _('Incorrect type. Expected string value, received ' - '{data_type}.'), + 'invalid_choice': _( + 'Invalid state {name}. Expected one of: ' '{choices}.' + ), + 'incorrect_type': _( + 'Incorrect type. Expected string value, received ' '{data_type}.' + ), } def to_internal_value(self, data): @@ -51,8 +54,11 @@ def to_internal_value(self, data): try: return self.get_queryset().get(slug=data) except State.DoesNotExist: - self.fail('invalid_choice', name=data, choices=', '.join([ - x.slug for x in self.get_queryset()])) + self.fail( + 'invalid_choice', + name=data, + choices=', '.join([x.slug for x in self.get_queryset()]), + ) def to_representation(self, obj): return obj.slug @@ -84,8 +90,11 @@ class PatchListSerializer(BaseHyperlinkedModelSerializer): checks = SerializerMethodField() tags = SerializerMethodField() related = PatchSerializer( - source='related.patches', many=True, default=[], - style={'base_template': 'input.html'}) + source='related.patches', + many=True, + default=[], + style={'base_template': 'input.html'}, + ) def get_web_url(self, instance): request = self.context.get('request') @@ -97,14 +106,16 @@ def get_mbox(self, instance): def get_comments(self, patch): return self.context.get('request').build_absolute_uri( - reverse('api-patch-comment-list', kwargs={'patch_id': patch.id})) + reverse('api-patch-comment-list', kwargs={'patch_id': patch.id}) + ) def get_check(self, instance): return instance.combined_check_state def get_checks(self, instance): return self.context.get('request').build_absolute_uri( - reverse('api-check-list', kwargs={'patch_id': instance.id})) + reverse('api-check-list', kwargs={'patch_id': instance.id}) + ) def get_tags(self, instance): # TODO(stephenfin): Make tags performant, possibly by reworking the @@ -116,10 +127,15 @@ def validate_delegate(self, value): if not value: return value - if not value.profile.maintainer_projects.only('id').filter( - id=self.instance.project.id).exists(): - raise ValidationError("User '%s' is not a maintainer for project " - "'%s'" % (value, self.instance.project)) + if ( + not value.profile.maintainer_projects.only('id') + .filter(id=self.instance.project.id) + .exists() + ): + raise ValidationError( + "User '%s' is not a maintainer for project " + "'%s'" % (value, self.instance.project) + ) return value def to_representation(self, instance): @@ -139,16 +155,52 @@ def to_representation(self, instance): class Meta: model = Patch - fields = ('id', 'url', 'web_url', 'project', 'msgid', - 'list_archive_url', 'date', 'name', 'commit_ref', 'pull_url', - 'state', 'archived', 'hash', 'submitter', 'delegate', 'mbox', - 'series', 'comments', 'check', 'checks', 'tags', 'related',) - read_only_fields = ('web_url', 'project', 'msgid', 'list_archive_url', - 'date', 'name', 'hash', 'submitter', 'mbox', - 'series', 'comments', 'check', 'checks', 'tags') + fields = ( + 'id', + 'url', + 'web_url', + 'project', + 'msgid', + 'list_archive_url', + 'date', + 'name', + 'commit_ref', + 'pull_url', + 'state', + 'archived', + 'hash', + 'submitter', + 'delegate', + 'mbox', + 'series', + 'comments', + 'check', + 'checks', + 'tags', + 'related', + ) + read_only_fields = ( + 'web_url', + 'project', + 'msgid', + 'list_archive_url', + 'date', + 'name', + 'hash', + 'submitter', + 'mbox', + 'series', + 'comments', + 'check', + 'checks', + 'tags', + ) versioned_fields = { '1.1': ('comments', 'web_url'), - '1.2': ('list_archive_url', 'related',), + '1.2': ( + 'list_archive_url', + 'related', + ), } extra_kwargs = { 'url': {'view_name': 'api-patch-detail'}, @@ -182,7 +234,8 @@ def update(self, instance, validated_data): # specifically ourselves and let d-r-f handle the rest if 'related' not in validated_data: return super(PatchDetailSerializer, self).update( - instance, validated_data) + instance, validated_data + ) related = validated_data.pop('related') @@ -227,7 +280,8 @@ def update(self, instance, validated_data): instance.related.delete() instance.related = None return super(PatchDetailSerializer, self).update( - instance, validated_data) + instance, validated_data + ) # break before make relations = {patch.related for patch in patches if patch.related} @@ -252,7 +306,8 @@ def update(self, instance, validated_data): instance.save() return super(PatchDetailSerializer, self).update( - instance, validated_data) + instance, validated_data + ) @staticmethod def check_user_maintains_all(user, patches): @@ -268,9 +323,17 @@ def check_user_maintains_all(user, patches): class Meta: model = Patch fields = PatchListSerializer.Meta.fields + ( - 'headers', 'content', 'diff', 'prefixes') + 'headers', + 'content', + 'diff', + 'prefixes', + ) read_only_fields = PatchListSerializer.Meta.read_only_fields + ( - 'headers', 'content', 'diff', 'prefixes') + 'headers', + 'content', + 'diff', + 'prefixes', + ) versioned_fields = PatchListSerializer.Meta.versioned_fields extra_kwargs = PatchListSerializer.Meta.extra_kwargs @@ -282,19 +345,33 @@ class PatchList(ListAPIView): serializer_class = PatchListSerializer filter_class = filterset_class = PatchFilterSet search_fields = ('name',) - ordering_fields = ('id', 'name', 'project', 'date', 'state', 'archived', - 'submitter', 'check') + ordering_fields = ( + 'id', + 'name', + 'project', + 'date', + 'state', + 'archived', + 'submitter', + 'check', + ) ordering = 'id' def get_queryset(self): # TODO(dja): we need to revisit this after the patch migration, paying # particular attention to cases with filtering - return Patch.objects.all()\ + return ( + Patch.objects.all() .prefetch_related( - 'check_set', 'delegate', 'project', 'series__project', - 'related__patches__project')\ - .select_related('state', 'submitter', 'series')\ + 'check_set', + 'delegate', + 'project', + 'series__project', + 'related__patches__project', + ) + .select_related('state', 'submitter', 'series') .defer('content', 'diff', 'headers') + ) class PatchDetail(RetrieveUpdateAPIView): @@ -308,11 +385,15 @@ class PatchDetail(RetrieveUpdateAPIView): put: Update a patch. """ + permission_classes = (PatchworkPermission,) serializer_class = PatchDetailSerializer def get_queryset(self): - return Patch.objects.all()\ - .prefetch_related('check_set', 'related__patches__project')\ - .select_related('project', 'state', 'submitter', 'delegate', - 'series') + return ( + Patch.objects.all() + .prefetch_related('check_set', 'related__patches__project') + .select_related( + 'project', 'state', 'submitter', 'delegate', 'series' + ) + ) diff --git a/patchwork/api/project.py b/patchwork/api/project.py index 294d90b5..085dafd0 100644 --- a/patchwork/api/project.py +++ b/patchwork/api/project.py @@ -19,21 +19,43 @@ class ProjectSerializer(BaseHyperlinkedModelSerializer): link_name = CharField(max_length=255, source='linkname', read_only=True) list_id = CharField(max_length=255, source='listid', read_only=True) list_email = CharField(max_length=200, source='listemail', read_only=True) - maintainers = UserProfileSerializer(many=True, read_only=True, - source='maintainer_project') + maintainers = UserProfileSerializer( + many=True, read_only=True, source='maintainer_project' + ) class Meta: model = Project - fields = ('id', 'url', 'name', 'link_name', 'list_id', 'list_email', - 'web_url', 'scm_url', 'webscm_url', 'maintainers', - 'subject_match', 'list_archive_url', - 'list_archive_url_format', 'commit_url_format') - read_only_fields = ('name', 'link_name', 'list_id', 'list_email', - 'maintainers', 'subject_match') + fields = ( + 'id', + 'url', + 'name', + 'link_name', + 'list_id', + 'list_email', + 'web_url', + 'scm_url', + 'webscm_url', + 'maintainers', + 'subject_match', + 'list_archive_url', + 'list_archive_url_format', + 'commit_url_format', + ) + read_only_fields = ( + 'name', + 'link_name', + 'list_id', + 'list_email', + 'maintainers', + 'subject_match', + ) versioned_fields = { - '1.1': ('subject_match', ), - '1.2': ('list_archive_url', 'list_archive_url_format', - 'commit_url_format'), + '1.1': ('subject_match',), + '1.2': ( + 'list_archive_url', + 'list_archive_url_format', + 'commit_url_format', + ), } extra_kwargs = { 'url': {'view_name': 'api-project-detail'}, @@ -70,9 +92,17 @@ def get_queryset(self): class ProjectList(ProjectMixin, ListAPIView): """List projects.""" - search_fields = ('link_name', 'list_id', 'list_email', 'web_url', - 'scm_url', 'webscm_url', 'list_archive_url', - 'list_archive_url_format', 'commit_url_format') + search_fields = ( + 'link_name', + 'list_id', + 'list_email', + 'web_url', + 'scm_url', + 'webscm_url', + 'list_archive_url', + 'list_archive_url_format', + 'commit_url_format', + ) ordering_fields = ('id', 'name', 'link_name', 'list_id') ordering = 'id' diff --git a/patchwork/api/series.py b/patchwork/api/series.py index 106e60f0..32f0e30f 100644 --- a/patchwork/api/series.py +++ b/patchwork/api/series.py @@ -36,13 +36,34 @@ def get_mbox(self, instance): class Meta: model = Series - fields = ('id', 'url', 'web_url', 'project', 'name', 'date', - 'submitter', 'version', 'total', 'received_total', - 'received_all', 'mbox', 'cover_letter', 'patches') - read_only_fields = ('date', 'submitter', 'total', 'received_total', - 'received_all', 'mbox', 'cover_letter', 'patches') + fields = ( + 'id', + 'url', + 'web_url', + 'project', + 'name', + 'date', + 'submitter', + 'version', + 'total', + 'received_total', + 'received_all', + 'mbox', + 'cover_letter', + 'patches', + ) + read_only_fields = ( + 'date', + 'submitter', + 'total', + 'received_total', + 'received_all', + 'mbox', + 'cover_letter', + 'patches', + ) versioned_fields = { - '1.1': ('web_url', ), + '1.1': ('web_url',), } extra_kwargs = { 'url': {'view_name': 'api-series-detail'}, @@ -55,9 +76,11 @@ class SeriesMixin(object): serializer_class = SeriesSerializer def get_queryset(self): - return Series.objects.all()\ - .prefetch_related('patches__project', 'cover_letter__project')\ + return ( + Series.objects.all() + .prefetch_related('patches__project', 'cover_letter__project') .select_related('submitter', 'project') + ) class SeriesList(SeriesMixin, ListAPIView): diff --git a/patchwork/api/user.py b/patchwork/api/user.py index 4ea2322e..980ad305 100644 --- a/patchwork/api/user.py +++ b/patchwork/api/user.py @@ -15,7 +15,6 @@ class IsOwnerOrReadOnly(permissions.BasePermission): - def has_object_permission(self, request, view, obj): if request.method in permissions.SAFE_METHODS: return True @@ -24,14 +23,12 @@ def has_object_permission(self, request, view, obj): class UserProfileSerializer(ModelSerializer): - class Meta: model = UserProfile fields = ('send_email', 'items_per_page', 'show_ids') class UserListSerializer(HyperlinkedModelSerializer): - class Meta: model = User fields = ('id', 'url', 'username', 'first_name', 'last_name', 'email') @@ -50,15 +47,19 @@ def update(self, instance, validated_data): settings_data = validated_data.pop('profile', None) request = self.context['request'] - if settings_data and has_version(request, '1.2') and ( - request.user.id == instance.id): + if ( + settings_data + and has_version(request, '1.2') + and (request.user.id == instance.id) + ): # TODO(stephenfin): We ignore this field rather than raise an error # to be consistent with the rest of the API. We should change this # when we change the overall settings self.fields['settings'].update(instance.profile, settings_data) return super(UserDetailSerializer, self).update( - instance, validated_data) + instance, validated_data + ) def to_representation(self, instance): data = super(UserDetailSerializer, self).to_representation(instance) diff --git a/patchwork/fields.py b/patchwork/fields.py index 6eca1a2f..5cdebcd5 100644 --- a/patchwork/fields.py +++ b/patchwork/fields.py @@ -10,7 +10,6 @@ class HashField(models.CharField): - def __init__(self, *args, **kwargs): self.n_bytes = len(hashlib.sha1().hexdigest()) kwargs['max_length'] = self.n_bytes diff --git a/patchwork/filters.py b/patchwork/filters.py index 12288eb9..d5e5d5e7 100644 --- a/patchwork/filters.py +++ b/patchwork/filters.py @@ -28,13 +28,13 @@ def __init__(self, filters): @property def condition(self): """The current condition of the filter, to be displayed in the - filter UI""" + filter UI""" raise NotImplementedError @property def key(self): """The key for this filter, to appear in the querystring. A key of - None will remove the param=key pair from the querystring.""" + None will remove the param=key pair from the querystring.""" raise NotImplementedError @key.setter @@ -55,8 +55,8 @@ def form(self): def set_status(self, *kwargs): """Views can call this to force a specific filter status. For example, - a user's todo page needs to setup the delegate filter to show - that user's delegated patches""" + a user's todo page needs to setup the delegate filter to show + that user's delegated patches""" pass def parse(self, values): @@ -111,8 +111,10 @@ def kwargs(self): @property def form(self): - return mark_safe('') + return mark_safe( + '' + ) class SubmitterFilter(Filter): @@ -167,8 +169,11 @@ def kwargs(self): if self.person: user = self.person.user if user: - return {'submitter__in': - Person.objects.filter(user=user).values('pk').query} + return { + 'submitter__in': Person.objects.filter(user=user) + .values('pk') + .query + } return {'submitter': self.person} elif self.person_match: return {'submitter__name__icontains': self.person_match} @@ -177,8 +182,10 @@ def kwargs(self): @property def form(self): - return mark_safe('') + return mark_safe( + '' + ) class StateFilter(Filter): @@ -237,8 +244,11 @@ def kwargs(self): if self.state is not None: return {'state': self.state} - return {'state__in': State.objects.filter( - action_required=True).values('pk').query} + return { + 'state__in': State.objects.filter(action_required=True) + .values('pk') + .query + } @property def form(self): @@ -253,7 +263,9 @@ def form(self): if self.applied and self.state is None: selected = 'selected' out += '' % ( - selected, self.action_req_str) + selected, + self.action_req_str, + ) for state in State.objects.all(): selected = '' @@ -261,7 +273,10 @@ def form(self): selected = ' selected="true"' out += '' % ( - state.id, selected, escape(state.name)) + state.id, + selected, + escape(state.name), + ) out += '' return mark_safe(out) @@ -300,23 +315,17 @@ def form(self): value = '' if self.search: value = escape(self.search) - return mark_safe('' % - (self.param, value)) + return mark_safe( + '' + % (self.param, value) + ) class ArchiveFilter(Filter): name = 'Archived' param = 'archive' - param_map = { - True: 'true', - False: '', - None: 'both' - } - description_map = { - True: 'Yes', - False: 'No', - None: 'Both' - } + param_map = {True: 'true', False: '', None: 'both'} + description_map = {True: 'Yes', False: 'No', None: 'Both'} def __init__(self, filters): super(ArchiveFilter, self).__init__(filters) @@ -369,15 +378,17 @@ def form(self): selected = '' if self.archive_state == b: selected = 'checked' - s += ('') % \ - {'label': label, - 'param': self.param, - 'selected': selected, - 'value': self.param_map[b] - } + s += ( + '' + ) % { + 'label': label, + 'param': self.param, + 'selected': selected, + 'value': self.param_map[b], + } return mark_safe(s) @@ -452,11 +463,14 @@ def kwargs(self): @property def form(self): if self.forced: - return mark_safe('%s' % ( - self.param, self.condition)) + return mark_safe( + '%s' + % (self.param, self.condition) + ) delegates = User.objects.filter( - profile__maintainer_projects__isnull=False) + profile__maintainer_projects__isnull=False + ) out = '' return mark_safe(out) @@ -497,12 +517,11 @@ def set_status(self, *args, **kwargs): StateFilter, SearchFilter, ArchiveFilter, - DelegateFilter + DelegateFilter, ] class Filters: - def __init__(self, request): self._filters = [c(self) for c in FILTERS] self.values = request.GET @@ -512,13 +531,15 @@ def __init__(self, request): @property def params(self): - return collections.OrderedDict([ - (f.param, f.key) for f in self._filters if f.key is not None]) + return collections.OrderedDict( + [(f.param, f.key) for f in self._filters if f.key is not None] + ) @property def applied_filters(self): - return collections.OrderedDict([ - (x.param, x) for x in self._filters if x.applied]) + return collections.OrderedDict( + [(x.param, x) for x in self._filters if x.applied] + ) @property def available_filters(self): @@ -551,8 +572,12 @@ def sanitise(s): s = str(s) return quote(s.encode('utf-8')) - return '?' + '&'.join(['%s=%s' % (sanitise(k), sanitise(v)) - for (k, v) in list(params.items())]) + return '?' + '&'.join( + [ + '%s=%s' % (sanitise(k), sanitise(v)) + for (k, v) in list(params.items()) + ] + ) def querystring_without_filter(self, filter): return self.querystring(filter) diff --git a/patchwork/forms.py b/patchwork/forms.py index 24322c78..7c9781af 100644 --- a/patchwork/forms.py +++ b/patchwork/forms.py @@ -17,11 +17,11 @@ class RegistrationForm(forms.Form): first_name = forms.CharField(max_length=30, required=False) last_name = forms.CharField(max_length=30, required=False) - username = forms.RegexField(regex=r'^\w+$', max_length=30, - label=u'Username') + username = forms.RegexField( + regex=r'^\w+$', max_length=30, label=u'Username' + ) email = forms.EmailField(max_length=100, label=u'Email address') - password = forms.CharField(widget=forms.PasswordInput(), - label='Password') + password = forms.CharField(widget=forms.PasswordInput(), label='Password') def clean_username(self): value = self.cleaned_data['username'] @@ -29,8 +29,9 @@ def clean_username(self): User.objects.get(username__iexact=value) except User.DoesNotExist: return self.cleaned_data['username'] - raise forms.ValidationError('This username is already taken. ' - 'Please choose another.') + raise forms.ValidationError( + 'This username is already taken. ' 'Please choose another.' + ) def clean_email(self): value = self.cleaned_data['email'] @@ -38,8 +39,10 @@ def clean_email(self): user = User.objects.get(email__iexact=value) except User.DoesNotExist: return self.cleaned_data['email'] - raise forms.ValidationError('This email address is already in use ' - 'for the account "%s".\n' % user.username) + raise forms.ValidationError( + 'This email address is already in use ' + 'for the account "%s".\n' % user.username + ) def clean(self): return self.cleaned_data @@ -51,8 +54,12 @@ class EmailForm(forms.Form): class BundleForm(forms.ModelForm): name = forms.RegexField( - regex=r'^[^/]+$', min_length=1, max_length=50, label=u'Name', - error_messages={'invalid': 'Bundle names can\'t contain slashes'}) + regex=r'^[^/]+$', + min_length=1, + max_length=50, + label=u'Name', + error_messages={'invalid': 'Bundle names can\'t contain slashes'}, + ) class Meta: model = Bundle @@ -60,7 +67,6 @@ class Meta: class CreateBundleForm(BundleForm): - def __init__(self, *args, **kwargs): super(CreateBundleForm, self).__init__(*args, **kwargs) @@ -70,11 +76,13 @@ class Meta: def clean_name(self): name = self.cleaned_data['name'] - count = Bundle.objects.filter(owner=self.instance.owner, - name=name).count() + count = Bundle.objects.filter( + owner=self.instance.owner, name=name + ).count() if count > 0: - raise forms.ValidationError('A bundle called %s already exists' - % name) + raise forms.ValidationError( + 'A bundle called %s already exists' % name + ) return name @@ -85,13 +93,10 @@ class DeleteBundleForm(forms.Form): class UserProfileForm(forms.ModelForm): - class Meta: model = UserProfile fields = ['items_per_page', 'show_ids'] - labels = { - 'show_ids': 'Show Patch IDs:' - } + labels = {'show_ids': 'Show Patch IDs:'} def _get_delegate_qs(project, instance=None): @@ -101,20 +106,22 @@ def _get_delegate_qs(project, instance=None): if not project: raise ValueError('Expected a project') - q = Q(profile__in=UserProfile.objects - .filter(maintainer_projects=project) - .values('pk').query) + q = Q( + profile__in=UserProfile.objects.filter(maintainer_projects=project) + .values('pk') + .query + ) if instance and instance.delegate: q = q | Q(username=instance.delegate) return User.objects.complex_filter(q) class PatchForm(forms.ModelForm): - def __init__(self, instance=None, project=None, *args, **kwargs): super(PatchForm, self).__init__(instance=instance, *args, **kwargs) self.fields['delegate'] = forms.ModelChoiceField( - queryset=_get_delegate_qs(project, instance), required=False) + queryset=_get_delegate_qs(project, instance), required=False + ) class Meta: model = Patch @@ -127,7 +134,8 @@ class OptionalModelChoiceField(forms.ModelChoiceField): def __init__(self, *args, **kwargs): super(OptionalModelChoiceField, self).__init__( - initial=self.no_change_choice[0], *args, **kwargs) + initial=self.no_change_choice[0], *args, **kwargs + ) def _get_choices(self): # _get_choices queries the database, which can fail if the db @@ -135,7 +143,8 @@ def _get_choices(self): # set of choices for now. try: choices = list( - super(OptionalModelChoiceField, self)._get_choices()) + super(OptionalModelChoiceField, self)._get_choices() + ) except ProgrammingError: choices = [] choices.append(self.no_change_choice) @@ -153,7 +162,6 @@ def clean(self, value): class OptionalBooleanField(forms.TypedChoiceField): - def is_no_change(self, value): return value == self.empty_value @@ -161,23 +169,31 @@ def is_no_change(self, value): class MultiplePatchForm(forms.Form): action = 'update' archived = OptionalBooleanField( - choices=[('*', 'no change'), ('True', 'Archived'), - ('False', 'Unarchived')], + choices=[ + ('*', 'no change'), + ('True', 'Archived'), + ('False', 'Unarchived'), + ], coerce=lambda x: x == 'True', - empty_value='*') + empty_value='*', + ) def __init__(self, project, *args, **kwargs): super(MultiplePatchForm, self).__init__(*args, **kwargs) self.fields['delegate'] = OptionalModelChoiceField( - queryset=_get_delegate_qs(project=project), required=False) + queryset=_get_delegate_qs(project=project), required=False + ) self.fields['state'] = OptionalModelChoiceField( - queryset=State.objects.all()) + queryset=State.objects.all() + ) def save(self, instance, commit=True): opts = instance.__class__._meta if self.errors: - raise ValueError("The %s could not be changed because the data " - "didn't validate." % opts.object_name) + raise ValueError( + "The %s could not be changed because the data " + "didn't validate." % opts.object_name + ) data = self.cleaned_data # Update the instance for f in opts.fields: diff --git a/patchwork/hasher.py b/patchwork/hasher.py index e2a96cd1..08aaf040 100644 --- a/patchwork/hasher.py +++ b/patchwork/hasher.py @@ -47,6 +47,7 @@ def fn(x): if not x: return 1 return int(x) + line_nos = list(map(fn, hunk_match.groups())) line = '@@ -%d +%d @@' % tuple(line_nos) elif line[0] in prefixes: diff --git a/patchwork/management/commands/cron.py b/patchwork/management/commands/cron.py index 609b93d9..71a9c580 100644 --- a/patchwork/management/commands/cron.py +++ b/patchwork/management/commands/cron.py @@ -10,13 +10,16 @@ class Command(BaseCommand): - help = ('Run periodic Patchwork functions: send notifications and ' - 'expire unused users') + help = ( + 'Run periodic Patchwork functions: send notifications and ' + 'expire unused users' + ) def handle(self, *args, **kwargs): errors = send_notifications() for (recipient, error) in errors: - self.stderr.write("Failed sending to %s: %s" % - (recipient.email, error)) + self.stderr.write( + "Failed sending to %s: %s" % (recipient.email, error) + ) expire_notifications() diff --git a/patchwork/management/commands/dumparchive.py b/patchwork/management/commands/dumparchive.py index e9445eab..af727b98 100644 --- a/patchwork/management/commands/dumparchive.py +++ b/patchwork/management/commands/dumparchive.py @@ -21,11 +21,15 @@ class Command(BaseCommand): def add_arguments(self, parser): parser.add_argument( - '-c', '--compress', action='store_true', + '-c', + '--compress', + action='store_true', help='compress generated archive.', ) parser.add_argument( - 'projects', metavar='PROJECT', nargs='*', + 'projects', + metavar='PROJECT', + nargs='*', help='list ID of project(s) to export. If not supplied, all ' 'projects will be exported.', ) @@ -54,16 +58,19 @@ def handle(self, *args, **options): with tarfile.open(name, 'w:gz', compresslevel=compress_level) as tar: for i, project in enumerate(projects): - self.stdout.write('Project %02d/%02d (%s)' % ( - i + 1, len(projects), project.linkname)) + self.stdout.write( + 'Project %02d/%02d (%s)' + % (i + 1, len(projects), project.linkname) + ) with tempfile.NamedTemporaryFile(delete=False) as mbox: patches = Patch.objects.filter(project=project) count = patches.count() for j, patch in enumerate(patches): if not (j % 10): - self.stdout.write('%06d/%06d\r' % (j, count), - ending='') + self.stdout.write( + '%06d/%06d\r' % (j, count), ending='' + ) self.stdout.flush() mbox.write(force_bytes(patch_to_mbox(patch) + '\n')) diff --git a/patchwork/management/commands/parsearchive.py b/patchwork/management/commands/parsearchive.py index f53b8db4..fdc1314f 100644 --- a/patchwork/management/commands/parsearchive.py +++ b/patchwork/management/commands/parsearchive.py @@ -21,13 +21,12 @@ class Command(BaseCommand): help = 'Parse an mbox archive file and store any patches/comments found.' def add_arguments(self, parser): - parser.add_argument( - 'infile', - help='input mbox filename') + parser.add_argument('infile', help='input mbox filename') parser.add_argument( '--list-id', help='mailing list ID. If not supplied, this will be ' - 'extracted from the mail headers.') + 'extracted from the mail headers.', + ) def handle(self, *args, **options): results = { @@ -117,7 +116,8 @@ def handle(self, *args, **options): ' %(duplicates)4d duplicates\n' ' %(dropped)4d dropped\n' ' %(errors)4d errors\n' - 'Total: %(new)s new entries' % { + 'Total: %(new)s new entries' + % { 'total': count, 'covers': results[models.Cover], 'patches': results[models.Patch], @@ -128,4 +128,5 @@ def handle(self, *args, **options): 'dropped': dropped, 'errors': errors, 'new': count - duplicates - dropped - errors, - }) + } + ) diff --git a/patchwork/management/commands/parsemail.py b/patchwork/management/commands/parsemail.py index 4c0f1ff1..6f45f51d 100644 --- a/patchwork/management/commands/parsemail.py +++ b/patchwork/management/commands/parsemail.py @@ -24,11 +24,13 @@ def add_arguments(self, parser): nargs='?', type=str, default=None, - help='input mbox file (a filename or stdin)') + help='input mbox file (a filename or stdin)', + ) parser.add_argument( '--list-id', help='mailing list ID. If not supplied, this will be ' - 'extracted from the mail headers.') + 'extracted from the mail headers.', + ) def handle(self, *args, **options): infile = args[0] if args else options['infile'] @@ -61,7 +63,9 @@ def handle(self, *args, **options): except DuplicateMailError as exc: logger.warning('Duplicate mail for message ID %s', exc.msgid) except (ValueError, Exception) as exc: - logger.exception('Error when parsing incoming email: %s', - repr(exc), - extra={'mail': mail.as_string()}) + logger.exception( + 'Error when parsing incoming email: %s', + repr(exc), + extra={'mail': mail.as_string()}, + ) sys.exit(1) diff --git a/patchwork/management/commands/replacerelations.py b/patchwork/management/commands/replacerelations.py index 1c72cd09..83470a50 100644 --- a/patchwork/management/commands/replacerelations.py +++ b/patchwork/management/commands/replacerelations.py @@ -17,13 +17,13 @@ class Command(BaseCommand): - help = ('Parse a relations file generated by PaStA and replace existing' - 'relations with the ones parsed') + help = ( + 'Parse a relations file generated by PaStA and replace existing' + 'relations with the ones parsed' + ) def add_arguments(self, parser): - parser.add_argument( - 'infile', - help='input relations filename') + parser.add_argument('infile', help='input relations filename') def handle(self, *args, **options): verbosity = int(options['verbosity']) diff --git a/patchwork/migrations/0001_initial.py b/patchwork/migrations/0001_initial.py index 581f1263..faa94664 100644 --- a/patchwork/migrations/0001_initial.py +++ b/patchwork/migrations/0001_initial.py @@ -265,9 +265,9 @@ class Migration(migrations.Migration): 'pattern', models.CharField( help_text=b'A simple regex to match the tag in the ' - b'content of a message. Will be used with ' - b'MULTILINE and IGNORECASE flags. eg. ' - b'^Acked-by:', + b'content of a message. Will be used with ' + b'MULTILINE and IGNORECASE flags. eg. ' + b'^Acked-by:', max_length=50, ), ), @@ -275,7 +275,7 @@ class Migration(migrations.Migration): 'abbrev', models.CharField( help_text=b'Short (one-or-two letter) abbreviation ' - b'for the tag, used in table column headers', + b'for the tag, used in table column headers', unique=True, max_length=2, ), @@ -300,7 +300,7 @@ class Migration(migrations.Migration): models.BooleanField( default=False, help_text=b'Selecting this option allows patchwork to ' - b'send email on your behalf', + b'send email on your behalf', ), ), ( @@ -457,18 +457,23 @@ class Migration(migrations.Migration): ), ), migrations.AlterUniqueTogether( - name='patchtag', unique_together=set([('patch', 'tag')]), + name='patchtag', + unique_together=set([('patch', 'tag')]), ), migrations.AlterUniqueTogether( - name='patch', unique_together=set([('msgid', 'project')]), + name='patch', + unique_together=set([('msgid', 'project')]), ), migrations.AlterUniqueTogether( - name='comment', unique_together=set([('msgid', 'patch')]), + name='comment', + unique_together=set([('msgid', 'patch')]), ), migrations.AlterUniqueTogether( - name='bundlepatch', unique_together=set([('bundle', 'patch')]), + name='bundlepatch', + unique_together=set([('bundle', 'patch')]), ), migrations.AlterUniqueTogether( - name='bundle', unique_together=set([('owner', 'name')]), + name='bundle', + unique_together=set([('owner', 'name')]), ), ] diff --git a/patchwork/migrations/0001_squashed_0040_add_related_patches.py b/patchwork/migrations/0001_squashed_0040_add_related_patches.py index e07804f8..a8bcca37 100644 --- a/patchwork/migrations/0001_squashed_0040_add_related_patches.py +++ b/patchwork/migrations/0001_squashed_0040_add_related_patches.py @@ -120,8 +120,8 @@ class Migration(migrations.Migration): models.URLField( blank=True, help_text=b'The target URL to associate with this ' - b'check. This should be specific to the ' - b'patch.', + b'check. This should be specific to the ' + b'patch.', null=True, ), ), @@ -138,7 +138,7 @@ class Migration(migrations.Migration): models.SlugField( default='default', help_text=b'A label to discern check from checks of ' - b'other testing systems.', + b'other testing systems.', max_length=255, ), ), @@ -227,13 +227,13 @@ class Migration(migrations.Migration): blank=True, default='', help_text=b'Regex to match the subject against if ' - b'only part of emails sent to the list ' - b'belongs to this project. Will be used ' - b'with IGNORECASE and MULTILINE flags. If ' - b'rules for more projects match the first ' - b'one returned from DB is chosen; empty ' - b'field serves as a default for every email ' - b'which has no other match.', + b'only part of emails sent to the list ' + b'belongs to this project. Will be used ' + b'with IGNORECASE and MULTILINE flags. If ' + b'rules for more projects match the first ' + b'one returned from DB is chosen; empty ' + b'field serves as a default for every email ' + b'which has no other match.', max_length=64, validators=[patchwork.models.validate_regex_compiles], ), @@ -250,8 +250,8 @@ class Migration(migrations.Migration): models.CharField( blank=True, help_text=b"URL format for the list archive's " - b"Message-ID redirector. {} will be " - b"replaced by the Message-ID.", + b"Message-ID redirector. {} will be " + b"replaced by the Message-ID.", max_length=2000, ), ), @@ -260,7 +260,7 @@ class Migration(migrations.Migration): models.CharField( blank=True, help_text=b'URL format for a particular commit. {} ' - b'will be replaced by the commit SHA.', + b'will be replaced by the commit SHA.', max_length=2000, ), ), @@ -289,7 +289,7 @@ class Migration(migrations.Migration): models.CharField( blank=True, help_text=b'An optional name to associate with the ' - b'series, e.g. "John\'s PCI series".', + b'series, e.g. "John\'s PCI series".', max_length=255, null=True, ), @@ -300,14 +300,14 @@ class Migration(migrations.Migration): models.IntegerField( default=1, help_text=b'Version of series as indicated by the ' - b'subject prefix(es)', + b'subject prefix(es)', ), ), ( 'total', models.IntegerField( help_text=b'Number of patches in series as indicated ' - b'by the subject prefix(es)' + b'by the subject prefix(es)' ), ), ( @@ -405,9 +405,9 @@ class Migration(migrations.Migration): 'pattern', models.CharField( help_text=b'A simple regex to match the tag in the ' - b'content of a message. Will be used with ' - b'MULTILINE and IGNORECASE flags. eg. ' - b'^Acked-by:', + b'content of a message. Will be used with ' + b'MULTILINE and IGNORECASE flags. eg. ' + b'^Acked-by:', max_length=50, validators=[patchwork.models.validate_regex_compiles], ), @@ -416,7 +416,7 @@ class Migration(migrations.Migration): 'abbrev', models.CharField( help_text=b'Short (one-or-two letter) abbreviation ' - b'for the tag, used in table column headers', + b'for the tag, used in table column headers', max_length=2, unique=True, ), @@ -426,7 +426,7 @@ class Migration(migrations.Migration): models.BooleanField( default=True, help_text=b"Show a column displaying this tag's count " - b"in the patch list view", + b"in the patch list view", ), ), ], @@ -485,7 +485,7 @@ class Migration(migrations.Migration): models.PositiveSmallIntegerField( default=None, help_text=b'The number assigned to this patch in the ' - b'series', + b'series', null=True, ), ), @@ -513,7 +513,7 @@ class Migration(migrations.Migration): models.BooleanField( default=False, help_text=b'Selecting this option allows patchwork to ' - b'send email on your behalf', + b'send email on your behalf', ), ), ( @@ -528,7 +528,7 @@ class Migration(migrations.Migration): models.BooleanField( default=False, help_text=b'Show click-to-copy patch IDs in the list ' - b'view', + b'view', ), ), ( @@ -623,7 +623,10 @@ class Migration(migrations.Migration): (b'patch-completed', b'Patch Completed'), (b'patch-state-changed', b'Patch State Changed'), (b'patch-delegated', b'Patch Delegate Changed'), - (b'patch-relation-changed', b'Patch Relation Changed'), # noqa + ( + b'patch-relation-changed', + b'Patch Relation Changed', + ), # noqa (b'check-created', b'Check Created'), (b'series-created', b'Series Created'), (b'series-completed', b'Series Completed'), @@ -735,7 +738,7 @@ class Migration(migrations.Migration): models.ForeignKey( blank=True, help_text=b'The series that this event was created ' - b'for.', + b'for.', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='+', @@ -801,7 +804,7 @@ class Migration(migrations.Migration): 'path', models.CharField( help_text=b'An fnmatch-style pattern to match ' - b'filenames against.', + b'filenames against.', max_length=255, ), ), @@ -810,8 +813,8 @@ class Migration(migrations.Migration): models.IntegerField( default=0, help_text=b'The priority of the rule. Rules with a ' - b'higher priority will override rules with ' - b'lower priorities', + b'higher priority will override rules with ' + b'lower priorities', ), ), ( @@ -927,10 +930,12 @@ class Migration(migrations.Migration): ), ), migrations.AlterUniqueTogether( - name='submission', unique_together={('msgid', 'project')}, + name='submission', + unique_together={('msgid', 'project')}, ), migrations.AlterUniqueTogether( - name='seriesreference', unique_together={('project', 'msgid')}, + name='seriesreference', + unique_together={('project', 'msgid')}, ), migrations.AddField( model_name='series', @@ -1033,7 +1038,8 @@ class Migration(migrations.Migration): ), ), migrations.AlterUniqueTogether( - name='delegationrule', unique_together={('path', 'project')}, + name='delegationrule', + unique_together={('path', 'project')}, ), migrations.AddIndex( model_name='comment', @@ -1042,7 +1048,8 @@ class Migration(migrations.Migration): ), ), migrations.AlterUniqueTogether( - name='comment', unique_together={('msgid', 'submission')}, + name='comment', + unique_together={('msgid', 'submission')}, ), migrations.AddField( model_name='check', @@ -1068,7 +1075,8 @@ class Migration(migrations.Migration): ), ), migrations.AlterUniqueTogether( - name='patchtag', unique_together={('patch', 'tag')}, + name='patchtag', + unique_together={('patch', 'tag')}, ), migrations.AddField( model_name='patchchangenotification', @@ -1086,12 +1094,15 @@ class Migration(migrations.Migration): ), ), migrations.AlterUniqueTogether( - name='patch', unique_together={('series', 'number')}, + name='patch', + unique_together={('series', 'number')}, ), migrations.AlterUniqueTogether( - name='bundlepatch', unique_together={('bundle', 'patch')}, + name='bundlepatch', + unique_together={('bundle', 'patch')}, ), migrations.AlterUniqueTogether( - name='bundle', unique_together={('owner', 'name')}, + name='bundle', + unique_together={('owner', 'name')}, ), ] diff --git a/patchwork/migrations/0003_add_check_model.py b/patchwork/migrations/0003_add_check_model.py index f8f5a67e..8c0df74c 100644 --- a/patchwork/migrations/0003_add_check_model.py +++ b/patchwork/migrations/0003_add_check_model.py @@ -43,8 +43,8 @@ class Migration(migrations.Migration): 'target_url', models.URLField( help_text=b'The target URL to associate with this ' - b'check. This should be specific to the ' - b'patch.', + b'check. This should be specific to the ' + b'patch.', null=True, blank=True, ), @@ -64,7 +64,7 @@ class Migration(migrations.Migration): max_length=255, null=True, help_text=b'A label to discern check from checks of ' - b'other testing systems.', + b'other testing systems.', blank=True, ), ), diff --git a/patchwork/migrations/0004_add_delegation_rule_model.py b/patchwork/migrations/0004_add_delegation_rule_model.py index 01682b5d..97967c12 100644 --- a/patchwork/migrations/0004_add_delegation_rule_model.py +++ b/patchwork/migrations/0004_add_delegation_rule_model.py @@ -43,6 +43,7 @@ class Migration(migrations.Migration): options={'ordering': ['-priority', 'path']}, ), migrations.AlterUniqueTogether( - name='delegationrule', unique_together=set([('path', 'project')]), + name='delegationrule', + unique_together=set([('path', 'project')]), ), ] diff --git a/patchwork/migrations/0006_add_patch_diff.py b/patchwork/migrations/0006_add_patch_diff.py index 61ed2966..ed63424d 100644 --- a/patchwork/migrations/0006_add_patch_diff.py +++ b/patchwork/migrations/0006_add_patch_diff.py @@ -10,7 +10,9 @@ class Migration(migrations.Migration): operations = [ migrations.RenameField( - model_name='patch', old_name='content', new_name='diff', + model_name='patch', + old_name='content', + new_name='diff', ), migrations.AddField( model_name='patch', diff --git a/patchwork/migrations/0009_add_submission_model.py b/patchwork/migrations/0009_add_submission_model.py index c3943040..5e0753dc 100644 --- a/patchwork/migrations/0009_add_submission_model.py +++ b/patchwork/migrations/0009_add_submission_model.py @@ -15,14 +15,18 @@ class Migration(migrations.Migration): # Rename the 'Patch' to 'Submission' migrations.RenameModel(old_name='Patch', new_name='Submission'), migrations.AlterModelOptions( - name='submission', options={'ordering': ['date']}, + name='submission', + options={'ordering': ['date']}, ), # Rename the non-Patch specific references to point to Submission migrations.RenameField( - model_name='comment', old_name='patch', new_name='submission', + model_name='comment', + old_name='patch', + new_name='submission', ), migrations.AlterUniqueTogether( - name='comment', unique_together=set([('msgid', 'submission')]), + name='comment', + unique_together=set([('msgid', 'submission')]), ), migrations.RenameField( model_name='userprofile', diff --git a/patchwork/migrations/0011_remove_temp_fields.py b/patchwork/migrations/0011_remove_temp_fields.py index d7596324..19a4e00c 100644 --- a/patchwork/migrations/0011_remove_temp_fields.py +++ b/patchwork/migrations/0011_remove_temp_fields.py @@ -10,37 +10,77 @@ class Migration(migrations.Migration): operations = [ # Remove duplicate fields from 'Submission' and rename 'Patch' version - migrations.RemoveField(model_name='submission', name='diff',), + migrations.RemoveField( + model_name='submission', + name='diff', + ), migrations.RenameField( - model_name='patch', old_name='diff2', new_name='diff', + model_name='patch', + old_name='diff2', + new_name='diff', + ), + migrations.RemoveField( + model_name='submission', + name='commit_ref', ), - migrations.RemoveField(model_name='submission', name='commit_ref',), migrations.RenameField( - model_name='patch', old_name='commit_ref2', new_name='commit_ref', + model_name='patch', + old_name='commit_ref2', + new_name='commit_ref', + ), + migrations.RemoveField( + model_name='submission', + name='pull_url', ), - migrations.RemoveField(model_name='submission', name='pull_url',), migrations.RenameField( - model_name='patch', old_name='pull_url2', new_name='pull_url', + model_name='patch', + old_name='pull_url2', + new_name='pull_url', + ), + migrations.RemoveField( + model_name='submission', + name='tags', ), - migrations.RemoveField(model_name='submission', name='tags',), migrations.RenameField( - model_name='patch', old_name='tags2', new_name='tags', + model_name='patch', + old_name='tags2', + new_name='tags', + ), + migrations.RemoveField( + model_name='submission', + name='delegate', ), - migrations.RemoveField(model_name='submission', name='delegate',), migrations.RenameField( - model_name='patch', old_name='delegate2', new_name='delegate', + model_name='patch', + old_name='delegate2', + new_name='delegate', + ), + migrations.RemoveField( + model_name='submission', + name='state', ), - migrations.RemoveField(model_name='submission', name='state',), migrations.RenameField( - model_name='patch', old_name='state2', new_name='state', + model_name='patch', + old_name='state2', + new_name='state', + ), + migrations.RemoveField( + model_name='submission', + name='archived', ), - migrations.RemoveField(model_name='submission', name='archived',), migrations.RenameField( - model_name='patch', old_name='archived2', new_name='archived', + model_name='patch', + old_name='archived2', + new_name='archived', + ), + migrations.RemoveField( + model_name='submission', + name='hash', ), - migrations.RemoveField(model_name='submission', name='hash',), migrations.RenameField( - model_name='patch', old_name='hash2', new_name='hash', + model_name='patch', + old_name='hash2', + new_name='hash', ), # Update any many-to-many fields to point to Patch now migrations.AlterField( diff --git a/patchwork/migrations/0013_slug_check_context.py b/patchwork/migrations/0013_slug_check_context.py index ecf6fecf..3536adf3 100644 --- a/patchwork/migrations/0013_slug_check_context.py +++ b/patchwork/migrations/0013_slug_check_context.py @@ -14,7 +14,7 @@ class Migration(migrations.Migration): field=models.SlugField( default=b'default', help_text=b'A label to discern check from checks of other ' - b'testing systems.', + b'testing systems.', max_length=255, ), ), diff --git a/patchwork/migrations/0014_remove_userprofile_primary_project.py b/patchwork/migrations/0014_remove_userprofile_primary_project.py index 15aea2f6..a5b02a5a 100644 --- a/patchwork/migrations/0014_remove_userprofile_primary_project.py +++ b/patchwork/migrations/0014_remove_userprofile_primary_project.py @@ -9,6 +9,7 @@ class Migration(migrations.Migration): operations = [ migrations.RemoveField( - model_name='userprofile', name='primary_project', + model_name='userprofile', + name='primary_project', ), ] diff --git a/patchwork/migrations/0015_add_series_models.py b/patchwork/migrations/0015_add_series_models.py index 1fd1e01b..3325aa57 100644 --- a/patchwork/migrations/0015_add_series_models.py +++ b/patchwork/migrations/0015_add_series_models.py @@ -41,7 +41,7 @@ class Migration(migrations.Migration): models.CharField( blank=True, help_text=b'An optional name to associate with the ' - b'series, e.g. "John\'s PCI series".', + b'series, e.g. "John\'s PCI series".', max_length=255, null=True, ), @@ -52,14 +52,14 @@ class Migration(migrations.Migration): models.IntegerField( default=1, help_text=b'Version of series as indicated by the ' - b'subject prefix(es)', + b'subject prefix(es)', ), ), ( 'total', models.IntegerField( help_text=b'Number of patches in series as indicated ' - b'by the subject prefix(es)' + b'by the subject prefix(es)' ), ), ( @@ -91,7 +91,7 @@ class Migration(migrations.Migration): 'number', models.PositiveSmallIntegerField( help_text=b'The number assigned to this patch in the ' - b'series' + b'series' ), ), ( diff --git a/patchwork/migrations/0016_series_project.py b/patchwork/migrations/0016_series_project.py index 63805769..691c4c5a 100644 --- a/patchwork/migrations/0016_series_project.py +++ b/patchwork/migrations/0016_series_project.py @@ -58,6 +58,7 @@ class Migration(migrations.Migration): field=models.CharField(max_length=255), ), migrations.AlterUniqueTogether( - name='seriesreference', unique_together=set([('series', 'msgid')]), + name='seriesreference', + unique_together=set([('series', 'msgid')]), ), ] diff --git a/patchwork/migrations/0017_improved_delegation_rule_docs.py b/patchwork/migrations/0017_improved_delegation_rule_docs.py index 20bfb91d..de9bb143 100644 --- a/patchwork/migrations/0017_improved_delegation_rule_docs.py +++ b/patchwork/migrations/0017_improved_delegation_rule_docs.py @@ -15,7 +15,7 @@ class Migration(migrations.Migration): name='path', field=models.CharField( help_text=b'An fnmatch-style pattern to match filenames ' - b'against.', + b'against.', max_length=255, ), ), @@ -25,8 +25,8 @@ class Migration(migrations.Migration): field=models.IntegerField( default=0, help_text=b'The priority of the rule. Rules with a higher ' - b'priority will override rules with lower ' - b'priorities', + b'priority will override rules with lower ' + b'priorities', ), ), migrations.AlterField( diff --git a/patchwork/migrations/0018_add_event_model.py b/patchwork/migrations/0018_add_event_model.py index 181f89dc..e5136101 100644 --- a/patchwork/migrations/0018_add_event_model.py +++ b/patchwork/migrations/0018_add_event_model.py @@ -55,7 +55,7 @@ class Migration(migrations.Migration): models.ForeignKey( blank=True, help_text=b'The cover letter that this event was ' - b'created for.', + b'created for.', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='+', @@ -97,7 +97,7 @@ class Migration(migrations.Migration): models.ForeignKey( blank=True, help_text=b'The patch that this event was created ' - b'for.', + b'for.', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='+', @@ -138,7 +138,7 @@ class Migration(migrations.Migration): models.ForeignKey( blank=True, help_text=b'The series that this event was created ' - b'for.', + b'for.', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='+', diff --git a/patchwork/migrations/0020_tag_show_column.py b/patchwork/migrations/0020_tag_show_column.py index 56278d74..d16a6911 100644 --- a/patchwork/migrations/0020_tag_show_column.py +++ b/patchwork/migrations/0020_tag_show_column.py @@ -14,7 +14,7 @@ class Migration(migrations.Migration): field=models.BooleanField( default=True, help_text=b"Show a column displaying this tag's count in the " - b"patch list view", + b"patch list view", ), ), ] diff --git a/patchwork/migrations/0022_add_subject_match_to_project.py b/patchwork/migrations/0022_add_subject_match_to_project.py index 1596d22b..6f6e07a0 100644 --- a/patchwork/migrations/0022_add_subject_match_to_project.py +++ b/patchwork/migrations/0022_add_subject_match_to_project.py @@ -15,12 +15,12 @@ class Migration(migrations.Migration): blank=True, default=b'', help_text=b'Regex to match the subject against if only part ' - b'of emails sent to the list belongs to this ' - b'project. Will be used with IGNORECASE and ' - b'MULTILINE flags. If rules for more projects match ' - b'the first one returned from DB is chosen; empty ' - b'field serves as a default for every email which ' - b'has no other match.', + b'of emails sent to the list belongs to this ' + b'project. Will be used with IGNORECASE and ' + b'MULTILINE flags. If rules for more projects match ' + b'the first one returned from DB is chosen; empty ' + b'field serves as a default for every email which ' + b'has no other match.', max_length=64, ), ), @@ -30,6 +30,7 @@ class Migration(migrations.Migration): field=models.CharField(max_length=255), ), migrations.AlterUniqueTogether( - name='project', unique_together=set([('listid', 'subject_match')]), + name='project', + unique_together=set([('listid', 'subject_match')]), ), ] diff --git a/patchwork/migrations/0025_add_regex_validators.py b/patchwork/migrations/0025_add_regex_validators.py index 0615cc28..ca7da022 100644 --- a/patchwork/migrations/0025_add_regex_validators.py +++ b/patchwork/migrations/0025_add_regex_validators.py @@ -17,12 +17,12 @@ class Migration(migrations.Migration): blank=True, default=b'', help_text=b'Regex to match the subject against if only part ' - b'of emails sent to the list belongs to this ' - b'project. Will be used with IGNORECASE and ' - b'MULTILINE flags. If rules for more projects match ' - b'the first one returned from DB is chosen; empty ' - b'field serves as a default for every email which ' - b'has no other match.', + b'of emails sent to the list belongs to this ' + b'project. Will be used with IGNORECASE and ' + b'MULTILINE flags. If rules for more projects match ' + b'the first one returned from DB is chosen; empty ' + b'field serves as a default for every email which ' + b'has no other match.', max_length=64, validators=[patchwork.models.validate_regex_compiles], ), @@ -32,8 +32,8 @@ class Migration(migrations.Migration): name='pattern', field=models.CharField( help_text=b'A simple regex to match the tag in the content of ' - b'a message. Will be used with MULTILINE and ' - b'IGNORECASE flags. eg. ^Acked-by:', + b'a message. Will be used with MULTILINE and ' + b'IGNORECASE flags. eg. ^Acked-by:', max_length=50, validators=[patchwork.models.validate_regex_compiles], ), diff --git a/patchwork/migrations/0027_remove_series_ordering.py b/patchwork/migrations/0027_remove_series_ordering.py index ab725cfc..4f681546 100644 --- a/patchwork/migrations/0027_remove_series_ordering.py +++ b/patchwork/migrations/0027_remove_series_ordering.py @@ -9,6 +9,7 @@ class Migration(migrations.Migration): operations = [ migrations.AlterModelOptions( - name='series', options={'verbose_name_plural': 'Series'}, + name='series', + options={'verbose_name_plural': 'Series'}, ), ] diff --git a/patchwork/migrations/0031_add_patch_series_fields.py b/patchwork/migrations/0031_add_patch_series_fields.py index c4e04367..585b00e6 100644 --- a/patchwork/migrations/0031_add_patch_series_fields.py +++ b/patchwork/migrations/0031_add_patch_series_fields.py @@ -31,6 +31,7 @@ class Migration(migrations.Migration): ), ), migrations.AlterUniqueTogether( - name='patch', unique_together=set([('series_alt', 'number')]), + name='patch', + unique_together=set([('series_alt', 'number')]), ), ] diff --git a/patchwork/migrations/0033_remove_patch_series_model.py b/patchwork/migrations/0033_remove_patch_series_model.py index 7add14dd..20302589 100644 --- a/patchwork/migrations/0033_remove_patch_series_model.py +++ b/patchwork/migrations/0033_remove_patch_series_model.py @@ -11,16 +11,30 @@ class Migration(migrations.Migration): operations = [ # Remove SeriesPatch migrations.AlterUniqueTogether( - name='seriespatch', unique_together=set([]), + name='seriespatch', + unique_together=set([]), + ), + migrations.RemoveField( + model_name='seriespatch', + name='patch', + ), + migrations.RemoveField( + model_name='seriespatch', + name='series', + ), + migrations.RemoveField( + model_name='series', + name='patches', + ), + migrations.DeleteModel( + name='SeriesPatch', ), - migrations.RemoveField(model_name='seriespatch', name='patch',), - migrations.RemoveField(model_name='seriespatch', name='series',), - migrations.RemoveField(model_name='series', name='patches',), - migrations.DeleteModel(name='SeriesPatch',), # Now that SeriesPatch has been removed, we can use the now-unused # Patch.series field and add a backreference migrations.RenameField( - model_name='patch', old_name='series_alt', new_name='series', + model_name='patch', + old_name='series_alt', + new_name='series', ), migrations.AlterField( model_name='patch', @@ -35,7 +49,8 @@ class Migration(migrations.Migration): ), ), migrations.AlterUniqueTogether( - name='patch', unique_together=set([('series', 'number')]), + name='patch', + unique_together=set([('series', 'number')]), ), # Migrate CoverLetter to OneToOneField as a cover letter can no longer # be assigned to multiple series diff --git a/patchwork/migrations/0035_project_list_archive_url_format.py b/patchwork/migrations/0035_project_list_archive_url_format.py index 4f6e0f42..b9985b95 100644 --- a/patchwork/migrations/0035_project_list_archive_url_format.py +++ b/patchwork/migrations/0035_project_list_archive_url_format.py @@ -14,8 +14,8 @@ class Migration(migrations.Migration): field=models.CharField( blank=True, help_text=b"URL format for the list archive's Message-ID " - b"redirector. {} will be replaced by the " - b"Message-ID.", + b"redirector. {} will be replaced by the " + b"Message-ID.", max_length=2000, ), ), diff --git a/patchwork/migrations/0036_project_commit_url_format.py b/patchwork/migrations/0036_project_commit_url_format.py index f74d8cf1..7bfda699 100644 --- a/patchwork/migrations/0036_project_commit_url_format.py +++ b/patchwork/migrations/0036_project_commit_url_format.py @@ -14,7 +14,7 @@ class Migration(migrations.Migration): field=models.CharField( blank=True, help_text=b'URL format for a particular commit. {} will be ' - b'replaced by the commit SHA.', + b'replaced by the commit SHA.', max_length=2000, ), ), diff --git a/patchwork/migrations/0041_python3.py b/patchwork/migrations/0041_python3.py index b9316bac..69a669f3 100644 --- a/patchwork/migrations/0041_python3.py +++ b/patchwork/migrations/0041_python3.py @@ -31,7 +31,7 @@ class Migration(migrations.Migration): field=models.SlugField( default='default', help_text='A label to discern check from checks of other ' - 'testing systems.', + 'testing systems.', max_length=255, ), ), @@ -64,7 +64,7 @@ class Migration(migrations.Migration): field=models.URLField( blank=True, help_text='The target URL to associate with this check. This ' - 'should be specific to the patch.', + 'should be specific to the patch.', null=True, ), ), @@ -83,7 +83,7 @@ class Migration(migrations.Migration): name='path', field=models.CharField( help_text='An fnmatch-style pattern to match filenames ' - 'against.', + 'against.', max_length=255, ), ), @@ -93,7 +93,7 @@ class Migration(migrations.Migration): field=models.IntegerField( default=0, help_text='The priority of the rule. Rules with a higher ' - 'priority will override rules with lower priorities', + 'priority will override rules with lower priorities', ), ), migrations.AlterField( @@ -139,7 +139,10 @@ class Migration(migrations.Migration): ('patch-completed', 'Patch Completed'), ('patch-state-changed', 'Patch State Changed'), ('patch-delegated', 'Patch Delegate Changed'), - ('patch-relation-changed', 'Patch Relation Changed'), + ( + 'patch-relation-changed', + 'Patch Relation Changed', + ), ('check-created', 'Check Created'), ('series-created', 'Series Created'), ('series-completed', 'Series Completed'), @@ -218,7 +221,7 @@ class Migration(migrations.Migration): field=models.CharField( blank=True, help_text='URL format for a particular commit. {} will be ' - 'replaced by the commit SHA.', + 'replaced by the commit SHA.', max_length=2000, ), ), @@ -228,7 +231,7 @@ class Migration(migrations.Migration): field=models.CharField( blank=True, help_text="URL format for the list archive's Message-ID " - "redirector. {} will be replaced by the Message-ID.", + "redirector. {} will be replaced by the Message-ID.", max_length=2000, ), ), @@ -239,11 +242,11 @@ class Migration(migrations.Migration): blank=True, default='', help_text='Regex to match the subject against if only part ' - 'of emails sent to the list belongs to this project. Will be ' - 'used with IGNORECASE and MULTILINE flags. If rules for more ' - 'projects match the first one returned from DB is chosen; ' - 'empty field serves as a default for every email which has no ' - 'other match.', + 'of emails sent to the list belongs to this project. Will be ' + 'used with IGNORECASE and MULTILINE flags. If rules for more ' + 'projects match the first one returned from DB is chosen; ' + 'empty field serves as a default for every email which has no ' + 'other match.', max_length=64, validators=[patchwork.models.validate_regex_compiles], ), @@ -254,7 +257,7 @@ class Migration(migrations.Migration): field=models.CharField( blank=True, help_text='An optional name to associate with the series, ' - 'e.g. "John\'s PCI series".', + 'e.g. "John\'s PCI series".', max_length=255, null=True, ), @@ -264,7 +267,7 @@ class Migration(migrations.Migration): name='total', field=models.IntegerField( help_text='Number of patches in series as indicated by the ' - 'subject prefix(es)' + 'subject prefix(es)' ), ), migrations.AlterField( @@ -273,7 +276,7 @@ class Migration(migrations.Migration): field=models.IntegerField( default=1, help_text='Version of series as indicated by the subject ' - 'prefix(es)', + 'prefix(es)', ), ), migrations.AlterField( @@ -291,7 +294,7 @@ class Migration(migrations.Migration): name='abbrev', field=models.CharField( help_text='Short (one-or-two letter) abbreviation for the ' - 'tag, used in table column headers', + 'tag, used in table column headers', max_length=2, unique=True, ), @@ -301,8 +304,8 @@ class Migration(migrations.Migration): name='pattern', field=models.CharField( help_text='A simple regex to match the tag in the content of ' - 'a message. Will be used with MULTILINE and IGNORECASE flags. ' - 'eg. ^Acked-by:', + 'a message. Will be used with MULTILINE and IGNORECASE flags. ' + 'eg. ^Acked-by:', max_length=50, validators=[patchwork.models.validate_regex_compiles], ), @@ -313,14 +316,15 @@ class Migration(migrations.Migration): field=models.BooleanField( default=True, help_text="Show a column displaying this tag's count in the " - "patch list view", + "patch list view", ), ), migrations.AlterField( model_name='userprofile', name='items_per_page', field=models.PositiveIntegerField( - default=100, help_text='Number of items to display per page' + default=100, + help_text='Number of items to display per page', ), ), migrations.AlterField( @@ -329,7 +333,7 @@ class Migration(migrations.Migration): field=models.BooleanField( default=False, help_text='Selecting this option allows patchwork to send ' - 'email on your behalf', + 'email on your behalf', ), ), migrations.AlterField( diff --git a/patchwork/migrations/0042_add_cover_model.py b/patchwork/migrations/0042_add_cover_model.py index 3f4e0340..e785e17b 100644 --- a/patchwork/migrations/0042_add_cover_model.py +++ b/patchwork/migrations/0042_add_cover_model.py @@ -39,7 +39,6 @@ class Migration(migrations.Migration): operations = [ # create a new, separate cover (letter) model - migrations.CreateModel( name='Cover', fields=[ @@ -86,11 +85,10 @@ class Migration(migrations.Migration): ), ), migrations.AlterUniqueTogether( - name='cover', unique_together=set([('msgid', 'project')]), + name='cover', + unique_together=set([('msgid', 'project')]), ), - # create a new, separate cover (letter) comment model - migrations.CreateModel( name='CoverComment', fields=[ @@ -136,13 +134,12 @@ class Migration(migrations.Migration): ), ), migrations.AlterUniqueTogether( - name='covercomment', unique_together=set([('msgid', 'cover')]), + name='covercomment', + unique_together=set([('msgid', 'cover')]), ), - # copy all entries from the 'CoverLetter' model to the new 'Cover' # model; note that it's not possible to reverse this since we can't # guarantee IDs will be unique after the split - migrations.RunSQL( """ INSERT INTO patchwork_cover @@ -155,10 +152,8 @@ class Migration(migrations.Migration): """, None, ), - # copy all 'CoverLetter'-related comments to the new 'CoverComment' # table; as above, this is non-reversible - migrations.RunSQL( """ INSERT INTO patchwork_covercomment @@ -171,10 +166,8 @@ class Migration(migrations.Migration): """, None, ), - # update all references to the 'CoverLetter' model to point to the new # 'Cover' model instead - migrations.AlterField( model_name='event', name='cover', @@ -194,26 +187,20 @@ class Migration(migrations.Migration): null=True, on_delete=django.db.models.deletion.CASCADE, related_name='series', - to='patchwork.Cover' + to='patchwork.Cover', ), ), - # remove all the old 'CoverLetter'-related entries from the 'Comment' # table - migrations.RunPython(delete_coverletter_comments, None, atomic=False), - # delete the old 'CoverLetter' model - migrations.DeleteModel( name='CoverLetter', ), - # rename the 'Comment.submission' field to 'Comment.patch'; note our # use of 'AlterField' before and after to work around bug #31335 # # https://code.djangoproject.com/ticket/31335 - migrations.AlterField( model_name='comment', name='submission', @@ -255,9 +242,7 @@ class Migration(migrations.Migration): to='patchwork.Submission', ), ), - # rename the 'Comment' model to 'PatchComment' - migrations.RenameModel( old_name='Comment', new_name='PatchComment', diff --git a/patchwork/migrations/0043_merge_patch_submission.py b/patchwork/migrations/0043_merge_patch_submission.py index d351892e..23a4dbfb 100644 --- a/patchwork/migrations/0043_merge_patch_submission.py +++ b/patchwork/migrations/0043_merge_patch_submission.py @@ -83,8 +83,10 @@ class Migration(migrations.Migration): operations = [ # move the 'PatchTag' model to point to 'Submission' - - migrations.RemoveField(model_name='patch', name='tags',), + migrations.RemoveField( + model_name='patch', + name='tags', + ), migrations.AddField( model_name='submission', name='tags', @@ -100,9 +102,7 @@ class Migration(migrations.Migration): to='patchwork.Submission', ), ), - # do the same for any other field that references 'Patch' - migrations.AlterField( model_name='bundle', name='patches', @@ -148,47 +148,67 @@ class Migration(migrations.Migration): to='patchwork.Submission', ), ), - # rename all the fields on 'Patch' so we don't have duplicates when we # add them to 'Submission' - migrations.RemoveIndex( - model_name='patch', name='patch_list_covering_idx', + model_name='patch', + name='patch_list_covering_idx', + ), + migrations.AlterUniqueTogether( + name='patch', + unique_together=set([]), ), - migrations.AlterUniqueTogether(name='patch', unique_together=set([]),), migrations.RenameField( - model_name='patch', old_name='archived', new_name='archived2', + model_name='patch', + old_name='archived', + new_name='archived2', ), migrations.RenameField( - model_name='patch', old_name='commit_ref', new_name='commit_ref2', + model_name='patch', + old_name='commit_ref', + new_name='commit_ref2', ), migrations.RenameField( - model_name='patch', old_name='delegate', new_name='delegate2', + model_name='patch', + old_name='delegate', + new_name='delegate2', ), migrations.RenameField( - model_name='patch', old_name='diff', new_name='diff2', + model_name='patch', + old_name='diff', + new_name='diff2', ), migrations.RenameField( - model_name='patch', old_name='hash', new_name='hash2', + model_name='patch', + old_name='hash', + new_name='hash2', ), migrations.RenameField( - model_name='patch', old_name='number', new_name='number2', + model_name='patch', + old_name='number', + new_name='number2', ), migrations.RenameField( - model_name='patch', old_name='pull_url', new_name='pull_url2', + model_name='patch', + old_name='pull_url', + new_name='pull_url2', ), migrations.RenameField( - model_name='patch', old_name='related', new_name='related2', + model_name='patch', + old_name='related', + new_name='related2', ), migrations.RenameField( - model_name='patch', old_name='series', new_name='series2', + model_name='patch', + old_name='series', + new_name='series2', ), migrations.RenameField( - model_name='patch', old_name='state', new_name='state2', + model_name='patch', + old_name='state', + new_name='state2', ), - # add the fields found on 'Patch' to 'Submission' - migrations.AddField( model_name='submission', name='archived', @@ -268,13 +288,9 @@ class Migration(migrations.Migration): to='patchwork.State', ), ), - # copy the data from 'Patch' to 'Submission' - migrations.RunPython(migrate_data, None, atomic=False), - # configure metadata for the 'Submission' model - migrations.AlterModelOptions( name='submission', options={ @@ -288,7 +304,8 @@ class Migration(migrations.Migration): unique_together=set([('series', 'number'), ('msgid', 'project')]), ), migrations.RemoveIndex( - model_name='submission', name='submission_covering_idx', + model_name='submission', + name='submission_covering_idx', ), migrations.AddIndex( model_name='submission', @@ -305,18 +322,37 @@ class Migration(migrations.Migration): name='patch_covering_idx', ), ), - # remove the foreign key fields from the 'Patch' model - - migrations.RemoveField(model_name='patch', name='delegate2',), - migrations.RemoveField(model_name='patch', name='patch_project',), - migrations.RemoveField(model_name='patch', name='related2',), - migrations.RemoveField(model_name='patch', name='series2',), - migrations.RemoveField(model_name='patch', name='state2',), - migrations.RemoveField(model_name='patch', name='submission_ptr',), - + migrations.RemoveField( + model_name='patch', + name='delegate2', + ), + migrations.RemoveField( + model_name='patch', + name='patch_project', + ), + migrations.RemoveField( + model_name='patch', + name='related2', + ), + migrations.RemoveField( + model_name='patch', + name='series2', + ), + migrations.RemoveField( + model_name='patch', + name='state2', + ), + migrations.RemoveField( + model_name='patch', + name='submission_ptr', + ), # drop the 'Patch' model and rename 'Submission' to 'Patch' - - migrations.DeleteModel(name='Patch',), - migrations.RenameModel(old_name='Submission', new_name='Patch',), + migrations.DeleteModel( + name='Patch', + ), + migrations.RenameModel( + old_name='Submission', + new_name='Patch', + ), ] diff --git a/patchwork/migrations/0044_add_project_linkname_validation.py b/patchwork/migrations/0044_add_project_linkname_validation.py index 9319c818..d429bd56 100644 --- a/patchwork/migrations/0044_add_project_linkname_validation.py +++ b/patchwork/migrations/0044_add_project_linkname_validation.py @@ -21,10 +21,11 @@ class Migration(migrations.Migration): validators=[ django.core.validators.RegexValidator( re.compile('^[-\\w]+\\Z'), - 'Enter a valid “slug” consisting of Unicode ' + - 'letters, numbers, underscores, or hyphens.', - 'invalid') - ] + 'Enter a valid “slug” consisting of Unicode ' + + 'letters, numbers, underscores, or hyphens.', + 'invalid', + ) + ], ), ), ] diff --git a/patchwork/models.py b/patchwork/models.py index e4a910ed..264af532 100644 --- a/patchwork/models.py +++ b/patchwork/models.py @@ -37,8 +37,9 @@ class Person(models.Model): email = models.CharField(max_length=255, unique=True) name = models.CharField(max_length=255, null=True, blank=True) - user = models.ForeignKey(User, null=True, blank=True, - on_delete=models.SET_NULL) + user = models.ForeignKey( + User, null=True, blank=True, on_delete=models.SET_NULL + ) def link_to_user(self, user): self.name = user.profile.name @@ -57,19 +58,24 @@ class Meta: class Project(models.Model): # properties - linkname = models.CharField(max_length=255, unique=True, - validators=[validate_unicode_slug]) + linkname = models.CharField( + max_length=255, unique=True, validators=[validate_unicode_slug] + ) name = models.CharField(max_length=255, unique=True) listid = models.CharField(max_length=255) listemail = models.CharField(max_length=200) subject_match = models.CharField( - max_length=64, blank=True, default='', - validators=[validate_regex_compiles], help_text='Regex to match the ' + max_length=64, + blank=True, + default='', + validators=[validate_regex_compiles], + help_text='Regex to match the ' 'subject against if only part of emails sent to the list belongs to ' 'this project. Will be used with IGNORECASE and MULTILINE flags. If ' 'rules for more projects match the first one returned from DB is ' 'chosen; empty field serves as a default for every email which has no ' - 'other match.') + 'other match.', + ) # url metadata @@ -81,12 +87,14 @@ class Project(models.Model): max_length=2000, blank=True, help_text="URL format for the list archive's Message-ID redirector. " - "{} will be replaced by the Message-ID.") + "{} will be replaced by the Message-ID.", + ) commit_url_format = models.CharField( max_length=2000, blank=True, help_text='URL format for a particular commit. ' - '{} will be replaced by the commit SHA.') + '{} will be replaced by the commit SHA.', + ) # configuration options @@ -117,44 +125,54 @@ class DelegationRule(models.Model): user = models.ForeignKey( User, on_delete=models.CASCADE, - help_text='A user to delegate the patch to.') + help_text='A user to delegate the patch to.', + ) path = models.CharField( max_length=255, - help_text='An fnmatch-style pattern to match filenames against.') + help_text='An fnmatch-style pattern to match filenames against.', + ) priority = models.IntegerField( default=0, help_text='The priority of the rule. Rules with a higher priority ' - 'will override rules with lower priorities') + 'will override rules with lower priorities', + ) def __str__(self): return self.path class Meta: ordering = ['-priority', 'path'] - unique_together = (('path', 'project')) + unique_together = ('path', 'project') class UserProfile(models.Model): - user = models.OneToOneField(User, unique=True, related_name='profile', - on_delete=models.CASCADE) + user = models.OneToOneField( + User, unique=True, related_name='profile', on_delete=models.CASCADE + ) # projects maintainer_projects = models.ManyToManyField( - Project, related_name='maintainer_project', blank=True) + Project, related_name='maintainer_project', blank=True + ) # configuration options send_email = models.BooleanField( default=False, help_text='Selecting this option allows patchwork to send email on' - ' your behalf') + ' your behalf', + ) items_per_page = models.PositiveIntegerField( - default=100, null=False, blank=False, - help_text='Number of items to display per page') + default=100, + null=False, + blank=False, + help_text='Number of items to display per page', + ) show_ids = models.BooleanField( default=False, - help_text='Show click-to-copy patch IDs in the list view') + help_text='Show click-to-copy patch IDs in the list view', + ) @property def name(self): @@ -166,8 +184,11 @@ def name(self): @property def contributor_projects(self): submitters = Person.objects.filter(user=self.user) - return Project.objects.filter(id__in=Patch.objects.filter( - submitter__in=submitters).values('project_id').query) + return Project.objects.filter( + id__in=Patch.objects.filter(submitter__in=submitters) + .values('project_id') + .query + ) @property def n_todo_patches(self): @@ -190,9 +211,15 @@ def todo_patches(self, project=None): else: qs = Patch.objects - qs = qs.filter(archived=False).filter( - delegate=self.user).filter(state__in=State.objects.filter( - action_required=True).values('pk').query) + qs = ( + qs.filter(archived=False) + .filter(delegate=self.user) + .filter( + state__in=State.objects.filter(action_required=True) + .values('pk') + .query + ) + ) return qs def __str__(self): @@ -228,16 +255,23 @@ class Meta: class Tag(models.Model): name = models.CharField(max_length=20) pattern = models.CharField( - max_length=50, validators=[validate_regex_compiles], + max_length=50, + validators=[validate_regex_compiles], help_text='A simple regex to match the tag in the content of a ' 'message. Will be used with MULTILINE and IGNORECASE flags. eg. ' - '^Acked-by:') + '^Acked-by:', + ) abbrev = models.CharField( - max_length=2, unique=True, help_text='Short (one-or-two letter)' - ' abbreviation for the tag, used in table column headers') - show_column = models.BooleanField(help_text='Show a column displaying this' - ' tag\'s count in the patch list view', - default=True) + max_length=2, + unique=True, + help_text='Short (one-or-two letter)' + ' abbreviation for the tag, used in table column headers', + ) + show_column = models.BooleanField( + help_text='Show a column displaying this' + ' tag\'s count in the patch list view', + default=True, + ) @property def attr_name(self): @@ -264,7 +298,6 @@ def get_default_initial_patch_state(): class PatchQuerySet(models.query.QuerySet): - def with_tag_counts(self, project=None): if project and not project.use_tags: return self @@ -288,14 +321,14 @@ def with_tag_counts(self, project=None): "coalesce(" "(SELECT count FROM patchwork_patchtag" " WHERE patchwork_patchtag.patch_id=patchwork_patch.id" - " AND patchwork_patchtag.tag_id=%s), 0)") + " AND patchwork_patchtag.tag_id=%s), 0)" + ) select_params.append(tag.id) return qs.extra(select=select, select_params=select_params) class PatchManager(models.Manager): - def get_queryset(self): return PatchQuerySet(self.model, using=self.db) @@ -305,6 +338,7 @@ def with_tag_counts(self, project): class EmailMixin(models.Model): """Mixin for models with an email-origin.""" + # email metadata msgid = models.CharField(max_length=255) @@ -318,15 +352,20 @@ class EmailMixin(models.Model): response_re = re.compile( r'^(Tested|Reviewed|Acked|Signed-off|Nacked|Reported)-by:.*$', - re.M | re.I) + re.M | re.I, + ) @property def patch_responses(self): if not self.content: return '' - return ''.join([match.group(0) + '\n' for match in - self.response_re.finditer(self.content)]) + return ''.join( + [ + match.group(0) + '\n' + for match in self.response_re.finditer(self.content) + ] + ) @property def url_msgid(self): @@ -350,7 +389,6 @@ class Meta: class FilenameMixin(object): - @property def filename(self): """Return a sanitized filename without extension.""" @@ -393,16 +431,23 @@ class Meta: class Cover(SubmissionMixin): - def get_absolute_url(self): - return reverse('cover-detail', - kwargs={'project_id': self.project.linkname, - 'msgid': self.url_msgid}) + return reverse( + 'cover-detail', + kwargs={ + 'project_id': self.project.linkname, + 'msgid': self.url_msgid, + }, + ) def get_mbox_url(self): - return reverse('cover-mbox', - kwargs={'project_id': self.project.linkname, - 'msgid': self.url_msgid}) + return reverse( + 'cover-mbox', + kwargs={ + 'project_id': self.project.linkname, + 'msgid': self.url_msgid, + }, + ) class Meta: ordering = ['date'] @@ -457,8 +502,13 @@ class Patch(SubmissionMixin): # related patches metadata related = models.ForeignKey( - 'PatchRelation', null=True, blank=True, on_delete=models.SET_NULL, - related_name='patches', related_query_name='patch') + 'PatchRelation', + null=True, + blank=True, + on_delete=models.SET_NULL, + related_name='patches', + related_query_name='patch', + ) objects = PatchManager() @@ -589,7 +639,9 @@ def combined_check_state(self): # order sensitive for state in ( - Check.STATE_FAIL, Check.STATE_WARNING, Check.STATE_PENDING, + Check.STATE_FAIL, + Check.STATE_WARNING, + Check.STATE_PENDING, ): if state in states: return state_names[state] @@ -615,14 +667,22 @@ def check_count(self): return counts def get_absolute_url(self): - return reverse('patch-detail', - kwargs={'project_id': self.project.linkname, - 'msgid': self.url_msgid}) + return reverse( + 'patch-detail', + kwargs={ + 'project_id': self.project.linkname, + 'msgid': self.url_msgid, + }, + ) def get_mbox_url(self): - return reverse('patch-mbox', - kwargs={'project_id': self.project.linkname, - 'msgid': self.url_msgid}) + return reverse( + 'patch-mbox', + kwargs={ + 'project_id': self.project.linkname, + 'msgid': self.url_msgid, + }, + ) def __str__(self): return self.name @@ -750,28 +810,38 @@ class Series(FilenameMixin, models.Model): """A collection of patches.""" # parent - project = models.ForeignKey(Project, related_name='series', null=True, - blank=True, on_delete=models.CASCADE) + project = models.ForeignKey( + Project, + related_name='series', + null=True, + blank=True, + on_delete=models.CASCADE, + ) # content cover_letter = models.OneToOneField( - Cover, - related_name='series', - null=True, - on_delete=models.CASCADE + Cover, related_name='series', null=True, on_delete=models.CASCADE ) # metadata - name = models.CharField(max_length=255, blank=True, null=True, - help_text='An optional name to associate with ' - 'the series, e.g. "John\'s PCI series".') + name = models.CharField( + max_length=255, + blank=True, + null=True, + help_text='An optional name to associate with ' + 'the series, e.g. "John\'s PCI series".', + ) date = models.DateTimeField() submitter = models.ForeignKey(Person, on_delete=models.CASCADE) - version = models.IntegerField(default=1, - help_text='Version of series as indicated ' - 'by the subject prefix(es)') - total = models.IntegerField(help_text='Number of patches in series as ' - 'indicated by the subject prefix(es)') + version = models.IntegerField( + default=1, + help_text='Version of series as indicated ' + 'by the subject prefix(es)', + ) + total = models.IntegerField( + help_text='Number of patches in series as ' + 'indicated by the subject prefix(es)' + ) @staticmethod def _format_name(obj): @@ -849,9 +919,9 @@ def add_patch(self, patch, number): def get_absolute_url(self): # TODO(stephenfin): We really need a proper series view - return reverse('patch-list', - kwargs={'project_id': self.project.linkname}) + ( - '?series=%d' % self.id) + return reverse( + 'patch-list', kwargs={'project_id': self.project.linkname} + ) + ('?series=%d' % self.id) def get_mbox_url(self): return reverse('series-mbox', kwargs={'series_id': self.id}) @@ -871,10 +941,14 @@ class SeriesReference(models.Model): required to handle the case whereby one or more patches are received before the cover letter. """ + project = models.ForeignKey(Project, on_delete=models.CASCADE) - series = models.ForeignKey(Series, related_name='references', - related_query_name='reference', - on_delete=models.CASCADE) + series = models.ForeignKey( + Series, + related_name='references', + related_query_name='reference', + on_delete=models.CASCADE, + ) msgid = models.CharField(max_length=255) def __str__(self): @@ -885,9 +959,12 @@ class Meta: class Bundle(models.Model): - owner = models.ForeignKey(User, on_delete=models.CASCADE, - related_name='bundles', - related_query_name='bundle') + owner = models.ForeignKey( + User, + on_delete=models.CASCADE, + related_name='bundles', + related_query_name='bundle', + ) project = models.ForeignKey(Project, on_delete=models.CASCADE) name = models.CharField(max_length=50, null=False, blank=False) patches = models.ManyToManyField(Patch, through='BundlePatch') @@ -903,7 +980,8 @@ def ordered_patches(self): def append_patch(self, patch): orders = BundlePatch.objects.filter(bundle=self).aggregate( - models.Max('order')) + models.Max('order') + ) if orders and orders['order__max']: max_order = orders['order__max'] @@ -913,8 +991,9 @@ def append_patch(self, patch): if BundlePatch.objects.filter(bundle=self, patch=patch).exists(): return - return BundlePatch.objects.create(bundle=self, patch=patch, - order=max_order + 1) + return BundlePatch.objects.create( + bundle=self, patch=patch, order=max_order + 1 + ) def overwrite_patches(self, patches): BundlePatch.objects.filter(bundle=self).delete() @@ -923,16 +1002,19 @@ def overwrite_patches(self, patches): self.append_patch(patch) def get_absolute_url(self): - return reverse('bundle-detail', kwargs={ - 'username': self.owner.username, - 'bundlename': self.name, - }) + return reverse( + 'bundle-detail', + kwargs={ + 'username': self.owner.username, + 'bundlename': self.name, + }, + ) def get_mbox_url(self): - return reverse('bundle-mbox', kwargs={ - 'bundlename': self.name, - 'username': self.owner.username - }) + return reverse( + 'bundle-mbox', + kwargs={'bundlename': self.name, 'username': self.owner.username}, + ) class Meta: unique_together = [('owner', 'name')] @@ -949,7 +1031,6 @@ class Meta: class PatchRelation(models.Model): - def __str__(self): patches = self.patches.all() if not patches: @@ -968,6 +1049,7 @@ class Check(models.Model): given patch. This is useful, for example, when using a continuous integration (CI) system to test patches. """ + STATE_PENDING = 0 STATE_SUCCESS = 1 STATE_WARNING = 2 @@ -984,22 +1066,32 @@ class Check(models.Model): date = models.DateTimeField(default=datetime.datetime.utcnow) state = models.SmallIntegerField( - choices=STATE_CHOICES, default=STATE_PENDING, - help_text='The state of the check.') + choices=STATE_CHOICES, + default=STATE_PENDING, + help_text='The state of the check.', + ) target_url = models.URLField( - blank=True, null=True, + blank=True, + null=True, help_text='The target URL to associate with this check. This should ' - 'be specific to the patch.') + 'be specific to the patch.', + ) description = models.TextField( - blank=True, null=True, help_text='A brief description of the check.') + blank=True, null=True, help_text='A brief description of the check.' + ) context = models.SlugField( - max_length=255, default='default', + max_length=255, + default='default', help_text='A label to discern check from checks of other testing ' - 'systems.') + 'systems.', + ) def __repr__(self): return "= pages - TRAILING_PAGE_RANGE: adjacent_start = pages - TRAILING_PAGE_RANGE_DISPLAYED + 1 adjacent_end = pages + 1 - self.trailing_set = [n + 1 for n in - range(0, NUM_PAGES_OUTSIDE_RANGE)] + self.trailing_set = [ + n + 1 for n in range(0, NUM_PAGES_OUTSIDE_RANGE) + ] else: adjacent_start = page_no - ADJACENT_PAGES adjacent_end = page_no + ADJACENT_PAGES + 1 - self.leading_set = [n + pages for n in - range(0, -NUM_PAGES_OUTSIDE_RANGE, -1)] - self.trailing_set = [n + 1 for n in - range(0, NUM_PAGES_OUTSIDE_RANGE)] - - self.adjacent_set = [n for n in range(adjacent_start, adjacent_end) - if n > 0 and n <= pages] + self.leading_set = [ + n + pages for n in range(0, -NUM_PAGES_OUTSIDE_RANGE, -1) + ] + self.trailing_set = [ + n + 1 for n in range(0, NUM_PAGES_OUTSIDE_RANGE) + ] + + self.adjacent_set = [ + n + for n in range(adjacent_start, adjacent_end) + if n > 0 and n <= pages + ] self.leading_set.reverse() - self.long_page = len( - self.current_page.object_list) >= LONG_PAGE_THRESHOLD + self.long_page = ( + len(self.current_page.object_list) >= LONG_PAGE_THRESHOLD + ) diff --git a/patchwork/parser.py b/patchwork/parser.py index e6e1a7fb..026da4ec 100644 --- a/patchwork/parser.py +++ b/patchwork/parser.py @@ -44,18 +44,24 @@ # @see https://git-scm.com/docs/git-diff#_generating_patches_with_p EXTENDED_HEADER_LINES = ( - 'old mode ', 'new mode ', - 'deleted file mode ', 'new file mode ', - 'copy from ', 'copy to ', - 'rename from ', 'rename to ', - 'similarity index ', 'dissimilarity index ', - 'new file mode ', 'index ') + 'old mode ', + 'new mode ', + 'deleted file mode ', + 'new file mode ', + 'copy from ', + 'copy to ', + 'rename from ', + 'rename to ', + 'similarity index ', + 'dissimilarity index ', + 'new file mode ', + 'index ', +) logger = logging.getLogger(__name__) class DuplicateMailError(Exception): - def __init__(self, msgid): self.msgid = msgid @@ -98,9 +104,9 @@ def sanitise_header(header_contents, header_name=None): # handling any interesting headers. try: - header = make_header(value, - header_name=header_name, - continuation_ws='\t') + header = make_header( + value, header_name=header_name, continuation_ws='\t' + ) except (UnicodeDecodeError, LookupError, ValueError): # - a part cannot be encoded as ascii. (UnicodeDecodeError), or # - we don't have a codec matching the hint (LookupError) @@ -118,9 +124,9 @@ def sanitise_header(header_contents, header_name=None): # python3 - force coding to unknown-8bit new_value += [(part, 'unknown-8bit')] - header = make_header(new_value, - header_name=header_name, - continuation_ws='\t') + header = make_header( + new_value, header_name=header_name, continuation_ws='\t' + ) try: header.encode() @@ -157,8 +163,9 @@ def find_project_by_id_and_subject(list_id, subject): for project in projects: if not project.subject_match: default = project - elif re.search(project.subject_match, subject, - re.MULTILINE | re.IGNORECASE): + elif re.search( + project.subject_match, subject, re.MULTILINE | re.IGNORECASE + ): return project return default @@ -171,8 +178,10 @@ def find_project(mail, list_id=None): return find_project_by_id_and_subject(list_id, clean_subject) project = None - listid_res = [re.compile(r'.*<([^>]+)>.*', re.S), - re.compile(r'^([\S]+)$', re.S)] + listid_res = [ + re.compile(r'.*<([^>]+)>.*', re.S), + re.compile(r'^([\S]+)$', re.S), + ] for header in list_id_headers: if header in mail: @@ -195,8 +204,9 @@ def find_project(mail, list_id=None): break if not project: - logger.debug("Could not find a valid project for given list-id and " - "subject.") + logger.debug( + "Could not find a valid project for given list-id and " "subject." + ) return project @@ -243,7 +253,8 @@ def _find_series_by_references(project, mail): for ref in refs: try: series = SeriesReference.objects.get( - msgid=ref[:255], project=project).series + msgid=ref[:255], project=project + ).series if series.version != version: # if the versions don't match, at least make sure these were @@ -296,8 +307,12 @@ def _find_series_by_markers(project, mail, author): end_date = date + delta return Series.objects.filter( - submitter=author, project=project, version=version, total=total, - date__range=[start_date, end_date]) + submitter=author, + project=project, + version=version, + total=total, + date__range=[start_date, end_date], + ) def find_series(project, mail, author): @@ -329,14 +344,13 @@ def split_from_header(from_header): from_res = [ # for "Firstname Lastname" style addresses (re.compile(r'"?(.*?)"?\s*<([^>]+)>'), (lambda g: (g[0], g[1]))), - # for example at example.com (Firstname Lastname) style addresses - (re.compile(r'(.*?)\sat\s(.*?)\s*\(([^\)]+)\)'), - (lambda g: (g[2], '@'.join(g[0:2])))), - + ( + re.compile(r'(.*?)\sat\s(.*?)\s*\(([^\)]+)\)'), + (lambda g: (g[2], '@'.join(g[0:2]))), + ), # for example@example.com (Firstname Lastname) style addresses (re.compile(r'"?(.*?)"?\s*\(([^\)]+)\)'), (lambda g: (g[1], g[0]))), - # everything else (re.compile(r'(.*)'), (lambda g: (None, g[0]))), ] @@ -372,11 +386,11 @@ def get_original_sender(mail, name, email): if name and ' via ' in name: # Mailman uses the format " via " # Google Groups uses "'' via " - stripped_name = name[:name.rfind(' via ')].strip().strip("'") + stripped_name = name[: name.rfind(' via ')].strip().strip("'") elif name.endswith(' via'): # Sometimes this seems to happen (perhaps if Mailman isn't set up with # any list name) - stripped_name = name[:name.rfind(' via')].strip().strip("'") + stripped_name = name[: name.rfind(' via')].strip().strip("'") else: # We've hit a format that we don't expect stripped_name = None @@ -427,9 +441,9 @@ def get_or_create_author(mail, project=None): # the person and another process beats us to it. (If the record # does not exist, g_o_c invokes _create_object_from_params which # catches the IntegrityError and repeats the SELECT.) - person = Person.objects.get_or_create(email__iexact=email, - defaults={'name': name, - 'email': email})[0] + person = Person.objects.get_or_create( + email__iexact=email, defaults={'name': name, 'email': email} + )[0] if name and name != person.name: # use the latest provided name person.name = name @@ -463,11 +477,16 @@ def find_date(mail): def find_headers(mail): - headers = [(key, sanitise_header(value, header_name=key)) - for key, value in mail.items()] + headers = [ + (key, sanitise_header(value, header_name=key)) + for key, value in mail.items() + ] - strings = [('%s: %s' % (key, header.encode())) - for (key, header) in headers if header is not None] + strings = [ + ('%s: %s' % (key, header.encode())) + for (key, header) in headers + if header is not None + ] return '\n'.join(strings) @@ -771,8 +790,11 @@ def clean_subject(subject, drop_prefixes=None): while match: prefix_str = match.group(1) - prefixes += [p for p in split_prefixes(prefix_str) - if p.lower() not in drop_prefixes] + prefixes += [ + p + for p in split_prefixes(prefix_str) + if p.lower() not in drop_prefixes + ] subject = match.group(2) match = prefix_re.match(subject) @@ -860,8 +882,7 @@ def parse_patch(content): line += '\n' if state == 0: - if line.startswith('diff ') \ - or line.startswith('Index: '): + if line.startswith('diff ') or line.startswith('Index: '): state = 1 buf += line elif line.startswith('--- '): @@ -889,6 +910,7 @@ def parse_patch(content): elif state == 3: match = _hunk_re.match(line) if match: + def fn(x): if not x: return 1 @@ -963,7 +985,8 @@ def parse_pull_request(content): r'^The following changes since commit.*' r'^are available in the git repository at:\s*\n' r'^\s*([\w+-]+(?:://|@)[\w/.@:~-]+[\s\\]*[\w/._-]*)\s*$', - re.DOTALL | re.MULTILINE | re.IGNORECASE) + re.DOTALL | re.MULTILINE | re.IGNORECASE, + ) match = git_re.search(content) if match: return re.sub(r'\s+', ' ', match.group(1)).strip() @@ -1117,7 +1140,8 @@ def parse_mail(mail, list_id=None): diff=diff, pull_url=pull_url, delegate=delegate, - state=find_state(mail)) + state=find_state(mail), + ) logger.debug('Patch saved') for attempt in range(1, 11): # arbitrary retry count @@ -1138,7 +1162,8 @@ def parse_mail(mail, list_id=None): # another duplicate - find the best possible match for series in series.order_by('-date'): if Patch.objects.filter( - series=series, number=x).count(): + series=series, number=x + ).count(): continue break else: @@ -1152,14 +1177,19 @@ def parse_mail(mail, list_id=None): # - there is no existing series to assign this patch to, or # - there is an existing series, but it already has a patch # with this number in it - if not series or Patch.objects.filter( - series=series, number=x).count(): + if ( + not series + or Patch.objects.filter( + series=series, number=x + ).count() + ): series = Series.objects.create( project=project, date=date, submitter=author, version=version, - total=n) + total=n, + ) # NOTE(stephenfin) We must save references for series. # We do this to handle the case where a later patch is @@ -1177,32 +1207,40 @@ def parse_mail(mail, list_id=None): # series ref for this series, so check for the # msg-id only, not the msg-id/series pair. SeriesReference.objects.get( - msgid=ref, project=project) + msgid=ref, project=project + ) except SeriesReference.DoesNotExist: SeriesReference.objects.create( - msgid=ref, project=project, series=series) + msgid=ref, project=project, series=series + ) # attempt to pull the series in again, raising an # exception if we lost the race when creating a series # and force us to go through this again - if attempt != 10 and find_series( - project, mail, author).count() > 1: + if ( + attempt != 10 + and find_series(project, mail, author).count() > 1 + ): raise DuplicateSeriesError() break except (IntegrityError, DuplicateSeriesError): # we lost the race so go again - logger.warning('Conflict while saving series. This is ' - 'probably because multiple patches belonging ' - 'to the same series have been received at ' - 'once. Trying again (attempt %02d/10)', - attempt) + logger.warning( + 'Conflict while saving series. This is ' + 'probably because multiple patches belonging ' + 'to the same series have been received at ' + 'once. Trying again (attempt %02d/10)', + attempt, + ) else: # we failed to save the series so return the series-less patch - logger.warning('Failed to save series. Your patch with message ID ' - '%s has been saved but this should not happen. ' - 'Please report this as a bug and include logs', - msgid) + logger.warning( + 'Failed to save series. Your patch with message ID ' + '%s has been saved but this should not happen. ' + 'Please report this as a bug and include logs', + msgid, + ) return patch # add to a series if we have found one, and we have a numbered @@ -1241,7 +1279,8 @@ def parse_mail(mail, list_id=None): # message try: series = SeriesReference.objects.get( - msgid=msgid, project=project).series + msgid=msgid, project=project + ).series except SeriesReference.DoesNotExist: series = None @@ -1251,13 +1290,15 @@ def parse_mail(mail, list_id=None): date=date, submitter=author, version=version, - total=n) + total=n, + ) # we don't save the in-reply-to or references fields # for a cover letter, as they can't refer to the same # series SeriesReference.objects.create( - msgid=msgid, project=project, series=series) + msgid=msgid, project=project, series=series + ) with transaction.atomic(): if Cover.objects.filter(project=project, msgid=msgid): @@ -1270,7 +1311,8 @@ def parse_mail(mail, list_id=None): date=date, headers=headers, submitter=author, - content=message) + content=message, + ) logger.debug('Cover letter saved') @@ -1296,7 +1338,8 @@ def parse_mail(mail, list_id=None): headers=headers, submitter=author, content=message, - addressed=addressed) + addressed=addressed, + ) logger.debug('Comment saved') @@ -1317,7 +1360,8 @@ def parse_mail(mail, list_id=None): date=date, headers=headers, submitter=author, - content=message) + content=message, + ) logger.debug('Comment saved') diff --git a/patchwork/settings/base.py b/patchwork/settings/base.py index 1293bc2d..045f262f 100644 --- a/patchwork/settings/base.py +++ b/patchwork/settings/base.py @@ -4,8 +4,9 @@ import os -ROOT_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), - os.pardir, os.pardir) +ROOT_DIR = os.path.join( + os.path.dirname(os.path.abspath(__file__)), os.pardir, os.pardir +) # # Core settings @@ -167,8 +168,7 @@ REST_FRAMEWORK = { - 'DEFAULT_VERSIONING_CLASS': - 'rest_framework.versioning.URLPathVersioning', + 'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.URLPathVersioning', 'DEFAULT_PAGINATION_CLASS': 'patchwork.api.base.LinkHeaderPagination', 'DEFAULT_FILTER_BACKENDS': ( 'patchwork.api.filters.DjangoFilterBackend', diff --git a/patchwork/settings/dev.py b/patchwork/settings/dev.py index cb4cb19f..75c4b349 100644 --- a/patchwork/settings/dev.py +++ b/patchwork/settings/dev.py @@ -24,9 +24,7 @@ # https://docs.djangoproject.com/en/2.2/ref/settings/#core-settings # -ADMINS = ( - ('Joe Bloggs', 'jbloggs@example.com'), -) +ADMINS = (('Joe Bloggs', 'jbloggs@example.com'),) ALLOWED_HOSTS = ['*'] @@ -55,21 +53,16 @@ # django-debug-toolbar if debug_toolbar: - INSTALLED_APPS += [ # noqa: F405 - 'debug_toolbar' - ] + INSTALLED_APPS += ['debug_toolbar'] # noqa: F405 DEBUG_TOOLBAR_PATCH_SETTINGS = False -# This should go first in the middleware classes + # This should go first in the middleware classes MIDDLEWARE = [ 'debug_toolbar.middleware.DebugToolbarMiddleware', ] + MIDDLEWARE # noqa: F405 - INTERNAL_IPS = [ - '127.0.0.1', '::1', - '172.18.0.1' - ] + INTERNAL_IPS = ['127.0.0.1', '::1', '172.18.0.1'] # django-dbbackup diff --git a/patchwork/settings/production.example.py b/patchwork/settings/production.example.py index ff056294..8955a204 100644 --- a/patchwork/settings/production.example.py +++ b/patchwork/settings/production.example.py @@ -57,4 +57,5 @@ STATIC_ROOT = os.environ.get('STATIC_ROOT', '/srv/patchwork/htdocs/static') STATICFILES_STORAGE = ( - 'django.contrib.staticfiles.storage.ManifestStaticFilesStorage') + 'django.contrib.staticfiles.storage.ManifestStaticFilesStorage' +) diff --git a/patchwork/signals.py b/patchwork/signals.py index d1aac8cf..537abdfc 100644 --- a/patchwork/signals.py +++ b/patchwork/signals.py @@ -45,8 +45,9 @@ def patch_change_callback(sender, instance, raw, **kwargs): pass if notification is None: - notification = PatchChangeNotification(patch=instance, - orig_state=orig_patch.state) + notification = PatchChangeNotification( + patch=instance, orig_state=orig_patch.state + ) elif notification.orig_state == instance.state: # If we're back at the original state, there is no need to notify notification.delete() @@ -58,12 +59,12 @@ def patch_change_callback(sender, instance, raw, **kwargs): @receiver(post_save, sender=Cover) def create_cover_created_event(sender, instance, created, raw, **kwargs): - def create_event(cover): return Event.objects.create( category=Event.CATEGORY_COVER_CREATED, project=cover.project, - cover=cover) + cover=cover, + ) # don't trigger for items loaded from fixtures or new items if raw or not created: @@ -74,12 +75,12 @@ def create_event(cover): @receiver(post_save, sender=Patch) def create_patch_created_event(sender, instance, created, raw, **kwargs): - def create_event(patch): return Event.objects.create( category=Event.CATEGORY_PATCH_CREATED, project=patch.project, - patch=patch) + patch=patch, + ) # don't trigger for items loaded from fixtures or new items if raw or not created: @@ -90,7 +91,6 @@ def create_event(patch): @receiver(pre_save, sender=Patch) def create_patch_state_changed_event(sender, instance, raw, **kwargs): - def create_event(patch, before, after): return Event.objects.create( category=Event.CATEGORY_PATCH_STATE_CHANGED, @@ -98,7 +98,8 @@ def create_event(patch, before, after): actor=getattr(patch, '_edited_by', None), patch=patch, previous_state=before, - current_state=after) + current_state=after, + ) # don't trigger for items loaded from fixtures or new items if raw or not instance.pk: @@ -114,7 +115,6 @@ def create_event(patch, before, after): @receiver(pre_save, sender=Patch) def create_patch_delegated_event(sender, instance, raw, **kwargs): - def create_event(patch, before, after): return Event.objects.create( category=Event.CATEGORY_PATCH_DELEGATED, @@ -122,7 +122,8 @@ def create_event(patch, before, after): actor=getattr(patch, '_edited_by', None), patch=patch, previous_delegate=before, - current_delegate=after) + current_delegate=after, + ) # don't trigger for items loaded from fixtures or new items if raw or not instance.pk: @@ -138,13 +139,13 @@ def create_event(patch, before, after): @receiver(pre_save, sender=Patch) def create_patch_relation_changed_event(sender, instance, raw, **kwargs): - def create_event(patch): return Event.objects.create( category=Event.CATEGORY_PATCH_RELATION_CHANGED, project=patch.project, actor=getattr(patch, '_edited_by', None), - patch=patch) + patch=patch, + ) # don't trigger for items loaded from fixtures or new items if raw or not instance.pk: @@ -160,13 +161,13 @@ def create_event(patch): @receiver(pre_save, sender=Patch) def create_patch_completed_event(sender, instance, raw, **kwargs): - def create_event(patch): return Event.objects.create( category=Event.CATEGORY_PATCH_COMPLETED, project=patch.project, patch=patch, - series=patch.series) + series=patch.series, + ) # don't trigger for items loaded from fixtures, new items or items that # (still) don't have a series @@ -183,7 +184,8 @@ def create_event(patch): # if dependencies not met, don't raise event. There's also no point raising # events for successors since they'll have the same issue predecessors = Patch.objects.filter( - series=instance.series, number__lt=instance.number) + series=instance.series, number__lt=instance.number + ) if predecessors.count() != instance.number - 1: return @@ -193,7 +195,8 @@ def create_event(patch): # those count = instance.number + 1 for successor in Patch.objects.order_by('number').filter( - series=instance.series, number__gt=instance.number): + series=instance.series, number__gt=instance.number + ): if successor.number != count: break @@ -203,7 +206,6 @@ def create_event(patch): @receiver(post_save, sender=Check) def create_check_created_event(sender, instance, created, raw, **kwargs): - def create_event(check): # TODO(stephenfin): It might make sense to add a 'project' field to # 'check' to prevent lookups here and in the REST API @@ -212,7 +214,8 @@ def create_event(check): project=check.patch.project, actor=check.user, patch=check.patch, - created_check=check) + created_check=check, + ) # don't trigger for items loaded from fixtures or existing items if raw or not created: @@ -223,12 +226,12 @@ def create_event(check): @receiver(post_save, sender=Series) def create_series_created_event(sender, instance, created, raw, **kwargs): - def create_event(series): return Event.objects.create( category=Event.CATEGORY_SERIES_CREATED, project=series.project, - series=series) + series=series, + ) # don't trigger for items loaded from fixtures or existing items if raw or not created: @@ -250,7 +253,8 @@ def create_event(series): return Event.objects.create( category=Event.CATEGORY_SERIES_COMPLETED, project=series.project, - series=series) + series=series, + ) # don't trigger for items loaded from fixtures, new items or items that # (still) don't have a series @@ -273,7 +277,6 @@ def create_event(series): @receiver(post_save, sender=CoverComment) def create_cover_comment_created_event(sender, instance, raw, **kwargs): - def create_event(comment): return Event.objects.create( category=Event.CATEGORY_COVER_COMMENT_CREATED, @@ -287,7 +290,6 @@ def create_event(comment): @receiver(post_save, sender=PatchComment) def create_patch_comment_created_event(sender, instance, raw, **kwargs): - def create_event(comment): return Event.objects.create( category=Event.CATEGORY_PATCH_COMMENT_CREATED, diff --git a/patchwork/tests/api/test_bundle.py b/patchwork/tests/api/test_bundle.py index 1ada79c3..c2a5fce3 100644 --- a/patchwork/tests/api/test_bundle.py +++ b/patchwork/tests/api/test_bundle.py @@ -45,14 +45,14 @@ def assertSerialized(self, bundle_obj, bundle_json): # nested fields - self.assertEqual(bundle_obj.owner.id, - bundle_json['owner']['id']) - self.assertEqual(bundle_obj.project.id, - bundle_json['project']['id']) - self.assertEqual(bundle_obj.patches.count(), - len(bundle_json['patches'])) + self.assertEqual(bundle_obj.owner.id, bundle_json['owner']['id']) + self.assertEqual(bundle_obj.project.id, bundle_json['project']['id']) + self.assertEqual( + bundle_obj.patches.count(), len(bundle_json['patches']) + ) for patch_obj, patch_json in zip( - bundle_obj.patches.all(), bundle_json['patches']): + bundle_obj.patches.all(), bundle_json['patches'] + ): self.assertEqual(patch_obj.id, patch_json['id']) def test_list_empty(self): @@ -64,10 +64,10 @@ def test_list_empty(self): def _create_bundles(self): user = create_user(username='myuser') project = create_project(linkname='myproject') - bundle_public = create_bundle(public=True, owner=user, - project=project) - bundle_private = create_bundle(public=False, owner=user, - project=project) + bundle_public = create_bundle(public=True, owner=user, project=project) + bundle_private = create_bundle( + public=False, owner=user, project=project + ) return user, project, bundle_public, bundle_private @@ -95,7 +95,8 @@ def test_list_authenticated(self): self.assertEqual(status.HTTP_200_OK, resp.status_code) self.assertEqual(2, len(resp.data)) for bundle_rsp, bundle_obj in zip( - resp.data, [bundle_public, bundle_private]): + resp.data, [bundle_public, bundle_private] + ): self.assertSerialized(bundle_obj, bundle_rsp) def test_list_filter_project(self): @@ -105,8 +106,9 @@ def test_list_filter_project(self): # test filtering by project self.client.force_authenticate(user=user) resp = self.client.get(self.api_url(), {'project': 'myproject'}) - self.assertEqual([bundle_public.id, bundle_private.id], - [x['id'] for x in resp.data]) + self.assertEqual( + [bundle_public.id, bundle_private.id], [x['id'] for x in resp.data] + ) resp = self.client.get(self.api_url(), {'project': 'invalidproject'}) self.assertEqual(0, len(resp.data)) @@ -117,11 +119,13 @@ def test_list_filter_owner(self): # test filtering by owner, both ID and username self.client.force_authenticate(user=user) resp = self.client.get(self.api_url(), {'owner': user.id}) - self.assertEqual([bundle_public.id, bundle_private.id], - [x['id'] for x in resp.data]) + self.assertEqual( + [bundle_public.id, bundle_private.id], [x['id'] for x in resp.data] + ) resp = self.client.get(self.api_url(), {'owner': 'myuser'}) - self.assertEqual([bundle_public.id, bundle_private.id], - [x['id'] for x in resp.data]) + self.assertEqual( + [bundle_public.id, bundle_private.id], [x['id'] for x in resp.data] + ) resp = self.client.get(self.api_url(), {'owner': 'otheruser'}) self.assertEqual(0, len(resp.data)) @@ -213,7 +217,8 @@ def test_create_anonymous(self): Ensure creations can only be performed by signed in users. """ user, project, patch_a, patch_b = self._test_create_update( - authenticate=False) + authenticate=False + ) bundle = { 'name': 'test-bundle', 'public': True, @@ -277,11 +282,14 @@ def test_update_anonymous(self): Ensure updates can only be performed by signed in users. """ user, project, patch_a, patch_b = self._test_create_update( - authenticate=False) + authenticate=False + ) bundle = create_bundle(owner=user, project=project) - resp = self.client.patch(self.api_url(bundle.id), { - 'name': 'hello-bundle', 'patches': [patch_a.id, patch_b.id]}) + resp = self.client.patch( + self.api_url(bundle.id), + {'name': 'hello-bundle', 'patches': [patch_a.id, patch_b.id]}, + ) self.assertEqual(status.HTTP_404_NOT_FOUND, resp.status_code) @utils.store_samples('bundle-update') @@ -293,9 +301,10 @@ def test_update(self): self.assertEqual(1, Bundle.objects.all().count()) self.assertEqual(0, len(Bundle.objects.first().patches.all())) - resp = self.client.patch(self.api_url(bundle.id), { - 'name': 'hello-bundle', 'patches': [patch_a.id, patch_b.id] - }) + resp = self.client.patch( + self.api_url(bundle.id), + {'name': 'hello-bundle', 'patches': [patch_a.id, patch_b.id]}, + ) self.assertEqual(status.HTTP_200_OK, resp.status_code) self.assertEqual(2, len(resp.data['patches'])) self.assertEqual('hello-bundle', resp.data['name']) @@ -316,9 +325,13 @@ def test_update_no_patches(self): self.assertEqual(1, Bundle.objects.all().count()) self.assertEqual(2, len(Bundle.objects.first().patches.all())) - resp = self.client.patch(self.api_url(bundle.id), { - 'name': 'hello-bundle', 'public': True, - }) + resp = self.client.patch( + self.api_url(bundle.id), + { + 'name': 'hello-bundle', + 'public': True, + }, + ) self.assertEqual(status.HTTP_200_OK, resp.status_code) self.assertEqual(2, len(resp.data['patches'])) self.assertEqual('hello-bundle', resp.data['name']) @@ -335,7 +348,8 @@ def test_delete_anonymous(self): Ensure deletions can only be performed when signed in. """ user, project, patch_a, patch_b = self._test_create_update( - authenticate=False) + authenticate=False + ) bundle = create_bundle(owner=user, project=project) resp = self.client.delete(self.api_url(bundle.id)) @@ -363,8 +377,9 @@ def test_create_update_delete_version_1_1(self): resp = self.client.post(self.api_url(version='1.1'), {'name': 'test'}) self.assertEqual(status.HTTP_405_METHOD_NOT_ALLOWED, resp.status_code) - resp = self.client.patch(self.api_url(1, version='1.1'), - {'name': 'test'}) + resp = self.client.patch( + self.api_url(1, version='1.1'), {'name': 'test'} + ) self.assertEqual(status.HTTP_405_METHOD_NOT_ALLOWED, resp.status_code) resp = self.client.delete(self.api_url(1, version='1.1')) diff --git a/patchwork/tests/api/test_check.py b/patchwork/tests/api/test_check.py index f5edd69c..25c70aa4 100644 --- a/patchwork/tests/api/test_check.py +++ b/patchwork/tests/api/test_check.py @@ -31,8 +31,10 @@ class TestCheckAPI(utils.APITestCase): def api_url(self, item=None): if item is None: return reverse('api-check-list', args=[self.patch.id]) - return reverse('api-check-detail', kwargs={ - 'patch_id': self.patch.id, 'check_id': item.id}) + return reverse( + 'api-check-detail', + kwargs={'patch_id': self.patch.id, 'check_id': item.id}, + ) def setUp(self): super(TestCheckAPI, self).setUp() @@ -89,7 +91,8 @@ def test_list_filter_user(self): def test_list_invalid_patch(self): """Ensure we get a 404 for a non-existent patch.""" resp = self.client.get( - reverse('api-check-list', kwargs={'patch_id': '99999'})) + reverse('api-check-list', kwargs={'patch_id': '99999'}) + ) self.assertEqual(status.HTTP_404_NOT_FOUND, resp.status_code) @utils.store_samples('check-detail') @@ -180,7 +183,8 @@ def test_create_invalid_patch(self): self.client.force_authenticate(user=self.user) resp = self.client.post( - reverse('api-check-list', kwargs={'patch_id': '99999'}), check) + reverse('api-check-list', kwargs={'patch_id': '99999'}), check + ) self.assertEqual(status.HTTP_404_NOT_FOUND, resp.status_code) def test_update_delete(self): @@ -208,6 +212,7 @@ class TestCheckAPIMultipart(BaseAPITestCase): This is required due to the difference in handling JSON vs form-data in CheckSerializer's run_validation(). """ + fixtures = ['default_tags'] def setUp(self): @@ -235,12 +240,11 @@ def _test_create(self, user, state='success'): self.client.force_authenticate(user=user) return self.client.post( - reverse('api-check-list', args=[self.patch.id]), - check) + reverse('api-check-list', args=[self.patch.id]), check + ) def test_creates(self): - """Create a set of checks. - """ + """Create a set of checks.""" resp = self._test_create(user=self.user) self.assertEqual(status.HTTP_201_CREATED, resp.status_code) self.assertEqual(1, Check.objects.all().count()) diff --git a/patchwork/tests/api/test_comment.py b/patchwork/tests/api/test_comment.py index 5c035e82..d4b3eba1 100644 --- a/patchwork/tests/api/test_comment.py +++ b/patchwork/tests/api/test_comment.py @@ -28,7 +28,6 @@ @unittest.skipUnless(settings.ENABLE_REST_API, 'requires ENABLE_REST_API') class TestCoverComments(utils.APITestCase): - @staticmethod def api_url(cover, version=None, item=None): kwargs = {'cover_id': cover.id} @@ -47,8 +46,9 @@ def setUp(self): def assertSerialized(self, comment_obj, comment_json): self.assertEqual(comment_obj.id, comment_json['id']) - self.assertEqual(comment_obj.submitter.id, - comment_json['submitter']['id']) + self.assertEqual( + comment_obj.submitter.id, comment_json['submitter']['id'] + ) self.assertEqual(comment_obj.addressed, comment_json['addressed']) self.assertIn(SAMPLE_CONTENT, comment_json['content']) @@ -102,7 +102,8 @@ def test_list_version_1_0(self): def test_list_non_existent_cover(self): """Ensure we get a 404 for a non-existent cover letter.""" resp = self.client.get( - reverse('api-cover-comment-list', kwargs={'cover_id': '99999'})) + reverse('api-cover-comment-list', kwargs={'cover_id': '99999'}) + ) self.assertEqual(status.HTTP_404_NOT_FOUND, resp.status_code) def test_list_invalid_cover(self): @@ -127,7 +128,8 @@ def test_detail_version_1_2(self): with self.assertRaises(NoReverseMatch): self.client.get( - self.api_url(self.cover, version='1.2', item=comment)) + self.api_url(self.cover, version='1.2', item=comment) + ) def test_detail_version_1_1(self): """Show a cover letter comment using API v1.1.""" @@ -135,7 +137,8 @@ def test_detail_version_1_1(self): with self.assertRaises(NoReverseMatch): self.client.get( - self.api_url(self.cover, version='1.1', item=comment)) + self.api_url(self.cover, version='1.1', item=comment) + ) def test_detail_version_1_0(self): """Show a cover letter comment using API v1.0.""" @@ -143,16 +146,17 @@ def test_detail_version_1_0(self): with self.assertRaises(NoReverseMatch): self.client.get( - self.api_url(self.cover, version='1.0', item=comment)) + self.api_url(self.cover, version='1.0', item=comment) + ) @utils.store_samples('cover-comment-detail-error-not-found') def test_detail_invalid_cover(self): """Ensure we handle non-existent cover letters.""" comment = create_cover_comment() resp = self.client.get( - reverse('api-cover-comment-detail', kwargs={ - 'cover_id': '99999', - 'comment_id': comment.id} + reverse( + 'api-cover-comment-detail', + kwargs={'cover_id': '99999', 'comment_id': comment.id}, ), ) self.assertEqual(status.HTTP_404_NOT_FOUND, resp.status_code) @@ -167,7 +171,7 @@ def _test_update(self, person, **kwargs): return self.client.patch( self.api_url(cover, item=comment), {'addressed': kwargs.get('addressed', True)}, - validate_request=kwargs.get('validate_request', True) + validate_request=kwargs.get('validate_request', True), ) @utils.store_samples('cover-comment-detail-update-authorized') @@ -230,10 +234,12 @@ def test_update_invalid_addressed(self): """ person = create_person(name='cover-submitter', user=create_user()) cover = create_cover(submitter=person) - resp = self._test_update(person=person, - cover=cover, - addressed='not-valid', - validate_request=False) + resp = self._test_update( + person=person, + cover=cover, + addressed='not-valid', + validate_request=False, + ) self.assertEqual(status.HTTP_400_BAD_REQUEST, resp.status_code) self.assertFalse( getattr(CoverComment.objects.all().first(), 'addressed') @@ -273,8 +279,9 @@ def setUp(self): def assertSerialized(self, comment_obj, comment_json): self.assertEqual(comment_obj.id, comment_json['id']) - self.assertEqual(comment_obj.submitter.id, - comment_json['submitter']['id']) + self.assertEqual( + comment_obj.submitter.id, comment_json['submitter']['id'] + ) self.assertEqual(comment_obj.addressed, comment_json['addressed']) self.assertIn(SAMPLE_CONTENT, comment_json['content']) @@ -327,7 +334,8 @@ def test_list_version_1_0(self): def test_list_non_existent_patch(self): """Ensure we get a 404 for a non-existent patch.""" resp = self.client.get( - reverse('api-patch-comment-list', kwargs={'patch_id': '99999'})) + reverse('api-patch-comment-list', kwargs={'patch_id': '99999'}) + ) self.assertEqual(status.HTTP_404_NOT_FOUND, resp.status_code) def test_list_invalid_patch(self): @@ -352,7 +360,8 @@ def test_detail_version_1_2(self): with self.assertRaises(NoReverseMatch): self.client.get( - self.api_url(self.patch, version='1.2', item=comment)) + self.api_url(self.patch, version='1.2', item=comment) + ) def test_detail_version_1_1(self): """Show a patch comment using API v1.1.""" @@ -360,7 +369,8 @@ def test_detail_version_1_1(self): with self.assertRaises(NoReverseMatch): self.client.get( - self.api_url(self.patch, version='1.1', item=comment)) + self.api_url(self.patch, version='1.1', item=comment) + ) def test_detail_version_1_0(self): """Show a patch comment using API v1.0.""" @@ -368,16 +378,17 @@ def test_detail_version_1_0(self): with self.assertRaises(NoReverseMatch): self.client.get( - self.api_url(self.patch, version='1.0', item=comment)) + self.api_url(self.patch, version='1.0', item=comment) + ) @utils.store_samples('patch-comment-detail-error-not-found') def test_detail_invalid_patch(self): """Ensure we handle non-existent patches.""" comment = create_patch_comment() resp = self.client.get( - reverse('api-patch-comment-detail', kwargs={ - 'patch_id': '99999', - 'comment_id': comment.id} + reverse( + 'api-patch-comment-detail', + kwargs={'patch_id': '99999', 'comment_id': comment.id}, ), ) self.assertEqual(status.HTTP_404_NOT_FOUND, resp.status_code) @@ -392,7 +403,7 @@ def _test_update(self, person, **kwargs): return self.client.patch( self.api_url(patch, item=comment), {'addressed': kwargs.get('addressed', True)}, - validate_request=kwargs.get('validate_request', True) + validate_request=kwargs.get('validate_request', True), ) @utils.store_samples('patch-comment-detail-update-authorized') @@ -462,10 +473,12 @@ def test_update_invalid_addressed(self): """ person = create_person(name='patch-submitter', user=create_user()) patch = create_patch(submitter=person) - resp = self._test_update(person=person, - patch=patch, - addressed='not-valid', - validate_request=False) + resp = self._test_update( + person=person, + patch=patch, + addressed='not-valid', + validate_request=False, + ) self.assertEqual(status.HTTP_400_BAD_REQUEST, resp.status_code) self.assertFalse( getattr(PatchComment.objects.all().first(), 'addressed') diff --git a/patchwork/tests/api/test_cover.py b/patchwork/tests/api/test_cover.py index 1c232ddb..126b3af1 100644 --- a/patchwork/tests/api/test_cover.py +++ b/patchwork/tests/api/test_cover.py @@ -45,13 +45,13 @@ def assertSerialized(self, cover_obj, cover_json): # nested fields - self.assertEqual(cover_obj.submitter.id, - cover_json['submitter']['id']) + self.assertEqual(cover_obj.submitter.id, cover_json['submitter']['id']) if hasattr(cover_obj, 'series'): self.assertEqual(1, len(cover_json['series'])) - self.assertEqual(cover_obj.series.id, - cover_json['series'][0]['id']) + self.assertEqual( + cover_obj.series.id, cover_json['series'][0]['id'] + ) else: self.assertEqual([], cover_json['series']) @@ -104,12 +104,12 @@ def test_list_filter_submitter(self): resp = self.client.get(self.api_url(), {'submitter': submitter.id}) self.assertEqual([cover.id], [x['id'] for x in resp.data]) - resp = self.client.get(self.api_url(), { - 'submitter': submitter.email}) + resp = self.client.get(self.api_url(), {'submitter': submitter.email}) self.assertEqual([cover.id], [x['id'] for x in resp.data]) - resp = self.client.get(self.api_url(), { - 'submitter': 'test@example.org'}) + resp = self.client.get( + self.api_url(), {'submitter': 'test@example.org'} + ) self.assertEqual(0, len(resp.data)) def test_list_filter_msgid(self): @@ -120,8 +120,7 @@ def test_list_filter_msgid(self): self.assertEqual([cover.id], [x['id'] for x in resp.data]) # empty response if nothing matches - resp = self.client.get(self.api_url(), { - 'msgid': 'fishfish@fish.fish'}) + resp = self.client.get(self.api_url(), {'msgid': 'fishfish@fish.fish'}) self.assertEqual(0, len(resp.data)) @utils.store_samples('cover-list-1-0') @@ -156,8 +155,9 @@ def test_detail(self): # Make sure we don't regress and all headers with the same key are # included in the response - parsed_headers = email.parser.Parser().parsestr(cover_obj.headers, - True) + parsed_headers = email.parser.Parser().parsestr( + cover_obj.headers, True + ) for key, value in parsed_headers.items(): self.assertIn(value, resp.data['headers'][key]) diff --git a/patchwork/tests/api/test_event.py b/patchwork/tests/api/test_event.py index e5b5e925..9708f96b 100644 --- a/patchwork/tests/api/test_event.py +++ b/patchwork/tests/api/test_event.py @@ -29,7 +29,6 @@ # to fix our schema to work with recent versions of openapi_core @unittest.skipUnless(settings.ENABLE_REST_API, 'requires ENABLE_REST_API') class TestEventAPI(APITestCase): - @staticmethod def api_url(version=None): kwargs = {} @@ -46,11 +45,9 @@ def assertSerialized(self, event_obj, event_json): # nested fields - self.assertEqual(event_obj.project.id, - event_json['project']['id']) + self.assertEqual(event_obj.project.id, event_json['project']['id']) if event_obj.actor is not None: - self.assertEqual(event_obj.actor.id, - event_json['actor']['id']) + self.assertEqual(event_obj.actor.id, event_json['actor']['id']) # TODO(stephenfin): Check other fields @@ -116,8 +113,9 @@ def test_list_filter_category(self): """Filter events by category.""" events = self._create_events() - resp = self.client.get(self.api_url(), - {'category': events[0].category}) + resp = self.client.get( + self.api_url(), {'category': events[0].category} + ) # There should only be one self.assertEqual(1, len(resp.data)) @@ -184,8 +182,9 @@ def test_list_filter_actor_version_1_1(self): events = self._create_events() # we still see all the events since the actor field is ignored - resp = self.client.get(self.api_url(version='1.1'), - {'actor': 'foo-bar'}) + resp = self.client.get( + self.api_url(version='1.1'), {'actor': 'foo-bar'} + ) self.assertEqual(len(events), len(resp.data)) def test_list_bug_335(self): diff --git a/patchwork/tests/api/test_patch.py b/patchwork/tests/api/test_patch.py index 2fda7bb1..0ba3042b 100644 --- a/patchwork/tests/api/test_patch.py +++ b/patchwork/tests/api/test_patch.py @@ -59,15 +59,14 @@ def assertSerialized(self, patch_obj, patch_json): # nested fields - self.assertEqual(patch_obj.submitter.id, - patch_json['submitter']['id']) - self.assertEqual(patch_obj.project.id, - patch_json['project']['id']) + self.assertEqual(patch_obj.submitter.id, patch_json['submitter']['id']) + self.assertEqual(patch_obj.project.id, patch_json['project']['id']) if patch_obj.series: self.assertEqual(1, len(patch_json['series'])) - self.assertEqual(patch_obj.series.id, - patch_json['series'][0]['id']) + self.assertEqual( + patch_obj.series.id, patch_json['series'][0]['id'] + ) else: self.assertEqual([], patch_json['series']) @@ -81,8 +80,12 @@ def _create_patch(self, **kwargs): person_obj = create_person(email='test@example.com') project_obj = create_project(linkname='myproject') state_obj = create_state(name='Under Review') - patch_obj = create_patch(state=state_obj, project=project_obj, - submitter=person_obj, **kwargs) + patch_obj = create_patch( + state=state_obj, + project=project_obj, + submitter=person_obj, + **kwargs + ) return patch_obj @@ -125,8 +128,9 @@ def test_list_filter_state(self): create_patch(state=state_obj_c) self.client.force_authenticate(user=user) - resp = self.client.get(self.api_url(), [('state', 'under-review'), - ('state', 'new')]) + resp = self.client.get( + self.api_url(), [('state', 'under-review'), ('state', 'new')] + ) self.assertEqual(2, len(resp.data)) def test_list_filter_project(self): @@ -154,41 +158,50 @@ def test_list_filter_submitter(self): resp = self.client.get(self.api_url(), {'submitter': submitter.id}) self.assertEqual([patch.id], [x['id'] for x in resp.data]) - resp = self.client.get(self.api_url(), { - 'submitter': 'test@example.com'}) + resp = self.client.get( + self.api_url(), {'submitter': 'test@example.com'} + ) self.assertEqual([patch.id], [x['id'] for x in resp.data]) - resp = self.client.get(self.api_url(), { - 'submitter': 'test@example.org'}) + resp = self.client.get( + self.api_url(), {'submitter': 'test@example.org'} + ) self.assertEqual(0, len(resp.data)) def test_list_filter_hash(self): """Filter patches by hash.""" patch = self._create_patch() - patch_new_diff = create_patch(state=patch.state, project=patch.project, - submitter=patch.submitter, - diff=SAMPLE_DIFF) + patch_new_diff = create_patch( + state=patch.state, + project=patch.project, + submitter=patch.submitter, + diff=SAMPLE_DIFF, + ) # check regular filtering resp = self.client.get(self.api_url(), {'hash': patch.hash}) self.assertEqual([patch.id], [x['id'] for x in resp.data]) # 2 patches with identical diffs - patch_same_diff = create_patch(state=patch.state, - project=patch.project, - submitter=patch.submitter) + patch_same_diff = create_patch( + state=patch.state, project=patch.project, submitter=patch.submitter + ) resp = self.client.get(self.api_url(), {'hash': patch.hash}) - self.assertEqual([patch.id, patch_same_diff.id], - [x['id'] for x in resp.data]) + self.assertEqual( + [patch.id, patch_same_diff.id], [x['id'] for x in resp.data] + ) # case insensitive matching - resp = self.client.get(self.api_url(), - {'hash': patch_new_diff.hash.upper()}) + resp = self.client.get( + self.api_url(), {'hash': patch_new_diff.hash.upper()} + ) self.assertEqual([patch_new_diff.id], [x['id'] for x in resp.data]) # empty response if nothing matches - resp = self.client.get(self.api_url(), { - 'hash': 'da638d0746a115000bf890fada1f02679aa282e8'}) + resp = self.client.get( + self.api_url(), + {'hash': 'da638d0746a115000bf890fada1f02679aa282e8'}, + ) self.assertEqual(0, len(resp.data)) def test_list_filter_hash_version_1_1(self): @@ -196,8 +209,9 @@ def test_list_filter_hash_version_1_1(self): self._create_patch() # we still see the patch since the hash field is ignored - resp = self.client.get(self.api_url(version='1.1'), - {'hash': 'garbagevalue'}) + resp = self.client.get( + self.api_url(version='1.1'), {'hash': 'garbagevalue'} + ) self.assertEqual(1, len(resp.data)) def test_list_filter_msgid(self): @@ -208,8 +222,7 @@ def test_list_filter_msgid(self): self.assertEqual([patch.id], [x['id'] for x in resp.data]) # empty response if nothing matches - resp = self.client.get(self.api_url(), { - 'msgid': 'fishfish@fish.fish'}) + resp = self.client.get(self.api_url(), {'msgid': 'fishfish@fish.fish'}) self.assertEqual(0, len(resp.data)) @utils.store_samples('patch-list-1-0') @@ -236,7 +249,7 @@ def test_detail(self): """Show a specific patch.""" patch = create_patch( content='Reviewed-by: Test User \n', - headers='Received: from somewhere\nReceived: from another place' + headers='Received: from somewhere\nReceived: from another place', ) resp = self.client.get(self.api_url(patch.id)) @@ -332,16 +345,18 @@ def test_update_maintainer(self): user = create_maintainer(project) self.client.force_authenticate(user=user) - resp = self.client.patch(self.api_url(patch.id), - {'state': state.slug, 'delegate': user.id}) + resp = self.client.patch( + self.api_url(patch.id), {'state': state.slug, 'delegate': user.id} + ) self.assertEqual(status.HTTP_200_OK, resp.status_code, resp) self.assertEqual(Patch.objects.get(id=patch.id).state, state) self.assertEqual(Patch.objects.get(id=patch.id).delegate, user) # (who can unset fields too) # we need to send as JSON due to https://stackoverflow.com/q/30677216/ - resp = self.client.patch(self.api_url(patch.id), {'delegate': None}, - format='json') + resp = self.client.patch( + self.api_url(patch.id), {'delegate': None}, format='json' + ) self.assertEqual(status.HTTP_200_OK, resp.status_code, resp) self.assertIsNone(Patch.objects.get(id=patch.id).delegate) @@ -353,8 +368,10 @@ def test_update_maintainer_version_1_0(self): user = create_maintainer(project) self.client.force_authenticate(user=user) - resp = self.client.patch(self.api_url(patch.id, version="1.1"), - {'state': state.slug, 'delegate': user.id}) + resp = self.client.patch( + self.api_url(patch.id, version="1.1"), + {'state': state.slug, 'delegate': user.id}, + ) self.assertEqual(status.HTTP_200_OK, resp.status_code, resp) self.assertEqual(Patch.objects.get(id=patch.id).state, state) self.assertEqual(Patch.objects.get(id=patch.id).delegate, user) @@ -373,8 +390,11 @@ def test_update_invalid_state(self): self.client.force_authenticate(user=user) resp = self.client.patch(self.api_url(patch.id), {'state': 'foobar'}) self.assertEqual(status.HTTP_400_BAD_REQUEST, resp.status_code) - self.assertContains(resp, 'Expected one of: %s.' % state.slug, - status_code=status.HTTP_400_BAD_REQUEST) + self.assertContains( + resp, + 'Expected one of: %s.' % state.slug, + status_code=status.HTTP_400_BAD_REQUEST, + ) def test_update_legacy_delegate(self): """Regression test for bug #313.""" @@ -394,8 +414,9 @@ def test_update_legacy_delegate(self): self.assertNotEqual(user_b.id, user_b.profile.id) self.client.force_authenticate(user=user_a) - resp = self.client.patch(self.api_url(patch.id), - {'delegate': user_b.id}) + resp = self.client.patch( + self.api_url(patch.id), {'delegate': user_b.id} + ) self.assertEqual(status.HTTP_200_OK, resp.status_code, resp) self.assertEqual(Patch.objects.get(id=patch.id).state, state) self.assertEqual(Patch.objects.get(id=patch.id).delegate, user_b) @@ -412,11 +433,15 @@ def test_update_invalid_delegate(self): user_b = create_user() self.client.force_authenticate(user=user_a) - resp = self.client.patch(self.api_url(patch.id), - {'delegate': user_b.id}) + resp = self.client.patch( + self.api_url(patch.id), {'delegate': user_b.id} + ) self.assertEqual(status.HTTP_400_BAD_REQUEST, resp.status_code) - self.assertContains(resp, "User '%s' is not a maintainer" % user_b, - status_code=status.HTTP_400_BAD_REQUEST) + self.assertContains( + resp, + "User '%s' is not a maintainer" % user_b, + status_code=status.HTTP_400_BAD_REQUEST, + ) def test_delete(self): """Ensure deletions are always rejected.""" diff --git a/patchwork/tests/api/test_person.py b/patchwork/tests/api/test_person.py index e9070007..64c80651 100644 --- a/patchwork/tests/api/test_person.py +++ b/patchwork/tests/api/test_person.py @@ -20,7 +20,6 @@ @unittest.skipUnless(settings.ENABLE_REST_API, 'requires ENABLE_REST_API') class TestPersonAPI(utils.APITestCase): - @staticmethod def api_url(item=None): if item is None: @@ -36,8 +35,7 @@ def assertSerialized(self, person_obj, person_json, has_user=False): self.assertEqual(person_obj.user.profile.name, person_json['name']) self.assertEqual(person_obj.user.email, person_json['email']) # nested fields - self.assertEqual(person_obj.user.id, - person_json['user']['id']) + self.assertEqual(person_obj.user.id, person_json['user']['id']) def test_list_empty(self): """List people when none are present.""" diff --git a/patchwork/tests/api/test_project.py b/patchwork/tests/api/test_project.py index f2110af9..79a71d81 100644 --- a/patchwork/tests/api/test_project.py +++ b/patchwork/tests/api/test_project.py @@ -20,7 +20,6 @@ @unittest.skipUnless(settings.ENABLE_REST_API, 'requires ENABLE_REST_API') class TestProjectAPI(utils.APITestCase): - @staticmethod def api_url(item=None, version=None): kwargs = {} @@ -37,13 +36,16 @@ def assertSerialized(self, project_obj, project_json): self.assertEqual(project_obj.name, project_json['name']) self.assertEqual(project_obj.linkname, project_json['link_name']) self.assertEqual(project_obj.listid, project_json['list_id']) - self.assertEqual(project_obj.subject_match, - project_json['subject_match']) + self.assertEqual( + project_obj.subject_match, project_json['subject_match'] + ) # nested fields - self.assertEqual(len(project_json['maintainers']), - project_obj.maintainer_project.all().count()) + self.assertEqual( + len(project_json['maintainers']), + project_obj.maintainer_project.all().count(), + ) def test_list_empty(self): """List projects when none are present.""" diff --git a/patchwork/tests/api/test_relation.py b/patchwork/tests/api/test_relation.py index 5f8048f2..d7518cb3 100644 --- a/patchwork/tests/api/test_relation.py +++ b/patchwork/tests/api/test_relation.py @@ -161,7 +161,8 @@ def test_delete_from_three_patch_relation(self): def test_extend_relation_through_new(self): relation = create_relation() existing_patch_a = create_patches( - 2, project=self.project, related=relation)[0] + 2, project=self.project, related=relation + )[0] new_patch = create_patch(project=self.project) @@ -176,7 +177,8 @@ def test_extend_relation_through_new(self): def test_extend_relation_through_old(self): relation = create_relation() existing_patch_a = create_patches( - 2, project=self.project, related=relation)[0] + 2, project=self.project, related=relation + )[0] new_patch = create_patch(project=self.project) @@ -192,7 +194,8 @@ def test_extend_relation_through_old(self): def test_extend_relation_through_new_two(self): relation = create_relation() existing_patch_a = create_patches( - 2, project=self.project, related=relation)[0] + 2, project=self.project, related=relation + )[0] new_patch_a = create_patch(project=self.project) new_patch_b = create_patch(project=self.project) @@ -215,7 +218,8 @@ def test_extend_relation_through_new_two(self): def test_extend_relation_through_old_two(self): relation = create_relation() existing_patch_a = create_patches( - 2, project=self.project, related=relation)[0] + 2, project=self.project, related=relation + )[0] new_patch_a = create_patch(project=self.project) new_patch_b = create_patch(project=self.project) @@ -255,7 +259,8 @@ def test_remove_one_patch_from_relation_bad(self): def test_remove_one_patch_from_relation_good(self): relation = create_relation() target_patch = create_patches( - 3, project=self.project, related=relation)[0] + 3, project=self.project, related=relation + )[0] # maintainer self.client.force_authenticate(user=self.maintainer) diff --git a/patchwork/tests/api/test_series.py b/patchwork/tests/api/test_series.py index ef661773..10a074a1 100644 --- a/patchwork/tests/api/test_series.py +++ b/patchwork/tests/api/test_series.py @@ -42,21 +42,24 @@ def assertSerialized(self, series_obj, series_json): self.assertEqual(series_obj.name, series_json['name']) self.assertEqual(series_obj.version, series_json['version']) self.assertEqual(series_obj.total, series_json['total']) - self.assertEqual(series_obj.received_total, - series_json['received_total']) + self.assertEqual( + series_obj.received_total, series_json['received_total'] + ) self.assertIn(series_obj.get_mbox_url(), series_json['mbox']) self.assertIn(series_obj.get_absolute_url(), series_json['web_url']) # nested fields - self.assertEqual(series_obj.project.id, - series_json['project']['id']) - self.assertEqual(series_obj.submitter.id, - series_json['submitter']['id']) - self.assertEqual(series_obj.cover_letter.id, - series_json['cover_letter']['id']) - self.assertEqual(series_obj.patches.count(), - len(series_json['patches'])) + self.assertEqual(series_obj.project.id, series_json['project']['id']) + self.assertEqual( + series_obj.submitter.id, series_json['submitter']['id'] + ) + self.assertEqual( + series_obj.cover_letter.id, series_json['cover_letter']['id'] + ) + self.assertEqual( + series_obj.patches.count(), len(series_json['patches']) + ) def test_list_empty(self): """List series when none are present.""" @@ -114,12 +117,14 @@ def test_list_filter_owner(self): resp = self.client.get(self.api_url(), {'submitter': submitter.id}) self.assertEqual([series.id], [x['id'] for x in resp.data]) - resp = self.client.get(self.api_url(), { - 'submitter': 'test@example.com'}) + resp = self.client.get( + self.api_url(), {'submitter': 'test@example.com'} + ) self.assertEqual([series.id], [x['id'] for x in resp.data]) - resp = self.client.get(self.api_url(), { - 'submitter': 'test@example.org'}) + resp = self.client.get( + self.api_url(), {'submitter': 'test@example.org'} + ) self.assertEqual(0, len(resp.data)) @utils.store_samples('series-list-1-0') @@ -145,7 +150,8 @@ def test_list_bug_335(self): person_obj = create_person(email='test@example.com') for i in range(10): series_obj = create_series( - project=project_obj, submitter=person_obj, + project=project_obj, + submitter=person_obj, ) create_cover(series=series_obj) create_patch(series=series_obj) diff --git a/patchwork/tests/api/test_user.py b/patchwork/tests/api/test_user.py index 9e9008b8..10865757 100644 --- a/patchwork/tests/api/test_user.py +++ b/patchwork/tests/api/test_user.py @@ -19,7 +19,6 @@ @unittest.skipUnless(settings.ENABLE_REST_API, 'requires ENABLE_REST_API') class TestUserAPI(utils.APITestCase): - @staticmethod def api_url(item=None, version=None): kwargs = {} @@ -42,12 +41,17 @@ def assertSerialized(self, user_obj, user_json, has_settings=False): if has_settings: self.assertIn('settings', user_json) - self.assertEqual(user_json['settings']['send_email'], - user_obj.profile.send_email) - self.assertEqual(user_json['settings']['items_per_page'], - user_obj.profile.items_per_page) - self.assertEqual(user_json['settings']['show_ids'], - user_obj.profile.show_ids) + self.assertEqual( + user_json['settings']['send_email'], + user_obj.profile.send_email, + ) + self.assertEqual( + user_json['settings']['items_per_page'], + user_obj.profile.items_per_page, + ) + self.assertEqual( + user_json['settings']['show_ids'], user_obj.profile.show_ids + ) else: self.assertNotIn('settings', user_json) @@ -127,8 +131,9 @@ def test_update_other_user(self): user_b = create_user() self.client.force_authenticate(user=user_a) - resp = self.client.patch(self.api_url(user_b.id), - {'first_name': 'Tan'}) + resp = self.client.patch( + self.api_url(user_b.id), {'first_name': 'Tan'} + ) self.assertEqual(status.HTTP_403_FORBIDDEN, resp.status_code) @utils.store_samples('users-update-self') @@ -138,8 +143,10 @@ def test_update_self(self): self.assertFalse(user.profile.send_email) self.client.force_authenticate(user=user) - resp = self.client.patch(self.api_url(user.id), { - 'first_name': 'Tan', 'settings': {'send_email': True}}) + resp = self.client.patch( + self.api_url(user.id), + {'first_name': 'Tan', 'settings': {'send_email': True}}, + ) self.assertEqual(status.HTTP_200_OK, resp.status_code) self.assertSerialized(user, resp.data, has_settings=True) self.assertEqual('Tan', user.first_name) @@ -157,7 +164,8 @@ def test_update_self_version_1_1(self): resp = self.client.patch( self.api_url(user.id, version='1.1'), {'first_name': 'Tan', 'settings': {'send_email': True}}, - validate_request=False) + validate_request=False, + ) self.assertEqual(status.HTTP_200_OK, resp.status_code) self.assertSerialized(user, resp.data, has_settings=False) self.assertEqual('Tan', user.first_name) diff --git a/patchwork/tests/api/utils.py b/patchwork/tests/api/utils.py index c405f737..c3b03a8d 100644 --- a/patchwork/tests/api/utils.py +++ b/patchwork/tests/api/utils.py @@ -21,8 +21,14 @@ # docs/api/samples OUT_DIR = os.path.join( - os.path.dirname(os.path.abspath(__file__)), os.pardir, os.pardir, - os.pardir, 'docs', 'api', 'samples') + os.path.dirname(os.path.abspath(__file__)), + os.pardir, + os.pardir, + os.pardir, + 'docs', + 'api', + 'samples', +) _WRITTEN_FILES = {} @@ -40,7 +46,9 @@ def _duplicate_sample(filename, func): raise Exception( "Tests '{}' and '{}' write to the same file".format( - _WRITTEN_FILES[filename], func)) + _WRITTEN_FILES[filename], func + ) + ) _WRITTEN_FILES[filename] = func @@ -58,18 +66,18 @@ def store_samples(filename): os.mkdir(OUT_DIR) def inner(func): - def wrapper(self, *args, **kwargs): - - def client_wrapper(orig_func, path, data=None, *orig_args, - **orig_kwargs): + def client_wrapper( + orig_func, path, data=None, *orig_args, **orig_kwargs + ): req_filename = filename + '-req.json' resp_filename = filename + '-resp.json' # we don't have a request body for GET requests if orig_func != _get and not _duplicate_sample( - req_filename, func): + req_filename, func + ): with open(os.path.join(OUT_DIR, req_filename), 'w') as fh: json.dump(data, fh, indent=4, separators=(',', ': ')) @@ -77,8 +85,9 @@ def client_wrapper(orig_func, path, data=None, *orig_args, if not _duplicate_sample(resp_filename, func): with open(os.path.join(OUT_DIR, resp_filename), 'w') as fh: - json.dump(resp.data, fh, indent=4, - separators=(',', ': ')) + json.dump( + resp.data, fh, indent=4, separators=(',', ': ') + ) return resp @@ -106,7 +115,6 @@ def client_wrapper(orig_func, path, data=None, *orig_args, class APIClient(BaseAPIClient): - def __init__(self, *args, **kwargs): super(APIClient, self).__init__(*args, **kwargs) self.factory = APIRequestFactory() @@ -116,63 +124,123 @@ def get(self, path, data=None, follow=False, **extra): validate_response = extra.pop('validate_response', True) request = self.factory.get( - path, data=data, SERVER_NAME='example.com', **extra) + path, data=data, SERVER_NAME='example.com', **extra + ) response = super(APIClient, self).get( - path, data=data, follow=follow, SERVER_NAME='example.com', **extra) + path, data=data, follow=follow, SERVER_NAME='example.com', **extra + ) - validator.validate_data(path, request, response, validate_request, - validate_response) + validator.validate_data( + path, request, response, validate_request, validate_response + ) return response - def post(self, path, data=None, format=None, content_type=None, - follow=False, **extra): + def post( + self, + path, + data=None, + format=None, + content_type=None, + follow=False, + **extra + ): validate_request = extra.pop('validate_request', True) validate_response = extra.pop('validate_response', True) request = self.factory.post( - path, data=data, format='json', content_type=content_type, - SERVER_NAME='example.com', **extra) + path, + data=data, + format='json', + content_type=content_type, + SERVER_NAME='example.com', + **extra + ) response = super(APIClient, self).post( - path, data=data, format='json', content_type=content_type, - follow=follow, SERVER_NAME='example.com', **extra) - - validator.validate_data(path, request, response, validate_request, - validate_response) + path, + data=data, + format='json', + content_type=content_type, + follow=follow, + SERVER_NAME='example.com', + **extra + ) + + validator.validate_data( + path, request, response, validate_request, validate_response + ) return response - def put(self, path, data=None, format=None, content_type=None, - follow=False, **extra): + def put( + self, + path, + data=None, + format=None, + content_type=None, + follow=False, + **extra + ): validate_request = extra.pop('validate_request', True) validate_response = extra.pop('validate_response', True) request = self.factory.put( - path, data=data, format='json', content_type=content_type, - SERVER_NAME='example.com', **extra) + path, + data=data, + format='json', + content_type=content_type, + SERVER_NAME='example.com', + **extra + ) response = super(APIClient, self).put( - path, data=data, format='json', content_type=content_type, - follow=follow, SERVER_NAME='example.com', **extra) - - validator.validate_data(path, request, response, validate_request, - validate_response) + path, + data=data, + format='json', + content_type=content_type, + follow=follow, + SERVER_NAME='example.com', + **extra + ) + + validator.validate_data( + path, request, response, validate_request, validate_response + ) return response - def patch(self, path, data=None, format=None, content_type=None, - follow=False, **extra): + def patch( + self, + path, + data=None, + format=None, + content_type=None, + follow=False, + **extra + ): validate_request = extra.pop('validate_request', True) validate_response = extra.pop('validate_response', True) request = self.factory.patch( - path, data=data, format='json', content_type=content_type, - SERVER_NAME='example.com', **extra) + path, + data=data, + format='json', + content_type=content_type, + SERVER_NAME='example.com', + **extra + ) response = super(APIClient, self).patch( - path, data=data, format='json', content_type=content_type, - follow=follow, SERVER_NAME='example.com', **extra) - - validator.validate_data(path, request, response, validate_request, - validate_response) + path, + data=data, + format='json', + content_type=content_type, + follow=follow, + SERVER_NAME='example.com', + **extra + ) + + validator.validate_data( + path, request, response, validate_request, validate_response + ) return response diff --git a/patchwork/tests/api/validator.py b/patchwork/tests/api/validator.py index 96912227..eb74b37d 100644 --- a/patchwork/tests/api/validator.py +++ b/patchwork/tests/api/validator.py @@ -20,8 +20,14 @@ # docs/api/schemas SCHEMAS_DIR = os.path.join( - os.path.dirname(os.path.abspath(__file__)), os.pardir, os.pardir, - os.pardir, 'docs', 'api', 'schemas') + os.path.dirname(os.path.abspath(__file__)), + os.pardir, + os.pardir, + os.pardir, + 'docs', + 'api', + 'schemas', +) _LOADED_SPECS = {} @@ -41,7 +47,6 @@ def search(path_pattern, full_url_pattern): class RegexValidator(object): - def __init__(self, regex): self.regex = re.compile(regex, re.IGNORECASE) @@ -84,9 +89,11 @@ def _load_spec(version): if _LOADED_SPECS.get(version): return _LOADED_SPECS[version] - spec_path = os.path.join(SCHEMAS_DIR, - 'v{}'.format(version) if version else 'latest', - 'patchwork.yaml') + spec_path = os.path.join( + SCHEMAS_DIR, + 'v{}'.format(version) if version else 'latest', + 'patchwork.yaml', + ) with open(spec_path, 'r') as fh: data = yaml.load(fh, Loader=yaml.SafeLoader) @@ -96,8 +103,9 @@ def _load_spec(version): return _LOADED_SPECS[version] -def validate_data(path, request, response, validate_request, - validate_response): +def validate_data( + path, request, response, validate_request, validate_response +): if response.status_code == status.HTTP_405_METHOD_NOT_ALLOWED: return @@ -107,8 +115,7 @@ def validate_data(path, request, response, validate_request, # request if validate_request: - validator = RequestValidator( - spec, custom_formatters=CUSTOM_FORMATTERS) + validator = RequestValidator(spec, custom_formatters=CUSTOM_FORMATTERS) result = validator.validate(request) try: result.raise_for_errors() @@ -120,6 +127,7 @@ def validate_data(path, request, response, validate_request, # response if validate_response: validator = ResponseValidator( - spec, custom_formatters=CUSTOM_FORMATTERS) + spec, custom_formatters=CUSTOM_FORMATTERS + ) result = validator.validate(request, response) result.raise_for_errors() diff --git a/patchwork/tests/test_checks.py b/patchwork/tests/test_checks.py index d455e98f..eb59c7d2 100644 --- a/patchwork/tests/test_checks.py +++ b/patchwork/tests/test_checks.py @@ -15,7 +15,6 @@ class PatchChecksTest(TransactionTestCase): - def setUp(self): self.patch = create_patches()[0] self.user = create_user() @@ -31,8 +30,9 @@ def _create_check(self, **kwargs): def assertCheckEqual(self, patch, check_state): # noqa state_names = dict(Check.STATE_CHOICES) - self.assertEqual(self.patch.combined_check_state, - state_names[check_state]) + self.assertEqual( + self.patch.combined_check_state, state_names[check_state] + ) def assertChecksEqual(self, patch, checks=None): # noqa if not checks: @@ -41,7 +41,8 @@ def assertChecksEqual(self, patch, checks=None): # noqa self.assertEqual(len(self.patch.checks), len(checks)) self.assertEqual( sorted(self.patch.checks, key=lambda check: check.id), - sorted(checks, key=lambda check: check.id)) + sorted(checks, key=lambda check: check.id), + ) def assertCheckCountEqual(self, patch, total, state_counts=None): # noqa if not state_counts: diff --git a/patchwork/tests/test_expiry.py b/patchwork/tests/test_expiry.py index 2c6ec2b0..f7c810ab 100644 --- a/patchwork/tests/test_expiry.py +++ b/patchwork/tests/test_expiry.py @@ -17,41 +17,41 @@ class TestRegistrationExpiry(TestCase): - def register(self, date): user = create_user() user.is_active = False user.date_joined = user.last_login = date user.save() - conf = EmailConfirmation(type='registration', user=user, - email=user.email) + conf = EmailConfirmation( + type='registration', user=user, email=user.email + ) conf.date = date conf.save() return (user, conf) def test_old_registration_expiry(self): - date = ((datetime.datetime.utcnow() - EmailConfirmation.validity) - - datetime.timedelta(hours=1)) + date = ( + datetime.datetime.utcnow() - EmailConfirmation.validity + ) - datetime.timedelta(hours=1) user, conf = self.register(date) expire_notifications() self.assertFalse(User.objects.filter(pk=user.pk).exists()) - self.assertFalse( - EmailConfirmation.objects.filter(pk=conf.pk).exists()) + self.assertFalse(EmailConfirmation.objects.filter(pk=conf.pk).exists()) def test_recent_registration_expiry(self): - date = ((datetime.datetime.utcnow() - EmailConfirmation.validity) + - datetime.timedelta(hours=1)) + date = ( + datetime.datetime.utcnow() - EmailConfirmation.validity + ) + datetime.timedelta(hours=1) user, conf = self.register(date) expire_notifications() self.assertTrue(User.objects.filter(pk=user.pk).exists()) - self.assertTrue( - EmailConfirmation.objects.filter(pk=conf.pk).exists()) + self.assertTrue(EmailConfirmation.objects.filter(pk=conf.pk).exists()) def test_inactive_registration_expiry(self): user, conf = self.register(datetime.datetime.utcnow()) @@ -64,8 +64,7 @@ def test_inactive_registration_expiry(self): expire_notifications() self.assertTrue(User.objects.filter(pk=user.pk).exists()) - self.assertFalse( - EmailConfirmation.objects.filter(pk=conf.pk).exists()) + self.assertFalse(EmailConfirmation.objects.filter(pk=conf.pk).exists()) def test_patch_submitter_expiry(self): # someone submits a patch... @@ -73,15 +72,17 @@ def test_patch_submitter_expiry(self): submitter = patch.submitter # ... then starts registration... - date = ((datetime.datetime.utcnow() - EmailConfirmation.validity) - - datetime.timedelta(hours=1)) + date = ( + datetime.datetime.utcnow() - EmailConfirmation.validity + ) - datetime.timedelta(hours=1) user = create_user(link_person=False, email=submitter.email) user.is_active = False user.date_joined = user.last_login = date user.save() - conf = EmailConfirmation(type='registration', user=user, - email=user.email) + conf = EmailConfirmation( + type='registration', user=user, email=user.email + ) conf.date = date conf.save() @@ -89,8 +90,9 @@ def test_patch_submitter_expiry(self): expire_notifications() # we should see no matching user - self.assertFalse(User.objects.filter(email=patch.submitter.email) - .exists()) + self.assertFalse( + User.objects.filter(email=patch.submitter.email).exists() + ) # but the patch and person should still be present self.assertTrue(Person.objects.filter(pk=submitter.pk).exists()) self.assertTrue(Patch.objects.filter(pk=patch.pk).exists()) diff --git a/patchwork/tests/test_fields.py b/patchwork/tests/test_fields.py index 577b7b23..49946b58 100644 --- a/patchwork/tests/test_fields.py +++ b/patchwork/tests/test_fields.py @@ -9,7 +9,6 @@ class TestHashField(SimpleTestCase): - def test_n_bytes(self): """Sanity check the hashing algorithm. diff --git a/patchwork/tests/test_management.py b/patchwork/tests/test_management.py index d24eea98..44ff369c 100644 --- a/patchwork/tests/test_management.py +++ b/patchwork/tests/test_management.py @@ -17,7 +17,6 @@ class ParsemailTest(TestCase): - def test_invalid_path(self): # this can raise IOError, CommandError, or FileNotFoundError, # depending of the versions of Python and Django used. Just @@ -118,53 +117,53 @@ def test_invalid_path(self): def test_invalid_mbox(self): out = StringIO() # we haven't created a project yet, so this will fail - call_command('parsearchive', - os.path.join(TEST_MAIL_DIR, - '0001-git-pull-request.mbox'), - stdout=out) + call_command( + 'parsearchive', + os.path.join(TEST_MAIL_DIR, '0001-git-pull-request.mbox'), + stdout=out, + ) self.assertIn('Processed 1 messages -->', out.getvalue()) self.assertIn(' 1 dropped', out.getvalue()) class ReplacerelationsTest(TestCase): - def test_invalid_path(self): out = StringIO() with self.assertRaises(SystemExit) as exc: - call_command('replacerelations', 'xyz123random', '-v 0', - stdout=out) + call_command( + 'replacerelations', 'xyz123random', '-v 0', stdout=out + ) self.assertEqual(exc.exception.code, 1) def test_valid_relations(self): test_submitter = utils.create_person() utils.create_patches(8, submitter=test_submitter) - patch_ids = (models.Patch.objects - .filter(submitter=test_submitter) - .values_list('id', flat=True)) + patch_ids = models.Patch.objects.filter( + submitter=test_submitter + ).values_list('id', flat=True) - with tempfile.NamedTemporaryFile(delete=False, - mode='w+') as f1: + with tempfile.NamedTemporaryFile(delete=False, mode='w+') as f1: for i in range(0, len(patch_ids), 3): # we write out the patch IDs this way so that we can # have a mix of 3-patch and 2-patch lines without special # casing the format string. - f1.write('%s\n' % ' '.join(map(str, patch_ids[i:(i + 3)]))) + f1.write('%s\n' % ' '.join(map(str, patch_ids[i : (i + 3)]))) out = StringIO() call_command('replacerelations', f1.name, stdout=out) self.assertEqual(models.PatchRelation.objects.count(), 3) os.unlink(f1.name) - patch_ids_with_missing = ( - list(patch_ids) + - [i for i in range(max(patch_ids), max(patch_ids) + 3)] - ) - with tempfile.NamedTemporaryFile(delete=False, - mode='w+') as f2: + patch_ids_with_missing = list(patch_ids) + [ + i for i in range(max(patch_ids), max(patch_ids) + 3) + ] + with tempfile.NamedTemporaryFile(delete=False, mode='w+') as f2: for i in range(0, len(patch_ids_with_missing), 3): - f2.write('%s\n' % ' '.join( - map(str, patch_ids_with_missing[i:(i + 3)]))) + f2.write( + '%s\n' + % ' '.join(map(str, patch_ids_with_missing[i : (i + 3)])) + ) call_command('replacerelations', f2.name, stdout=out) self.assertEqual(models.PatchRelation.objects.count(), 3) diff --git a/patchwork/tests/test_notifications.py b/patchwork/tests/test_notifications.py index 5b575fb2..b2fd0049 100644 --- a/patchwork/tests/test_notifications.py +++ b/patchwork/tests/test_notifications.py @@ -69,7 +69,7 @@ def test_notification_cancelled(self): def test_notification_updated(self): """Ensure we update notifications when the patch has a second change, - but keep the original patch details""" + but keep the original patch details""" patch = create_patch(project=self.project) oldstate = patch.state newstates = [create_state(), create_state()] @@ -91,7 +91,7 @@ def test_notification_updated(self): def test_notifications_disabled(self): """Ensure we don't see notifications created when a project is - configured not to send them""" + configured not to send them""" patch = create_patch() # don't use self.project state = create_state() @@ -101,13 +101,13 @@ def test_notifications_disabled(self): class PatchNotificationEmailTest(TestCase): - def setUp(self): self.project = create_project(send_notifications=True) def _expire_notifications(self, **kwargs): - timestamp = datetime.datetime.utcnow() - \ - datetime.timedelta(minutes=settings.NOTIFICATION_DELAY_MINUTES + 1) + timestamp = datetime.datetime.utcnow() - datetime.timedelta( + minutes=settings.NOTIFICATION_DELAY_MINUTES + 1 + ) qs = PatchChangeNotification.objects.all() if kwargs: @@ -141,8 +141,9 @@ def test_notifications(self): self.assertIn(patch.get_absolute_url(), msg.body) def test_notification_escaping(self): - patch = create_patch(name='Patch name with " character', - project=self.project) + patch = create_patch( + name='Patch name with " character', project=self.project + ) PatchChangeNotification(patch=patch, orig_state=patch.state).save() self._expire_notifications() @@ -157,8 +158,7 @@ def test_notification_escaping(self): def test_notification_optout(self): """Ensure opt-out addresses don't get notifications.""" patch = create_patch(project=self.project) - PatchChangeNotification(patch=patch, - orig_state=patch.state).save() + PatchChangeNotification(patch=patch, orig_state=patch.state).save() self._expire_notifications() @@ -186,8 +186,8 @@ def test_notification_merge(self): def test_unexpired_notification_merge(self): """Test that when there are multiple pending notifications, with - at least one within the notification delay, that other notifications - are held""" + at least one within the notification delay, that other notifications + are held""" patches = create_patches(2, project=self.project) for patch in patches: patch.save() diff --git a/patchwork/tests/test_paginator.py b/patchwork/tests/test_paginator.py index 5cf47a68..329dc44b 100644 --- a/patchwork/tests/test_paginator.py +++ b/patchwork/tests/test_paginator.py @@ -14,7 +14,6 @@ class PaginatorTest(TestCase): - def setUp(self): self.user = create_user() self.user.profile.items_per_page = ITEMS_PER_PAGE @@ -24,37 +23,46 @@ def setUp(self): def _get_patches(self, params): return self.client.get( - reverse('patch-list', kwargs={ - 'project_id': self.project.linkname}), - params) + reverse( + 'patch-list', kwargs={'project_id': self.project.linkname} + ), + params, + ) def test_items_per_page(self): response = self._get_patches({}) self.assertEqual(response.status_code, 200) - self.assertEqual(len(response.context['page'].object_list), - len(self.patches)) + self.assertEqual( + len(response.context['page'].object_list), len(self.patches) + ) - self.client.login(username=self.user.username, - password=self.user.username) + self.client.login( + username=self.user.username, password=self.user.username + ) response = self._get_patches({}) self.assertEqual(response.status_code, 200) - self.assertEqual(len(response.context['page'].object_list), - ITEMS_PER_PAGE) + self.assertEqual( + len(response.context['page'].object_list), ITEMS_PER_PAGE + ) def test_page_valid(self): page = 2 - self.client.login(username=self.user.username, - password=self.user.username) + self.client.login( + username=self.user.username, password=self.user.username + ) for page_ in [2, str(2)]: response = self._get_patches({'page': page_}) self.assertEqual(response.status_code, 200) - self.assertEqual(response.context['page'].object_list[0].id, - self.patches[-page].id) + self.assertEqual( + response.context['page'].object_list[0].id, + self.patches[-page].id, + ) def test_navigation(self): - self.client.login(username=self.user.username, - password=self.user.username) + self.client.login( + username=self.user.username, password=self.user.username + ) for page_num in range(1, 11): response = self._get_patches({'page': page_num}) @@ -65,8 +73,7 @@ def test_navigation(self): # if there is a prev page, it should be: if page.has_previous(): - self.assertEqual(page.previous_page_number(), - page_num - 1) + self.assertEqual(page.previous_page_number(), page_num - 1) # ... either in the adjacent set or in the trailing set if adjacent is not None: self.assertIn(page_num - 1, adjacent) @@ -75,8 +82,7 @@ def test_navigation(self): # if there is a next page, it should be: if page.has_next(): - self.assertEqual(page.next_page_number(), - page_num + 1) + self.assertEqual(page.next_page_number(), page_num + 1) # ... either in the adjacent set or in the leading set if adjacent is not None: self.assertIn(page_num + 1, adjacent) @@ -91,9 +97,11 @@ def test_navigation(self): self.assertNotIn(x, trailing) def test_page_invalid(self): - self.client.login(username=self.user.username, - password=self.user.username) + self.client.login( + username=self.user.username, password=self.user.username + ) response = self._get_patches({'page': 'foo'}) self.assertEqual(response.status_code, 200) - self.assertEqual(response.context['page'].object_list[0].id, - self.patches[-1].id) + self.assertEqual( + response.context['page'].object_list[0].id, self.patches[-1].id + ) diff --git a/patchwork/tests/test_parser.py b/patchwork/tests/test_parser.py index 12f984bc..c22974a4 100644 --- a/patchwork/tests/test_parser.py +++ b/patchwork/tests/test_parser.py @@ -102,7 +102,8 @@ def create_email( msg = MIMEText(content, _charset='us-ascii') return _create_email( - msg, msgid, subject, sender, listid, in_reply_to, headers) + msg, msgid, subject, sender, listid, in_reply_to, headers + ) def parse_mail(*args, **kwargs): @@ -111,7 +112,6 @@ def parse_mail(*args, **kwargs): class PatchTest(TestCase): - def _find_content(self, mbox_filename): mail = read_mail(mbox_filename) diff, message = find_content(mail) @@ -161,8 +161,9 @@ class UTF8InlinePatchTest(InlinePatchTest): orig_diff = read_patch('0002-utf-8.patch', 'utf-8') def setUp(self): - msg = MIMEText(self.orig_content + '\n' + self.orig_diff, - _charset='utf-8') + msg = MIMEText( + self.orig_content + '\n' + self.orig_diff, _charset='utf-8' + ) email = _create_email(msg) self.diff, self.content = find_content(email) @@ -184,8 +185,9 @@ class SignatureCommentTest(InlinePatchTest): orig_content = 'Test comment\nmore comment' def setUp(self): - email = create_email(self.orig_content + '\n-- \nsig\n' + - self.orig_diff) + email = create_email( + self.orig_content + '\n-- \nsig\n' + self.orig_diff + ) self.diff, self.content = find_content(email) @@ -202,19 +204,26 @@ class ListFooterTest(InlinePatchTest): orig_content = 'Test comment\nmore comment' def setUp(self): - email = create_email('\n'.join([ - self.orig_content, - '_______________________________________________', - 'Linuxppc-dev mailing list', - self.orig_diff])) + email = create_email( + '\n'.join( + [ + self.orig_content, + '_______________________________________________', + 'Linuxppc-dev mailing list', + self.orig_diff, + ] + ) + ) self.diff, self.content = find_content(email) class DiffWordInCommentTest(InlinePatchTest): - orig_content = 'Lines can start with words beginning in "diff"\n' + \ - 'difficult\nDifferent' + orig_content = ( + 'Lines can start with words beginning in "diff"\n' + + 'difficult\nDifferent' + ) class UpdateCommentTest(InlinePatchTest): @@ -228,10 +237,12 @@ class SenderEncodingTest(TestCase): @staticmethod def _create_email(from_header): - mail = 'Message-Id: %s\n' % make_msgid() + \ - 'From: %s\n' % from_header + \ - 'Subject: test\n\n' + \ - 'test' + mail = ( + 'Message-Id: %s\n' % make_msgid() + + 'From: %s\n' % from_header + + 'Subject: test\n\n' + + 'test' + ) return message_from_string(mail) def _test_encoding(self, from_header, sender_name, sender_email): @@ -288,10 +299,10 @@ class SenderCorrelationTest(TestCase): """ @staticmethod - def _create_email(from_header, reply_tos=None, ccs=None, - x_original_from=None): - mail = 'Message-Id: %s\n' % make_msgid() + \ - 'From: %s\n' % from_header + def _create_email( + from_header, reply_tos=None, ccs=None, x_original_from=None + ): + mail = 'Message-Id: %s\n' % make_msgid() + 'From: %s\n' % from_header if reply_tos: mail += 'Reply-To: %s\n' % ', '.join(reply_tos) @@ -302,8 +313,7 @@ def _create_email(from_header, reply_tos=None, ccs=None, if x_original_from: mail += 'X-Original-From: %s\n' % x_original_from - mail += 'Subject: Tests\n\n'\ - 'test\n' + mail += 'Subject: Tests\n\n' 'test\n' return message_from_string(mail) @@ -350,7 +360,8 @@ def test_mailman_dmarc_munging(self): project = create_project() real_sender = 'Existing Sender ' munged_sender = 'Existing Sender via List <{}>'.format( - project.listemail) + project.listemail + ) other_email = 'Other Person ' # Unmunged author @@ -371,8 +382,11 @@ def test_mailman_dmarc_munging(self): self.assertEqual(person_b.id, person_a.id) # Multiple Reply-Tos and Ccs - mail = self._create_email(munged_sender, [other_email, real_sender], - [other_email, other_email]) + mail = self._create_email( + munged_sender, + [other_email, real_sender], + [other_email, other_email], + ) person_b = get_or_create_author(mail, project) self.assertEqual(person_b._state.adding, False) self.assertEqual(person_b.id, person_a.id) @@ -381,7 +395,8 @@ def test_google_dmarc_munging(self): project = create_project() real_sender = 'Existing Sender ' munged_sender = "'Existing Sender' via List <{}>".format( - project.listemail) + project.listemail + ) # Unmunged author mail = self._create_email(real_sender) @@ -430,9 +445,11 @@ def _create_email(msgid, references=None): references (list): A list of preceding messages' msgids, oldest first """ - mail = 'Message-Id: %s\n' % msgid + \ - 'From: example user \n' + \ - 'Subject: Tests\n' + mail = ( + 'Message-Id: %s\n' % msgid + + 'From: example user \n' + + 'Subject: Tests\n' + ) if references: mail += 'In-Reply-To: %s\n' % references[-1] @@ -446,8 +463,9 @@ def test_new_series(self): email = self._create_email(msgid) project = create_project() - self.assertFalse(find_series(project, email, - get_or_create_author(email))) + self.assertFalse( + find_series(project, email, get_or_create_author(email)) + ) def test_first_reply(self): msgid_a = make_msgid() @@ -457,8 +475,9 @@ def test_first_reply(self): # assume msgid_a was already handled ref = create_series_reference(msgid=msgid_a) - series = find_series(ref.series.project, email, - get_or_create_author(email)) + series = find_series( + ref.series.project, email, get_or_create_author(email) + ) self.assertEqual(series.first(), ref.series) def test_nested_series(self): @@ -467,20 +486,23 @@ def test_nested_series(self): msgids = [make_msgid()] project = create_project() series_v1 = create_series(project=project) - create_series_reference(msgid=msgids[0], series=series_v1, - project=project) + create_series_reference( + msgid=msgids[0], series=series_v1, project=project + ) # ...and three patches for i in range(3): msgids.append(make_msgid()) - create_series_reference(msgid=msgids[-1], series=series_v1, - project=project) + create_series_reference( + msgid=msgids[-1], series=series_v1, project=project + ) # now create a new series with "cover letter" msgids.append(make_msgid()) series_v2 = create_series(project=project) - ref_v2 = create_series_reference(msgid=msgids[-1], series=series_v2, - project=project) + ref_v2 = create_series_reference( + msgid=msgids[-1], series=series_v2, project=project + ) # ...and the "first patch" of this new series msgid = make_msgid() @@ -497,10 +519,13 @@ class SubjectEncodingTest(TestCase): @staticmethod def _create_email(subject): - mail = 'Message-Id: %s\n' % make_msgid() + \ - 'From: example user \n' + \ - 'Subject: %s\n\n' % subject + \ - 'test\n\n' + SAMPLE_DIFF + mail = ( + 'Message-Id: %s\n' % make_msgid() + + 'From: example user \n' + + 'Subject: %s\n\n' % subject + + 'test\n\n' + + SAMPLE_DIFF + ) return message_from_string(mail) def _test_encoding(self, subject_header, parsed_subject): @@ -526,7 +551,7 @@ def test_subject_utf8qp_multiple_encoding(self): class MultipleProjectPatchTest(TestCase): """Test that patches sent to multiple patchwork projects are - handled correctly.""" + handled correctly.""" orig_content = 'Test Comment' patch_filename = '0001-add-line.patch' @@ -540,7 +565,8 @@ def setUp(self): email = create_email( content=''.join([self.orig_content, '\n', patch]), msgid=self.msgid, - listid='<%s>' % self.p1.listid) + listid='<%s>' % self.p1.listid, + ) parse_mail(email) del email['List-ID'] @@ -554,7 +580,7 @@ def test_parsed_projects(self): class MultipleProjectPatchCommentTest(MultipleProjectPatchTest): """Test that followups to multiple-project patches end up on the - correct patch.""" + correct patch.""" comment_msgid = '<2@example.com>' comment_content = 'test comment' @@ -566,7 +592,8 @@ def setUp(self): email = create_email( content=self.comment_content, msgid=self.comment_msgid, - listid='<%s>' % project.listid) + listid='<%s>' % project.listid, + ) email['In-Reply-To'] = self.msgid parse_mail(email) @@ -575,7 +602,8 @@ def test_parsed_comment(self): patch = Patch.objects.filter(project=project)[0] # we should see the reply comment only self.assertEqual( - PatchComment.objects.filter(patch=patch).count(), 1) + PatchComment.objects.filter(patch=patch).count(), 1 + ) class ListIdHeaderTest(TestCase): @@ -609,7 +637,7 @@ def test_substring_list_id(self): def test_short_list_id(self): """Some mailing lists have List-Id headers in short formats, where it - is only the list ID itself (without enclosing angle-brackets). """ + is only the list ID itself (without enclosing angle-brackets).""" email = MIMEText('') email['List-Id'] = self.project.listid project = find_project(email) @@ -656,33 +684,39 @@ def test_git_pull_git_2_14_3(self): def test_git_pull_with_diff(self): diff, message = self._find_content( - '0003-git-pull-request-with-diff.mbox') + '0003-git-pull-request-with-diff.mbox' + ) pull_url = parse_pull_request(message) self.assertEqual( 'git://git.kernel.org/pub/scm/linux/kernel/git/tip/' 'linux-2.6-tip.git x86-fixes-for-linus', - pull_url) + pull_url, + ) self.assertTrue( - diff.startswith('diff --git a/arch/x86/include/asm/smp.h'), - diff) + diff.startswith('diff --git a/arch/x86/include/asm/smp.h'), diff + ) def test_git_pull_newline_in_url(self): diff, message = self._find_content( - '0023-git-pull-request-newline-in-url.mbox') + '0023-git-pull-request-newline-in-url.mbox' + ) pull_url = parse_pull_request(message) self.assertEqual( 'https://git.kernel.org/pub/scm/linux/kernel/git/matthias.bgg/' 'linux.git/ tags/v5.4-next-soc', - pull_url) + pull_url, + ) def test_git_pull_trailing_space(self): diff, message = self._find_content( - '0024-git-pull-request-trailing-space.mbox') + '0024-git-pull-request-trailing-space.mbox' + ) pull_url = parse_pull_request(message) self.assertEqual( 'git://git.kernel.org/pub/scm/linux/kernel/git/nsekhar/' 'linux-davinci.git tags/davinci-for-v5.6/soc', - pull_url) + pull_url, + ) def test_git_rename(self): diff, _ = self._find_content('0008-git-rename.mbox') @@ -724,17 +758,21 @@ def test_invalid_charset(self): def test_no_newline(self): """Validate behavior when trailing newline is absent.""" diff, message = self._find_content( - '0011-no-newline-at-end-of-file.mbox') + '0011-no-newline-at-end-of-file.mbox' + ) self.assertTrue(diff is not None) self.assertTrue(message is not None) - self.assertTrue(diff.startswith( - 'diff --git a/tools/testing/selftests/powerpc/Makefile')) + self.assertTrue( + diff.startswith( + 'diff --git a/tools/testing/selftests/powerpc/Makefile' + ) + ) # Confirm the trailing no newline marker doesn't end up in the comment - self.assertFalse(message.rstrip().endswith( - r'\ No newline at end of file')) + self.assertFalse( + message.rstrip().endswith(r'\ No newline at end of file') + ) # Confirm it's instead at the bottom of the patch - self.assertTrue(diff.rstrip().endswith( - r'\ No newline at end of file')) + self.assertTrue(diff.rstrip().endswith(r'\ No newline at end of file')) # Confirm we got both markers self.assertEqual(2, diff.count(r'\ No newline at end of file')) @@ -872,8 +910,10 @@ def _create_submission_and_comments(self, submission_email): def test_patch_comment(self): body = read_patch('0001-add-line.patch') patch_email = create_email(body, listid=self.project.listid) - comment_a_msgid, comment_b_msgid = \ - self._create_submission_and_comments(patch_email) + ( + comment_a_msgid, + comment_b_msgid, + ) = self._create_submission_and_comments(patch_email) self.assertEqual(1, Patch.objects.count()) self.assertEqual(2, PatchComment.objects.count()) @@ -886,9 +926,12 @@ def test_cover_comment(self): cover_email = create_email( 'test cover letter', subject='[0/2] A cover letter', - listid=self.project.listid) - comment_a_msgid, comment_b_msgid = \ - self._create_submission_and_comments(cover_email) + listid=self.project.listid, + ) + ( + comment_a_msgid, + comment_b_msgid, + ) = self._create_submission_and_comments(cover_email) self.assertEqual(1, Cover.objects.count()) self.assertEqual(2, CoverComment.objects.count()) @@ -914,7 +957,8 @@ def setUp(self): def _get_email(self): email = create_email( - self.patch, msgid=self.msgid, listid='<%s>' % self.project.listid) + self.patch, msgid=self.msgid, listid='<%s>' % self.project.listid + ) return email def assertState(self, state): # noqa @@ -957,88 +1001,116 @@ class ParseInitialTagsTest(PatchTest): fixtures = ['default_tags'] patch_filename = '0001-add-line.patch' - orig_content = ('test comment\n\n' + - 'Tested-by: Test User \n' + - 'Reviewed-by: Test User \n') + orig_content = ( + 'test comment\n\n' + + 'Tested-by: Test User \n' + + 'Reviewed-by: Test User \n' + ) def setUp(self): project = create_project(listid='test.example.com') self.orig_diff = read_patch(self.patch_filename) - email = create_email(self.orig_content + '\n' + self.orig_diff, - listid=project.listid) + email = create_email( + self.orig_content + '\n' + self.orig_diff, listid=project.listid + ) parse_mail(email) def test_tags(self): self.assertEqual(Patch.objects.count(), 1) patch = Patch.objects.all()[0] - self.assertEqual(patch.patchtag_set.filter( - tag__name='Acked-by').count(), 0) - self.assertEqual(patch.patchtag_set.get( - tag__name='Reviewed-by').count, 1) - self.assertEqual(patch.patchtag_set.get( - tag__name='Tested-by').count, 1) + self.assertEqual( + patch.patchtag_set.filter(tag__name='Acked-by').count(), 0 + ) + self.assertEqual( + patch.patchtag_set.get(tag__name='Reviewed-by').count, 1 + ) + self.assertEqual( + patch.patchtag_set.get(tag__name='Tested-by').count, 1 + ) class ParseCommentTagsTest(PatchTest): fixtures = ['default_tags'] patch_filename = '0001-add-line.patch' - comment_content = ('test comment\n\n' + - 'Tested-by: Test User \n' + - 'Reviewed-by: Test User \n') + comment_content = ( + 'test comment\n\n' + + 'Tested-by: Test User \n' + + 'Reviewed-by: Test User \n' + ) def setUp(self): project = create_project(listid='test.example.com') self.orig_diff = read_patch(self.patch_filename) - email = create_email(self.orig_diff, - listid=project.listid) + email = create_email(self.orig_diff, listid=project.listid) parse_mail(email) - email2 = create_email(self.comment_content, - in_reply_to=email['Message-Id']) + email2 = create_email( + self.comment_content, in_reply_to=email['Message-Id'] + ) parse_mail(email2) def test_tags(self): self.assertEqual(Patch.objects.count(), 1) patch = Patch.objects.all()[0] - self.assertEqual(patch.patchtag_set.filter( - tag__name='Acked-by').count(), 0) - self.assertEqual(patch.patchtag_set.get( - tag__name='Reviewed-by').count, 1) - self.assertEqual(patch.patchtag_set.get( - tag__name='Tested-by').count, 1) + self.assertEqual( + patch.patchtag_set.filter(tag__name='Acked-by').count(), 0 + ) + self.assertEqual( + patch.patchtag_set.get(tag__name='Reviewed-by').count, 1 + ) + self.assertEqual( + patch.patchtag_set.get(tag__name='Tested-by').count, 1 + ) class SubjectTest(TestCase): - def test_clean_subject(self): self.assertEqual(clean_subject('meep'), ('meep', [])) self.assertEqual(clean_subject('Re: meep'), ('meep', [])) self.assertEqual(clean_subject('[PATCH] meep'), ('meep', [])) - self.assertEqual(clean_subject("[PATCH] meep \n meep"), - ('meep meep', [])) - self.assertEqual(clean_subject("[PATCH] meep,\n meep"), - ('meep, meep', [])) - self.assertEqual(clean_subject('[PATCH RFC] meep'), - ('[RFC] meep', ['RFC'])) - self.assertEqual(clean_subject('[PATCH,RFC] meep'), - ('[RFC] meep', ['RFC'])) - self.assertEqual(clean_subject('[PATCH,1/2] meep'), - ('[1/2] meep', ['1/2'])) - self.assertEqual(clean_subject('[PATCH RFC 1/2] meep'), - ('[RFC,1/2] meep', ['RFC', '1/2'])) - self.assertEqual(clean_subject('[PATCH] [RFC] meep'), - ('[RFC] meep', ['RFC'])) - self.assertEqual(clean_subject('[PATCH] [RFC,1/2] meep'), - ('[RFC,1/2] meep', ['RFC', '1/2'])) - self.assertEqual(clean_subject('[PATCH] [RFC] [1/2] meep'), - ('[RFC,1/2] meep', ['RFC', '1/2'])) - self.assertEqual(clean_subject('[PATCH] rewrite [a-z] regexes'), - ('rewrite [a-z] regexes', [])) - self.assertEqual(clean_subject('[PATCH] [RFC] rewrite [a-z] regexes'), - ('[RFC] rewrite [a-z] regexes', ['RFC'])) - self.assertEqual(clean_subject('[foo] [bar] meep', ['foo']), - ('[bar] meep', ['bar'])) - self.assertEqual(clean_subject('[FOO] [bar] meep', ['foo']), - ('[bar] meep', ['bar'])) + self.assertEqual( + clean_subject("[PATCH] meep \n meep"), ('meep meep', []) + ) + self.assertEqual( + clean_subject("[PATCH] meep,\n meep"), ('meep, meep', []) + ) + self.assertEqual( + clean_subject('[PATCH RFC] meep'), ('[RFC] meep', ['RFC']) + ) + self.assertEqual( + clean_subject('[PATCH,RFC] meep'), ('[RFC] meep', ['RFC']) + ) + self.assertEqual( + clean_subject('[PATCH,1/2] meep'), ('[1/2] meep', ['1/2']) + ) + self.assertEqual( + clean_subject('[PATCH RFC 1/2] meep'), + ('[RFC,1/2] meep', ['RFC', '1/2']), + ) + self.assertEqual( + clean_subject('[PATCH] [RFC] meep'), ('[RFC] meep', ['RFC']) + ) + self.assertEqual( + clean_subject('[PATCH] [RFC,1/2] meep'), + ('[RFC,1/2] meep', ['RFC', '1/2']), + ) + self.assertEqual( + clean_subject('[PATCH] [RFC] [1/2] meep'), + ('[RFC,1/2] meep', ['RFC', '1/2']), + ) + self.assertEqual( + clean_subject('[PATCH] rewrite [a-z] regexes'), + ('rewrite [a-z] regexes', []), + ) + self.assertEqual( + clean_subject('[PATCH] [RFC] rewrite [a-z] regexes'), + ('[RFC] rewrite [a-z] regexes', ['RFC']), + ) + self.assertEqual( + clean_subject('[foo] [bar] meep', ['foo']), ('[bar] meep', ['bar']) + ) + self.assertEqual( + clean_subject('[FOO] [bar] meep', ['foo']), ('[bar] meep', ['bar']) + ) def test_subject_check(self): self.assertIsNotNone(subject_check('RE: meep')) @@ -1087,18 +1159,19 @@ def test_version(self): class SubjectMatchTest(TestCase): - def setUp(self): self.list_id = 'test-subject-match.test.org' - self.project_x = create_project(name='PROJECT X', - listid=self.list_id, - subject_match=r'.*PROJECT[\s]?X.*') - self.default_project = create_project(name='Default', - listid=self.list_id, - subject_match=r'') - self.keyword_project = create_project(name='keyword', - listid=self.list_id, - subject_match=r'keyword') + self.project_x = create_project( + name='PROJECT X', + listid=self.list_id, + subject_match=r'.*PROJECT[\s]?X.*', + ) + self.default_project = create_project( + name='Default', listid=self.list_id, subject_match=r'' + ) + self.keyword_project = create_project( + name='keyword', listid=self.list_id, subject_match=r'keyword' + ) self.email = MIMEText('') self.email['List-Id'] = self.list_id @@ -1135,8 +1208,9 @@ def test_nonexistent_project(self): self.assertEqual(project, None) def test_list_id_override(self): - project = find_project(self.email_no_project, - self.keyword_project.listid) + project = find_project( + self.email_no_project, self.keyword_project.listid + ) self.assertEqual(project, self.keyword_project) @@ -1227,8 +1301,12 @@ def test_duplicate_comment(self): m1 = create_email(diff, listid=self.listid, msgid='1@example.com') _parse_mail(m1) - m2 = create_email('test', listid=self.listid, msgid='2@example.com', - in_reply_to='1@example.com') + m2 = create_email( + 'test', + listid=self.listid, + msgid='2@example.com', + in_reply_to='1@example.com', + ) self._test_duplicate_mail(m2) self.assertEqual(Patch.objects.count(), 1) @@ -1245,7 +1323,6 @@ def test_duplicate_coverletter(self): class TestCommentCorrelation(TestCase): - def test_find_patch_for_comment__no_reply(self): """Test behavior for mails that don't match anything we have.""" project = create_project() diff --git a/patchwork/tests/test_series.py b/patchwork/tests/test_series.py index 26749239..8d8c4e14 100644 --- a/patchwork/tests/test_series.py +++ b/patchwork/tests/test_series.py @@ -19,7 +19,6 @@ class _BaseTestCase(TestCase): - def setUp(self): utils.create_state() @@ -82,8 +81,9 @@ def assertSerialized(self, patches, counts): # TODO(stephenfin): Rework this function into two different # functions - we're clearly not always testing patches here if isinstance(patch, models.Patch): - self.assertEqual(series[idx].patches.get(id=patch.id), - patch) + self.assertEqual( + series[idx].patches.get(id=patch.id), patch + ) else: self.assertEqual(series[idx].cover_letter, patch) @@ -102,8 +102,7 @@ def test_single_patch(self): - [PATCH] test: Add some lorem ipsum """ - _, patches, _ = self._parse_mbox( - 'base-single-patch.mbox', [0, 1, 0]) + _, patches, _ = self._parse_mbox('base-single-patch.mbox', [0, 1, 0]) self.assertSerialized(patches, [1]) @@ -119,7 +118,8 @@ def test_cover_letter(self): - [PATCH 2/2] test: Convert to Markdown """ covers, patches, _ = self._parse_mbox( - 'base-cover-letter.mbox', [1, 2, 0]) + 'base-cover-letter.mbox', [1, 2, 0] + ) self.assertSerialized(patches, [2]) self.assertSerialized(covers, [1]) @@ -135,7 +135,8 @@ def test_no_cover_letter(self): - [PATCH 2/2] test: Convert to Markdown """ _, patches, _ = self._parse_mbox( - 'base-no-cover-letter.mbox', [0, 2, 0]) + 'base-no-cover-letter.mbox', [0, 2, 0] + ) self.assertSerialized(patches, [2]) @@ -152,7 +153,8 @@ def test_deep_threaded(self): - [PATCH 2/2] test: Convert to Markdown """ covers, patches, _ = self._parse_mbox( - 'base-deep-threaded.mbox', [1, 2, 0]) + 'base-deep-threaded.mbox', [1, 2, 0] + ) self.assertSerialized(patches, [2]) self.assertSerialized(covers, [1]) @@ -170,7 +172,8 @@ def test_out_of_order(self): - [PATCH 0/2] A sample series """ covers, patches, _ = self._parse_mbox( - 'base-out-of-order.mbox', [1, 2, 0]) + 'base-out-of-order.mbox', [1, 2, 0] + ) self.assertSerialized(patches, [2]) self.assertSerialized(covers, [1]) @@ -193,9 +196,11 @@ def test_duplicated(self): project_b = utils.create_project() _, patches_a, _ = self._parse_mbox( - 'base-no-cover-letter.mbox', [0, 2, 0], project=project_a) + 'base-no-cover-letter.mbox', [0, 2, 0], project=project_a + ) _, patches_b, _ = self._parse_mbox( - 'base-no-cover-letter.mbox', [0, 2, 0], project=project_b) + 'base-no-cover-letter.mbox', [0, 2, 0], project=project_b + ) self.assertSerialized(patches_a + patches_b, [2, 2]) @@ -212,7 +217,8 @@ def test_different_versions(self): - [PATCH 4/4] net: dsa: Introduce dsa_get_cpu_port() """ covers, patches, _ = self._parse_mbox( - 'base-different-versions.mbox', [1, 4, 0]) + 'base-different-versions.mbox', [1, 4, 0] + ) self.assertSerialized(covers, [1]) self.assertSerialized(patches, [4]) @@ -234,7 +240,8 @@ def test_multiple_references(self): directory unnecessarily """ covers, patches, _ = self._parse_mbox( - 'bugs-multiple-references.mbox', [1, 4, 0]) + 'bugs-multiple-references.mbox', [1, 4, 0] + ) self.assertSerialized(covers, [1]) self.assertSerialized(patches, [4]) @@ -249,8 +256,7 @@ def test_no_references(self): - [PATCH 1/2] net: ieee802154: remove explicit set skb->sk - [PATCH 2/2] net: ieee802154: fix net_device reference release too """ - _, patches, _ = self._parse_mbox( - 'base-no-references.mbox', [0, 2, 0]) + _, patches, _ = self._parse_mbox('base-no-references.mbox', [0, 2, 0]) self.assertSerialized(patches, [2]) @@ -267,7 +273,8 @@ def test_no_references_no_cover(self): - [Patch 2/2]: powerpc/hotplug/mm: Fix hot-add memory node assoc """ covers, patches, _ = self._parse_mbox( - 'base-no-references-no-cover.mbox', [1, 2, 0]) + 'base-no-references-no-cover.mbox', [1, 2, 0] + ) self.assertSerialized(patches, [2]) self.assertSerialized(covers, [1]) @@ -277,7 +284,8 @@ def test_multiple_content_types(self): Content-Type headers.""" _, patches, _ = self._parse_mbox( - 'bugs-multiple-content-types.mbox', [0, 1, 1]) + 'bugs-multiple-content-types.mbox', [0, 1, 1] + ) patch = patches[0] self.assertEqual(patch_to_mbox(patch).count('Content-Type:'), 1) @@ -308,8 +316,7 @@ def test_basic(self): - [PATCH v2 1/2] test: Add some lorem ipsum - [PATCH v2 2/2] test: Convert to Markdown """ - covers, patches, _ = self._parse_mbox( - 'revision-basic.mbox', [2, 4, 0]) + covers, patches, _ = self._parse_mbox('revision-basic.mbox', [2, 4, 0]) self.assertSerialized(patches, [2, 2]) self.assertSerialized(covers, [1, 1]) @@ -325,7 +332,8 @@ def test_threaded_to_single_patch(self): - [PATCH v2] test: Add some lorem ipsum """ _, patches, _ = self._parse_mbox( - 'revision-threaded-to-single-patch.mbox', [0, 2, 0]) + 'revision-threaded-to-single-patch.mbox', [0, 2, 0] + ) self.assertSerialized(patches, [1, 1]) @@ -347,7 +355,8 @@ def test_threaded_to_cover(self): - [PATCH v2 2/2] test: Convert to Markdown """ covers, patches, _ = self._parse_mbox( - 'revision-threaded-to-cover.mbox', [2, 4, 0]) + 'revision-threaded-to-cover.mbox', [2, 4, 0] + ) self.assertSerialized(patches, [2, 2]) self.assertSerialized(covers, [1, 1]) @@ -370,7 +379,8 @@ def test_threaded_to_patch(self): - [PATCH v2 2/2] test: Convert to Markdown """ covers, patches, _ = self._parse_mbox( - 'revision-threaded-to-patch.mbox', [2, 4, 0]) + 'revision-threaded-to-patch.mbox', [2, 4, 0] + ) self.assertSerialized(patches, [2, 2]) self.assertSerialized(covers, [1, 1]) @@ -393,7 +403,8 @@ def test_out_of_order(self): - [PATCH v2 0/2] A sample series """ covers, patches, _ = self._parse_mbox( - 'revision-out-of-order.mbox', [2, 4, 0]) + 'revision-out-of-order.mbox', [2, 4, 0] + ) self.assertSerialized(patches, [2, 2]) self.assertSerialized(covers, [1, 1]) @@ -414,7 +425,8 @@ def test_no_cover_letter(self): - [PATCH 2/2] test: Convert to Markdown """ covers, patches, _ = self._parse_mbox( - 'revision-no-cover-letter.mbox', [1, 4, 0]) + 'revision-no-cover-letter.mbox', [1, 4, 0] + ) self.assertSerialized(patches, [2, 2]) self.assertSerialized(covers, [1, 0]) @@ -436,7 +448,8 @@ def test_unlabeled(self): - [PATCH 2/2] test: Convert to Markdown """ covers, patches, _ = self._parse_mbox( - 'revision-unlabeled.mbox', [2, 4, 0]) + 'revision-unlabeled.mbox', [2, 4, 0] + ) self.assertSerialized(patches, [2, 2]) self.assertSerialized(covers, [1, 1]) @@ -457,7 +470,8 @@ def test_unlabeled_noreferences(self): - [PATCH 2/2] net: ieee802154: fix net_device reference release too """ _, patches, _ = self._parse_mbox( - 'revision-unlabeled-noreferences.mbox', [0, 4, 0]) + 'revision-unlabeled-noreferences.mbox', [0, 4, 0] + ) self.assertSerialized(patches, [2, 2]) @@ -475,7 +489,8 @@ def test_unnumbered(self): - This is an orphaned patch! """ covers, patches, _ = self._parse_mbox( - 'bugs-unnumbered.mbox', [1, 2, 0]) + 'bugs-unnumbered.mbox', [1, 2, 0] + ) self.assertSerialized(patches, [1, 1]) self.assertSerialized(covers, [1, 0]) @@ -498,7 +513,8 @@ def test_reply_nocover_noversion(self): - [PATCH 2/2] test: Convert to Markdown """ covers, patches, _ = self._parse_mbox( - 'bugs-nocover-noversion.mbox', [0, 4, 0]) + 'bugs-nocover-noversion.mbox', [0, 4, 0] + ) self.assertSerialized(patches, [2, 2]) @@ -516,8 +532,7 @@ def test_reply_nocover(self): - [PATCH v2 1/2] test: Add some lorem ipsum - [PATCH v2 2/2] test: Convert to Markdown """ - _, patches, _ = self._parse_mbox( - 'bugs-nocover.mbox', [0, 4, 0]) + _, patches, _ = self._parse_mbox('bugs-nocover.mbox', [0, 4, 0]) self.assertSerialized(patches, [2, 2]) @@ -535,8 +550,7 @@ def test_spamming(self): - [PATCH v2 1/4] Rework tagging infrastructure - [PATCH v2 1/4] Rework tagging infrastructure """ - _, patches, _ = self._parse_mbox( - 'bugs-spamming.mbox', [0, 3, 0]) + _, patches, _ = self._parse_mbox('bugs-spamming.mbox', [0, 3, 0]) self.assertSerialized(patches, [1, 1, 1]) @@ -552,14 +566,14 @@ def test_mixed_versions(self): - [PATCH v2 2/2] test: Convert to Markdown """ _, patches, _ = self._parse_mbox( - 'bugs-mixed-versions.mbox', [0, 2, 0], + 'bugs-mixed-versions.mbox', + [0, 2, 0], ) self.assertSerialized(patches, [1, 1]) class SeriesTotalTest(_BaseTestCase): - def test_incomplete(self): """Series received with patches missing. @@ -571,7 +585,8 @@ def test_incomplete(self): - [PATCH 1/2] test: Add some lorem ipsum """ covers, patches, _ = self._parse_mbox( - 'base-incomplete.mbox', [1, 1, 0]) + 'base-incomplete.mbox', [1, 1, 0] + ) self.assertSerialized(patches, [1]) self.assertSerialized(covers, [1]) @@ -591,7 +606,8 @@ def test_complete(self): - [PATCH 2/2] test: Convert to Markdown """ covers, patches, _ = self._parse_mbox( - 'base-cover-letter.mbox', [1, 2, 0]) + 'base-cover-letter.mbox', [1, 2, 0] + ) self.assertSerialized(covers, [1]) self.assertSerialized(patches, [2]) @@ -612,7 +628,8 @@ def test_extra_patches(self): - [PATCH 3/n] test: Remove Markdown formatting """ covers, patches, _ = self._parse_mbox( - 'base-extra-patches.mbox', [1, 3, 0]) + 'base-extra-patches.mbox', [1, 3, 0] + ) self.assertSerialized(covers, [1]) self.assertSerialized(patches, [3]) @@ -642,7 +659,8 @@ def test_cover_letter(self): slightly different output """ covers, patches, comments = self._parse_mbox( - 'mercurial-cover-letter.mbox', [1, 2, 0]) + 'mercurial-cover-letter.mbox', [1, 2, 0] + ) self.assertSerialized(patches, [2]) self.assertSerialized(covers, [1]) @@ -660,13 +678,13 @@ def test_no_cover_letter(self): slightly different output """ _, patches, _ = self._parse_mbox( - 'mercurial-no-cover-letter.mbox', [0, 2, 0]) + 'mercurial-no-cover-letter.mbox', [0, 2, 0] + ) self.assertSerialized(patches, [2]) class SeriesNameTestCase(TestCase): - def setUp(self): self.project = utils.create_project() utils.create_state() diff --git a/patchwork/tests/test_signals.py b/patchwork/tests/test_signals.py index 5d5d8e3b..c2c6cc6d 100644 --- a/patchwork/tests/test_signals.py +++ b/patchwork/tests/test_signals.py @@ -22,7 +22,6 @@ def _get_events(**filters): class _BaseTestCase(TestCase): - def assertEventFields(self, event, parent_type='patch', **fields): for field_name in [x for x in BASE_FIELDS]: field = getattr(event, field_name) @@ -33,7 +32,6 @@ def assertEventFields(self, event, parent_type='patch', **fields): class PatchCreatedTest(_BaseTestCase): - def test_patch_created(self): """No series, so patch dependencies implicitly exist.""" patch = utils.create_patch(series=None) @@ -90,8 +88,9 @@ def test_patch_dependencies_out_of_order(self): events = _get_events(patch=patch) self.assertEqual(events.count(), 2) self.assertEqual(events[0].category, Event.CATEGORY_PATCH_CREATED) - self.assertEqual(events[1].category, - Event.CATEGORY_PATCH_COMPLETED) + self.assertEqual( + events[1].category, Event.CATEGORY_PATCH_COMPLETED + ) self.assertEventFields(events[0]) self.assertEventFields(events[1]) @@ -108,7 +107,6 @@ def test_patch_dependencies_missing(self): class PatchChangedTest(_BaseTestCase): - def test_patch_state_changed(self): # purposefully setting series to None to minimize additional events patch = utils.create_patch(series=None) @@ -123,12 +121,14 @@ def test_patch_state_changed(self): events = _get_events(patch=patch) self.assertEqual(events.count(), 2) # we don't care about the CATEGORY_PATCH_CREATED event here - self.assertEqual(events[1].category, - Event.CATEGORY_PATCH_STATE_CHANGED) + self.assertEqual( + events[1].category, Event.CATEGORY_PATCH_STATE_CHANGED + ) self.assertEqual(events[1].project, patch.project) self.assertEqual(events[1].actor, actor) - self.assertEventFields(events[1], previous_state=old_state, - current_state=new_state) + self.assertEventFields( + events[1], previous_state=old_state, current_state=new_state + ) def test_patch_delegated(self): # purposefully setting series to None to minimize additional events @@ -145,8 +145,7 @@ def test_patch_delegated(self): events = _get_events(patch=patch) self.assertEqual(events.count(), 2) # we don't care about the CATEGORY_PATCH_CREATED event here - self.assertEqual(events[1].category, - Event.CATEGORY_PATCH_DELEGATED) + self.assertEqual(events[1].category, Event.CATEGORY_PATCH_DELEGATED) self.assertEqual(events[1].project, patch.project) self.assertEqual(events[1].actor, actor) self.assertEventFields(events[1], current_delegate=delegate_a) @@ -160,10 +159,12 @@ def test_patch_delegated(self): events = _get_events(patch=patch) self.assertEqual(events.count(), 3) - self.assertEqual(events[2].category, - Event.CATEGORY_PATCH_DELEGATED) - self.assertEventFields(events[2], previous_delegate=delegate_a, - current_delegate=delegate_b) + self.assertEqual(events[2].category, Event.CATEGORY_PATCH_DELEGATED) + self.assertEventFields( + events[2], + previous_delegate=delegate_a, + current_delegate=delegate_b, + ) # Delegate B -> None @@ -172,8 +173,7 @@ def test_patch_delegated(self): events = _get_events(patch=patch) self.assertEqual(events.count(), 4) - self.assertEqual(events[3].category, - Event.CATEGORY_PATCH_DELEGATED) + self.assertEqual(events[3].category, Event.CATEGORY_PATCH_DELEGATED) self.assertEventFields(events[3], previous_delegate=delegate_b) def test_patch_relations_changed(self): @@ -192,7 +192,8 @@ def test_patch_relations_changed(self): events = _get_events(patch=patches[1]) self.assertEqual(events.count(), 2) self.assertEqual( - events[1].category, Event.CATEGORY_PATCH_RELATION_CHANGED) + events[1].category, Event.CATEGORY_PATCH_RELATION_CHANGED + ) self.assertEqual(events[1].project, patches[1].project) self.assertIsNone(events[1].previous_relation) self.assertIsNone(events[1].current_relation) @@ -205,7 +206,8 @@ def test_patch_relations_changed(self): events = _get_events(patch=patches[2]) self.assertEqual(events.count(), 2) self.assertEqual( - events[1].category, Event.CATEGORY_PATCH_RELATION_CHANGED) + events[1].category, Event.CATEGORY_PATCH_RELATION_CHANGED + ) self.assertEqual(events[1].project, patches[1].project) self.assertIsNone(events[1].previous_relation) self.assertIsNone(events[1].current_relation) @@ -218,14 +220,14 @@ def test_patch_relations_changed(self): events = _get_events(patch=patches[2]) self.assertEqual(events.count(), 3) self.assertEqual( - events[2].category, Event.CATEGORY_PATCH_RELATION_CHANGED) + events[2].category, Event.CATEGORY_PATCH_RELATION_CHANGED + ) self.assertEqual(events[2].project, patches[1].project) self.assertIsNone(events[2].previous_relation) self.assertIsNone(events[2].current_relation) class CheckCreatedTest(_BaseTestCase): - def test_check_created(self): check = utils.create_check() events = _get_events(created_check=check) @@ -237,7 +239,6 @@ def test_check_created(self): class CoverCreatedTest(_BaseTestCase): - def test_cover_created(self): cover = utils.create_cover() events = _get_events(cover=cover) @@ -248,7 +249,6 @@ def test_cover_created(self): class SeriesCreatedTest(_BaseTestCase): - def test_series_created(self): series = utils.create_series() events = _get_events(series=series) @@ -259,53 +259,55 @@ def test_series_created(self): class SeriesChangedTest(_BaseTestCase): - def test_series_completed(self): """Validate 'series-completed' events.""" series = utils.create_series(total=2) # the series has no patches associated with it so it's not yet complete events = _get_events(series=series) - self.assertNotIn(Event.CATEGORY_SERIES_COMPLETED, - [x.category for x in events]) + self.assertNotIn( + Event.CATEGORY_SERIES_COMPLETED, [x.category for x in events] + ) # create the second of two patches in the series; series is still not # complete utils.create_patch(series=series, number=2) events = _get_events(series=series) - self.assertNotIn(Event.CATEGORY_SERIES_COMPLETED, - [x.category for x in events]) + self.assertNotIn( + Event.CATEGORY_SERIES_COMPLETED, [x.category for x in events] + ) # now create the first patch, which will "complete" the series utils.create_patch(series=series, number=1) events = _get_events(series=series) - self.assertIn(Event.CATEGORY_SERIES_COMPLETED, - [x.category for x in events]) + self.assertIn( + Event.CATEGORY_SERIES_COMPLETED, [x.category for x in events] + ) class CoverCommentCreatedTest(_BaseTestCase): - def test_cover_comment_created(self): """Validate 'cover-comment-created' events.""" comment = utils.create_cover_comment() events = _get_events(cover_comment=comment) self.assertEqual(events.count(), 1) self.assertEqual( - events[0].category, Event.CATEGORY_COVER_COMMENT_CREATED, + events[0].category, + Event.CATEGORY_COVER_COMMENT_CREATED, ) self.assertEqual(events[0].project, comment.cover.project) self.assertEventFields(events[0]) class PatchCommentCreatedTest(_BaseTestCase): - def test_patch_comment_created(self): """Validate 'patch-comment-created' events.""" comment = utils.create_patch_comment() events = _get_events(patch_comment=comment) self.assertEqual(events.count(), 1) self.assertEqual( - events[0].category, Event.CATEGORY_PATCH_COMMENT_CREATED, + events[0].category, + Event.CATEGORY_PATCH_COMMENT_CREATED, ) self.assertEqual(events[0].project, comment.patch.project) self.assertEventFields(events[0]) diff --git a/patchwork/tests/test_tags.py b/patchwork/tests/test_tags.py index 6c13687f..046c3e0b 100644 --- a/patchwork/tests/test_tags.py +++ b/patchwork/tests/test_tags.py @@ -21,10 +21,14 @@ class ExtractTagsTest(TestCase): def assertTagsEqual(self, str, acks, reviews, tests): # noqa counts = Patch.extract_tags(str, Tag.objects.all()) - self.assertEqual((acks, reviews, tests), - (counts[Tag.objects.get(name='Acked-by')], - counts[Tag.objects.get(name='Reviewed-by')], - counts[Tag.objects.get(name='Tested-by')])) + self.assertEqual( + (acks, reviews, tests), + ( + counts[Tag.objects.get(name='Acked-by')], + counts[Tag.objects.get(name='Reviewed-by')], + counts[Tag.objects.get(name='Tested-by')], + ), + ) def test_empty(self): self.assertTagsEqual('', 0, 0, 0) @@ -53,7 +57,8 @@ def test_multiple_acks(self): def test_multiple_types(self): str = 'Acked-by: %s\nAcked-by: %s\nReviewed-by: %s\n' % ( - (self.name_email,) * 3) + (self.name_email,) * 3 + ) self.assertTagsEqual(str, 2, 1, 0) def test_lower(self): @@ -99,7 +104,7 @@ def create_tag(self, tagtype=None): tags = { self.ACK: 'Acked', self.REVIEW: 'Reviewed', - self.TEST: 'Tested' + self.TEST: 'Tested', } if tagtype not in tags: return '' @@ -108,8 +113,8 @@ def create_tag(self, tagtype=None): def create_tag_comment(self, patch, tagtype=None): comment = create_patch_comment( - patch=patch, - content=self.create_tag(tagtype)) + patch=patch, content=self.create_tag(tagtype) + ) return comment def test_no_comments(self): @@ -168,7 +173,6 @@ def test_multiple_comments_multiple_tags(self): class PatchTagManagerTest(PatchTagsTest): - def assertTagsEqual(self, patch, acks, reviews, tests): # noqa tagattrs = {} for tag in Tag.objects.all(): @@ -181,8 +185,9 @@ def assertTagsEqual(self, patch, acks, reviews, tests): # noqa # the patch table lookup, and the prefetch_related for the # projects table. with self.assertNumQueries(2): - patch = Patch.objects.with_tag_counts(project=patch.project) \ - .get(pk=patch.pk) + patch = Patch.objects.with_tag_counts(project=patch.project).get( + pk=patch.pk + ) counts = ( getattr(patch, tagattrs['Acked-by']), diff --git a/patchwork/tests/test_xmlrpc.py b/patchwork/tests/test_xmlrpc.py index 4726fdff..61275219 100644 --- a/patchwork/tests/test_xmlrpc.py +++ b/patchwork/tests/test_xmlrpc.py @@ -14,16 +14,15 @@ class ServerProxy(xmlrpc_client.ServerProxy): - def close(self): self.__close() -@unittest.skipUnless(settings.ENABLE_XMLRPC, - 'requires xmlrpc interface (use the ENABLE_XMLRPC ' - 'setting)') +@unittest.skipUnless( + settings.ENABLE_XMLRPC, + 'requires xmlrpc interface (use the ENABLE_XMLRPC ' 'setting)', +) class XMLRPCTest(LiveServerTestCase): - def setUp(self): self.url = self.live_server_url + reverse('xmlrpc') self.rpc = ServerProxy(self.url) @@ -33,7 +32,6 @@ def tearDown(self): class XMLRPCGenericTest(XMLRPCTest): - def test_pw_rpc_version(self): # If you update the RPC version, update the tests! self.assertEqual(self.rpc.pw_rpc_version(), [1, 3, 0]) @@ -51,19 +49,21 @@ def test_absent_auth(self): self.rpc.patch_set(0, {}) -@unittest.skipUnless(settings.ENABLE_XMLRPC, - 'requires xmlrpc interface (use the ENABLE_XMLRPC ' - 'setting)') +@unittest.skipUnless( + settings.ENABLE_XMLRPC, + 'requires xmlrpc interface (use the ENABLE_XMLRPC ' 'setting)', +) class XMLRPCAuthenticatedTest(LiveServerTestCase): - def setUp(self): self.url = self.live_server_url + reverse('xmlrpc') # url is of the form http://localhost:PORT/PATH # strip the http and replace it with the username/passwd of a user. self.project = utils.create_project() self.user = utils.create_maintainer(self.project) - self.url = ('http://%s:%s@' + self.url[7:]) % (self.user.username, - self.user.username) + self.url = ('http://%s:%s@' + self.url[7:]) % ( + self.user.username, + self.user.username, + ) self.rpc = ServerProxy(self.url) def tearDown(self): @@ -82,7 +82,6 @@ def test_patch_set(self): class XMLRPCModelTestMixin(object): - def create_multiple(self, count): return [self.create_single() for i in range(count)] @@ -191,7 +190,6 @@ def test_patch_get_by_hash(self): class XMLRPCPersonTest(XMLRPCTest, XMLRPCModelTestMixin): - def setUp(self): super(XMLRPCPersonTest, self).setUp() self.get_endpoint = self.rpc.person_get @@ -200,7 +198,6 @@ def setUp(self): class XMLRPCProjectTest(XMLRPCTest, XMLRPCModelTestMixin): - def setUp(self): super(XMLRPCProjectTest, self).setUp() self.get_endpoint = self.rpc.project_get @@ -216,7 +213,6 @@ def test_list_named(self): class XMLRPCStateTest(XMLRPCTest, XMLRPCModelTestMixin): - def setUp(self): super(XMLRPCStateTest, self).setUp() self.get_endpoint = self.rpc.state_get @@ -225,7 +221,6 @@ def setUp(self): class XMLRPCCheckTest(XMLRPCTest, XMLRPCFilterModelTestMixin): - def setUp(self): super(XMLRPCCheckTest, self).setUp() self.get_endpoint = self.rpc.check_get diff --git a/patchwork/tests/utils.py b/patchwork/tests/utils.py index cc09e84c..f379270c 100644 --- a/patchwork/tests/utils.py +++ b/patchwork/tests/utils.py @@ -100,15 +100,19 @@ def create_user(link_person=True, **kwargs): values.update(kwargs) # this one must be done rather specifically - user = User.objects.create_user(values['username'], values['email'], - values['username'], - first_name=values['first_name'], - last_name=values['last_name']) + user = User.objects.create_user( + values['username'], + values['email'], + values['username'], + first_name=values['first_name'], + last_name=values['last_name'], + ) if link_person: # unfortunately we don't split on these - values['name'] = ' '.join([values.pop('first_name'), - values.pop('last_name')]) + values['name'] = ' '.join( + [values.pop('first_name'), values.pop('last_name')] + ) values.pop('username') create_person(user=user, **values) @@ -331,8 +335,7 @@ def _create_submissions(create_func, count=1, **kwargs): objects = [] for i in range(0, count): - obj = create_func(date=date + timedelta(minutes=i), - **values) + obj = create_func(date=date + timedelta(minutes=i), **values) objects.append(obj) return objects @@ -350,9 +353,7 @@ def create_patches(count=1, **kwargs): count (int): Number of patches to create kwargs (dict): Overrides for various patch fields """ - values = { - 'state': create_state() if 'state' not in kwargs else None - } + values = {'state': create_state() if 'state' not in kwargs else None} values.update(kwargs) return _create_submissions(create_patch, count, **values) diff --git a/patchwork/tests/views/test_about.py b/patchwork/tests/views/test_about.py index d8c35b9f..e6b0a8fd 100644 --- a/patchwork/tests/views/test_about.py +++ b/patchwork/tests/views/test_about.py @@ -11,7 +11,6 @@ class AboutViewTest(TestCase): - def _test_redirect(self, view): requested_url = reverse(view) redirect_url = reverse('about') @@ -23,9 +22,10 @@ def test_redirects(self): for view in ['help', 'help-about']: self._test_redirect(view) - @unittest.skipUnless(settings.ENABLE_XMLRPC, - 'requires xmlrpc interface (use the ENABLE_XMLRPC ' - 'setting)') + @unittest.skipUnless( + settings.ENABLE_XMLRPC, + 'requires xmlrpc interface (use the ENABLE_XMLRPC ' 'setting)', + ) def test_redirects_xmlrpc(self): self._test_redirect('help-pwclient') diff --git a/patchwork/tests/views/test_api.py b/patchwork/tests/views/test_api.py index 1b1a18df..b14d402b 100644 --- a/patchwork/tests/views/test_api.py +++ b/patchwork/tests/views/test_api.py @@ -24,8 +24,10 @@ def test_name_complete(self): self.assertEqual(data[0]['name'], people[0].name) def test_email_complete(self): - people = [create_person(email='test1@example.com'), - create_person(email='test2@example.com')] + people = [ + create_person(email='test1@example.com'), + create_person(email='test2@example.com'), + ] response = self.client.get(reverse('api-submitters'), {'q': 'test2'}) self.assertEqual(response.status_code, 200) data = json.loads(response.content.decode()) @@ -35,8 +37,9 @@ def test_email_complete(self): def test_param_limit(self): for i in range(10): create_person() - response = self.client.get(reverse('api-submitters'), - {'q': 'test', 'l': 5}) + response = self.client.get( + reverse('api-submitters'), {'q': 'test', 'l': 5} + ) self.assertEqual(response.status_code, 200) data = json.loads(response.content.decode()) self.assertEqual(len(data), 5) diff --git a/patchwork/tests/views/test_bundles.py b/patchwork/tests/views/test_bundles.py index 6a744093..b26badc8 100644 --- a/patchwork/tests/views/test_bundles.py +++ b/patchwork/tests/views/test_bundles.py @@ -23,21 +23,25 @@ def bundle_url(bundle): - return reverse('bundle-detail', kwargs={ - 'username': bundle.owner.username, 'bundlename': bundle.name}) + return reverse( + 'bundle-detail', + kwargs={'username': bundle.owner.username, 'bundlename': bundle.name}, + ) def bundle_mbox_url(bundle): - return reverse('bundle-mbox', kwargs={ - 'username': bundle.owner.username, 'bundlename': bundle.name}) + return reverse( + 'bundle-mbox', + kwargs={'username': bundle.owner.username, 'bundlename': bundle.name}, + ) class BundleListTest(TestCase): - def setUp(self): self.user = create_user() - self.client.login(username=self.user.username, - password=self.user.username) + self.client.login( + username=self.user.username, password=self.user.username + ) def test_no_bundles(self): response = self.client.get(reverse('user-bundles')) @@ -53,18 +57,17 @@ def test_single_bundle(self): class BundleTestBase(TestCase): - def setUp(self, count=3): self.user = create_user() - self.client.login(username=self.user.username, - password=self.user.username) + self.client.login( + username=self.user.username, password=self.user.username + ) self.bundle = create_bundle(owner=self.user) self.project = create_project() self.patches = create_patches(count, project=self.project) class BundleViewTest(BundleTestBase): - def test_empty_bundle(self): response = self.client.get(bundle_url(self.bundle)) self.assertEqual(response.status_code, 200) @@ -95,8 +98,9 @@ def test_bundle_order(self): # reorder and recheck i = 0 for patch in self.patches.__reversed__(): - bundlepatch = BundlePatch.objects.get(bundle=self.bundle, - patch=patch) + bundlepatch = BundlePatch.objects.get( + bundle=self.bundle, patch=patch + ) bundlepatch.order = i bundlepatch.save() i += 1 @@ -111,7 +115,6 @@ def test_bundle_order(self): class BundleMboxTest(BundleTestBase): - def test_empty_bundle(self): response = self.client.get(bundle_mbox_url(self.bundle)) self.assertEqual(response.status_code, 200) @@ -126,7 +129,6 @@ def test_non_empty_bundle(self): class BundleUpdateTest(BundleTestBase): - def test_no_action(self): newname = 'newbundlename' data = { @@ -179,7 +181,6 @@ def test_update_public(self): class BundleMaintainerUpdateTest(BundleUpdateTest): - def setUp(self): super(BundleMaintainerUpdateTest, self).setUp() @@ -189,7 +190,6 @@ def setUp(self): class BundlePublicViewTest(BundleTestBase): - def setUp(self): super(BundlePublicViewTest, self).setUp() self.client.logout() @@ -211,12 +211,15 @@ def test_private_bundle(self): class BundlePublicViewMboxTest(BundlePublicViewTest): - def setUp(self): super(BundlePublicViewMboxTest, self).setUp() - self.url = reverse('bundle-mbox', kwargs={ - 'username': self.bundle.owner.username, - 'bundlename': self.bundle.name}) + self.url = reverse( + 'bundle-mbox', + kwargs={ + 'username': self.bundle.owner.username, + 'bundlename': self.bundle.name, + }, + ) class BundlePublicModifyTest(BundleTestBase): @@ -231,8 +234,10 @@ def setUp(self): def test_bundle_form_presence(self): """Check for presence of the modify form on the bundle""" - self.client.login(username=self.other_user.username, - password=self.other_user.username) + self.client.login( + username=self.other_user.username, + password=self.other_user.username, + ) response = self.client.get(bundle_url(self.bundle)) self.assertNotContains(response, 'name="form" value="bundle"') self.assertNotContains(response, 'Change order') @@ -249,8 +254,9 @@ def test_bundle_form_submission(self): self.bundle.save() # first, check that we can modify with the owner - self.client.login(username=self.user.username, - password=self.user.username) + self.client.login( + username=self.user.username, password=self.user.username + ) self.client.post(bundle_url(self.bundle), data) self.bundle = Bundle.objects.get(pk=self.bundle.pk) self.assertEqual(self.bundle.name, newname) @@ -260,8 +266,10 @@ def test_bundle_form_submission(self): self.bundle.save() # log in with a different user, and check that we can no longer modify - self.client.login(username=self.other_user.username, - password=self.other_user.username) + self.client.login( + username=self.other_user.username, + password=self.other_user.username, + ) self.client.post(bundle_url(self.bundle), data) self.bundle = Bundle.objects.get(pk=self.bundle.pk) self.assertNotEqual(self.bundle.name, newname) @@ -281,15 +289,18 @@ def setUp(self): def test_private_bundle(self): # Check we can view as owner - self.client.login(username=self.user.username, - password=self.user.username) + self.client.login( + username=self.user.username, password=self.user.username + ) response = self.client.get(self.url) self.assertEqual(response.status_code, 200) self.assertContains(response, self.patches[0].name) # Check we can't view as another user - self.client.login(username=self.other_user.username, - password=self.other_user.username) + self.client.login( + username=self.other_user.username, + password=self.other_user.username, + ) response = self.client.get(self.url) self.assertEqual(response.status_code, 404) @@ -301,18 +312,26 @@ class BundlePrivateViewMboxTest(BundlePrivateViewTest): def setUp(self): super(BundlePrivateViewMboxTest, self).setUp() - self.url = reverse('bundle-mbox', kwargs={ - 'username': self.bundle.owner.username, - 'bundlename': self.bundle.name}) + self.url = reverse( + 'bundle-mbox', + kwargs={ + 'username': self.bundle.owner.username, + 'bundlename': self.bundle.name, + }, + ) def test_private_bundle_mbox_basic_auth(self): self.client.logout() def _get_auth_string(user): - return 'Basic ' + base64.b64encode(b':'.join(( - user.username.encode(), - user.username.encode())) - ).strip().decode() + return ( + 'Basic ' + + base64.b64encode( + b':'.join((user.username.encode(), user.username.encode())) + ) + .strip() + .decode() + ) # Check we can view as owner auth_string = _get_auth_string(self.user) @@ -350,18 +369,21 @@ def _get_auth_string(user): class BundleCreateFromListTest(BundleTestBase): - def test_create_empty_bundle(self): newbundlename = 'testbundle-new' - params = {'form': 'patchlistform', - 'bundle_name': newbundlename, - 'action': 'Create', - 'project': self.project.id} + params = { + 'form': 'patchlistform', + 'bundle_name': newbundlename, + 'action': 'Create', + 'project': self.project.id, + } response = self.client.post( - reverse('patch-list', kwargs={ - 'project_id': self.project.linkname}), - params) + reverse( + 'patch-list', kwargs={'project_id': self.project.linkname} + ), + params, + ) self.assertContains(response, 'Bundle %s created' % newbundlename) @@ -369,20 +391,25 @@ def test_create_non_empty_bundle(self): newbundlename = 'testbundle-new' patch = self.patches[0] - params = {'form': 'patchlistform', - 'bundle_name': newbundlename, - 'action': 'Create', - 'project': self.project.id, - 'patch_id:%d' % patch.id: 'checked'} + params = { + 'form': 'patchlistform', + 'bundle_name': newbundlename, + 'action': 'Create', + 'project': self.project.id, + 'patch_id:%d' % patch.id: 'checked', + } response = self.client.post( - reverse('patch-list', kwargs={ - 'project_id': self.project.linkname}), - params) + reverse( + 'patch-list', kwargs={'project_id': self.project.linkname} + ), + params, + ) self.assertContains(response, 'Bundle %s created' % newbundlename) - self.assertContains(response, 'added to bundle %s' % newbundlename, - count=1) + self.assertContains( + response, 'added to bundle %s' % newbundlename, count=1 + ) bundle = Bundle.objects.get(name=newbundlename) self.assertEqual(bundle.patches.count(), 1) @@ -393,19 +420,24 @@ def test_create_non_empty_bundle_empty_name(self): n_bundles = Bundle.objects.count() - params = {'form': 'patchlistform', - 'bundle_name': '', - 'action': 'Create', - 'project': self.project.id, - 'patch_id:%d' % patch.id: 'checked'} + params = { + 'form': 'patchlistform', + 'bundle_name': '', + 'action': 'Create', + 'project': self.project.id, + 'patch_id:%d' % patch.id: 'checked', + } response = self.client.post( - reverse('patch-list', kwargs={ - 'project_id': self.project.linkname}), - params) + reverse( + 'patch-list', kwargs={'project_id': self.project.linkname} + ), + params, + ) - self.assertContains(response, 'No bundle name was specified', - status_code=200) + self.assertContains( + response, 'No bundle name was specified', status_code=200 + ) # test that no new bundles are present self.assertEqual(n_bundles, Bundle.objects.count()) @@ -414,30 +446,37 @@ def test_create_duplicate_name(self): newbundlename = 'testbundle-dup' patch = self.patches[0] - params = {'form': 'patchlistform', - 'bundle_name': newbundlename, - 'action': 'Create', - 'project': self.project.id, - 'patch_id:%d' % patch.id: 'checked'} + params = { + 'form': 'patchlistform', + 'bundle_name': newbundlename, + 'action': 'Create', + 'project': self.project.id, + 'patch_id:%d' % patch.id: 'checked', + } response = self.client.post( - reverse('patch-list', kwargs={ - 'project_id': self.project.linkname}), - params) + reverse( + 'patch-list', kwargs={'project_id': self.project.linkname} + ), + params, + ) n_bundles = Bundle.objects.count() self.assertContains(response, 'Bundle %s created' % newbundlename) - self.assertContains(response, 'added to bundle %s' % newbundlename, - count=1) + self.assertContains( + response, 'added to bundle %s' % newbundlename, count=1 + ) bundle = Bundle.objects.get(name=newbundlename) self.assertEqual(bundle.patches.count(), 1) self.assertEqual(bundle.patches.all()[0], patch) response = self.client.post( - reverse('patch-list', kwargs={ - 'project_id': self.project.linkname}), - params) + reverse( + 'patch-list', kwargs={'project_id': self.project.linkname} + ), + params, + ) self.assertNotContains(response, 'Bundle %s created' % newbundlename) self.assertContains(response, 'You already have a bundle called') @@ -446,21 +485,24 @@ def test_create_duplicate_name(self): class BundleCreateFromPatchTest(BundleTestBase): - def test_create_non_empty_bundle(self): newbundlename = 'testbundle-new' patch = self.patches[0] - params = {'name': newbundlename, - 'action': 'createbundle'} + params = {'name': newbundlename, 'action': 'createbundle'} response = self.client.post( - reverse('patch-detail', - kwargs={'project_id': patch.project.linkname, - 'msgid': patch.url_msgid}), params) + reverse( + 'patch-detail', + kwargs={ + 'project_id': patch.project.linkname, + 'msgid': patch.url_msgid, + }, + ), + params, + ) - self.assertContains(response, - 'Bundle %s created' % newbundlename) + self.assertContains(response, 'Bundle %s created' % newbundlename) bundle = Bundle.objects.get(name=newbundlename) self.assertEqual(bundle.patches.count(), 1) @@ -470,38 +512,47 @@ def test_create_with_existing_name(self): newbundlename = self.bundle.name patch = self.patches[0] - params = {'name': newbundlename, - 'action': 'createbundle'} + params = {'name': newbundlename, 'action': 'createbundle'} response = self.client.post( - reverse('patch-detail', - kwargs={'project_id': patch.project.linkname, - 'msgid': patch.url_msgid}), params) + reverse( + 'patch-detail', + kwargs={ + 'project_id': patch.project.linkname, + 'msgid': patch.url_msgid, + }, + ), + params, + ) self.assertContains( - response, - 'A bundle called %s already exists' % newbundlename) + response, 'A bundle called %s already exists' % newbundlename + ) self.assertEqual(Bundle.objects.count(), 1) class BundleAddFromListTest(BundleTestBase): - def test_add_to_empty_bundle(self): patch = self.patches[0] - params = {'form': 'patchlistform', - 'action': 'Add', - 'project': self.project.id, - 'bundle_id': self.bundle.id, - 'patch_id:%d' % patch.id: 'checked'} + params = { + 'form': 'patchlistform', + 'action': 'Add', + 'project': self.project.id, + 'bundle_id': self.bundle.id, + 'patch_id:%d' % patch.id: 'checked', + } response = self.client.post( - reverse('patch-list', kwargs={ - 'project_id': self.project.linkname}), - params) + reverse( + 'patch-list', kwargs={'project_id': self.project.linkname} + ), + params, + ) - self.assertContains(response, 'added to bundle %s' % self.bundle.name, - count=1) + self.assertContains( + response, 'added to bundle %s' % self.bundle.name, count=1 + ) self.assertEqual(self.bundle.patches.count(), 1) self.assertEqual(self.bundle.patches.all()[0], patch) @@ -509,28 +560,34 @@ def test_add_to_empty_bundle(self): def test_add_to_non_empty_bundle(self): self.bundle.append_patch(self.patches[0]) patch = self.patches[1] - params = {'form': 'patchlistform', - 'action': 'Add', - 'project': self.project.id, - 'bundle_id': self.bundle.id, - 'patch_id:%d' % patch.id: 'checked'} + params = { + 'form': 'patchlistform', + 'action': 'Add', + 'project': self.project.id, + 'bundle_id': self.bundle.id, + 'patch_id:%d' % patch.id: 'checked', + } response = self.client.post( - reverse('patch-list', kwargs={ - 'project_id': self.project.linkname}), - params) + reverse( + 'patch-list', kwargs={'project_id': self.project.linkname} + ), + params, + ) - self.assertContains(response, 'added to bundle %s' % self.bundle.name, - count=1) + self.assertContains( + response, 'added to bundle %s' % self.bundle.name, count=1 + ) self.assertEqual(self.bundle.patches.count(), 2) self.assertIn(self.patches[0], self.bundle.patches.all()) self.assertIn(self.patches[1], self.bundle.patches.all()) # check order - bps = [BundlePatch.objects.get(bundle=self.bundle, - patch=self.patches[i]) - for i in [0, 1]] + bps = [ + BundlePatch.objects.get(bundle=self.bundle, patch=self.patches[i]) + for i in [0, 1] + ] self.assertTrue(bps[0].order < bps[1].order) def test_add_duplicate(self): @@ -538,16 +595,20 @@ def test_add_duplicate(self): count = self.bundle.patches.count() patch = self.patches[0] - params = {'form': 'patchlistform', - 'action': 'Add', - 'project': self.project.id, - 'bundle_id': self.bundle.id, - 'patch_id:%d' % patch.id: 'checked'} + params = { + 'form': 'patchlistform', + 'action': 'Add', + 'project': self.project.id, + 'bundle_id': self.bundle.id, + 'patch_id:%d' % patch.id: 'checked', + } response = self.client.post( - reverse('patch-list', kwargs={ - 'project_id': self.project.linkname}), - params) + reverse( + 'patch-list', kwargs={'project_id': self.project.linkname} + ), + params, + ) expected = escape(f"Patch '{patch.name}' already in bundle") self.assertContains(response, expected, count=1, status_code=200) @@ -559,17 +620,21 @@ def test_add_new_and_duplicate(self): count = self.bundle.patches.count() patch = self.patches[0] - params = {'form': 'patchlistform', - 'action': 'Add', - 'project': self.project.id, - 'bundle_id': self.bundle.id, - 'patch_id:%d' % patch.id: 'checked', - 'patch_id:%d' % self.patches[1].id: 'checked'} + params = { + 'form': 'patchlistform', + 'action': 'Add', + 'project': self.project.id, + 'bundle_id': self.bundle.id, + 'patch_id:%d' % patch.id: 'checked', + 'patch_id:%d' % self.patches[1].id: 'checked', + } response = self.client.post( - reverse('patch-list', kwargs={ - 'project_id': self.project.linkname}), - params) + reverse( + 'patch-list', kwargs={'project_id': self.project.linkname} + ), + params, + ) for expected in ( escape(f"Patch '{patch.name}' already in bundle"), @@ -581,21 +646,26 @@ def test_add_new_and_duplicate(self): class BundleAddFromPatchTest(BundleTestBase): - def test_add_to_empty_bundle(self): patch = self.patches[0] - params = {'action': 'addtobundle', - 'bundle_id': self.bundle.id} + params = {'action': 'addtobundle', 'bundle_id': self.bundle.id} response = self.client.post( - reverse('patch-detail', - kwargs={'project_id': patch.project.linkname, - 'msgid': patch.url_msgid}), params) + reverse( + 'patch-detail', + kwargs={ + 'project_id': patch.project.linkname, + 'msgid': patch.url_msgid, + }, + ), + params, + ) self.assertContains( response, 'added to bundle "%s"' % self.bundle.name, - count=1) + count=1, + ) self.assertEqual(self.bundle.patches.count(), 1) self.assertEqual(self.bundle.patches.all()[0], patch) @@ -603,34 +673,41 @@ def test_add_to_empty_bundle(self): def test_add_to_non_empty_bundle(self): self.bundle.append_patch(self.patches[0]) patch = self.patches[1] - params = {'action': 'addtobundle', - 'bundle_id': self.bundle.id} + params = {'action': 'addtobundle', 'bundle_id': self.bundle.id} response = self.client.post( - reverse('patch-detail', - kwargs={'project_id': patch.project.linkname, - 'msgid': patch.url_msgid}), params) + reverse( + 'patch-detail', + kwargs={ + 'project_id': patch.project.linkname, + 'msgid': patch.url_msgid, + }, + ), + params, + ) self.assertContains( response, 'added to bundle "%s"' % self.bundle.name, - count=1) + count=1, + ) self.assertEqual(self.bundle.patches.count(), 2) self.assertIn(self.patches[0], self.bundle.patches.all()) self.assertIn(self.patches[1], self.bundle.patches.all()) # check order - bps = [BundlePatch.objects.get(bundle=self.bundle, - patch=self.patches[i]) - for i in [0, 1]] + bps = [ + BundlePatch.objects.get(bundle=self.bundle, patch=self.patches[i]) + for i in [0, 1] + ] self.assertTrue(bps[0].order < bps[1].order) class BundleInitialOrderTest(BundleTestBase): """When creating bundles from a patch list, ensure that the patches in the - bundle are ordered by date""" + bundle are ordered by date""" def setUp(self): super(BundleInitialOrderTest, self).setUp(5) @@ -650,25 +727,29 @@ def _test_order(self, ids, expected_order): newbundlename = 'testbundle-new' # need to define our querystring explicity to enforce ordering - params = {'form': 'patchlistform', - 'bundle_name': newbundlename, - 'action': 'Create', - 'project': self.project.id, - } + params = { + 'form': 'patchlistform', + 'bundle_name': newbundlename, + 'action': 'Create', + 'project': self.project.id, + } - data = urlencode(params) + \ - ''.join(['&patch_id:%d=checked' % i for i in ids]) + data = urlencode(params) + ''.join( + ['&patch_id:%d=checked' % i for i in ids] + ) response = self.client.post( - reverse('patch-list', kwargs={ - 'project_id': self.project.linkname}), + reverse( + 'patch-list', kwargs={'project_id': self.project.linkname} + ), data=data, content_type='application/x-www-form-urlencoded', ) self.assertContains(response, 'Bundle %s created' % newbundlename) - self.assertContains(response, 'added to bundle %s' % newbundlename, - count=5) + self.assertContains( + response, 'added to bundle %s' % newbundlename, count=5 + ) bundle = Bundle.objects.get(name=newbundlename) @@ -691,7 +772,6 @@ def test_bundle_reverse_order(self): class BundleReorderTest(BundleTestBase): - def setUp(self): super(BundleReorderTest, self).setUp(5) for i in range(5): @@ -700,13 +780,16 @@ def setUp(self): def check_reordering(self, neworder, start, end): neworder_ids = [self.patches[i].id for i in neworder] - firstpatch = BundlePatch.objects.get(bundle=self.bundle, - patch=self.patches[start]).patch + firstpatch = BundlePatch.objects.get( + bundle=self.bundle, patch=self.patches[start] + ).patch slice_ids = neworder_ids[start:end] - params = {'form': 'reorderform', - 'order_start': firstpatch.id, - 'neworder': slice_ids} + params = { + 'form': 'reorderform', + 'order_start': firstpatch.id, + 'neworder': slice_ids, + } response = self.client.post(bundle_url(self.bundle), params) @@ -741,9 +824,10 @@ def test_bundle_reorder_middle(self): self.check_reordering([0, 2, 3, 1, 4], 1, 4) -@unittest.skipUnless(settings.COMPAT_REDIR, - 'requires compat redirection (use the COMPAT_REDIR ' - 'setting)') +@unittest.skipUnless( + settings.COMPAT_REDIR, + 'requires compat redirection (use the COMPAT_REDIR ' 'setting)', +) class BundleRedirTest(BundleTestBase): """Validate redirection of legacy URLs.""" @@ -752,12 +836,21 @@ def setUp(self): def test_bundle_redir(self): response = self.client.get( - reverse('bundle-redir', kwargs={'bundle_id': self.bundle.id})) + reverse('bundle-redir', kwargs={'bundle_id': self.bundle.id}) + ) self.assertRedirects(response, bundle_url(self.bundle)) def test_mbox_redir(self): - response = self.client.get(reverse( - 'bundle-mbox-redir', kwargs={'bundle_id': self.bundle.id})) - self.assertRedirects(response, reverse('bundle-mbox', kwargs={ - 'username': self.bundle.owner.username, - 'bundlename': self.bundle.name})) + response = self.client.get( + reverse('bundle-mbox-redir', kwargs={'bundle_id': self.bundle.id}) + ) + self.assertRedirects( + response, + reverse( + 'bundle-mbox', + kwargs={ + 'username': self.bundle.owner.username, + 'bundlename': self.bundle.name, + }, + ), + ) diff --git a/patchwork/tests/views/test_cover.py b/patchwork/tests/views/test_cover.py index 6dc65bb7..f33a6238 100644 --- a/patchwork/tests/views/test_cover.py +++ b/patchwork/tests/views/test_cover.py @@ -12,16 +12,23 @@ class CoverViewTest(TestCase): - def test_redirect(self): cover = create_cover() - requested_url = reverse('patch-detail', - kwargs={'project_id': cover.project.linkname, - 'msgid': cover.url_msgid}) - redirect_url = reverse('cover-detail', - kwargs={'project_id': cover.project.linkname, - 'msgid': cover.url_msgid}) + requested_url = reverse( + 'patch-detail', + kwargs={ + 'project_id': cover.project.linkname, + 'msgid': cover.url_msgid, + }, + ) + redirect_url = reverse( + 'cover-detail', + kwargs={ + 'project_id': cover.project.linkname, + 'msgid': cover.url_msgid, + }, + ) response = self.client.get(requested_url) self.assertRedirects(response, redirect_url) @@ -29,11 +36,16 @@ def test_redirect(self): def test_old_detail_url(self): cover = create_cover() - requested_url = reverse('cover-id-redirect', - kwargs={'cover_id': cover.id}) - redirect_url = reverse('cover-detail', - kwargs={'project_id': cover.project.linkname, - 'msgid': cover.url_msgid}) + requested_url = reverse( + 'cover-id-redirect', kwargs={'cover_id': cover.id} + ) + redirect_url = reverse( + 'cover-detail', + kwargs={ + 'project_id': cover.project.linkname, + 'msgid': cover.url_msgid, + }, + ) response = self.client.get(requested_url) self.assertRedirects(response, redirect_url) @@ -41,11 +53,16 @@ def test_old_detail_url(self): def test_old_mbox_url(self): cover = create_cover() - requested_url = reverse('cover-mbox-redirect', - kwargs={'cover_id': cover.id}) - redirect_url = reverse('cover-mbox', - kwargs={'project_id': cover.project.linkname, - 'msgid': cover.url_msgid}) + requested_url = reverse( + 'cover-mbox-redirect', kwargs={'cover_id': cover.id} + ) + redirect_url = reverse( + 'cover-mbox', + kwargs={ + 'project_id': cover.project.linkname, + 'msgid': cover.url_msgid, + }, + ) response = self.client.get(requested_url) self.assertRedirects(response, redirect_url) @@ -69,18 +86,23 @@ def test_invalid_cover_id(self): class CommentRedirectTest(TestCase): - def test_cover_redirect(self): cover = create_cover() comment_id = create_cover_comment(cover=cover).id - requested_url = reverse('comment-redirect', - kwargs={'comment_id': comment_id}) + requested_url = reverse( + 'comment-redirect', kwargs={'comment_id': comment_id} + ) redirect_url = '%s#%d' % ( - reverse('cover-detail', - kwargs={'project_id': cover.project.linkname, - 'msgid': cover.url_msgid}), - comment_id) + reverse( + 'cover-detail', + kwargs={ + 'project_id': cover.project.linkname, + 'msgid': cover.url_msgid, + }, + ), + comment_id, + ) response = self.client.get(requested_url) self.assertRedirects(response, redirect_url) diff --git a/patchwork/tests/views/test_mail.py b/patchwork/tests/views/test_mail.py index 8991252c..f2b19973 100644 --- a/patchwork/tests/views/test_mail.py +++ b/patchwork/tests/views/test_mail.py @@ -17,7 +17,6 @@ class MailSettingsTest(TestCase): - def test_get(self): response = self.client.get(reverse('mail-settings')) self.assertEqual(response.status_code, 200) @@ -34,8 +33,9 @@ def test_post_empty(self): response = self.client.post(reverse('mail-settings'), {'email': ''}) self.assertEqual(response.status_code, 200) self.assertTemplateUsed(response, 'patchwork/mail.html') - self.assertFormError(response, 'form', 'email', - 'This field is required.') + self.assertFormError( + response, 'form', 'email', 'This field is required.' + ) def test_post_invalid(self): response = self.client.post(reverse('mail-settings'), {'email': 'foo'}) @@ -64,7 +64,6 @@ def test_post_optout(self): class OptoutRequestTest(TestCase): - def test_get(self): response = self.client.get(reverse('mail-optout')) self.assertRedirects(response, reverse('mail-settings')) @@ -92,8 +91,9 @@ def test_post(self): def test_post_empty(self): response = self.client.post(reverse('mail-optout'), {'email': ''}) self.assertEqual(response.status_code, 200) - self.assertFormError(response, 'form', 'email', - 'This field is required.') + self.assertFormError( + response, 'form', 'email', 'This field is required.' + ) self.assertTrue(response.context['error']) self.assertNotIn('confirmation', response.context) self.assertEqual(len(mail.outbox), 0) @@ -108,7 +108,6 @@ def test_post_non_email(self): class OptoutTest(TestCase): - def setUp(self): self.email = u'foo@example.com' self.conf = EmailConfirmation(type='optout', email=self.email) @@ -127,8 +126,7 @@ def test_valid_hash(self): self.assertEqual(EmailOptout.objects.all()[0].email, self.email) # check that the confirmation is now inactive - self.assertFalse(EmailConfirmation.objects.get( - pk=self.conf.pk).active) + self.assertFalse(EmailConfirmation.objects.get(pk=self.conf.pk).active) class OptoutPreexistingTest(OptoutTest): @@ -151,8 +149,9 @@ def test_get(self): self.assertRedirects(response, reverse('mail-settings')) def test_post(self): - response = self.client.post(reverse('mail-optin'), - {'email': self.email}) + response = self.client.post( + reverse('mail-optin'), {'email': self.email} + ) # check for a confirmation object self.assertEqual(EmailConfirmation.objects.count(), 1) @@ -173,8 +172,9 @@ def test_post(self): def test_post_empty(self): response = self.client.post(reverse('mail-optin'), {'email': ''}) self.assertEqual(response.status_code, 200) - self.assertFormError(response, 'form', 'email', - 'This field is required.') + self.assertFormError( + response, 'form', 'email', 'This field is required.' + ) self.assertTrue(response.context['error']) self.assertNotIn('confirmation', response.context) self.assertEqual(len(mail.outbox), 0) @@ -189,7 +189,6 @@ def test_post_non_email(self): class OptinTest(TestCase): - def setUp(self): self.email = u'foo@example.com' self.optout = EmailOptout(email=self.email) @@ -199,7 +198,8 @@ def setUp(self): def test_valid_hash(self): response = self.client.get( - reverse('confirm', kwargs={'key': self.conf.key})) + reverse('confirm', kwargs={'key': self.conf.key}) + ) self.assertEqual(response.status_code, 200) self.assertTemplateUsed(response, 'patchwork/optin.html') @@ -209,8 +209,7 @@ def test_valid_hash(self): self.assertEqual(EmailOptout.objects.count(), 0) # check that the confirmation is now inactive - self.assertFalse(EmailConfirmation.objects.get( - pk=self.conf.pk).active) + self.assertFalse(EmailConfirmation.objects.get(pk=self.conf.pk).active) class OptinWithoutOptoutTest(TestCase): @@ -231,21 +230,25 @@ class UserProfileOptoutFormTest(TestCase): """Validate presence of correct optin/optout forms.""" - form_re_template = (r']*action="%(url)s"[^>]*>' - r'.*?]*value="%(email)s"[^>]*>.*?' - r'') + form_re_template = ( + r']*action="%(url)s"[^>]*>' + r'.*?]*value="%(email)s"[^>]*>.*?' + r'' + ) def setUp(self): self.secondary_email = 'test2@example.com' self.user = create_user() - self.client.login(username=self.user.username, - password=self.user.username) + self.client.login( + username=self.user.username, password=self.user.username + ) def _form_re(self, url, email): - return re.compile(self.form_re_template % {'url': url, 'email': email}, - re.DOTALL) + return re.compile( + self.form_re_template % {'url': url, 'email': email}, re.DOTALL + ) def test_primary_email_optout_form(self): form_re = self._form_re(reverse('mail-optout'), self.user.email) diff --git a/patchwork/tests/views/test_patch.py b/patchwork/tests/views/test_patch.py index 1a1243cf..97b0e97e 100644 --- a/patchwork/tests/views/test_patch.py +++ b/patchwork/tests/views/test_patch.py @@ -28,7 +28,6 @@ class EmptyPatchListTest(TestCase): - def test_empty_patch_list(self): """Validates absence of table with zero patches.""" project = create_project() @@ -40,26 +39,56 @@ def test_empty_patch_list(self): class PatchListOrderingTest(TestCase): patchmeta = [ - ('AlCMyjOsx', 'AlxMyjOsx@nRbqkQV.wBw', - dt(2014, 3, 16, 13, 4, 50, 155643)), - ('MMZnrcDjT', 'MMmnrcDjT@qGaIfOl.tbk', - dt(2014, 1, 25, 13, 4, 50, 162814)), - ('WGirwRXgK', 'WGSrwRXgK@TriIETY.GhE', - dt(2014, 2, 14, 13, 4, 50, 169305)), - ('isjNIuiAc', 'issNIuiAc@OsEirYx.EJh', - dt(2014, 3, 15, 13, 4, 50, 176264)), - ('XkAQpYGws', 'XkFQpYGws@hzntTcm.JSE', - dt(2014, 1, 18, 13, 4, 50, 182493)), - ('uJuCPWMvi', 'uJACPWMvi@AVRBOBl.ecy', - dt(2014, 3, 12, 13, 4, 50, 189554)), - ('TyQmWtcbg', 'TylmWtcbg@DzrNeNH.JuB', - dt(2014, 2, 3, 13, 4, 50, 195685)), - ('FpvAhWRdX', 'FpKAhWRdX@agxnCAI.wFO', - dt(2014, 3, 15, 13, 4, 50, 201398)), - ('bmoYvnyWa', 'bmdYvnyWa@aeoPnlX.juy', - dt(2014, 3, 4, 13, 4, 50, 206800)), - ('CiReUQsAq', 'CiieUQsAq@DnOYRuf.TTI', - dt(2014, 3, 28, 13, 4, 50, 212169)), + ( + 'AlCMyjOsx', + 'AlxMyjOsx@nRbqkQV.wBw', + dt(2014, 3, 16, 13, 4, 50, 155643), + ), + ( + 'MMZnrcDjT', + 'MMmnrcDjT@qGaIfOl.tbk', + dt(2014, 1, 25, 13, 4, 50, 162814), + ), + ( + 'WGirwRXgK', + 'WGSrwRXgK@TriIETY.GhE', + dt(2014, 2, 14, 13, 4, 50, 169305), + ), + ( + 'isjNIuiAc', + 'issNIuiAc@OsEirYx.EJh', + dt(2014, 3, 15, 13, 4, 50, 176264), + ), + ( + 'XkAQpYGws', + 'XkFQpYGws@hzntTcm.JSE', + dt(2014, 1, 18, 13, 4, 50, 182493), + ), + ( + 'uJuCPWMvi', + 'uJACPWMvi@AVRBOBl.ecy', + dt(2014, 3, 12, 13, 4, 50, 189554), + ), + ( + 'TyQmWtcbg', + 'TylmWtcbg@DzrNeNH.JuB', + dt(2014, 2, 3, 13, 4, 50, 195685), + ), + ( + 'FpvAhWRdX', + 'FpKAhWRdX@agxnCAI.wFO', + dt(2014, 3, 15, 13, 4, 50, 201398), + ), + ( + 'bmoYvnyWa', + 'bmdYvnyWa@aeoPnlX.juy', + dt(2014, 3, 4, 13, 4, 50, 206800), + ), + ( + 'CiReUQsAq', + 'CiieUQsAq@DnOYRuf.TTI', + dt(2014, 3, 28, 13, 4, 50, 212169), + ), ] def setUp(self): @@ -67,13 +96,13 @@ def setUp(self): for name, email, date in self.patchmeta: person = create_person(name=name, email=email) - create_patch(submitter=person, project=self.project, - date=date) + create_patch(submitter=person, project=self.project, date=date) def _extract_patch_ids(self, response): id_re = re.compile(r'TEST'.encode('utf-8'), response.content) @@ -273,12 +341,18 @@ def test_patch_with_checks(self): user = create_user() patch = create_patch() check_a = create_check( - patch=patch, user=user, context='foo', state=Check.STATE_FAIL, - date=(dt.utcnow() - timedelta(days=1))) + patch=patch, + user=user, + context='foo', + state=Check.STATE_FAIL, + date=(dt.utcnow() - timedelta(days=1)), + ) create_check( - patch=patch, user=user, context='foo', state=Check.STATE_SUCCESS) + patch=patch, user=user, context='foo', state=Check.STATE_SUCCESS + ) check_b = create_check( - patch=patch, user=user, context='bar', state=Check.STATE_PENDING) + patch=patch, user=user, context='bar', state=Check.STATE_PENDING + ) requested_url = reverse( 'patch-detail', kwargs={ @@ -293,13 +367,17 @@ def test_patch_with_checks(self): # and it should only show the unique checks self.assertEqual( - 1, response.content.decode().count( + 1, + response.content.decode().count( f'{check_a.user}/{check_a.context}' - )) + ), + ) self.assertEqual( - 1, response.content.decode().count( + 1, + response.content.decode().count( f'{check_b.user}/{check_b.context}' - )) + ), + ) class PatchUpdateTest(TestCase): @@ -311,8 +389,9 @@ def setUp(self): self.user = create_maintainer(self.project) self.patches = create_patches(3, project=self.project) - self.client.login(username=self.user.username, - password=self.user.username) + self.client.login( + username=self.user.username, password=self.user.username + ) self.url = reverse('patch-list', args=[self.project.linkname]) self.base_data = { @@ -321,7 +400,7 @@ def setUp(self): 'form': 'patchlistform', 'archived': '*', 'delegate': '*', - 'state': '*' + 'state': '*', } def _select_all_patches(self, data): @@ -335,8 +414,7 @@ def test_archiving_patches(self): response = self.client.post(self.url, data) - self.assertContains(response, 'No patches to display', - status_code=200) + self.assertContains(response, 'No patches to display', status_code=200) # Don't use the cached version of patches: retrieve from the DB for patch in [Patch.objects.get(pk=p.pk) for p in self.patches]: self.assertTrue(patch.archived) @@ -352,8 +430,7 @@ def test_unarchiving_patches(self): response = self.client.post(self.url, data) - self.assertContains(response, self.properties_form_id, - status_code=200) + self.assertContains(response, self.properties_form_id, status_code=200) for patch in [Patch.objects.get(pk=p.pk) for p in self.patches]: self.assertFalse(patch.archived) @@ -364,8 +441,7 @@ def _test_state_change(self, state): response = self.client.post(self.url, data) - self.assertContains(response, self.properties_form_id, - status_code=200) + self.assertContains(response, self.properties_form_id, status_code=200) return response def test_state_change_valid(self): @@ -384,9 +460,13 @@ def test_state_change_invalid(self): new_states = [Patch.objects.get(pk=p.pk).state for p in self.patches] self.assertEqual(new_states, orig_states) - self.assertFormError(response, 'patchform', 'state', - 'Select a valid choice. That choice is not one ' - 'of the available choices.') + self.assertFormError( + response, + 'patchform', + 'state', + 'Select a valid choice. That choice is not one ' + 'of the available choices.', + ) def _test_delegate_change(self, delegate_str): data = self.base_data.copy() @@ -414,34 +494,41 @@ def test_delegate_clear(self): class UTF8PatchViewTest(TestCase): - def setUp(self): patch_content = read_patch('0002-utf-8.patch', encoding='utf-8') self.patch = create_patch(diff=patch_content) def test_patch_view(self): - response = self.client.get(reverse( - 'patch-detail', args=[self.patch.project.linkname, - self.patch.url_msgid])) + response = self.client.get( + reverse( + 'patch-detail', + args=[self.patch.project.linkname, self.patch.url_msgid], + ) + ) self.assertContains(response, self.patch.name) def test_mbox_view(self): response = self.client.get( - reverse('patch-mbox', args=[self.patch.project.linkname, - self.patch.url_msgid])) + reverse( + 'patch-mbox', + args=[self.patch.project.linkname, self.patch.url_msgid], + ) + ) self.assertEqual(response.status_code, 200) self.assertTrue(self.patch.diff in response.content.decode('utf-8')) def test_raw_view(self): - response = self.client.get(reverse('patch-raw', - args=[self.patch.project.linkname, - self.patch.url_msgid])) + response = self.client.get( + reverse( + 'patch-raw', + args=[self.patch.project.linkname, self.patch.url_msgid], + ) + ) self.assertEqual(response.status_code, 200) self.assertEqual(response.content.decode('utf-8'), self.patch.diff) class UTF8HeaderPatchViewTest(UTF8PatchViewTest): - def setUp(self): author = create_person(name=u'P\xe4tch Author') patch_content = read_patch('0002-utf-8.patch', encoding='utf-8') diff --git a/patchwork/tests/views/test_projects.py b/patchwork/tests/views/test_projects.py index 81d163c4..f7762b7a 100644 --- a/patchwork/tests/views/test_projects.py +++ b/patchwork/tests/views/test_projects.py @@ -10,13 +10,13 @@ class ProjectViewTest(TestCase): - def test_redirect(self): project = utils.create_project() requested_url = reverse('project-list') - redirect_url = reverse('patch-list', kwargs={ - 'project_id': project.linkname}) + redirect_url = reverse( + 'patch-list', kwargs={'project_id': project.linkname} + ) response = self.client.get(requested_url) self.assertRedirects(response, redirect_url) @@ -34,8 +34,9 @@ def test_no_redirect(self): def test_n_patches(self): project = utils.create_project() - requested_url = reverse('project-detail', kwargs={ - 'project_id': project.linkname}) + requested_url = reverse( + 'project-detail', kwargs={'project_id': project.linkname} + ) response = self.client.get(requested_url) self.assertEqual(response.status_code, 200) @@ -59,8 +60,9 @@ def test_n_patches(self): def test_maintainers(self): project = utils.create_project() - requested_url = reverse('project-detail', kwargs={ - 'project_id': project.linkname}) + requested_url = reverse( + 'project-detail', kwargs={'project_id': project.linkname} + ) response = self.client.get(requested_url) self.assertEqual(response.status_code, 200) diff --git a/patchwork/tests/views/test_user.py b/patchwork/tests/views/test_user.py index 86f8972d..1f74ad50 100644 --- a/patchwork/tests/views/test_user.py +++ b/patchwork/tests/views/test_user.py @@ -27,15 +27,13 @@ def _generate_secondary_email(user): class _UserTestCase(TestCase): - def setUp(self): self.user = create_user() self.password = User.objects.make_random_password() self.user.set_password(self.password) self.user.save() - self.client.login(username=self.user.username, - password=self.password) + self.client.login(username=self.user.username, password=self.password) class TestUser(object): @@ -48,7 +46,6 @@ class TestUser(object): class RegistrationTest(TestCase): - def setUp(self): self.user = TestUser() self.client = Client() @@ -89,8 +86,11 @@ def test_existing_username(self): response = self.client.post('/register/', data) self.assertEqual(response.status_code, 200) self.assertFormError( - response, 'form', 'username', - 'This username is already taken. Please choose another.') + response, + 'form', + 'username', + 'This username is already taken. Please choose another.', + ) def test_existing_email(self): user = create_user() @@ -99,9 +99,12 @@ def test_existing_email(self): response = self.client.post('/register/', data) self.assertEqual(response.status_code, 200) self.assertFormError( - response, 'form', 'email', + response, + 'form', + 'email', 'This email address is already in use for the account ' - '"%s".\n' % user.username) + '"%s".\n' % user.username, + ) def test_valid_registration(self): response = self.client.post('/register/', self.default_data) @@ -118,7 +121,8 @@ def test_valid_registration(self): # check for confirmation object confs = EmailConfirmation.objects.filter( - user=user, type='registration') + user=user, type='registration' + ) self.assertEqual(len(confs), 1) conf = confs[0] self.assertEqual(conf.email, self.user.email) @@ -136,7 +140,6 @@ def test_valid_registration(self): class RegistrationConfirmationTest(TestCase): - def setUp(self): self.user = TestUser() self.default_data = { @@ -144,7 +147,7 @@ def setUp(self): 'first_name': self.user.firstname, 'last_name': self.user.lastname, 'email': self.user.email, - 'password': self.user.password + 'password': self.user.password, } def test_valid(self): @@ -162,7 +165,8 @@ def test_valid(self): response = self.client.get(_confirmation_url(conf)) self.assertEqual(response.status_code, 200) self.assertTemplateUsed( - response, 'patchwork/registration-confirm.html') + response, 'patchwork/registration-confirm.html' + ) conf = EmailConfirmation.objects.get(pk=conf.pk) self.assertTrue(conf.user.is_active) @@ -233,11 +237,11 @@ def test_existing_person_unmodified(self): self.assertEqual(response.status_code, 200) self.assertEqual( - Person.objects.get(pk=person.pk).name, self.user.fullname) + Person.objects.get(pk=person.pk).name, self.user.fullname + ) class UserLinkTest(_UserTestCase): - def setUp(self): super().setUp() self.secondary_email = _generate_secondary_email(self.user) @@ -251,19 +255,22 @@ def test_user_person_request_empty(self): response = self.client.post(reverse('user-link'), {'email': ''}) self.assertEqual(response.status_code, 200) self.assertTrue(response.context['linkform']) - self.assertFormError(response, 'linkform', 'email', - 'This field is required.') + self.assertFormError( + response, 'linkform', 'email', 'This field is required.' + ) def test_user_person_request_invalid(self): response = self.client.post(reverse('user-link'), {'email': 'foo'}) self.assertEqual(response.status_code, 200) self.assertTrue(response.context['linkform']) - self.assertFormError(response, 'linkform', 'email', - error_strings['email']) + self.assertFormError( + response, 'linkform', 'email', error_strings['email'] + ) def test_user_person_request_valid(self): - response = self.client.post(reverse('user-link'), - {'email': self.secondary_email}) + response = self.client.post( + reverse('user-link'), {'email': self.secondary_email} + ) self.assertEqual(response.status_code, 200) self.assertTrue(response.context['confirmation']) @@ -288,20 +295,18 @@ def test_user_person_request_valid(self): class ConfirmationTest(TestCase): - def setUp(self): self.user = create_user(link_person=False) self.password = User.objects.make_random_password() self.user.set_password(self.password) self.user.save() - self.client.login(username=self.user.username, - password=self.password) + self.client.login(username=self.user.username, password=self.password) self.secondary_email = _generate_secondary_email(self.user) - self.conf = EmailConfirmation(type='userperson', - email=self.secondary_email, - user=self.user) + self.conf = EmailConfirmation( + type='userperson', email=self.secondary_email, user=self.user + ) self.conf.save() def test_user_person_confirm(self): @@ -322,14 +327,13 @@ def test_user_person_confirm(self): class InvalidConfirmationTest(TestCase): - def setUp(self): self.user = create_user() self.secondary_email = _generate_secondary_email(self.user) - self.conf = EmailConfirmation(type='userperson', - email=self.secondary_email, - user=self.user) + self.conf = EmailConfirmation( + type='userperson', email=self.secondary_email, user=self.user + ) self.conf.save() def test_inactive_confirmation(self): @@ -352,7 +356,6 @@ def test_expired_confirmation(self): class LoginRedirectTest(TestCase): - def test_user_login_redirect(self): url = reverse('user-profile') response = self.client.get(url) @@ -360,7 +363,6 @@ def test_user_login_redirect(self): class UserProfileTest(_UserTestCase): - def test_user_profile(self): response = self.client.get(reverse('user-profile')) self.assertContains(response, 'Your Profile') @@ -408,7 +410,6 @@ def test_user_profile_invalid_post(self): class PasswordChangeTest(_UserTestCase): - def test_password_change_form(self): response = self.client.get(reverse('password_change')) self.assertContains(response, 'Change my password') @@ -432,16 +433,16 @@ def test_password_change(self): self.assertTrue(user.check_password(new_password)) response = self.client.get(reverse('password_change_done')) - self.assertContains(response, - "Your password has been changed successfully") + self.assertContains( + response, "Your password has been changed successfully" + ) class UserUnlinkTest(_UserTestCase): - def _create_confirmation(self, email): - conf = EmailConfirmation(type='userperson', - email=email, - user=self.user) + conf = EmailConfirmation( + type='userperson', email=email, user=self.user + ) conf.save() self.client.get(_confirmation_url(conf)) diff --git a/patchwork/tests/views/test_utils.py b/patchwork/tests/views/test_utils.py index 50d935ec..e10c3bde 100644 --- a/patchwork/tests/views/test_utils.py +++ b/patchwork/tests/views/test_utils.py @@ -23,12 +23,12 @@ class MboxPatchResponseTest(TestCase): - def test_tags(self): """Test that tags are taken from a patch comment.""" patch = create_patch(content='comment 1 text\nAcked-by: 1\n') create_patch_comment( - patch=patch, content='comment 2 text\nAcked-by: 2\n') + patch=patch, content='comment 2 text\nAcked-by: 2\n' + ) mbox = utils.patch_to_mbox(patch) self.assertIn('Acked-by: 1\nAcked-by: 2\n', mbox) @@ -37,7 +37,8 @@ def test_utf8_nbsp_tags(self): """Test that UTF-8 NBSP characters are correctly handled.""" patch = create_patch(content='patch text\n') create_patch_comment( - patch=patch, content=u'comment\nAcked-by:\u00A0 foo') + patch=patch, content=u'comment\nAcked-by:\u00A0 foo' + ) mbox = utils.patch_to_mbox(patch) self.assertIn(u'\u00A0 foo\n', mbox) @@ -54,11 +55,13 @@ def test_multiple_tags(self): project=self.project, submitter=self.person, diff='', - content='comment 1 text\nAcked-by: 1\n---\nupdate\n') + content='comment 1 text\nAcked-by: 1\n---\nupdate\n', + ) self.comment = create_patch_comment( patch=self.patch, submitter=self.person, - content='comment 2 text\nAcked-by: 2\n') + content='comment 2 text\nAcked-by: 2\n', + ) mbox = utils.patch_to_mbox(self.patch) self.assertIn('Acked-by: 1\nAcked-by: 2\n', mbox) @@ -159,9 +162,11 @@ def test_dmarc_from_header(self): rewritten_from_header = 'Person ' project = create_project(listemail='list@example.com') person = create_person(name='Person', email='person@example.com') - patch = create_patch(project=project, - headers='From: ' + orig_from_header, - submitter=person) + patch = create_patch( + project=project, + headers='From: ' + orig_from_header, + submitter=person, + ) mbox = utils.patch_to_mbox(patch) mail = email.message_from_string(mbox) self.assertEqual(mail['From'], rewritten_from_header) @@ -173,8 +178,9 @@ def test_date_header(self): mail = email.message_from_string(mbox) mail_date = dateutil.parser.parse(mail['Date']) # patch dates are all in UTC - patch_date = patch.date.replace(tzinfo=dateutil.tz.tzutc(), - microsecond=0) + patch_date = patch.date.replace( + tzinfo=dateutil.tz.tzutc(), microsecond=0 + ) self.assertEqual(mail_date, patch_date) def test_supplied_date_header(self): @@ -211,7 +217,6 @@ def test_comment_unchanged(self): class MboxSeriesPatchTest(TestCase): - @staticmethod def _create_patches(): series = create_series() @@ -253,7 +258,6 @@ def test_legacy_patch(self): class MboxSeriesTest(TestCase): - def test_series(self): series = create_series() patch_a = create_patch(series=series) diff --git a/patchwork/urls.py b/patchwork/urls.py index 5ddf2dbd..ab606f1c 100644 --- a/patchwork/urls.py +++ b/patchwork/urls.py @@ -363,9 +363,7 @@ re_path( r'^api/(?:(?P(1.1|1.2|1.3))/)?', include(api_1_1_patterns) ), - re_path( - r'^api/(?:(?P(1.3))/)?', include(api_1_3_patterns) - ), + re_path(r'^api/(?:(?P(1.3))/)?', include(api_1_3_patterns)), # token change path( 'user/generate-token/', diff --git a/patchwork/version.py b/patchwork/version.py index 834e0f67..d1913138 100644 --- a/patchwork/version.py +++ b/patchwork/version.py @@ -7,8 +7,7 @@ import os -ROOT_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), - os.pardir) +ROOT_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), os.pardir) def get_latest_version(version): @@ -25,8 +24,12 @@ def get_latest_version(version): def format_version(version): """Format version tuple.""" - return '.'.join(['.'.join([str(x) for x in version[:3]]), - '-'.join([str(x) for x in version[3:]])]) + return '.'.join( + [ + '.'.join([str(x) for x in version[:3]]), + '-'.join([str(x) for x in version[3:]]), + ] + ) def format_git_version(version): @@ -41,9 +44,9 @@ def format_git_version(version): def get_raw_git_version(): """Returns the raw git version via 'git-describe'.""" try: - git_version = subprocess.check_output(['git', 'describe'], - stderr=subprocess.STDOUT, - cwd=ROOT_DIR) + git_version = subprocess.check_output( + ['git', 'describe'], stderr=subprocess.STDOUT, cwd=ROOT_DIR + ) except (OSError, subprocess.CalledProcessError): return '' diff --git a/patchwork/views/__init__.py b/patchwork/views/__init__.py index 3efe90cd..c3199ffd 100644 --- a/patchwork/views/__init__.py +++ b/patchwork/views/__init__.py @@ -124,8 +124,7 @@ def set_bundle(request, project, action, data, patches, context): if Bundle.objects.filter(owner=user, name=bundle_name).count() > 0: return ['You already have a bundle called "%s"' % bundle_name] - bundle = Bundle(owner=user, project=project, - name=bundle_name) + bundle = Bundle(owner=user, project=project, name=bundle_name) bundle.save() messages.success(request, "Bundle %s created" % bundle.name) elif action == 'add': @@ -138,15 +137,22 @@ def set_bundle(request, project, action, data, patches, context): for patch in patches: if action in ['create', 'add']: - bundlepatch_count = BundlePatch.objects.filter(bundle=bundle, - patch=patch).count() + bundlepatch_count = BundlePatch.objects.filter( + bundle=bundle, patch=patch + ).count() if bundlepatch_count == 0: bundle.append_patch(patch) - messages.success(request, "Patch '%s' added to bundle %s" % - (patch.name, bundle.name)) + messages.success( + request, + "Patch '%s' added to bundle %s" + % (patch.name, bundle.name), + ) else: - messages.warning(request, "Patch '%s' already in bundle %s" % - (patch.name, bundle.name)) + messages.warning( + request, + "Patch '%s' already in bundle %s" + % (patch.name, bundle.name), + ) elif action == 'remove': try: bp = BundlePatch.objects.get(bundle=bundle, patch=patch) @@ -156,16 +162,24 @@ def set_bundle(request, project, action, data, patches, context): else: messages.success( request, - "Patch '%s' removed from bundle %s\n" % (patch.name, - bundle.name)) + "Patch '%s' removed from bundle %s\n" + % (patch.name, bundle.name), + ) bundle.save() return [] -def generic_list(request, project, view, view_args=None, filter_settings=None, - patches=None, editable_order=False): +def generic_list( + request, + project, + view, + view_args=None, + filter_settings=None, + patches=None, + editable_order=False, +): if not filter_settings: filter_settings = [] @@ -198,13 +212,16 @@ def generic_list(request, project, view, view_args=None, filter_settings=None, data = request.POST order = Order(data.get('order'), editable=editable_order) - context.update({ - 'order': order, - 'list_view': { - 'view': view, - 'view_params': view_args or {}, - 'params': params - }}) + context.update( + { + 'order': order, + 'list_view': { + 'view': view, + 'view_params': view_args or {}, + 'params': params, + }, + } + ) # form processing @@ -240,8 +257,9 @@ def generic_list(request, project, view, view_args=None, filter_settings=None, errors = set_bundle(request, project, action, data, ps, context) elif properties_form and action == properties_form.action: - errors = process_multiplepatch_form(request, properties_form, - action, ps, context) + errors = process_multiplepatch_form( + request, properties_form, action, ps, context + ) else: errors = [] @@ -272,25 +290,41 @@ def generic_list(request, project, view, view_args=None, filter_settings=None, # but we will need to follow the state and submitter relations for # rendering the list template - patches = patches.select_related('state', 'submitter', 'delegate', - 'series') - - patches = patches.only('state', 'submitter', 'delegate', 'project', - 'series__name', 'name', 'date', 'msgid') + patches = patches.select_related( + 'state', 'submitter', 'delegate', 'series' + ) + + patches = patches.only( + 'state', + 'submitter', + 'delegate', + 'project', + 'series__name', + 'name', + 'date', + 'msgid', + ) # we also need checks and series patches = patches.prefetch_related( - Prefetch('check_set', queryset=Check.objects.only( - 'context', 'user_id', 'patch_id', 'state', 'date'))) + Prefetch( + 'check_set', + queryset=Check.objects.only( + 'context', 'user_id', 'patch_id', 'state', 'date' + ), + ) + ) paginator = Paginator(request, patches) - context.update({ - 'page': paginator.current_page, - 'patchform': properties_form, - 'project': project, - 'order': order, - }) + context.update( + { + 'page': paginator.current_page, + 'patchform': properties_form, + 'project': project, + 'order': order, + } + ) return context @@ -308,8 +342,9 @@ def process_multiplepatch_form(request, form, action, patches, context): changed_patches = 0 for patch in patches: if not patch.is_editable(request.user): - errors.append("You don't have permissions to edit patch '%s'" - % patch.name) + errors.append( + "You don't have permissions to edit patch '%s'" % patch.name + ) continue changed_patches += 1 diff --git a/patchwork/views/api.py b/patchwork/views/api.py index 55ffad9c..283e6712 100644 --- a/patchwork/views/api.py +++ b/patchwork/views/api.py @@ -41,8 +41,9 @@ def _handle_request(request, queryset_fn, formatter): def submitters(request): def queryset(search): - return Person.objects.filter(Q(name__icontains=search) | - Q(email__icontains=search)) + return Person.objects.filter( + Q(name__icontains=search) | Q(email__icontains=search) + ) def formatter(submitter): return { @@ -56,9 +57,11 @@ def formatter(submitter): def delegates(request): def queryset(search): - return User.objects.filter(Q(username__icontains=search) | - Q(first_name__icontains=search) | - Q(last_name__icontains=search)) + return User.objects.filter( + Q(username__icontains=search) + | Q(first_name__icontains=search) + | Q(last_name__icontains=search) + ) def formatter(user): return { diff --git a/patchwork/views/bundle.py b/patchwork/views/bundle.py index 3e227f4c..323a1f74 100644 --- a/patchwork/views/bundle.py +++ b/patchwork/views/bundle.py @@ -52,8 +52,9 @@ def bundle_list(request, project_id=None): if form_name == DeleteBundleForm.name: form = DeleteBundleForm(request.POST) if form.is_valid(): - bundle = get_object_or_404(Bundle, - id=form.cleaned_data['bundle_id']) + bundle = get_object_or_404( + Bundle, id=form.cleaned_data['bundle_id'] + ) bundle.delete() if project_id is None: @@ -63,8 +64,9 @@ def bundle_list(request, project_id=None): bundles = request.user.bundles.filter(project=project) for bundle in bundles: - bundle.delete_form = DeleteBundleForm(auto_id=False, - initial={'bundle_id': bundle.id}) + bundle.delete_form = DeleteBundleForm( + auto_id=False, initial={'bundle_id': bundle.id} + ) context = { 'bundles': bundles, @@ -75,8 +77,9 @@ def bundle_list(request, project_id=None): def bundle_detail(request, username, bundlename): - bundle = get_object_or_404(Bundle, owner__username=username, - name=bundlename) + bundle = get_object_or_404( + Bundle, owner__username=username, name=bundlename + ) filter_settings = [(DelegateFilter, DelegateFilter.ANY_DELEGATE)] is_owner = request.user == bundle.owner @@ -105,30 +108,38 @@ def bundle_detail(request, username, bundlename): else: form = BundleForm(instance=bundle) - if (request.method == 'POST' and - request.POST.get('form') == 'reorderform'): + if ( + request.method == 'POST' + and request.POST.get('form') == 'reorderform' + ): order = get_object_or_404( BundlePatch, bundle=bundle, - patch__id=request.POST.get('order_start')).order + patch__id=request.POST.get('order_start'), + ).order for patch_id in request.POST.getlist('neworder'): - bundlepatch = get_object_or_404(BundlePatch, - bundle=bundle, - patch__id=patch_id) + bundlepatch = get_object_or_404( + BundlePatch, bundle=bundle, patch__id=patch_id + ) bundlepatch.order = order bundlepatch.save() order += 1 else: form = None - context = generic_list(request, bundle.project, - 'bundle-detail', - view_args={'username': bundle.owner.username, - 'bundlename': bundle.name}, - filter_settings=filter_settings, - patches=bundle.ordered_patches(), - editable_order=is_owner) + context = generic_list( + request, + bundle.project, + 'bundle-detail', + view_args={ + 'username': bundle.owner.username, + 'bundlename': bundle.name, + }, + filter_settings=filter_settings, + patches=bundle.ordered_patches(), + editable_order=is_owner, + ) context['bundle'] = bundle context['bundleform'] = form @@ -137,16 +148,18 @@ def bundle_detail(request, username, bundlename): def bundle_mbox(request, username, bundlename): - bundle = get_object_or_404(Bundle, owner__username=username, - name=bundlename) + bundle = get_object_or_404( + Bundle, owner__username=username, name=bundlename + ) request.user = rest_auth(request) if not (request.user == bundle.owner or bundle.public): return HttpResponseNotFound() response = HttpResponse(content_type='text/plain') - response['Content-Disposition'] = \ - 'attachment; filename=bundle-%d-%s.mbox' % (bundle.id, bundle.name) + response[ + 'Content-Disposition' + ] = 'attachment; filename=bundle-%d-%s.mbox' % (bundle.id, bundle.name) response.write(bundle_to_mbox(bundle)) return response @@ -162,7 +175,11 @@ def bundle_detail_redir(request, bundle_id): def bundle_mbox_redir(request, bundle_id): bundle = get_object_or_404(Bundle, id=bundle_id, owner=request.user) return HttpResponseRedirect( - reverse('bundle-mbox', kwargs={ - 'username': request.user.username, - 'bundlename': bundle.name, - })) + reverse( + 'bundle-mbox', + kwargs={ + 'username': request.user.username, + 'bundlename': bundle.name, + }, + ) + ) diff --git a/patchwork/views/cover.py b/patchwork/views/cover.py index 8ab0ba99..3368186b 100644 --- a/patchwork/views/cover.py +++ b/patchwork/views/cover.py @@ -18,12 +18,11 @@ def cover_detail(request, project_id, msgid): project = get_object_or_404(Project, linkname=project_id) - db_msgid = ('<%s>' % msgid) + db_msgid = '<%s>' % msgid # redirect to patches where necessary try: - cover = get_object_or_404(Cover, project_id=project.id, - msgid=db_msgid) + cover = get_object_or_404(Cover, project_id=project.id, msgid=db_msgid) except Http404 as exc: patches = Patch.objects.filter( project_id=project.id, @@ -31,9 +30,11 @@ def cover_detail(request, project_id, msgid): ) if patches: return HttpResponseRedirect( - reverse('patch-detail', - kwargs={'project_id': project.linkname, - 'msgid': msgid})) + reverse( + 'patch-detail', + kwargs={'project_id': project.linkname, 'msgid': msgid}, + ) + ) raise exc context = { @@ -43,23 +44,22 @@ def cover_detail(request, project_id, msgid): comments = cover.comments.all() comments = comments.select_related('submitter') - comments = comments.only('submitter', 'date', 'id', 'content', - 'cover') + comments = comments.only('submitter', 'date', 'id', 'content', 'cover') context['comments'] = comments return render(request, 'patchwork/submission.html', context) def cover_mbox(request, project_id, msgid): - db_msgid = ('<%s>' % msgid) + db_msgid = '<%s>' % msgid project = get_object_or_404(Project, linkname=project_id) - cover = get_object_or_404(Cover, project_id=project.id, - msgid=db_msgid) + cover = get_object_or_404(Cover, project_id=project.id, msgid=db_msgid) response = HttpResponse(content_type='text/plain') response.write(cover_to_mbox(cover)) response['Content-Disposition'] = 'attachment; filename=%s.mbox' % ( - cover.filename) + cover.filename + ) return response @@ -67,8 +67,13 @@ def cover_mbox(request, project_id, msgid): def cover_by_id(request, cover_id): cover = get_object_or_404(Cover, id=cover_id) - url = reverse('cover-detail', kwargs={'project_id': cover.project.linkname, - 'msgid': cover.url_msgid}) + url = reverse( + 'cover-detail', + kwargs={ + 'project_id': cover.project.linkname, + 'msgid': cover.url_msgid, + }, + ) return HttpResponseRedirect(url) @@ -76,7 +81,12 @@ def cover_by_id(request, cover_id): def cover_mbox_by_id(request, cover_id): cover = get_object_or_404(Cover, id=cover_id) - url = reverse('cover-mbox', kwargs={'project_id': cover.project.linkname, - 'msgid': cover.url_msgid}) + url = reverse( + 'cover-mbox', + kwargs={ + 'project_id': cover.project.linkname, + 'msgid': cover.url_msgid, + }, + ) return HttpResponseRedirect(url) diff --git a/patchwork/views/mail.py b/patchwork/views/mail.py index 4bc7be0f..8b31fc9e 100644 --- a/patchwork/views/mail.py +++ b/patchwork/views/mail.py @@ -78,17 +78,21 @@ def _optinout(request, action): form = EmailForm(data=request.POST) if not form.is_valid(): - context['error'] = ('There was an error in the form. Please review ' - 'and re-submit.') + context['error'] = ( + 'There was an error in the form. Please review ' 'and re-submit.' + ) context['form'] = form return render(request, html_template, context) email = form.cleaned_data['email'] - if action == 'optin' and EmailOptout.objects.filter( - email=email).count() == 0: - context['error'] = ("The email address %s is not on the patchwork " - "opt-out list, so you don't need to opt back in" % - email) + if ( + action == 'optin' + and EmailOptout.objects.filter(email=email).count() == 0 + ): + context['error'] = ( + "The email address %s is not on the patchwork " + "opt-out list, so you don't need to opt back in" % email + ) context['form'] = form return render(request, html_template, context) @@ -104,8 +108,10 @@ def _optinout(request, action): send_mail(subject, message, conf_settings.DEFAULT_FROM_EMAIL, [email]) except smtplib.SMTPException: context['confirmation'] = None - context['error'] = ('An error occurred during confirmation . ' - 'Please try again later.') + context['error'] = ( + 'An error occurred during confirmation . ' + 'Please try again later.' + ) context['admins'] = conf_settings.ADMINS return render(request, html_template, context) diff --git a/patchwork/views/patch.py b/patchwork/views/patch.py index 00b0147f..75705720 100644 --- a/patchwork/views/patch.py +++ b/patchwork/views/patch.py @@ -25,8 +25,12 @@ def patch_list(request, project_id): project = get_object_or_404(Project, linkname=project_id) - context = generic_list(request, project, 'patch-list', - view_args={'project_id': project.linkname}) + context = generic_list( + request, + project, + 'patch-list', + view_args={'project_id': project.linkname}, + ) if request.user.is_authenticated: context['bundles'] = request.user.bundles.all() @@ -36,7 +40,7 @@ def patch_list(request, project_id): def patch_detail(request, project_id, msgid): project = get_object_or_404(Project, linkname=project_id) - db_msgid = ('<%s>' % msgid) + db_msgid = '<%s>' % msgid # redirect to cover letters where necessary try: @@ -48,15 +52,15 @@ def patch_detail(request, project_id, msgid): ) if covers: return HttpResponseRedirect( - reverse('cover-detail', - kwargs={'project_id': project.linkname, - 'msgid': msgid})) + reverse( + 'cover-detail', + kwargs={'project_id': project.linkname, 'msgid': msgid}, + ) + ) raise Http404('Patch does not exist') editable = patch.is_editable(request.user) - context = { - 'project': patch.project - } + context = {'project': patch.project} form = None createbundleform = None @@ -73,8 +77,9 @@ def patch_detail(request, project_id, msgid): if action == 'createbundle': bundle = Bundle(owner=request.user, project=project) - createbundleform = CreateBundleForm(instance=bundle, - data=request.POST) + createbundleform = CreateBundleForm( + instance=bundle, data=request.POST + ) if createbundleform.is_valid(): createbundleform.save() bundle.append_patch(patch) @@ -83,16 +88,20 @@ def patch_detail(request, project_id, msgid): messages.success(request, 'Bundle %s created' % bundle.name) elif action == 'addtobundle': bundle = get_object_or_404( - Bundle, id=request.POST.get('bundle_id')) + Bundle, id=request.POST.get('bundle_id') + ) if bundle.append_patch(patch): - messages.success(request, - 'Patch "%s" added to bundle "%s"' % ( - patch.name, bundle.name)) + messages.success( + request, + 'Patch "%s" added to bundle "%s"' + % (patch.name, bundle.name), + ) else: - messages.error(request, - 'Failed to add patch "%s" to bundle "%s": ' - 'patch is already in bundle' % ( - patch.name, bundle.name)) + messages.error( + request, + 'Failed to add patch "%s" to bundle "%s": ' + 'patch is already in bundle' % (patch.name, bundle.name), + ) # all other actions require edit privs elif not editable: @@ -109,15 +118,18 @@ def patch_detail(request, project_id, msgid): comments = patch.comments.all() comments = comments.select_related('submitter') - comments = comments.only('submitter', 'date', 'id', 'content', 'patch', - 'addressed') + comments = comments.only( + 'submitter', 'date', 'id', 'content', 'patch', 'addressed' + ) if patch.related: related_same_project = patch.related.patches.only( - 'name', 'msgid', 'project', 'related') + 'name', 'msgid', 'project', 'related' + ) # avoid a second trip out to the db for info we already have related_different_project = [ - related_patch for related_patch in related_same_project + related_patch + for related_patch in related_same_project if related_patch.project_id != patch.project_id ] else: @@ -140,20 +152,21 @@ def patch_detail(request, project_id, msgid): def patch_raw(request, project_id, msgid): - db_msgid = ('<%s>' % msgid) + db_msgid = '<%s>' % msgid project = get_object_or_404(Project, linkname=project_id) patch = get_object_or_404(Patch, project_id=project.id, msgid=db_msgid) response = HttpResponse(content_type="text/x-patch") response.write(patch.diff) response['Content-Disposition'] = 'attachment; filename=%s.diff' % ( - patch.filename) + patch.filename + ) return response def patch_mbox(request, project_id, msgid): - db_msgid = ('<%s>' % msgid) + db_msgid = '<%s>' % msgid project = get_object_or_404(Project, linkname=project_id) patch = get_object_or_404(Patch, project_id=project.id, msgid=db_msgid) series_id = request.GET.get('series') @@ -164,7 +177,8 @@ def patch_mbox(request, project_id, msgid): else: response.write(patch_to_mbox(patch)) response['Content-Disposition'] = 'attachment; filename=%s.patch' % ( - patch.filename) + patch.filename + ) return response @@ -172,8 +186,13 @@ def patch_mbox(request, project_id, msgid): def patch_by_id(request, patch_id): patch = get_object_or_404(Patch, id=patch_id) - url = reverse('patch-detail', kwargs={'project_id': patch.project.linkname, - 'msgid': patch.url_msgid}) + url = reverse( + 'patch-detail', + kwargs={ + 'project_id': patch.project.linkname, + 'msgid': patch.url_msgid, + }, + ) return HttpResponseRedirect(url) @@ -181,8 +200,13 @@ def patch_by_id(request, patch_id): def patch_mbox_by_id(request, patch_id): patch = get_object_or_404(Patch, id=patch_id) - url = reverse('patch-mbox', kwargs={'project_id': patch.project.linkname, - 'msgid': patch.url_msgid}) + url = reverse( + 'patch-mbox', + kwargs={ + 'project_id': patch.project.linkname, + 'msgid': patch.url_msgid, + }, + ) return HttpResponseRedirect(url) @@ -190,7 +214,12 @@ def patch_mbox_by_id(request, patch_id): def patch_raw_by_id(request, patch_id): patch = get_object_or_404(Patch, id=patch_id) - url = reverse('patch-raw', kwargs={'project_id': patch.project.linkname, - 'msgid': patch.url_msgid}) + url = reverse( + 'patch-raw', + kwargs={ + 'project_id': patch.project.linkname, + 'msgid': patch.url_msgid, + }, + ) return HttpResponseRedirect(url) diff --git a/patchwork/views/project.py b/patchwork/views/project.py index 4c25f715..a993618a 100644 --- a/patchwork/views/project.py +++ b/patchwork/views/project.py @@ -19,8 +19,8 @@ def project_list(request): if projects.count() == 1: return HttpResponseRedirect( - reverse('patch-list', - kwargs={'project_id': projects[0].linkname})) + reverse('patch-list', kwargs={'project_id': projects[0].linkname}) + ) context = { 'projects': projects, @@ -35,7 +35,8 @@ def project_detail(request, project_id): context = { 'project': project, 'maintainers': User.objects.filter( - profile__maintainer_projects=project).select_related('profile'), + profile__maintainer_projects=project + ).select_related('profile'), 'n_patches': patches.filter(archived=False).count(), 'n_archived_patches': patches.filter(archived=True).count(), 'enable_xmlrpc': settings.ENABLE_XMLRPC, diff --git a/patchwork/views/pwclient.py b/patchwork/views/pwclient.py index 72ebcbbb..a8be425b 100644 --- a/patchwork/views/pwclient.py +++ b/patchwork/views/pwclient.py @@ -21,8 +21,9 @@ def pwclientrc(request, project_id): else: context['scheme'] = 'http' - response = render(request, 'patchwork/pwclientrc', context, - content_type='text/plain') + response = render( + request, 'patchwork/pwclientrc', context, content_type='text/plain' + ) response['Content-Disposition'] = 'attachment; filename=.pwclientrc' return response diff --git a/patchwork/views/series.py b/patchwork/views/series.py index e0df3adf..a8892ae6 100644 --- a/patchwork/views/series.py +++ b/patchwork/views/series.py @@ -16,6 +16,7 @@ def series_mbox(request, series_id): response = HttpResponse(content_type='text/plain') response.write(series_to_mbox(series)) response['Content-Disposition'] = 'attachment; filename=%s.patch' % ( - series.filename) + series.filename + ) return response diff --git a/patchwork/views/user.py b/patchwork/views/user.py index 6b09adb2..7bf6377e 100644 --- a/patchwork/views/user.py +++ b/patchwork/views/user.py @@ -38,35 +38,41 @@ def register(request): data = form.cleaned_data # create inactive user - user = auth.models.User.objects.create_user(data['username'], - data['email'], - data['password']) + user = auth.models.User.objects.create_user( + data['username'], data['email'], data['password'] + ) user.is_active = False user.first_name = data.get('first_name', '') user.last_name = data.get('last_name', '') user.save() # create confirmation - conf = EmailConfirmation(type='registration', user=user, - email=user.email) + conf = EmailConfirmation( + type='registration', user=user, email=user.email + ) conf.save() context['confirmation'] = conf # send email subject = render_to_string( - 'patchwork/mails/activation-subject.txt') + 'patchwork/mails/activation-subject.txt' + ) message = render_to_string( 'patchwork/mails/activation.txt', - {'site': Site.objects.get_current(), 'confirmation': conf}) + {'site': Site.objects.get_current(), 'confirmation': conf}, + ) try: - send_mail(subject, message, settings.DEFAULT_FROM_EMAIL, - [conf.email]) + send_mail( + subject, message, settings.DEFAULT_FROM_EMAIL, [conf.email] + ) except smtplib.SMTPException: context['confirmation'] = None - context['error'] = ('An error occurred during registration. ' - 'Please try again later') + context['error'] = ( + 'An error occurred during registration. ' + 'Please try again later' + ) else: form = RegistrationForm() @@ -83,8 +89,7 @@ def register_confirm(request, conf): try: person = Person.objects.get(email__iexact=conf.user.email) except Person.DoesNotExist: - person = Person(email=conf.user.email, - name=conf.user.profile.name) + person = Person(email=conf.user.email, name=conf.user.profile.name) person.user = conf.user person.save() @@ -94,8 +99,9 @@ def register_confirm(request, conf): @login_required def profile(request): if request.method == 'POST': - form = UserProfileForm(instance=request.user.profile, - data=request.POST) + form = UserProfileForm( + instance=request.user.profile, data=request.POST + ) if form.is_valid(): form.save() else: @@ -115,9 +121,11 @@ def profile(request): Person._meta.db_table, Person._meta.get_field('email').column, EmailOptout._meta.get_field('email').column, - EmailOptout._meta.db_table) - people = Person.objects.filter(user=request.user) \ - .extra(select={'is_optout': optout_query}) + EmailOptout._meta.db_table, + ) + people = Person.objects.filter(user=request.user).extra( + select={'is_optout': optout_query} + ) context['linked_emails'] = people context['linkform'] = EmailForm() context['api_token'] = request.user.profile.token @@ -134,25 +142,32 @@ def link(request): if request.method == 'POST': form = EmailForm(request.POST) if form.is_valid(): - conf = EmailConfirmation(type='userperson', - user=request.user, - email=form.cleaned_data['email']) + conf = EmailConfirmation( + type='userperson', + user=request.user, + email=form.cleaned_data['email'], + ) conf.save() context['confirmation'] = conf subject = render_to_string('patchwork/mails/user-link-subject.txt') - message = render_to_string('patchwork/mails/user-link.txt', - context, request=request) + message = render_to_string( + 'patchwork/mails/user-link.txt', context, request=request + ) try: - send_mail(subject, - message, - settings.DEFAULT_FROM_EMAIL, - [form.cleaned_data['email']]) + send_mail( + subject, + message, + settings.DEFAULT_FROM_EMAIL, + [form.cleaned_data['email']], + ) except smtplib.SMTPException: context['confirmation'] = None - context['error'] = ('An error occurred during confirmation. ' - 'Please try again later') + context['error'] = ( + 'An error occurred during confirmation. ' + 'Please try again later' + ) else: form = EmailForm() @@ -205,7 +220,9 @@ def todo_lists(request): return HttpResponseRedirect( reverse( 'user-todo', - kwargs={'project_id': todo_lists[0]['project'].linkname})) + kwargs={'project_id': todo_lists[0]['project'].linkname}, + ) + ) context = { 'todo_lists': todo_lists, @@ -218,19 +235,22 @@ def todo_lists(request): def todo_list(request, project_id): project = get_object_or_404(Project, linkname=project_id) patches = request.user.profile.todo_patches(project=project) - filter_settings = [(DelegateFilter, - {'delegate': request.user})] + filter_settings = [(DelegateFilter, {'delegate': request.user})] # TODO(stephenfin): Build the context dict here - context = generic_list(request, project, - 'user-todo', - view_args={'project_id': project.linkname}, - filter_settings=filter_settings, - patches=patches) + context = generic_list( + request, + project, + 'user-todo', + view_args={'project_id': project.linkname}, + filter_settings=filter_settings, + patches=patches, + ) context['bundles'] = request.user.bundles.all() context['action_required_states'] = State.objects.filter( - action_required=True).all() + action_required=True + ).all() return render(request, 'patchwork/todo-list.html', context) diff --git a/patchwork/views/utils.py b/patchwork/views/utils.py index 4631229b..1f7ee0da 100644 --- a/patchwork/views/utils.py +++ b/patchwork/views/utils.py @@ -28,8 +28,9 @@ class PatchMbox(MIMENonMultipart): patch_charset = 'utf-8' def __init__(self, _text): - MIMENonMultipart.__init__(self, 'text', 'plain', - **{'charset': self.patch_charset}) + MIMENonMultipart.__init__( + self, 'text', 'plain', **{'charset': self.patch_charset} + ) self.set_payload(_text.encode(self.patch_charset)) encode_7or8bit(self) @@ -79,9 +80,12 @@ def _submission_to_mbox(submission): utc_timestamp = delta.seconds + delta.days * 24 * 3600 mail = PatchMbox(body) - mail['X-Patchwork-Submitter'] = email.utils.formataddr(( - str(Header(submission.submitter.name, mail.patch_charset)), - submission.submitter.email)) + mail['X-Patchwork-Submitter'] = email.utils.formataddr( + ( + str(Header(submission.submitter.name, mail.patch_charset)), + submission.submitter.email, + ) + ) mail['X-Patchwork-Id'] = str(submission.id) if is_patch and submission.delegate: mail['X-Patchwork-Delegate'] = str(submission.delegate.email) @@ -152,13 +156,15 @@ def series_patch_to_mbox(patch, series_id): 'Patch does not have an associated series. This is ' 'because the patch was processed with an older ' 'version of Patchwork. It is not possible to ' - 'provide dependencies for this patch.') + 'provide dependencies for this patch.' + ) else: try: series_id = int(series_id) except ValueError: - raise Http404('Expected integer series value or *. Received: %r' % - series_id) + raise Http404( + 'Expected integer series value or *. Received: %r' % series_id + ) if patch.series.id != series_id: raise Http404('Patch does not belong to series %d' % series_id) @@ -166,8 +172,9 @@ def series_patch_to_mbox(patch, series_id): mbox = [] # get the series-ified patch - for dep in patch.series.patches.filter( - number__lt=patch.number).order_by('number'): + for dep in patch.series.patches.filter(number__lt=patch.number).order_by( + 'number' + ): mbox.append(patch_to_mbox(dep)) mbox.append(patch_to_mbox(patch)) diff --git a/patchwork/views/xmlrpc.py b/patchwork/views/xmlrpc.py index f70c1055..f134275d 100644 --- a/patchwork/views/xmlrpc.py +++ b/patchwork/views/xmlrpc.py @@ -24,15 +24,13 @@ from patchwork.views.utils import patch_to_mbox -class PatchworkXMLRPCDispatcher(SimpleXMLRPCDispatcher, - XMLRPCDocGenerator): +class PatchworkXMLRPCDispatcher(SimpleXMLRPCDispatcher, XMLRPCDocGenerator): server_name = 'Patchwork XML-RPC API' server_title = 'Patchwork XML-RPC API v1 Documentation' def __init__(self): - SimpleXMLRPCDispatcher.__init__(self, allow_none=False, - encoding=None) + SimpleXMLRPCDispatcher.__init__(self, allow_none=False, encoding=None) XMLRPCDocGenerator.__init__(self) def _dumps(obj, *args, **kwargs): @@ -65,7 +63,7 @@ def _user_for_request(self, request): if not header.startswith('Basic '): raise Exception('Authentication scheme not supported') - header = header[len('Basic '):].strip() + header = header[len('Basic ') :].strip() try: decoded = base64.b64decode(header.encode('ascii')).decode('ascii') @@ -104,7 +102,8 @@ def _marshaled_dispatch(self, request): # report exception back to server response = self.dumps( xmlrpc_client.Fault( - 1, '%s:%s' % (sys.exc_info()[0], sys.exc_info()[1])), + 1, '%s:%s' % (sys.exc_info()[0], sys.exc_info()[1]) + ), ) return response @@ -134,6 +133,7 @@ def xmlrpc(request): return response + # decorator for XMLRPC methods. Setting login_required to true will call # the decorated function with a non-optional user as the first argument. @@ -147,15 +147,31 @@ def wrap(f): # We allow most of the Django field lookup types for remote queries -LOOKUP_TYPES = ['iexact', 'contains', 'icontains', 'gt', 'gte', 'lt', - 'in', 'startswith', 'istartswith', 'endswith', - 'iendswith', 'range', 'year', 'month', 'day', 'isnull'] +LOOKUP_TYPES = [ + 'iexact', + 'contains', + 'icontains', + 'gt', + 'gte', + 'lt', + 'in', + 'startswith', + 'istartswith', + 'endswith', + 'iendswith', + 'range', + 'year', + 'month', + 'day', + 'isnull', +] ####################################################################### # Helper functions ####################################################################### + def project_to_dict(obj): """Serialize a project object. @@ -312,7 +328,7 @@ def patch_check_to_dict(obj): return { 'state': obj.combined_check_state, 'total': len(obj.checks), - 'checks': [check_to_dict(check) for check in obj.checks] + 'checks': [check_to_dict(check) for check in obj.checks], } @@ -320,6 +336,7 @@ def patch_check_to_dict(obj): # Public XML-RPC methods ####################################################################### + def _get_objects(serializer, objects, max_count): if max_count > 0: return [serializer(x) for x in objects[:max_count]] @@ -416,8 +433,9 @@ def person_list(search_str=None, max_count=0): of all persons if no filter given. """ if search_str: - people = (Person.objects.filter(name__icontains=search_str) | - Person.objects.filter(email__icontains=search_str)) + people = Person.objects.filter( + name__icontains=search_str + ) | Person.objects.filter(email__icontains=search_str) else: people = Person.objects.all() @@ -625,8 +643,7 @@ def patch_get_by_project_hash(project, hash): if any, else an empty dict. """ try: - patch = Patch.objects.get(project__linkname=project, - hash=hash) + patch = Patch.objects.get(project__linkname=project, hash=hash) return patch_to_dict(patch) except Patch.DoesNotExist: return {} @@ -860,8 +877,7 @@ def check_list(filt=None): if parts[0] == 'user_id': dfilter['user'] = Person.objects.filter(id=filt[key])[0] if parts[0] == 'project_id': - dfilter['patch__project'] = Project.objects.filter( - id=filt[key])[0] + dfilter['patch__project'] = Project.objects.filter(id=filt[key])[0] elif parts[0] == 'patch_id': dfilter['patch'] = Patch.objects.filter(id=filt[key])[0] elif parts[0] == 'max_count': @@ -895,8 +911,9 @@ def check_get(check_id): @xmlrpc_method(login_required=True) -def check_create(user, patch_id, context, state, target_url="", - description=""): +def check_create( + user, patch_id, context, state, target_url="", description="" +): """Add a Check to a patch. **NOTE:** Authentication is required for this method. @@ -920,8 +937,14 @@ def check_create(user, patch_id, context, state, target_url="", break else: raise Exception("Invalid check state: %s" % state) - Check.objects.create(patch=patch, context=context, state=state, user=user, - target_url=target_url, description=description) + Check.objects.create( + patch=patch, + context=context, + state=state, + user=user, + target_url=target_url, + description=description, + ) return True diff --git a/tox.ini b/tox.ini index 0033c4dc..f687df73 100644 --- a/tox.ini +++ b/tox.ini @@ -43,8 +43,9 @@ commands = flake8 {posargs:patchwork manage.py} # Some rules are ignored as their use makes the code more difficult to read: # # E129 visually indented line with same indent as next logical line -# W504 line break after binary operator -ignore = E129, W504 +# E203 whitespace before ':' +# W503 line break before binary operator +ignore = E129, E203, W503 [testenv:docs] deps =