From 377a4e1ef1c5fd0664250d4fde76f132d92b6d69 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Fri, 17 Sep 2021 21:39:18 -0700 Subject: [PATCH] Updating string format syntax part 1 --- bookwyrm/activitypub/base_activity.py | 12 +++---- bookwyrm/activitystreams.py | 4 +-- bookwyrm/emailing.py | 8 ++--- bookwyrm/forms.py | 2 +- bookwyrm/models/user.py | 20 ++++++----- bookwyrm/preview_images.py | 5 +-- bookwyrm/sanitize_html.py | 2 +- bookwyrm/settings.py | 15 ++++---- bookwyrm/signatures.py | 20 +++++------ bookwyrm/suggested_users.py | 4 +-- bookwyrm/urls.py | 52 ++++++++++++++------------- 11 files changed, 73 insertions(+), 71 deletions(-) diff --git a/bookwyrm/activitypub/base_activity.py b/bookwyrm/activitypub/base_activity.py index 4f7b55d50..24d383ac7 100644 --- a/bookwyrm/activitypub/base_activity.py +++ b/bookwyrm/activitypub/base_activity.py @@ -101,7 +101,7 @@ class ActivityObject: except KeyError: if field.default == MISSING and field.default_factory == MISSING: raise ActivitySerializerError( - "Missing required field: %s" % field.name + f"Missing required field: {field.name}" ) value = field.default setattr(self, field.name, value) @@ -219,8 +219,8 @@ def set_related_field( model_name, origin_model_name, related_field_name, related_remote_id, data ): """load reverse related fields (editions, attachments) without blocking""" - model = apps.get_model("bookwyrm.%s" % model_name, require_ready=True) - origin_model = apps.get_model("bookwyrm.%s" % origin_model_name, require_ready=True) + model = apps.get_model(f"bookwyrm.{model_name}", require_ready=True) + origin_model = apps.get_model(f"bookwyrm.{origin_model_name}", require_ready=True) with transaction.atomic(): if isinstance(data, str): @@ -234,7 +234,7 @@ def set_related_field( # this must exist because it's the object that triggered this function instance = origin_model.find_existing_by_remote_id(related_remote_id) if not instance: - raise ValueError("Invalid related remote id: %s" % related_remote_id) + raise ValueError(f"Invalid related remote id: {related_remote_id}") # set the origin's remote id on the activity so it will be there when # the model instance is created @@ -265,7 +265,7 @@ def get_model_from_type(activity_type): ] if not model: raise ActivitySerializerError( - 'No model found for activity type "%s"' % activity_type + f'No model found for activity type "{activity_type}"' ) return model[0] @@ -286,7 +286,7 @@ def resolve_remote_id( data = get_data(remote_id) except ConnectorException: raise ActivitySerializerError( - "Could not connect to host for remote_id in: %s" % (remote_id) + f"Could not connect to host for remote_id: {remote_id}" ) # determine the model implicitly, if not provided # or if it's a model with subclasses like Status, check again diff --git a/bookwyrm/activitystreams.py b/bookwyrm/activitystreams.py index 10149993f..13d56f849 100644 --- a/bookwyrm/activitystreams.py +++ b/bookwyrm/activitystreams.py @@ -16,11 +16,11 @@ class ActivityStream(RedisStore): def stream_id(self, user): """the redis key for this user's instance of this stream""" - return "{}-{}".format(user.id, self.key) + return f"{user.id}-{self.key}" def unread_id(self, user): """the redis key for this user's unread count for this stream""" - return "{}-unread".format(self.stream_id(user)) + return "{}-unread".format(self.stream_id(user)) # pylint: disable=consider-using-f-string def get_rank(self, obj): # pylint: disable=no-self-use """statuses are sorted by date published""" diff --git a/bookwyrm/emailing.py b/bookwyrm/emailing.py index 4f43c69e6..10db4f130 100644 --- a/bookwyrm/emailing.py +++ b/bookwyrm/emailing.py @@ -11,7 +11,7 @@ def email_data(): """fields every email needs""" site = models.SiteSettings.objects.get() if site.logo_small: - logo_path = "/images/{}".format(site.logo_small.url) + logo_path = f"/images/{site.logo_small.url}" else: logo_path = "/static/images/logo-small.png" @@ -49,15 +49,15 @@ def password_reset_email(reset_code): def format_email(email_name, data): """render the email templates""" subject = ( - get_template("email/{}/subject.html".format(email_name)).render(data).strip() + get_template(f"email/{email_name}/subject.html").render(data).strip() ) html_content = ( - get_template("email/{}/html_content.html".format(email_name)) + get_template(f"email/{email_name}/html_content.html") .render(data) .strip() ) text_content = ( - get_template("email/{}/text_content.html".format(email_name)) + get_template(f"email/{email_name}/text_content.html") .render(data) .strip() ) diff --git a/bookwyrm/forms.py b/bookwyrm/forms.py index b53d0b6a5..f20cdd00c 100644 --- a/bookwyrm/forms.py +++ b/bookwyrm/forms.py @@ -261,7 +261,7 @@ class CreateInviteForm(CustomForm): ), "use_limit": widgets.Select( choices=[ - (i, _("%(count)d uses" % {"count": i})) + (i, _(f"{i} uses")) for i in [1, 5, 10, 25, 50, 100] ] + [(None, _("Unlimited"))] diff --git a/bookwyrm/models/user.py b/bookwyrm/models/user.py index a33daf729..750cfa44a 100644 --- a/bookwyrm/models/user.py +++ b/bookwyrm/models/user.py @@ -152,12 +152,13 @@ class User(OrderedCollectionPageMixin, AbstractUser): @property def following_link(self): """just how to find out the following info""" - return "{:s}/following".format(self.remote_id) + return f"{self.remote_id}/following" @property def alt_text(self): """alt text with username""" - return "avatar for %s" % (self.localname or self.username) + # pylint: disable=consider-using-f-string + return "avatar for {:s}".format(self.localname or self.username) @property def display_name(self): @@ -198,7 +199,7 @@ class User(OrderedCollectionPageMixin, AbstractUser): """an ordered collection of statuses""" if filter_type: filter_class = apps.get_model( - "bookwyrm.%s" % filter_type, require_ready=True + f"bookwyrm.{filter_type}", require_ready=True ) if not issubclass(filter_class, Status): raise TypeError( @@ -223,7 +224,7 @@ class User(OrderedCollectionPageMixin, AbstractUser): def to_following_activity(self, **kwargs): """activitypub following list""" - remote_id = "%s/following" % self.remote_id + remote_id = f"{self.remote_id}/following" return self.to_ordered_collection( self.following.order_by("-updated_date").all(), remote_id=remote_id, @@ -266,7 +267,7 @@ class User(OrderedCollectionPageMixin, AbstractUser): if not self.local and not re.match(regex.FULL_USERNAME, self.username): # generate a username that uses the domain (webfinger format) actor_parts = urlparse(self.remote_id) - self.username = "%s@%s" % (self.username, actor_parts.netloc) + self.username = f"{self.username}@{actor_parts.netloc}" # this user already exists, no need to populate fields if not created: @@ -320,7 +321,8 @@ class User(OrderedCollectionPageMixin, AbstractUser): @property def local_path(self): """this model doesn't inherit bookwyrm model, so here we are""" - return "/user/%s" % (self.localname or self.username) + # pylint: disable=consider-using-f-string + return "/user/{:s}".format(self.localname or self.username) def create_shelves(self): """default shelves for a new user""" @@ -361,7 +363,7 @@ class KeyPair(ActivitypubMixin, BookWyrmModel): def get_remote_id(self): # self.owner is set by the OneToOneField on User - return "%s/#main-key" % self.owner.remote_id + return f"{self.owner.remote_id}/#main-key" def save(self, *args, **kwargs): """create a key pair""" @@ -398,7 +400,7 @@ class AnnualGoal(BookWyrmModel): def get_remote_id(self): """put the year in the path""" - return "{:s}/goal/{:d}".format(self.user.remote_id, self.year) + return f"{self.user.remote_id}/goal/{self.year}" @property def books(self): @@ -454,7 +456,7 @@ def get_or_create_remote_server(domain): pass try: - data = get_data("https://%s/.well-known/nodeinfo" % domain) + data = get_data(f"https://{domain}/.well-known/nodeinfo") try: nodeinfo_url = data.get("links")[0].get("href") except (TypeError, KeyError): diff --git a/bookwyrm/preview_images.py b/bookwyrm/preview_images.py index 900a3e123..c9ef46436 100644 --- a/bookwyrm/preview_images.py +++ b/bookwyrm/preview_images.py @@ -315,7 +315,8 @@ def save_and_cleanup(image, instance=None): """Save and close the file""" if not isinstance(instance, (models.Book, models.User, models.SiteSettings)): return False - file_name = "%s-%s.jpg" % (str(instance.id), str(uuid4())) + uuid = uuid4() + file_name = f"{instance.id}-{uuid}.jpg" image_buffer = BytesIO() try: @@ -412,7 +413,7 @@ def generate_user_preview_image_task(user_id): texts = { "text_one": user.display_name, - "text_three": "@{}@{}".format(user.localname, settings.DOMAIN), + "text_three": "@{user.localname}@{settings.DOMAIN}" } if user.avatar: diff --git a/bookwyrm/sanitize_html.py b/bookwyrm/sanitize_html.py index 0be64c58c..8b0e3c4cb 100644 --- a/bookwyrm/sanitize_html.py +++ b/bookwyrm/sanitize_html.py @@ -48,7 +48,7 @@ class InputHtmlParser(HTMLParser): # pylint: disable=abstract-method return self.tag_stack = self.tag_stack[:-1] - self.output.append(("tag", "" % tag)) + self.output.append(("tag", f"")) def handle_data(self, data): """extract the answer, if we're in an answer tag""" diff --git a/bookwyrm/settings.py b/bookwyrm/settings.py index 452b8d940..521014a11 100644 --- a/bookwyrm/settings.py +++ b/bookwyrm/settings.py @@ -23,7 +23,7 @@ EMAIL_HOST_USER = env("EMAIL_HOST_USER") EMAIL_HOST_PASSWORD = env("EMAIL_HOST_PASSWORD") EMAIL_USE_TLS = env.bool("EMAIL_USE_TLS", True) EMAIL_USE_SSL = env.bool("EMAIL_USE_SSL", False) -DEFAULT_FROM_EMAIL = "admin@{:s}".format(env("DOMAIN")) +DEFAULT_FROM_EMAIL = "admin@{:s}".format(env("DOMAIN")) # pylint: disable=consider-using-f-string # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) @@ -177,11 +177,8 @@ USE_L10N = True USE_TZ = True -USER_AGENT = "%s (BookWyrm/%s; +https://%s/)" % ( - requests.utils.default_user_agent(), - VERSION, - DOMAIN, -) +agent = requests.utils.default_user_agent() +USER_AGENT = f"{agent} (BookWyrm/{VERSION}; +https://{DOMAIN}/)" # Imagekit generated thumbnails ENABLE_THUMBNAIL_GENERATION = env.bool("ENABLE_THUMBNAIL_GENERATION", False) @@ -212,11 +209,11 @@ if USE_S3: AWS_S3_OBJECT_PARAMETERS = {"CacheControl": "max-age=86400"} # S3 Static settings STATIC_LOCATION = "static" - STATIC_URL = "https://%s/%s/" % (AWS_S3_CUSTOM_DOMAIN, STATIC_LOCATION) + STATIC_URL = f"https://{AWS_S3_CUSTOM_DOMAIN}/{STATIC_LOCATION}/" STATICFILES_STORAGE = "bookwyrm.storage_backends.StaticStorage" # S3 Media settings MEDIA_LOCATION = "images" - MEDIA_URL = "https://%s/%s/" % (AWS_S3_CUSTOM_DOMAIN, MEDIA_LOCATION) + MEDIA_URL = f"https://{AWS_S3_CUSTOM_DOMAIN}/{MEDIA_LOCATION}/" MEDIA_FULL_URL = MEDIA_URL DEFAULT_FILE_STORAGE = "bookwyrm.storage_backends.ImagesStorage" # I don't know if it's used, but the site crashes without it @@ -226,5 +223,5 @@ else: STATIC_URL = "/static/" STATIC_ROOT = os.path.join(BASE_DIR, env("STATIC_ROOT", "static")) MEDIA_URL = "/images/" - MEDIA_FULL_URL = "%s://%s%s" % (PROTOCOL, DOMAIN, MEDIA_URL) + MEDIA_FULL_URL = f"{PROTOCOL}://{DOMAIN}{MEDIA_URL}" MEDIA_ROOT = os.path.join(BASE_DIR, env("MEDIA_ROOT", "images")) diff --git a/bookwyrm/signatures.py b/bookwyrm/signatures.py index c8c900283..61cafe71f 100644 --- a/bookwyrm/signatures.py +++ b/bookwyrm/signatures.py @@ -26,21 +26,21 @@ def make_signature(sender, destination, date, digest): """uses a private key to sign an outgoing message""" inbox_parts = urlparse(destination) signature_headers = [ - "(request-target): post %s" % inbox_parts.path, - "host: %s" % inbox_parts.netloc, - "date: %s" % date, - "digest: %s" % digest, + f"(request-target): post {inbox_parts.path}", + f"host: {inbox_parts.netloc}", + f"date: {date}", + f"digest: {digest}", ] message_to_sign = "\n".join(signature_headers) signer = pkcs1_15.new(RSA.import_key(sender.key_pair.private_key)) signed_message = signer.sign(SHA256.new(message_to_sign.encode("utf8"))) signature = { - "keyId": "%s#main-key" % sender.remote_id, + "keyId": f"{sender.remote_id}#main-key", "algorithm": "rsa-sha256", "headers": "(request-target) host date digest", "signature": b64encode(signed_message).decode("utf8"), } - return ",".join('%s="%s"' % (k, v) for (k, v) in signature.items()) + return ",".join(f'{k}="{v}"' for (k, v) in signature.items()) def make_digest(data): @@ -58,7 +58,7 @@ def verify_digest(request): elif algorithm == "SHA-512": hash_function = hashlib.sha512 else: - raise ValueError("Unsupported hash function: {}".format(algorithm)) + raise ValueError(f"Unsupported hash function: {algorithm}") expected = hash_function(request.body).digest() if b64decode(digest) != expected: @@ -95,18 +95,18 @@ class Signature: def verify(self, public_key, request): """verify rsa signature""" if http_date_age(request.headers["date"]) > MAX_SIGNATURE_AGE: - raise ValueError("Request too old: %s" % (request.headers["date"],)) + raise ValueError(f"Request too old: {request.headers['date']}") public_key = RSA.import_key(public_key) comparison_string = [] for signed_header_name in self.headers.split(" "): if signed_header_name == "(request-target)": - comparison_string.append("(request-target): post %s" % request.path) + comparison_string.append(f"(request-target): post {request.path}") else: if signed_header_name == "digest": verify_digest(request) comparison_string.append( - "%s: %s" % (signed_header_name, request.headers[signed_header_name]) + f"{signed_header_name}: {request.headers[signed_header_name]}" ) comparison_string = "\n".join(comparison_string) diff --git a/bookwyrm/suggested_users.py b/bookwyrm/suggested_users.py index 883430614..e8f236324 100644 --- a/bookwyrm/suggested_users.py +++ b/bookwyrm/suggested_users.py @@ -24,8 +24,8 @@ class SuggestedUsers(RedisStore): def store_id(self, user): # pylint: disable=no-self-use """the key used to store this user's recs""" if isinstance(user, int): - return "{:d}-suggestions".format(user) - return "{:d}-suggestions".format(user.id) + return f"{user}-suggestions" + return f"{user.id}-suggestions" def get_counts_from_rank(self, rank): # pylint: disable=no-self-use """calculate mutuals count and shared books count from rank""" diff --git a/bookwyrm/urls.py b/bookwyrm/urls.py index 6acf75cb3..6ff0c183f 100644 --- a/bookwyrm/urls.py +++ b/bookwyrm/urls.py @@ -7,8 +7,8 @@ from django.views.generic.base import TemplateView from bookwyrm import settings, views from bookwyrm.utils import regex -USER_PATH = r"^user/(?P%s)" % regex.USERNAME -LOCAL_USER_PATH = r"^user/(?P%s)" % regex.LOCALNAME +USER_PATH = rf"^user/(?P{regex.USERNAME})" +LOCAL_USER_PATH = rf"^user/(?P{regex.LOCALNAME})" status_types = [ "status", @@ -19,7 +19,9 @@ status_types = [ "boost", "generatednote", ] -STATUS_PATH = r"%s/(%s)/(?P\d+)" % (USER_PATH, "|".join(status_types)) + +status_types_string = "|".join(status_types) +STATUS_PATH = rf"{USER_PATH}/({status_types_string})/(?P\d+)" BOOK_PATH = r"^book/(?P\d+)" @@ -33,8 +35,8 @@ urlpatterns = [ ), # federation endpoints re_path(r"^inbox/?$", views.Inbox.as_view()), - re_path(r"%s/inbox/?$" % LOCAL_USER_PATH, views.Inbox.as_view()), - re_path(r"%s/outbox/?$" % LOCAL_USER_PATH, views.Outbox.as_view()), + re_path(rf"{LOCAL_USER_PATH}/inbox/?$", views.Inbox.as_view()), + re_path(rf"{LOCAL_USER_PATH}/outbox/?$", views.Outbox.as_view()), re_path(r"^\.well-known/webfinger/?$", views.webfinger), re_path(r"^\.well-known/nodeinfo/?$", views.nodeinfo_pointer), re_path(r"^\.well-known/host-meta/?$", views.host_meta), @@ -210,12 +212,12 @@ urlpatterns = [ name="get-started-users", ), # feeds - re_path(r"^(?P{:s})/?$".format(STREAMS), views.Feed.as_view()), + re_path(rf"^(?P{STREAMS})/?$", views.Feed.as_view()), re_path( r"^direct-messages/?$", views.DirectMessage.as_view(), name="direct-messages" ), re_path( - r"^direct-messages/(?P%s)?$" % regex.USERNAME, + rf"^direct-messages/(?P{regex.USERNAME})?$", views.DirectMessage.as_view(), name="direct-messages-user", ), @@ -225,22 +227,22 @@ urlpatterns = [ re_path(r"^import/?$", views.Import.as_view(), name="import"), re_path(r"^import/(\d+)/?$", views.ImportStatus.as_view(), name="import-status"), # users - re_path(r"%s\.json$" % USER_PATH, views.User.as_view()), - re_path(r"%s/?$" % USER_PATH, views.User.as_view(), name="user-feed"), - re_path(r"%s/rss" % USER_PATH, views.rss_feed.RssFeed(), name="user-rss"), + re_path(rf"{USER_PATH}\.json$", views.User.as_view()), + re_path(rf"{USER_PATH}/?$", views.User.as_view(), name="user-feed"), + re_path(rf"{USER_PATH}/rss/?$", views.rss_feed.RssFeed(), name="user-rss"), re_path( - r"%s/followers(.json)?/?$" % USER_PATH, + rf"{USER_PATH}/followers(.json)?/?$", views.Followers.as_view(), name="user-followers", ), re_path( - r"%s/following(.json)?/?$" % USER_PATH, + rf"{USER_PATH}/following(.json)?/?$", views.Following.as_view(), name="user-following", ), re_path(r"^hide-suggestions/?$", views.hide_suggestions, name="hide-suggestions"), # lists - re_path(r"%s/lists/?$" % USER_PATH, views.UserLists.as_view(), name="user-lists"), + re_path(rf"{USER_PATH}/lists/?$", views.UserLists.as_view(), name="user-lists"), re_path(r"^list/?$", views.Lists.as_view(), name="lists"), re_path(r"^list/saved/?$", views.SavedLists.as_view(), name="saved-lists"), re_path(r"^list/(?P\d+)(.json)?/?$", views.List.as_view(), name="list"), @@ -262,14 +264,14 @@ urlpatterns = [ re_path(r"^save-list/(?P\d+)/?$", views.save_list, name="list-save"), re_path(r"^unsave-list/(?P\d+)/?$", views.unsave_list, name="list-unsave"), # User books - re_path(r"%s/books/?$" % USER_PATH, views.Shelf.as_view(), name="user-shelves"), + re_path(rf"{USER_PATH}/books/?$", views.Shelf.as_view(), name="user-shelves"), re_path( - r"^%s/(helf|books)/(?P[\w-]+)(.json)?/?$" % USER_PATH, + rf"^{USER_PATH}/(helf|books)/(?P[\w-]+)(.json)?/?$", views.Shelf.as_view(), name="shelf", ), re_path( - r"^%s/(books|shelf)/(?P[\w-]+)(.json)?/?$" % LOCAL_USER_PATH, + rf"^{LOCAL_USER_PATH}/(books|shelf)/(?P[\w-]+)(.json)?/?$", views.Shelf.as_view(), name="shelf", ), @@ -279,7 +281,7 @@ urlpatterns = [ re_path(r"^unshelve/?$", views.unshelve), # goals re_path( - r"%s/goal/(?P\d{4})/?$" % USER_PATH, + rf"{USER_PATH}/goal/(?P\d{4})/?$", views.Goal.as_view(), name="user-goal", ), @@ -296,10 +298,10 @@ urlpatterns = [ re_path(r"^block/(?P\d+)/?$", views.Block.as_view()), re_path(r"^unblock/(?P\d+)/?$", views.unblock), # statuses - re_path(r"%s(.json)?/?$" % STATUS_PATH, views.Status.as_view(), name="status"), - re_path(r"%s/activity/?$" % STATUS_PATH, views.Status.as_view(), name="status"), + re_path(rf"{STATUS_PATH}(.json)?/?$", views.Status.as_view(), name="status"), + re_path(rf"{STATUS_PATH}/activity/?$", views.Status.as_view(), name="status"), re_path( - r"%s/replies(.json)?/?$" % STATUS_PATH, views.Replies.as_view(), name="replies" + rf"{STATUS_PATH}/replies(.json)?/?$", views.Replies.as_view(), name="replies" ), re_path( r"^post/?$", @@ -329,17 +331,17 @@ urlpatterns = [ re_path(r"^boost/(?P\d+)/?$", views.Boost.as_view()), re_path(r"^unboost/(?P\d+)/?$", views.Unboost.as_view()), # books - re_path(r"%s(.json)?/?$" % BOOK_PATH, views.Book.as_view(), name="book"), + re_path(rf"{BOOK_PATH}(.json)?/?$", views.Book.as_view(), name="book"), re_path( - r"%s/(?Preview|comment|quote)/?$" % BOOK_PATH, + rf"{BOOK_PATH}/(?Preview|comment|quote)/?$", views.Book.as_view(), name="book-user-statuses", ), - re_path(r"%s/edit/?$" % BOOK_PATH, views.EditBook.as_view(), name="edit-book"), - re_path(r"%s/confirm/?$" % BOOK_PATH, views.ConfirmEditBook.as_view()), + re_path(rf"{BOOK_PATH}/edit/?$", views.EditBook.as_view(), name="edit-book"), + re_path(rf"{BOOK_PATH}/confirm/?$", views.ConfirmEditBook.as_view()), re_path(r"^create-book/?$", views.EditBook.as_view(), name="create-book"), re_path(r"^create-book/confirm?$", views.ConfirmEditBook.as_view()), - re_path(r"%s/editions(.json)?/?$" % BOOK_PATH, views.Editions.as_view()), + re_path(rf"{BOOK_PATH}/editions(.json)?/?$", views.Editions.as_view()), re_path( r"^upload-cover/(?P\d+)/?$", views.upload_cover, name="upload-cover" ),