Run code through black.
Signed-off-by: Stephen Finucane <stephen@that.guru>
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)
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}.')
# 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'
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
class UserAdmin(BaseUserAdmin):
- inlines = (UserProfileInline, )
+ inlines = (UserProfileInline,)
admin.site.unregister(User)
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'
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')
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)
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'
# [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:
field=self.field_name,
serializer=self.parent.__class__.__name__,
instance=instance.__class__.__name__,
- exc=exc
+ exc=exc,
)
)
raise type(exc)(msg)
if DRF_VERSION > (3, 11):
+
class CurrentPatchDefault(object):
requires_context = True
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
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'
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:
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]
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:
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', {}):
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:
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:
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')
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
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
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'},
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):
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'},
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 = {}
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',),
}
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):
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):
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):
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):
lookup_url_kwarg = 'cover_id'
-class CoverCommentDetail(CoverCommentMixin, MultipleFieldLookupMixin,
- RetrieveUpdateAPIView):
+class CoverCommentDetail(
+ CoverCommentMixin, MultipleFieldLookupMixin, RetrieveUpdateAPIView
+):
"""
get:
Show a cover comment.
put:
Update a cover comment.
"""
+
lookup_url_kwargs = ('cover_id', 'comment_id')
lookup_fields = ('cover_id', 'id')
lookup_url_kwarg = 'patch_id'
-class PatchCommentDetail(PatchCommentMixin, MultipleFieldLookupMixin,
- RetrieveUpdateAPIView):
+class PatchCommentDetail(
+ PatchCommentMixin, MultipleFieldLookupMixin, RetrieveUpdateAPIView
+):
"""
get:
Show a patch comment.
put:
Update a patch comment.
"""
+
lookup_url_kwargs = ('patch_id', 'comment_id')
lookup_fields = ('patch_id', 'id')
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'),
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
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):
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'
+ )
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)
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
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 = {
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 = {
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 = {
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 = {
class PersonSerializer(SerializedRelatedField):
-
class _Serializer(BaseHyperlinkedModelSerializer):
-
class Meta:
model = models.Person
fields = ('id', 'url', 'name', 'email')
class ProjectSerializer(SerializedRelatedField):
-
class _Serializer(BaseHyperlinkedModelSerializer):
link_name = CharField(max_length=255, source='linkname')
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'},
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'},
class UserProfileSerializer(SerializedRelatedField):
-
class _Serializer(BaseHyperlinkedModelSerializer):
username = CharField(source='user.username')
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'},
# 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)
# custom fields, filters
-class ModelMultipleChoiceField(BaseMultipleChoiceField):
+class ModelMultipleChoiceField(BaseMultipleChoiceField):
def _get_filter(self, value):
if not self.alternate_lookup:
return 'pk', value
class StateChoiceField(ModelMultipleChoiceField):
-
def _get_filter(self, value):
try:
return 'pk', int(value)
class BaseFilterSet(FilterSet):
-
@property
def form(self):
form = super(BaseFilterSet, self).form
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)
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)
# 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'),
}
# 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',),
}
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),
+ }
+ )
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):
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
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')
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
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):
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'},
# 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')
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}
instance.save()
return super(PatchDetailSerializer, self).update(
- instance, validated_data)
+ instance, validated_data
+ )
@staticmethod
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
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):
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'
+ )
+ )
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'},
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'
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'},
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):
class IsOwnerOrReadOnly(permissions.BasePermission):
-
def has_object_permission(self, request, view, obj):
if request.method in permissions.SAFE_METHODS:
return True
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')
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)
class HashField(models.CharField):
-
def __init__(self, *args, **kwargs):
self.n_bytes = len(hashlib.sha1().hexdigest())
kwargs['max_length'] = self.n_bytes
@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
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):
@property
def form(self):
- return mark_safe('<input type="text" name="series" '
- 'id="series_input" class="form-control">')
+ return mark_safe(
+ '<input type="text" name="series" '
+ 'id="series_input" class="form-control">'
+ )
class SubmitterFilter(Filter):
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}
@property
def form(self):
- return mark_safe('<input type="text" name="submitter" '
- 'id="submitter_input" class="form-control">')
+ return mark_safe(
+ '<input type="text" name="submitter" '
+ 'id="submitter_input" class="form-control">'
+ )
class StateFilter(Filter):
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):
if self.applied and self.state is None:
selected = 'selected'
out += '<option %s value="">%s</option>' % (
- selected, self.action_req_str)
+ selected,
+ self.action_req_str,
+ )
for state in State.objects.all():
selected = ''
selected = ' selected="true"'
out += '<option value="%d" %s>%s</option>' % (
- state.id, selected, escape(state.name))
+ state.id,
+ selected,
+ escape(state.name),
+ )
out += '</select>'
return mark_safe(out)
value = ''
if self.search:
value = escape(self.search)
- return mark_safe('<input name="%s" class="form-control" value="%s">' %
- (self.param, value))
+ return mark_safe(
+ '<input name="%s" class="form-control" value="%s">'
+ % (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)
selected = ''
if self.archive_state == b:
selected = 'checked'
- s += ('<label class="checkbox-inline">'
- ' <input type="radio" name="%(param)s" '
- '%(selected)s value="%(value)s">%(label)s'
- '</label>') % \
- {'label': label,
- 'param': self.param,
- 'selected': selected,
- 'value': self.param_map[b]
- }
+ s += (
+ '<label class="checkbox-inline">'
+ ' <input type="radio" name="%(param)s" '
+ '%(selected)s value="%(value)s">%(label)s'
+ '</label>'
+ ) % {
+ 'label': label,
+ 'param': self.param,
+ 'selected': selected,
+ 'value': self.param_map[b],
+ }
return mark_safe(s)
@property
def form(self):
if self.forced:
- return mark_safe('<input type="hidden" value="%s">%s' % (
- self.param, self.condition))
+ return mark_safe(
+ '<input type="hidden" value="%s">%s'
+ % (self.param, self.condition)
+ )
delegates = User.objects.filter(
- profile__maintainer_projects__isnull=False)
+ profile__maintainer_projects__isnull=False
+ )
out = '<select name="delegate" class="form-control">'
if self.applied and self.delegate is None:
selected = 'selected'
out += '<option %s value="%s">%s</option>' % (
- selected, self.no_delegate_str, self.no_delegate_str)
+ selected,
+ self.no_delegate_str,
+ self.no_delegate_str,
+ )
for delegate in delegates:
selected = ''
selected = ' selected'
out += '<option %s value="%s">%s</option>' % (
- selected, delegate.id, delegate.username)
+ selected,
+ delegate.id,
+ delegate.username,
+ )
out += '</select>'
return mark_safe(out)
StateFilter,
SearchFilter,
ArchiveFilter,
- DelegateFilter
+ DelegateFilter,
]
class Filters:
-
def __init__(self, request):
self._filters = [c(self) for c in FILTERS]
self.values = request.GET
@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):
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)
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']
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']
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
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
class CreateBundleForm(BundleForm):
-
def __init__(self, *args, **kwargs):
super(CreateBundleForm, self).__init__(*args, **kwargs)
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
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):
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
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
# 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)
class OptionalBooleanField(forms.TypedChoiceField):
-
def is_no_change(self, value):
return value == self.empty_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:
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:
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()
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.',
)
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'))
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 = {
' %(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],
'dropped': dropped,
'errors': errors,
'new': count - duplicates - dropped - errors,
- })
+ }
+ )
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']
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)
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'])
'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,
),
),
'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,
),
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',
),
),
(
),
),
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')]),
),
]
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,
),
),
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,
),
),
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],
),
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,
),
),
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,
),
),
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,
),
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)'
),
),
(
'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],
),
'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,
),
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",
),
),
],
models.PositiveSmallIntegerField(
default=None,
help_text=b'The number assigned to this patch in the '
- b'series',
+ b'series',
null=True,
),
),
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',
),
),
(
models.BooleanField(
default=False,
help_text=b'Show click-to-copy patch IDs in the list '
- b'view',
+ b'view',
),
),
(
(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'),
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='+',
'path',
models.CharField(
help_text=b'An fnmatch-style pattern to match '
- b'filenames against.',
+ b'filenames against.',
max_length=255,
),
),
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',
),
),
(
),
),
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',
),
),
migrations.AlterUniqueTogether(
- name='delegationrule', unique_together={('path', 'project')},
+ name='delegationrule',
+ unique_together={('path', 'project')},
),
migrations.AddIndex(
model_name='comment',
),
),
migrations.AlterUniqueTogether(
- name='comment', unique_together={('msgid', 'submission')},
+ name='comment',
+ unique_together={('msgid', 'submission')},
),
migrations.AddField(
model_name='check',
),
),
migrations.AlterUniqueTogether(
- name='patchtag', unique_together={('patch', 'tag')},
+ name='patchtag',
+ unique_together={('patch', 'tag')},
),
migrations.AddField(
model_name='patchchangenotification',
),
),
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')},
),
]
'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,
),
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,
),
),
options={'ordering': ['-priority', 'path']},
),
migrations.AlterUniqueTogether(
- name='delegationrule', unique_together=set([('path', 'project')]),
+ name='delegationrule',
+ unique_together=set([('path', 'project')]),
),
]
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',
# 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',
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(
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,
),
),
operations = [
migrations.RemoveField(
- model_name='userprofile', name='primary_project',
+ model_name='userprofile',
+ name='primary_project',
),
]
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,
),
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)'
),
),
(
'number',
models.PositiveSmallIntegerField(
help_text=b'The number assigned to this patch in the '
- b'series'
+ b'series'
),
),
(
field=models.CharField(max_length=255),
),
migrations.AlterUniqueTogether(
- name='seriesreference', unique_together=set([('series', 'msgid')]),
+ name='seriesreference',
+ unique_together=set([('series', 'msgid')]),
),
]
name='path',
field=models.CharField(
help_text=b'An fnmatch-style pattern to match filenames '
- b'against.',
+ b'against.',
max_length=255,
),
),
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(
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='+',
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='+',
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='+',
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",
),
),
]
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,
),
),
field=models.CharField(max_length=255),
),
migrations.AlterUniqueTogether(
- name='project', unique_together=set([('listid', 'subject_match')]),
+ name='project',
+ unique_together=set([('listid', 'subject_match')]),
),
]
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],
),
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],
),
operations = [
migrations.AlterModelOptions(
- name='series', options={'verbose_name_plural': 'Series'},
+ name='series',
+ options={'verbose_name_plural': 'Series'},
),
]
),
),
migrations.AlterUniqueTogether(
- name='patch', unique_together=set([('series_alt', 'number')]),
+ name='patch',
+ unique_together=set([('series_alt', 'number')]),
),
]
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',
),
),
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
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,
),
),
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,
),
),
field=models.SlugField(
default='default',
help_text='A label to discern check from checks of other '
- 'testing systems.',
+ 'testing systems.',
max_length=255,
),
),
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,
),
),
name='path',
field=models.CharField(
help_text='An fnmatch-style pattern to match filenames '
- 'against.',
+ 'against.',
max_length=255,
),
),
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(
('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'),
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,
),
),
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,
),
),
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],
),
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,
),
name='total',
field=models.IntegerField(
help_text='Number of patches in series as indicated by the '
- 'subject prefix(es)'
+ 'subject prefix(es)'
),
),
migrations.AlterField(
field=models.IntegerField(
default=1,
help_text='Version of series as indicated by the subject '
- 'prefix(es)',
+ 'prefix(es)',
),
),
migrations.AlterField(
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,
),
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],
),
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(
field=models.BooleanField(
default=False,
help_text='Selecting this option allows patchwork to send '
- 'email on your behalf',
+ 'email on your behalf',
),
),
migrations.AlterField(
operations = [
# create a new, separate cover (letter) model
-
migrations.CreateModel(
name='Cover',
fields=[
),
),
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=[
),
),
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
""",
None,
),
-
# copy all 'CoverLetter'-related comments to the new 'CoverComment'
# table; as above, this is non-reversible
-
migrations.RunSQL(
"""
INSERT INTO patchwork_covercomment
""",
None,
),
-
# update all references to the 'CoverLetter' model to point to the new
# 'Cover' model instead
-
migrations.AlterField(
model_name='event',
name='cover',
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',
to='patchwork.Submission',
),
),
-
# rename the 'Comment' model to 'PatchComment'
-
migrations.RenameModel(
old_name='Comment',
new_name='PatchComment',
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',
to='patchwork.Submission',
),
),
-
# do the same for any other field that references 'Patch'
-
migrations.AlterField(
model_name='bundle',
name='patches',
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',
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={
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',
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',
+ ),
]
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',
+ )
+ ],
),
),
]
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
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
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
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):
@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):
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):
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):
class PatchQuerySet(models.query.QuerySet):
-
def with_tag_counts(self, project=None):
if project and not project.use_tags:
return self
"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)
class EmailMixin(models.Model):
"""Mixin for models with an email-origin."""
+
# email metadata
msgid = models.CharField(max_length=255)
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):
class FilenameMixin(object):
-
@property
def filename(self):
"""Return a sanitized filename without extension."""
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']
# 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()
# 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]
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
"""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):
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})
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):
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')
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']
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()
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')]
class PatchRelation(models.Model):
-
def __str__(self):
patches = self.patches.all()
if not patches:
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
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 "<Check id='%d' context='%s' state='%s'" % (
- self.id, self.context, self.get_state_display())
+ self.id,
+ self.context,
+ self.get_state_display(),
+ )
def __str__(self):
return '%s (%s)' % (self.context, self.get_state_display())
the future. Refer to https://code.djangoproject.com/ticket/24272 for more
information.
"""
+
CATEGORY_COVER_CREATED = 'cover-created'
CATEGORY_PATCH_CREATED = 'patch-created'
CATEGORY_PATCH_COMPLETED = 'patch-completed'
# parents
project = models.ForeignKey(
- Project, related_name='+', db_index=True,
+ Project,
+ related_name='+',
+ db_index=True,
on_delete=models.CASCADE,
- help_text='The project that the events belongs to.')
+ help_text='The project that the events belongs to.',
+ )
# event metadata
max_length=25,
choices=CATEGORY_CHOICES,
db_index=True,
- help_text='The category of the event.')
+ help_text='The category of the event.',
+ )
date = models.DateTimeField(
default=datetime.datetime.utcnow,
- help_text='The time this event was created.')
+ help_text='The time this event was created.',
+ )
actor = models.ForeignKey(
- User, related_name='+', null=True, blank=True,
+ User,
+ related_name='+',
+ null=True,
+ blank=True,
on_delete=models.SET_NULL,
- help_text='The user that caused/created this event.')
+ help_text='The user that caused/created this event.',
+ )
# event object
# used
patch = models.ForeignKey(
- Patch, related_name='+', null=True, blank=True,
+ Patch,
+ related_name='+',
+ null=True,
+ blank=True,
on_delete=models.CASCADE,
- help_text='The patch that this event was created for.')
+ help_text='The patch that this event was created for.',
+ )
series = models.ForeignKey(
- Series, related_name='+', null=True, blank=True,
+ Series,
+ related_name='+',
+ null=True,
+ blank=True,
on_delete=models.CASCADE,
- help_text='The series that this event was created for.')
+ help_text='The series that this event was created for.',
+ )
cover = models.ForeignKey(
- Cover, related_name='+', null=True, blank=True,
+ Cover,
+ related_name='+',
+ null=True,
+ blank=True,
on_delete=models.CASCADE,
- help_text='The cover letter that this event was created for.')
+ help_text='The cover letter that this event was created for.',
+ )
# fields for 'patch-state-changed' events
previous_state = models.ForeignKey(
- State, related_name='+', null=True, blank=True,
- on_delete=models.CASCADE)
+ State,
+ related_name='+',
+ null=True,
+ blank=True,
+ on_delete=models.CASCADE,
+ )
current_state = models.ForeignKey(
- State, related_name='+', null=True, blank=True,
- on_delete=models.CASCADE)
+ State,
+ related_name='+',
+ null=True,
+ blank=True,
+ on_delete=models.CASCADE,
+ )
# fields for 'patch-delegate-changed' events
previous_delegate = models.ForeignKey(
- User, related_name='+', null=True, blank=True,
- on_delete=models.CASCADE)
+ User, related_name='+', null=True, blank=True, on_delete=models.CASCADE
+ )
current_delegate = models.ForeignKey(
- User, related_name='+', null=True, blank=True,
- on_delete=models.CASCADE)
+ User, related_name='+', null=True, blank=True, on_delete=models.CASCADE
+ )
# fields for 'patch-relation-changed-changed' events
previous_relation = models.ForeignKey(
- PatchRelation, related_name='+', null=True, blank=True,
- on_delete=models.CASCADE)
+ PatchRelation,
+ related_name='+',
+ null=True,
+ blank=True,
+ on_delete=models.CASCADE,
+ )
current_relation = models.ForeignKey(
- PatchRelation, related_name='+', null=True, blank=True,
- on_delete=models.CASCADE)
+ PatchRelation,
+ related_name='+',
+ null=True,
+ blank=True,
+ on_delete=models.CASCADE,
+ )
# fields or 'patch-check-created' events
created_check = models.ForeignKey(
- Check, related_name='+', null=True, blank=True,
- on_delete=models.CASCADE)
+ Check,
+ related_name='+',
+ null=True,
+ blank=True,
+ on_delete=models.CASCADE,
+ )
# fields for 'cover-comment-created' events
cover_comment = models.ForeignKey(
- CoverComment, related_name='+', null=True, blank=True,
- on_delete=models.CASCADE)
+ CoverComment,
+ related_name='+',
+ null=True,
+ blank=True,
+ on_delete=models.CASCADE,
+ )
# fields for 'patch-comment-created' events
patch_comment = models.ForeignKey(
- PatchComment, related_name='+', null=True, blank=True,
- on_delete=models.CASCADE)
+ PatchComment,
+ related_name='+',
+ null=True,
+ blank=True,
+ on_delete=models.CASCADE,
+ )
# TODO(stephenfin): Validate that the correct fields are being set by way
# of a 'clean' method
class EmailConfirmation(models.Model):
validity = datetime.timedelta(days=settings.CONFIRMATION_VALIDITY_DAYS)
- type = models.CharField(max_length=20, choices=[
- ('userperson', 'User-Person association'),
- ('registration', 'Registration'),
- ('optout', 'Email opt-out'),
- ])
+ type = models.CharField(
+ max_length=20,
+ choices=[
+ ('userperson', 'User-Person association'),
+ ('registration', 'Registration'),
+ ('optout', 'Email opt-out'),
+ ],
+ )
email = models.CharField(max_length=200)
user = models.ForeignKey(User, null=True, on_delete=models.CASCADE)
key = HashField()
def send_notifications():
date_limit = datetime.datetime.utcnow() - datetime.timedelta(
- minutes=settings.NOTIFICATION_DELAY_MINUTES)
+ minutes=settings.NOTIFICATION_DELAY_MINUTES
+ )
# We delay sending notifications to a user if they have other
# notifications that are still in the "pending" state. To do this,
# we compare the total number of patch change notifications queued
# for each user against the number of "ready" notifications.
qs = PatchChangeNotification.objects.all()
- qs2 = PatchChangeNotification.objects\
- .filter(last_modified__lt=date_limit)\
- .values('patch__submitter')\
+ qs2 = (
+ PatchChangeNotification.objects.filter(last_modified__lt=date_limit)
+ .values('patch__submitter')
.annotate(count=Count('patch__submitter'))
+ )
qs2 = {elem['patch__submitter']: elem['count'] for elem in qs2}
- groups = itertools.groupby(qs.order_by('patch__submitter'),
- lambda n: n.patch.submitter)
+ groups = itertools.groupby(
+ qs.order_by('patch__submitter'), lambda n: n.patch.submitter
+ )
errors = []
}
subject = render_to_string(
- 'patchwork/mails/patch-change-notification-subject.txt',
- context).strip()
+ 'patchwork/mails/patch-change-notification-subject.txt', context
+ ).strip()
content = render_to_string(
- 'patchwork/mails/patch-change-notification.txt',
- context)
+ 'patchwork/mails/patch-change-notification.txt', context
+ )
- message = EmailMessage(subject=subject, body=content,
- from_email=settings.NOTIFICATION_FROM_EMAIL,
- to=[recipient.email],
- headers={'Precedence': 'bulk'})
+ message = EmailMessage(
+ subject=subject,
+ body=content,
+ from_email=settings.NOTIFICATION_FROM_EMAIL,
+ to=[recipient.email],
+ headers={'Precedence': 'bulk'},
+ )
try:
message.send()
Users whose registration confirmation has expired are removed.
"""
# expire any invalid confirmations
- q = (Q(date__lt=datetime.datetime.utcnow() - EmailConfirmation.validity) |
- Q(active=False))
+ q = Q(
+ date__lt=datetime.datetime.utcnow() - EmailConfirmation.validity
+ ) | Q(active=False)
EmailConfirmation.objects.filter(q).delete()
# remove inactive users with no pending confirmation
- pending_confs = (EmailConfirmation.objects
- .filter(user__isnull=False).values('user'))
+ pending_confs = EmailConfirmation.objects.filter(
+ user__isnull=False
+ ).values('user')
users = User.objects.filter(is_active=False).exclude(id__in=pending_confs)
# delete users
class Paginator(paginator.Paginator):
-
def __init__(self, request, objects):
items_per_page = settings.DEFAULT_ITEMS_PER_PAGE
elif page_no < LEADING_PAGE_RANGE:
adjacent_start = 1
adjacent_end = LEADING_PAGE_RANGE_DISPLAYED + 1
- self.leading_set = [n + pages for n in
- range(0, -NUM_PAGES_OUTSIDE_RANGE, -1)]
+ self.leading_set = [
+ n + pages for n in range(0, -NUM_PAGES_OUTSIDE_RANGE, -1)
+ ]
elif page_no >= 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
+ )
# @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
# 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)
# 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()
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
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:
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
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
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):
from_res = [
# for "Firstname Lastname" <example@example.com> 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]))),
]
if name and ' via ' in name:
# Mailman uses the format "<name> via <list>"
# Google Groups uses "'<name>' via <list>"
- 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
# 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
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)
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)
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('--- '):
elif state == 3:
match = _hunk_re.match(line)
if match:
+
def fn(x):
if not x:
return 1
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()
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
# 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:
# - 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
# 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
# message
try:
series = SeriesReference.objects.get(
- msgid=msgid, project=project).series
+ msgid=msgid, project=project
+ ).series
except SeriesReference.DoesNotExist:
series = 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):
date=date,
headers=headers,
submitter=author,
- content=message)
+ content=message,
+ )
logger.debug('Cover letter saved')
headers=headers,
submitter=author,
content=message,
- addressed=addressed)
+ addressed=addressed,
+ )
logger.debug('Comment saved')
date=date,
headers=headers,
submitter=author,
- content=message)
+ content=message,
+ )
logger.debug('Comment saved')
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
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',
# 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 = ['*']
# 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
STATIC_ROOT = os.environ.get('STATIC_ROOT', '/srv/patchwork/htdocs/static')
STATICFILES_STORAGE = (
- 'django.contrib.staticfiles.storage.ManifestStaticFilesStorage')
+ 'django.contrib.staticfiles.storage.ManifestStaticFilesStorage'
+)
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()
@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:
@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:
@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,
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:
@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,
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:
@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:
@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
# 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
# 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
@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
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:
@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:
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
@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,
@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,
# 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):
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
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):
# 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))
# 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))
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,
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')
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'])
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'])
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))
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'))
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()
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')
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):
This is required due to the difference in handling JSON vs form-data in
CheckSerializer's run_validation().
"""
+
fixtures = ['default_tags']
def setUp(self):
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())
@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}
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'])
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):
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."""
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."""
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)
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')
"""
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')
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'])
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):
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."""
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."""
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)
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')
"""
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')
# 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'])
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):
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')
# 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])
# 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 = {}
# 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
"""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))
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):
# 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'])
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
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):
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):
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):
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')
"""Show a specific patch."""
patch = create_patch(
content='Reviewed-by: Test User <test@example.com>\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))
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)
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)
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."""
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)
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."""
@unittest.skipUnless(settings.ENABLE_REST_API, 'requires ENABLE_REST_API')
class TestPersonAPI(utils.APITestCase):
-
@staticmethod
def api_url(item=None):
if item is None:
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."""
@unittest.skipUnless(settings.ENABLE_REST_API, 'requires ENABLE_REST_API')
class TestProjectAPI(utils.APITestCase):
-
@staticmethod
def api_url(item=None, version=None):
kwargs = {}
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."""
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)
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)
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)
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)
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)
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."""
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')
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)
@unittest.skipUnless(settings.ENABLE_REST_API, 'requires ENABLE_REST_API')
class TestUserAPI(utils.APITestCase):
-
@staticmethod
def api_url(item=None, version=None):
kwargs = {}
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)
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')
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)
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)
# 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 = {}
raise Exception(
"Tests '{}' and '{}' write to the same file".format(
- _WRITTEN_FILES[filename], func))
+ _WRITTEN_FILES[filename], func
+ )
+ )
_WRITTEN_FILES[filename] = func
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=(',', ': '))
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
class APIClient(BaseAPIClient):
-
def __init__(self, *args, **kwargs):
super(APIClient, self).__init__(*args, **kwargs)
self.factory = APIRequestFactory()
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
# 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 = {}
class RegexValidator(object):
-
def __init__(self, regex):
self.regex = re.compile(regex, re.IGNORECASE)
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)
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
# 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()
# 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()
class PatchChecksTest(TransactionTestCase):
-
def setUp(self):
self.patch = create_patches()[0]
self.user = create_user()
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:
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:
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())
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...
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()
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())
class TestHashField(SimpleTestCase):
-
def test_n_bytes(self):
"""Sanity check the hashing algorithm.
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
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)
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()]
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()
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:
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()
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()
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()
class PaginatorTest(TestCase):
-
def setUp(self):
self.user = create_user()
self.user.profile.items_per_page = ITEMS_PER_PAGE
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})
# 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)
# 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)
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
+ )
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):
class PatchTest(TestCase):
-
def _find_content(self, mbox_filename):
mail = read_mail(mbox_filename)
diff, message = find_content(mail)
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)
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)
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):
@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):
"""
@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)
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)
project = create_project()
real_sender = 'Existing Sender <existing@example.com>'
munged_sender = 'Existing Sender via List <{}>'.format(
- project.listemail)
+ project.listemail
+ )
other_email = 'Other Person <other@example.com>'
# Unmunged author
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)
project = create_project()
real_sender = 'Existing Sender <existing@example.com>'
munged_sender = "'Existing Sender' via List <{}>".format(
- project.listemail)
+ project.listemail
+ )
# Unmunged author
mail = self._create_email(real_sender)
references (list): A list of preceding messages' msgids,
oldest first
"""
- mail = 'Message-Id: %s\n' % msgid + \
- 'From: example user <user@example.com>\n' + \
- 'Subject: Tests\n'
+ mail = (
+ 'Message-Id: %s\n' % msgid
+ + 'From: example user <user@example.com>\n'
+ + 'Subject: Tests\n'
+ )
if references:
mail += 'In-Reply-To: %s\n' % references[-1]
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()
# 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):
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()
@staticmethod
def _create_email(subject):
- mail = 'Message-Id: %s\n' % make_msgid() + \
- 'From: example user <user@example.com>\n' + \
- 'Subject: %s\n\n' % subject + \
- 'test\n\n' + SAMPLE_DIFF
+ mail = (
+ 'Message-Id: %s\n' % make_msgid()
+ + 'From: example user <user@example.com>\n'
+ + 'Subject: %s\n\n' % subject
+ + 'test\n\n'
+ + SAMPLE_DIFF
+ )
return message_from_string(mail)
def _test_encoding(self, subject_header, parsed_subject):
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'
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']
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'
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)
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):
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)
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')
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'))
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())
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())
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
fixtures = ['default_tags']
patch_filename = '0001-add-line.patch'
- orig_content = ('test comment\n\n' +
- 'Tested-by: Test User <test@example.com>\n' +
- 'Reviewed-by: Test User <test@example.com>\n')
+ orig_content = (
+ 'test comment\n\n'
+ + 'Tested-by: Test User <test@example.com>\n'
+ + 'Reviewed-by: Test User <test@example.com>\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 <test@example.com>\n' +
- 'Reviewed-by: Test User <test@example.com>\n')
+ comment_content = (
+ 'test comment\n\n'
+ + 'Tested-by: Test User <test@example.com>\n'
+ + 'Reviewed-by: Test User <test@example.com>\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'))
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
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)
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)
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()
class _BaseTestCase(TestCase):
-
def setUp(self):
utils.create_state()
# 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)
- [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])
- [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])
- [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])
- [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])
- [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])
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])
- [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])
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])
- [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])
- [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])
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)
- [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])
- [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])
- [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])
- [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])
- [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])
- [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])
- [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])
- [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])
- 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])
- [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])
- [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])
- [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])
- [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.
- [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])
- [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])
- [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])
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])
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()
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)
class PatchCreatedTest(_BaseTestCase):
-
def test_patch_created(self):
"""No series, so patch dependencies implicitly exist."""
patch = utils.create_patch(series=None)
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])
class PatchChangedTest(_BaseTestCase):
-
def test_patch_state_changed(self):
# purposefully setting series to None to minimize additional events
patch = utils.create_patch(series=None)
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
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)
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
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):
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)
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)
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)
class CoverCreatedTest(_BaseTestCase):
-
def test_cover_created(self):
cover = utils.create_cover()
events = _get_events(cover=cover)
class SeriesCreatedTest(_BaseTestCase):
-
def test_series_created(self):
series = utils.create_series()
events = _get_events(series=series)
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])
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)
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):
tags = {
self.ACK: 'Acked',
self.REVIEW: 'Reviewed',
- self.TEST: 'Tested'
+ self.TEST: 'Tested',
}
if tagtype not in tags:
return ''
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):
class PatchTagManagerTest(PatchTagsTest):
-
def assertTagsEqual(self, patch, acks, reviews, tests): # noqa
tagattrs = {}
for tag in Tag.objects.all():
# 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']),
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)
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])
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):
class XMLRPCModelTestMixin(object):
-
def create_multiple(self, count):
return [self.create_single() for i in range(count)]
class XMLRPCPersonTest(XMLRPCTest, XMLRPCModelTestMixin):
-
def setUp(self):
super(XMLRPCPersonTest, self).setUp()
self.get_endpoint = self.rpc.person_get
class XMLRPCProjectTest(XMLRPCTest, XMLRPCModelTestMixin):
-
def setUp(self):
super(XMLRPCProjectTest, self).setUp()
self.get_endpoint = self.rpc.project_get
class XMLRPCStateTest(XMLRPCTest, XMLRPCModelTestMixin):
-
def setUp(self):
super(XMLRPCStateTest, self).setUp()
self.get_endpoint = self.rpc.state_get
class XMLRPCCheckTest(XMLRPCTest, XMLRPCFilterModelTestMixin):
-
def setUp(self):
super(XMLRPCCheckTest, self).setUp()
self.get_endpoint = self.rpc.check_get
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)
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
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)
class AboutViewTest(TestCase):
-
def _test_redirect(self, view):
requested_url = reverse(view)
redirect_url = reverse('about')
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')
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())
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)
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'))
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)
# 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
class BundleMboxTest(BundleTestBase):
-
def test_empty_bundle(self):
response = self.client.get(bundle_mbox_url(self.bundle))
self.assertEqual(response.status_code, 200)
class BundleUpdateTest(BundleTestBase):
-
def test_no_action(self):
newname = 'newbundlename'
data = {
class BundleMaintainerUpdateTest(BundleUpdateTest):
-
def setUp(self):
super(BundleMaintainerUpdateTest, self).setUp()
class BundlePublicViewTest(BundleTestBase):
-
def setUp(self):
super(BundlePublicViewTest, self).setUp()
self.client.logout()
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):
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')
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)
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)
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)
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)
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)
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)
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())
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')
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)
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)
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):
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)
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"),
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)
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)
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)
class BundleReorderTest(BundleTestBase):
-
def setUp(self):
super(BundleReorderTest, self).setUp(5)
for i in range(5):
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)
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."""
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,
+ },
+ ),
+ )
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)
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)
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)
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)
class MailSettingsTest(TestCase):
-
def test_get(self):
response = self.client.get(reverse('mail-settings'))
self.assertEqual(response.status_code, 200)
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'})
class OptoutRequestTest(TestCase):
-
def test_get(self):
response = self.client.get(reverse('mail-optout'))
self.assertRedirects(response, reverse('mail-settings'))
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)
class OptoutTest(TestCase):
-
def setUp(self):
self.email = u'foo@example.com'
self.conf = EmailConfirmation(type='optout', email=self.email)
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):
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)
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)
class OptinTest(TestCase):
-
def setUp(self):
self.email = u'foo@example.com'
self.optout = EmailOptout(email=self.email)
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')
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):
"""Validate presence of correct optin/optout forms."""
- form_re_template = (r'<form\s+[^>]*action="%(url)s"[^>]*>'
- r'.*?<input\s+[^>]*value="%(email)s"[^>]*>.*?'
- r'</form>')
+ form_re_template = (
+ r'<form\s+[^>]*action="%(url)s"[^>]*>'
+ r'.*?<input\s+[^>]*value="%(email)s"[^>]*>.*?'
+ r'</form>'
+ )
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)
class EmptyPatchListTest(TestCase):
-
def test_empty_patch_list(self):
"""Validates absence of table with zero patches."""
project = create_project()
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):
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'<tr id="patch_row:(\d+)"')
- ids = [int(m.group(1))
- for m in id_re.finditer(response.content.decode())]
+ ids = [
+ int(m.group(1)) for m in id_re.finditer(response.content.decode())
+ ]
return ids
test_fn(p1, p2)
def test_date_order(self):
- url = reverse('patch-list',
- kwargs={'project_id': self.project.linkname})
+ url = reverse(
+ 'patch-list', kwargs={'project_id': self.project.linkname}
+ )
response = self.client.get(url + '?order=date')
def test_fn(p1, p2):
self._test_sequence(response, test_fn)
def test_date_reverse_order(self):
- url = reverse('patch-list',
- kwargs={'project_id': self.project.linkname})
+ url = reverse(
+ 'patch-list', kwargs={'project_id': self.project.linkname}
+ )
response = self.client.get(url + '?order=-date')
def test_fn(p1, p2):
#
# [1] https://code.djangoproject.com/ticket/30248
# [2] https://michaelsoolee.com/case-insensitive-sorting-sqlite/
- @unittest.skipIf('sqlite3' in settings.DATABASES['default']['ENGINE'],
- 'The sqlite3 backend does not support case insensitive '
- 'ordering')
+ @unittest.skipIf(
+ 'sqlite3' in settings.DATABASES['default']['ENGINE'],
+ 'The sqlite3 backend does not support case insensitive ' 'ordering',
+ )
def test_submitter_order(self):
- url = reverse('patch-list',
- kwargs={'project_id': self.project.linkname})
+ url = reverse(
+ 'patch-list', kwargs={'project_id': self.project.linkname}
+ )
response = self.client.get(url + '?order=submitter')
def test_fn(p1, p2):
- self.assertLessEqual(p1.submitter.name.lower(),
- p2.submitter.name.lower())
+ self.assertLessEqual(
+ p1.submitter.name.lower(), p2.submitter.name.lower()
+ )
self._test_sequence(response, test_fn)
- @unittest.skipIf('sqlite3' in settings.DATABASES['default']['ENGINE'],
- 'The sqlite3 backend does not support case insensitive '
- 'ordering')
+ @unittest.skipIf(
+ 'sqlite3' in settings.DATABASES['default']['ENGINE'],
+ 'The sqlite3 backend does not support case insensitive ' 'ordering',
+ )
def test_submitter_reverse_order(self):
- url = reverse('patch-list',
- kwargs={'project_id': self.project.linkname})
+ url = reverse(
+ 'patch-list', kwargs={'project_id': self.project.linkname}
+ )
response = self.client.get(url + '?order=-submitter')
def test_fn(p1, p2):
- self.assertGreaterEqual(p1.submitter.name.lower(),
- p2.submitter.name.lower())
+ self.assertGreaterEqual(
+ p1.submitter.name.lower(), p2.submitter.name.lower()
+ )
self._test_sequence(response, test_fn)
class PatchListFilteringTest(TestCase):
-
def test_escaping(self):
"""Validate escaping of filter fragments in a query string.
class PatchViewTest(TestCase):
-
def test_redirect(self):
patch = create_patch()
- requested_url = reverse('cover-detail',
- kwargs={'project_id': patch.project.linkname,
- 'msgid': patch.url_msgid})
- redirect_url = reverse('patch-detail',
- kwargs={'project_id': patch.project.linkname,
- 'msgid': patch.url_msgid})
+ requested_url = reverse(
+ 'cover-detail',
+ kwargs={
+ 'project_id': patch.project.linkname,
+ 'msgid': patch.url_msgid,
+ },
+ )
+ redirect_url = reverse(
+ 'patch-detail',
+ kwargs={
+ 'project_id': patch.project.linkname,
+ 'msgid': patch.url_msgid,
+ },
+ )
response = self.client.get(requested_url)
self.assertRedirects(response, redirect_url)
patch = create_patch()
comment_id = create_patch_comment(patch=patch).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('patch-detail',
- kwargs={'project_id': patch.project.linkname,
- 'msgid': patch.url_msgid}),
- comment_id)
+ reverse(
+ 'patch-detail',
+ kwargs={
+ 'project_id': patch.project.linkname,
+ 'msgid': patch.url_msgid,
+ },
+ ),
+ comment_id,
+ )
response = self.client.get(requested_url)
self.assertRedirects(response, redirect_url)
def test_old_detail_url(self):
patch = create_patch()
- requested_url = reverse('patch-id-redirect',
- kwargs={'patch_id': patch.id})
- redirect_url = reverse('patch-detail',
- kwargs={'project_id': patch.project.linkname,
- 'msgid': patch.url_msgid})
+ requested_url = reverse(
+ 'patch-id-redirect', kwargs={'patch_id': patch.id}
+ )
+ redirect_url = reverse(
+ 'patch-detail',
+ kwargs={
+ 'project_id': patch.project.linkname,
+ 'msgid': patch.url_msgid,
+ },
+ )
response = self.client.get(requested_url)
self.assertRedirects(response, redirect_url)
def test_old_mbox_url(self):
patch = create_patch()
- requested_url = reverse('patch-mbox-redirect',
- kwargs={'patch_id': patch.id})
- redirect_url = reverse('patch-mbox',
- kwargs={'project_id': patch.project.linkname,
- 'msgid': patch.url_msgid})
+ requested_url = reverse(
+ 'patch-mbox-redirect', kwargs={'patch_id': patch.id}
+ )
+ redirect_url = reverse(
+ 'patch-mbox',
+ kwargs={
+ 'project_id': patch.project.linkname,
+ 'msgid': patch.url_msgid,
+ },
+ )
response = self.client.get(requested_url)
self.assertRedirects(response, redirect_url)
def test_old_raw_url(self):
patch = create_patch()
- requested_url = reverse('patch-raw-redirect',
- kwargs={'patch_id': patch.id})
- redirect_url = reverse('patch-raw',
- kwargs={'project_id': patch.project.linkname,
- 'msgid': patch.url_msgid})
+ requested_url = reverse(
+ 'patch-raw-redirect', kwargs={'patch_id': patch.id}
+ )
+ redirect_url = reverse(
+ 'patch-raw',
+ kwargs={
+ 'project_id': patch.project.linkname,
+ 'msgid': patch.url_msgid,
+ },
+ )
response = self.client.get(requested_url)
self.assertRedirects(response, redirect_url)
patch.headers = unescaped_string
patch.content = unescaped_string
patch.save()
- requested_url = reverse('patch-detail',
- kwargs={'project_id': patch.project.linkname,
- 'msgid': patch.url_msgid})
+ requested_url = reverse(
+ 'patch-detail',
+ kwargs={
+ 'project_id': patch.project.linkname,
+ 'msgid': patch.url_msgid,
+ },
+ )
response = self.client.get(requested_url)
self.assertNotIn('<b>TEST</b>'.encode('utf-8'), response.content)
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={
# and it should only show the unique checks
self.assertEqual(
- 1, response.content.decode().count(
+ 1,
+ response.content.decode().count(
f'<td>{check_a.user}/{check_a.context}</td>'
- ))
+ ),
+ )
self.assertEqual(
- 1, response.content.decode().count(
+ 1,
+ response.content.decode().count(
f'<td>{check_b.user}/{check_b.context}</td>'
- ))
+ ),
+ )
class PatchUpdateTest(TestCase):
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 = {
'form': 'patchlistform',
'archived': '*',
'delegate': '*',
- 'state': '*'
+ 'state': '*',
}
def _select_all_patches(self, data):
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)
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)
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):
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()
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')
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)
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)
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)
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):
class RegistrationTest(TestCase):
-
def setUp(self):
self.user = TestUser()
self.client = Client()
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()
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)
# 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)
class RegistrationConfirmationTest(TestCase):
-
def setUp(self):
self.user = TestUser()
self.default_data = {
'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):
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)
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)
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'])
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):
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):
class LoginRedirectTest(TestCase):
-
def test_user_login_redirect(self):
url = reverse('user-profile')
response = self.client.get(url)
class UserProfileTest(_UserTestCase):
-
def test_user_profile(self):
response = self.client.get(reverse('user-profile'))
self.assertContains(response, 'Your Profile')
class PasswordChangeTest(_UserTestCase):
-
def test_password_change_form(self):
response = self.client.get(reverse('password_change'))
self.assertContains(response, 'Change my password')
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))
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)
"""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)
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)
rewritten_from_header = 'Person <person@example.com>'
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)
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):
class MboxSeriesPatchTest(TestCase):
-
@staticmethod
def _create_patches():
series = create_series()
class MboxSeriesTest(TestCase):
-
def test_series(self):
series = create_series()
patch_a = create_patch(series=series)
re_path(
r'^api/(?:(?P<version>(1.1|1.2|1.3))/)?', include(api_1_1_patterns)
),
- re_path(
- r'^api/(?:(?P<version>(1.3))/)?', include(api_1_3_patterns)
- ),
+ re_path(r'^api/(?:(?P<version>(1.3))/)?', include(api_1_3_patterns)),
# token change
path(
'user/generate-token/',
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):
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):
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 ''
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':
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)
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 = []
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
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 = []
# 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
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
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 {
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 {
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:
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,
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
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
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
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,
+ },
+ )
+ )
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,
)
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 = {
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
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)
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)
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)
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)
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()
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:
)
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
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)
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:
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:
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')
else:
response.write(patch_to_mbox(patch))
response['Content-Disposition'] = 'attachment; filename=%s.patch' % (
- patch.filename)
+ patch.filename
+ )
return response
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)
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)
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)
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,
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,
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
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
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()
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()
@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:
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
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()
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,
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)
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)
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)
'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)
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))
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):
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')
# 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
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.
# 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.
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],
}
# Public XML-RPC methods
#######################################################################
+
def _get_objects(serializer, objects, max_count):
if max_count > 0:
return [serializer(x) for x in objects[:max_count]]
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()
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 {}
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':
@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.
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
# 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 =