so far so good

all tests pass
This commit is contained in:
Jeremy Langley 2022-02-13 07:56:58 -08:00
parent efc7c1d664
commit e7ecbf92c4
18 changed files with 59 additions and 80 deletions

View File

@ -140,7 +140,7 @@ if markdown is not None and pygments is not None:
code = m.group(2).replace('\t', ' ') code = m.group(2).replace('\t', ' ')
code = pygments.highlight(code, lexer, self.formatter) code = pygments.highlight(code, lexer, self.formatter)
code = code.replace('\n\n', '\n&nbsp;\n').replace('\n', '<br />').replace('\\@', '@') code = code.replace('\n\n', '\n&nbsp;\n').replace('\n', '<br />').replace('\\@', '@')
return '\n\n%s\n\n' % code return f'\n\n{code}\n\n'
ret = self.pattern.sub(repl, "\n".join(lines)) ret = self.pattern.sub(repl, "\n".join(lines))
return ret.split("\n") return ret.split("\n")

View File

@ -41,7 +41,7 @@ def api_view(http_method_names=None):
# api_view applied with eg. string instead of list of strings # api_view applied with eg. string instead of list of strings
assert isinstance(http_method_names, (list, tuple)), \ assert isinstance(http_method_names, (list, tuple)), \
'@api_view expected a list of strings, received %s' % type(http_method_names).__name__ f'@api_view expected a list of strings, received {type(http_method_names).__name__}'
allowed_methods = set(http_method_names) | {'options'} allowed_methods = set(http_method_names) | {'options'}
WrappedAPIView.http_method_names = [method.lower() for method in allowed_methods] WrappedAPIView.http_method_names = [method.lower() for method in allowed_methods]
@ -199,7 +199,7 @@ class MethodMapper(dict):
def _map(self, method, func): def _map(self, method, func):
assert method not in self, ( assert method not in self, (
"Method '%s' has already been mapped to '.%s'." % (method, self[method])) f"Method '{method}' has already been mapped to '.{self[method]}'.")
assert func.__name__ != self.action.__name__, ( assert func.__name__ != self.action.__name__, (
"Method mapping does not behave like the property decorator. You " "Method mapping does not behave like the property decorator. You "
"cannot use the same method name for each mapping declaration.") "cannot use the same method name for each mapping declaration.")

View File

@ -84,10 +84,8 @@ class ErrorDetail(str):
return not self.__eq__(other) return not self.__eq__(other)
def __repr__(self): def __repr__(self):
return 'ErrorDetail(string=%r, code=%r)' % ( return f"ErrorDetail(string='{str(self)}', code='{self.code}')"
str(self),
self.code,
)
def __hash__(self): def __hash__(self):
return hash(str(self)) return hash(str(self))

View File

@ -280,7 +280,7 @@ class CreateOnlyDefault:
return self.default return self.default
def __repr__(self): def __repr__(self):
return '%s(%s)' % (self.__class__.__name__, repr(self.default)) return f'{self.__class__.__name__}({repr(self.default)})'
class CurrentUserDefault: class CurrentUserDefault:
@ -290,7 +290,7 @@ class CurrentUserDefault:
return serializer_field.context['request'].user return serializer_field.context['request'].user
def __repr__(self): def __repr__(self):
return '%s()' % self.__class__.__name__ return f'{self.__class__.__name__}()'
class SkipField(Exception): class SkipField(Exception):

View File

@ -294,8 +294,8 @@ class OrderingFilter(BaseFilterBackend):
'param': self.ordering_param, 'param': self.ordering_param,
} }
for key, label in self.get_valid_fields(queryset, view, context): for key, label in self.get_valid_fields(queryset, view, context):
options.append((key, '%s - %s' % (label, _('ascending')))) options.append((key, f'{label} - {_("ascending")}'))
options.append(('-' + key, '%s - %s' % (label, _('descending')))) options.append(('-' + key, f'{label} - {_("descending")}'))
context['options'] = options context['options'] = options
return context return context

View File

@ -61,9 +61,9 @@ class GenericAPIView(views.APIView):
(Eg. return a list of items that is specific to the user) (Eg. return a list of items that is specific to the user)
""" """
assert self.queryset is not None, ( assert self.queryset is not None, (
"'%s' should either include a `queryset` attribute, " f"'{self.__class__.__name__}' should either include a `queryset` attribute, "
"or override the `get_queryset()` method." f"or override the `get_queryset()` method."
% self.__class__.__name__
) )
queryset = self.queryset queryset = self.queryset
@ -120,9 +120,8 @@ class GenericAPIView(views.APIView):
(Eg. admins get full serialization, others get basic serialization) (Eg. admins get full serialization, others get basic serialization)
""" """
assert self.serializer_class is not None, ( assert self.serializer_class is not None, (
"'%s' should either include a `serializer_class` attribute, " f"'{self.__class__.__name__}' should either include a `serializer_class` attribute, "
"or override the `get_serializer_class()` method." "or override the `get_serializer_class()` method."
% self.__class__.__name__
) )
return self.serializer_class return self.serializer_class

View File

@ -64,7 +64,7 @@ class JSONParser(BaseParser):
parse_constant = json.strict_constant if self.strict else None parse_constant = json.strict_constant if self.strict else None
return json.load(decoded_stream, parse_constant=parse_constant) return json.load(decoded_stream, parse_constant=parse_constant)
except ValueError as exc: except ValueError as exc:
raise ParseError('JSON parse error - %s' % str(exc)) raise ParseError(f'JSON parse error - {str(exc)}')
class FormParser(BaseParser): class FormParser(BaseParser):
@ -109,7 +109,7 @@ class MultiPartParser(BaseParser):
data, files = parser.parse() data, files = parser.parse()
return DataAndFiles(data, files) return DataAndFiles(data, files)
except MultiPartParserError as exc: except MultiPartParserError as exc:
raise ParseError('Multipart form parse error - %s' % str(exc)) raise ParseError(f'Multipart form parse error - {str(exc)}')
class FileUploadParser(BaseParser): class FileUploadParser(BaseParser):

View File

@ -46,6 +46,7 @@ class Hyperlink(str):
We use this for hyperlinked URLs that may render as a named link We use this for hyperlinked URLs that may render as a named link
in some contexts, or render as a plain URL in others. in some contexts, or render as a plain URL in others.
""" """
def __new__(cls, url, obj): def __new__(cls, url, obj):
ret = super().__new__(cls, url) ret = super().__new__(cls, url)
ret.obj = obj ret.obj = obj
@ -70,11 +71,12 @@ class PKOnlyObject:
instance, but still want to return an object with a .pk attribute, instance, but still want to return an object with a .pk attribute,
in order to keep the same interface as a regular model instance. in order to keep the same interface as a regular model instance.
""" """
def __init__(self, pk): def __init__(self, pk):
self.pk = pk self.pk = pk
def __str__(self): def __str__(self):
return "%s" % self.pk return f"{self.pk}"
# We assume that 'validators' are intended for the child serializer, # We assume that 'validators' are intended for the child serializer,
@ -376,9 +378,9 @@ class HyperlinkedRelatedField(RelatedField):
def to_representation(self, value): def to_representation(self, value):
assert 'request' in self.context, ( assert 'request' in self.context, (
"`%s` requires the request in the serializer" f"`{self.__class__.__name__}` requires the request in the serializer"
" context. Add `context={'request': request}` when instantiating " " context. Add `context={'request': request}` when instantiating "
"the serializer." % self.__class__.__name__ "the serializer."
) )
request = self.context['request'] request = self.context['request']

View File

@ -197,7 +197,7 @@ class TemplateHTMLRenderer(BaseRenderer):
return self.resolve_template(template_names) return self.resolve_template(template_names)
except Exception: except Exception:
# Fall back to using eg '404 Not Found' # Fall back to using eg '404 Not Found'
body = '%d %s' % (response.status_code, response.status_text.title()) body = f'{response.status_code} {response.status_text.title()}'
template = engines['django'].from_string(body) template = engines['django'].from_string(body)
return template return template
@ -414,9 +414,9 @@ class BrowsableAPIRenderer(BaseRenderer):
render_style = getattr(renderer, 'render_style', 'text') render_style = getattr(renderer, 'render_style', 'text')
assert render_style in ['text', 'binary'], 'Expected .render_style ' \ assert render_style in ['text', 'binary'], 'Expected .render_style ' \
'"text" or "binary", but got "%s"' % render_style f'"text" or "binary", but got "{render_style}"'
if render_style == 'binary': if render_style == 'binary':
return '[%d bytes of binary content]' % len(content) return f'[{len(content)} bytes of binary content]'
return content.decode('utf-8') if isinstance(content, bytes) else content return content.decode('utf-8') if isinstance(content, bytes) else content
@ -660,9 +660,9 @@ class BrowsableAPIRenderer(BaseRenderer):
response_headers = OrderedDict(sorted(response.items())) response_headers = OrderedDict(sorted(response.items()))
renderer_content_type = '' renderer_content_type = ''
if renderer: if renderer:
renderer_content_type = '%s' % renderer.media_type renderer_content_type = f'{renderer.media_type}'
if renderer.charset: if renderer.charset:
renderer_content_type += ' ;%s' % renderer.charset renderer_content_type += f' ;{renderer.charset}'
response_headers['Content-Type'] = renderer_content_type response_headers['Content-Type'] = renderer_content_type
if getattr(view, 'paginator', None) and view.paginator.display_page_controls: if getattr(view, 'paginator', None) and view.paginator.display_page_controls:

View File

@ -180,11 +180,8 @@ class Request:
self.authenticators = (forced_auth,) self.authenticators = (forced_auth,)
def __repr__(self): def __repr__(self):
return '<%s.%s: %s %r>' % ( return f'<{self.__class__.__module__}.{self.__class__.__name__}: ' \
self.__class__.__module__, f"{self.method} '{self.get_full_path()}'>"
self.__class__.__name__,
self.method,
self.get_full_path())
def _default_negotiator(self): def _default_negotiator(self):
return api_settings.DEFAULT_CONTENT_NEGOTIATION_CLASS() return api_settings.DEFAULT_CONTENT_NEGOTIATION_CLASS()

View File

@ -1105,20 +1105,17 @@ class ModelSerializer(Serializer):
if fields and fields != ALL_FIELDS and not isinstance(fields, (list, tuple)): if fields and fields != ALL_FIELDS and not isinstance(fields, (list, tuple)):
raise TypeError( raise TypeError(
'The `fields` option must be a list or tuple or "__all__". ' 'The `fields` option must be a list or tuple or "__all__". '
'Got %s.' % type(fields).__name__ f'Got {type(fields).__name__}.'
) )
if exclude and not isinstance(exclude, (list, tuple)): if exclude and not isinstance(exclude, (list, tuple)):
raise TypeError( raise TypeError(
'The `exclude` option must be a list or tuple. Got %s.' % f'The `exclude` option must be a list or tuple. Got {type(exclude).__name__}.'
type(exclude).__name__
) )
assert not (fields and exclude), ( assert not (fields and exclude), (
"Cannot set both 'fields' and 'exclude' options on " f"Cannot set both 'fields' and 'exclude' options on "
"serializer {serializer_class}.".format( f"serializer {self.__class__.__name__}."
serializer_class=self.__class__.__name__
)
) )
assert not (fields is None and exclude is None), ( assert not (fields is None and exclude is None), (

View File

@ -91,7 +91,7 @@ class SimpleRateThrottle(BaseThrottle):
try: try:
return self.THROTTLE_RATES[self.scope] return self.THROTTLE_RATES[self.scope]
except KeyError: except KeyError:
msg = "No default throttle rate set for '%s' scope" % self.scope msg = f"No default throttle rate set for '{self.scope}' scope"
raise ImproperlyConfigured(msg) raise ImproperlyConfigured(msg)
def parse_rate(self, rate): def parse_rate(self, rate):

View File

@ -9,7 +9,7 @@ def _get_format_path_converter(suffix_kwarg, allowed):
if len(allowed) == 1: if len(allowed) == 1:
allowed_pattern = allowed[0] allowed_pattern = allowed[0]
else: else:
allowed_pattern = '(?:%s)' % '|'.join(allowed) allowed_pattern = f'(?:{"|".join(allowed)})'
suffix_pattern = r"\.%s/?" % allowed_pattern suffix_pattern = r"\.%s/?" % allowed_pattern
else: else:
suffix_pattern = r"\.[a-z0-9]+/?" suffix_pattern = r"\.[a-z0-9]+/?"
@ -99,7 +99,7 @@ def format_suffix_patterns(urlpatterns, suffix_required=False, allowed=None):
if len(allowed) == 1: if len(allowed) == 1:
allowed_pattern = allowed[0] allowed_pattern = allowed[0]
else: else:
allowed_pattern = '(%s)' % '|'.join(allowed) allowed_pattern = f'({"|".join(allowed)})'
suffix_pattern = r'\.(?P<%s>%s)/?$' % (suffix_kwarg, allowed_pattern) suffix_pattern = r'\.(?P<%s>%s)/?$' % (suffix_kwarg, allowed_pattern)
else: else:
suffix_pattern = r'\.(?P<%s>[a-z0-9]+)/?$' % suffix_kwarg suffix_pattern = r'\.(?P<%s>[a-z0-9]+)/?$' % suffix_kwarg
@ -107,6 +107,6 @@ def format_suffix_patterns(urlpatterns, suffix_required=False, allowed=None):
converter_name, suffix_converter = _get_format_path_converter(suffix_kwarg, allowed) converter_name, suffix_converter = _get_format_path_converter(suffix_kwarg, allowed)
register_converter(suffix_converter, converter_name) register_converter(suffix_converter, converter_name)
suffix_route = '<%s:%s>' % (converter_name, suffix_kwarg) suffix_route = f'<{converter_name}:{suffix_kwarg}>'
return apply_suffix_patterns(urlpatterns, suffix_pattern, suffix_required, suffix_route) return apply_suffix_patterns(urlpatterns, suffix_pattern, suffix_required, suffix_route)

View File

@ -48,7 +48,7 @@ class UniqueValidator:
""" """
Filter the queryset to all instances matching the given attribute. Filter the queryset to all instances matching the given attribute.
""" """
filter_kwargs = {'%s__%s' % (field_name, self.lookup): value} filter_kwargs = {f'{field_name}__{self.lookup}': value}
return qs_filter(queryset, **filter_kwargs) return qs_filter(queryset, **filter_kwargs)
def exclude_current_instance(self, queryset, instance): def exclude_current_instance(self, queryset, instance):
@ -74,10 +74,7 @@ class UniqueValidator:
raise ValidationError(self.message, code='unique') raise ValidationError(self.message, code='unique')
def __repr__(self): def __repr__(self):
return '<%s(queryset=%s)>' % ( return f'<{self.__class__.__name__}(queryset={smart_repr(self.queryset)})>'
self.__class__.__name__,
smart_repr(self.queryset)
)
class UniqueTogetherValidator: class UniqueTogetherValidator:
@ -160,11 +157,7 @@ class UniqueTogetherValidator:
raise ValidationError(message, code='unique') raise ValidationError(message, code='unique')
def __repr__(self): def __repr__(self):
return '<%s(queryset=%s, fields=%s)>' % ( return f'<{self.__class__.__name__}(queryset={smart_repr(self.queryset)}, fields={smart_repr(self.fields)})>'
self.__class__.__name__,
smart_repr(self.queryset),
smart_repr(self.fields)
)
class ProhibitSurrogateCharactersValidator: class ProhibitSurrogateCharactersValidator:
@ -231,12 +224,8 @@ class BaseUniqueForValidator:
}, code='unique') }, code='unique')
def __repr__(self): def __repr__(self):
return '<%s(queryset=%s, field=%s, date_field=%s)>' % ( return f'<{self.__class__.__name__}(queryset={smart_repr(self.queryset)}, ' \
self.__class__.__name__, f'field={smart_repr(self.field)}, date_field={smart_repr(self.date_field)})>'
smart_repr(self.queryset),
smart_repr(self.field),
smart_repr(self.date_field)
)
class UniqueForDateValidator(BaseUniqueForValidator): class UniqueForDateValidator(BaseUniqueForValidator):
@ -246,11 +235,10 @@ class UniqueForDateValidator(BaseUniqueForValidator):
value = attrs[self.field] value = attrs[self.field]
date = attrs[self.date_field] date = attrs[self.date_field]
filter_kwargs = {} filter_kwargs = {field_name: value,
filter_kwargs[field_name] = value f'{date_field_name}__day': date.day,
filter_kwargs['%s__day' % date_field_name] = date.day f'{date_field_name}__month': date.month,
filter_kwargs['%s__month' % date_field_name] = date.month f'{date_field_name}__year': date.year}
filter_kwargs['%s__year' % date_field_name] = date.year
return qs_filter(queryset, **filter_kwargs) return qs_filter(queryset, **filter_kwargs)
@ -263,7 +251,7 @@ class UniqueForMonthValidator(BaseUniqueForValidator):
filter_kwargs = {} filter_kwargs = {}
filter_kwargs[field_name] = value filter_kwargs[field_name] = value
filter_kwargs['%s__month' % date_field_name] = date.month filter_kwargs[f'{date_field_name}__month'] = date.month
return qs_filter(queryset, **filter_kwargs) return qs_filter(queryset, **filter_kwargs)
@ -274,7 +262,5 @@ class UniqueForYearValidator(BaseUniqueForValidator):
value = attrs[self.field] value = attrs[self.field]
date = attrs[self.date_field] date = attrs[self.date_field]
filter_kwargs = {} filter_kwargs = {field_name: value, f'{date_field_name}__year': date.year}
filter_kwargs[field_name] = value
filter_kwargs['%s__year' % date_field_name] = date.year
return qs_filter(queryset, **filter_kwargs) return qs_filter(queryset, **filter_kwargs)

View File

@ -197,7 +197,7 @@ class ViewSetMixin:
for action in actions: for action in actions:
try: try:
url_name = '%s-%s' % (self.basename, action.url_name) url_name = f'{self.basename}-{action.url_name}'
url = reverse(url_name, self.args, self.kwargs, request=self.request) url = reverse(url_name, self.args, self.kwargs, request=self.request)
view = self.__class__(**action.kwargs) view = self.__class__(**action.kwargs)
action_urls[view.get_view_name()] = url action_urls[view.get_view_name()] = url

View File

@ -70,9 +70,9 @@ class ErrorDetailTests(TestCase):
def test_repr(self): def test_repr(self):
assert repr(ErrorDetail('msg1')) == \ assert repr(ErrorDetail('msg1')) == \
'ErrorDetail(string={!r}, code=None)'.format('msg1') f"ErrorDetail(string='msg1', code='None')"
assert repr(ErrorDetail('msg1', 'code')) == \ assert repr(ErrorDetail('msg1', 'code')) == \
'ErrorDetail(string={!r}, code={!r})'.format('msg1', 'code') f"ErrorDetail(string='msg1', code='code')"
def test_str(self): def test_str(self):
assert str(ErrorDetail('msg1')) == 'msg1' assert str(ErrorDetail('msg1')) == 'msg1'

View File

@ -208,7 +208,7 @@ class SearchFilterTests(TestCase):
def as_sql(self, compiler, connection): def as_sql(self, compiler, connection):
sql, params = compiler.compile(self.lhs) sql, params = compiler.compile(self.lhs)
return "trim(%s, 'a')" % sql, params return f"trim({sql}, 'a')", params
with register_lookup(CharField, TrimA): with register_lookup(CharField, TrimA):
# Search including `a` # Search including `a`

View File

@ -13,7 +13,7 @@ class MockObject:
'%s=%s' % (key, value) '%s=%s' % (key, value)
for key, value in sorted(self._kwargs.items()) for key, value in sorted(self._kwargs.items())
]) ])
return '<MockObject %s>' % kwargs_str return f'<MockObject {kwargs_str}>'
class MockQueryset: class MockQueryset: