Merge branch 'main' into csv

This commit is contained in:
Mouse Reeve 2024-07-27 12:26:21 -07:00 committed by GitHub
commit 43577f3ca0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
71 changed files with 125 additions and 132 deletions

View file

@ -1,10 +1,5 @@
<!-- <!--
Thanks for contributing! Thanks for contributing! This template has some checkboxes that help keep track of what changes go into a release.
Please ensure the name of your PR is written in imperative present tense. For example:
- "fix color contrast on submit buttons"
- "add 'favourite food' value to Author model"
To check (tick) a list item, replace the space between square brackets with an x, like this: To check (tick) a list item, replace the space between square brackets with an x, like this:
@ -12,24 +7,23 @@ To check (tick) a list item, replace the space between square brackets with an x
You can find more information and tips for BookWyrm contributors at https://docs.joinbookwyrm.com/contributing.html You can find more information and tips for BookWyrm contributors at https://docs.joinbookwyrm.com/contributing.html
--> -->
## Description
## Are you finished?
### Linters
<!-- <!--
Please run linters on your code before submitting your PR. Describe what your pull request does here
If you miss this step it is likely that the GitHub task runners will fail.
--> -->
- [ ] I have checked my code with `black`, `pylint`, and `mypy`, or `./bw-dev formatters`
### Tests <!--
<!-- Check one --> For pull requests that relate or close an issue, please include them
below. We like to follow [Github's guidance on linking issues to pull requests](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue).
- [ ] My changes do not need new tests For example having the text: "closes #1234" would connect the current pull
- [ ] All tests I have added are passing request to issue 1234. And when we merge the pull request, Github will
- [ ] I have written tests but need help to make them pass automatically close the issue.
- [ ] I have not written tests and need help to write them -->
- Related Issue #
- Closes #
## What type of Pull Request is this? ## What type of Pull Request is this?
<!-- Check all that apply --> <!-- Check all that apply -->
@ -48,21 +42,6 @@ If you miss this step it is likely that the GitHub task runners will fail.
### Details of breaking or configuration changes (if any of above checked) ### Details of breaking or configuration changes (if any of above checked)
## Description
<!--
Describe what your pull request does here.
For pull requests that relate or close an issue, please include them
below. We like to follow [Github's guidance on linking issues to pull requests](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue).
For example having the text: "closes #1234" would connect the current pull
request to issue 1234. And when we merge the pull request, Github will
automatically close the issue.
-->
- Related Issue #
- Closes #
## Documentation ## Documentation
<!-- <!--
@ -76,3 +55,14 @@ Our documentation is maintained in a separate repository at https://github.com/b
- [ ] I have created a matching pull request in the Documentation repository - [ ] I have created a matching pull request in the Documentation repository
- [ ] I intend to create a matching pull request in the Documentation repository after this PR is merged - [ ] I intend to create a matching pull request in the Documentation repository after this PR is merged
<!-- Amazing! Thanks for filling that out. Your PR will need to have passing tests and happy linters before we can merge
You will need to check your code with `black`, `pylint`, and `mypy`, or `./bw-dev formatters`
-->
### Tests
<!-- Check one -->
- [ ] My changes do not need new tests
- [ ] All tests I have added are passing
- [ ] I have written tests but need help to make them pass
- [ ] I have not written tests and need help to write them

View file

@ -48,7 +48,7 @@ jobs:
- name: Set up .env - name: Set up .env
run: cp .env.example .env run: cp .env.example .env
- name: Check migrations up-to-date - name: Check migrations up-to-date
run: python ./manage.py makemigrations --check run: python ./manage.py makemigrations --check -v 3
- name: Run Tests - name: Run Tests
run: pytest -n 3 run: pytest -n 3

View file

@ -3,7 +3,19 @@ ignore=migrations
load-plugins=pylint.extensions.no_self_use load-plugins=pylint.extensions.no_self_use
[MESSAGES CONTROL] [MESSAGES CONTROL]
disable=E1101,E1135,E1136,R0903,R0901,R0902,W0707,W0511,W0406,R0401,R0801,C3001,import-error disable =
cyclic-import,
duplicate-code,
fixme,
no-member,
raise-missing-from,
too-few-public-methods,
too-many-ancestors,
too-many-instance-attributes,
unnecessary-lambda-assignment,
unsubscriptable-object,
enable =
useless-suppression
[FORMAT] [FORMAT]
max-line-length=88 max-line-length=88

View file

@ -67,7 +67,6 @@ class Edition(Book):
type: str = "Edition" type: str = "Edition"
# pylint: disable=invalid-name
@dataclass(init=False) @dataclass(init=False)
class Work(Book): class Work(Book):
"""work instance of a book object""" """work instance of a book object"""

View file

@ -18,7 +18,6 @@ class OrderedCollection(ActivityObject):
type: str = "OrderedCollection" type: str = "OrderedCollection"
# pylint: disable=invalid-name
@dataclass(init=False) @dataclass(init=False)
class OrderedCollectionPrivate(OrderedCollection): class OrderedCollectionPrivate(OrderedCollection):
"""an ordered collection with privacy settings""" """an ordered collection with privacy settings"""

View file

@ -22,7 +22,6 @@ class Verb(ActivityObject):
self.object.to_model(allow_external_connections=allow_external_connections) self.object.to_model(allow_external_connections=allow_external_connections)
# pylint: disable=invalid-name
@dataclass(init=False) @dataclass(init=False)
class Create(Verb): class Create(Verb):
"""Create activity""" """Create activity"""
@ -33,7 +32,6 @@ class Create(Verb):
type: str = "Create" type: str = "Create"
# pylint: disable=invalid-name
@dataclass(init=False) @dataclass(init=False)
class Delete(Verb): class Delete(Verb):
"""Create activity""" """Create activity"""
@ -63,7 +61,6 @@ class Delete(Verb):
# if we can't find it, we don't need to delete it because we don't have it # if we can't find it, we don't need to delete it because we don't have it
# pylint: disable=invalid-name
@dataclass(init=False) @dataclass(init=False)
class Update(Verb): class Update(Verb):
"""Update activity""" """Update activity"""
@ -227,7 +224,6 @@ class Like(Verb):
self.to_model(allow_external_connections=allow_external_connections) self.to_model(allow_external_connections=allow_external_connections)
# pylint: disable=invalid-name
@dataclass(init=False) @dataclass(init=False)
class Announce(Verb): class Announce(Verb):
"""boosting a status""" """boosting a status"""

View file

@ -32,7 +32,7 @@ class ActivityStream(RedisStore):
stream_id = self.stream_id(user_id) stream_id = self.stream_id(user_id)
return f"{stream_id}-unread-by-type" return f"{stream_id}-unread-by-type"
def get_rank(self, obj): # pylint: disable=no-self-use def get_rank(self, obj):
"""statuses are sorted by date published""" """statuses are sorted by date published"""
return obj.published_date.timestamp() return obj.published_date.timestamp()

View file

@ -33,7 +33,6 @@ class BookwyrmConfig(AppConfig):
name = "bookwyrm" name = "bookwyrm"
verbose_name = "BookWyrm" verbose_name = "BookWyrm"
# pylint: disable=no-self-use
def ready(self): def ready(self):
"""set up OTLP and preview image files, if desired""" """set up OTLP and preview image files, if desired"""
if settings.OTEL_EXPORTER_OTLP_ENDPOINT or settings.OTEL_EXPORTER_CONSOLE: if settings.OTEL_EXPORTER_OTLP_ENDPOINT or settings.OTEL_EXPORTER_CONSOLE:

View file

@ -36,7 +36,6 @@ def search(
... ...
# pylint: disable=arguments-differ
def search( def search(
query: str, query: str,
*, *,

View file

@ -2,7 +2,7 @@
from bookwyrm import models, settings from bookwyrm import models, settings
def site_settings(request): # pylint: disable=unused-argument def site_settings(request):
"""include the custom info about the site""" """include the custom info about the site"""
request_protocol = "https://" request_protocol = "https://"
if not request.is_secure(): if not request.is_secure():

View file

@ -15,9 +15,9 @@ class StyledForm(ModelForm):
css_classes["number"] = "input" css_classes["number"] = "input"
css_classes["checkbox"] = "checkbox" css_classes["checkbox"] = "checkbox"
css_classes["textarea"] = "textarea" css_classes["textarea"] = "textarea"
# pylint: disable=super-with-arguments
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
for visible in self.visible_fields(): for visible in self.visible_fields():
input_type = ""
if hasattr(visible.field.widget, "input_type"): if hasattr(visible.field.widget, "input_type"):
input_type = visible.field.widget.input_type input_type = visible.field.widget.input_type
if isinstance(visible.field.widget, Textarea): if isinstance(visible.field.widget, Textarea):

View file

@ -34,7 +34,6 @@ class LoginForm(CustomForm):
def add_invalid_password_error(self): def add_invalid_password_error(self):
"""We don't want to be too specific about this""" """We don't want to be too specific about this"""
# pylint: disable=attribute-defined-outside-init
self.non_field_errors = _("Username or password are incorrect") self.non_field_errors = _("Username or password are incorrect")

View file

@ -5,8 +5,6 @@ from django import forms
class ArrayWidget(forms.widgets.TextInput): class ArrayWidget(forms.widgets.TextInput):
"""Inputs for postgres array fields""" """Inputs for postgres array fields"""
# pylint: disable=unused-argument
# pylint: disable=no-self-use
def value_from_datadict(self, data, files, name): def value_from_datadict(self, data, files, name):
"""get all values for this name""" """get all values for this name"""
return [i for i in data.getlist(name) if i] return [i for i in data.getlist(name) if i]

View file

@ -45,7 +45,6 @@ class Importer:
"reading": ["currently-reading", "reading", "currently reading"], "reading": ["currently-reading", "reading", "currently reading"],
} }
# pylint: disable=too-many-locals.too-many-arguments
def create_job( def create_job(
self, self,
user: User, user: User,

View file

@ -20,7 +20,7 @@ class LibrarythingImporter(Importer):
def normalize_row( def normalize_row(
self, entry: dict[str, str], mappings: dict[str, Optional[str]] self, entry: dict[str, str], mappings: dict[str, Optional[str]]
) -> dict[str, Optional[str]]: # pylint: disable=no-self-use ) -> dict[str, Optional[str]]:
"""use the dataclass to create the formatted row of data""" """use the dataclass to create the formatted row of data"""
normalized = { normalized = {
k: _remove_brackets(entry.get(v) if v else None) k: _remove_brackets(entry.get(v) if v else None)

View file

@ -18,7 +18,7 @@ class ListsStream(RedisStore):
return f"{user}-lists" return f"{user}-lists"
return f"{user.id}-lists" return f"{user.id}-lists"
def get_rank(self, obj): # pylint: disable=no-self-use def get_rank(self, obj):
"""lists are sorted by updated date""" """lists are sorted by updated date"""
return obj.updated_date.timestamp() return obj.updated_date.timestamp()

View file

@ -0,0 +1,51 @@
# Generated by Django 4.2.11 on 2024-07-27 18:18
from django.db import migrations, models
import pgtrigger.compiler
import pgtrigger.migrations
class Migration(migrations.Migration):
dependencies = [
("bookwyrm", "0206_merge_20240415_1537"),
]
operations = [
pgtrigger.migrations.RemoveTrigger(
model_name="author",
name="reset_book_search_vector_on_author_edit",
),
pgtrigger.migrations.RemoveTrigger(
model_name="book",
name="update_search_vector_on_book_edit",
),
pgtrigger.migrations.AddTrigger(
model_name="author",
trigger=pgtrigger.compiler.Trigger(
name="reset_book_search_vector_on_author_edit",
sql=pgtrigger.compiler.UpsertTriggerSql(
func="WITH updated_books AS (SELECT book_id FROM bookwyrm_book_authors WHERE author_id = new.id) UPDATE bookwyrm_book SET search_vector = '' FROM updated_books WHERE id = updated_books.book_id;RETURN NEW;",
hash="4eeb17d1c9c53f543615bcae1234bd0260adefcc",
operation='UPDATE OF "name", "aliases"',
pgid="pgtrigger_reset_book_search_vector_on_author_edit_a50c7",
table="bookwyrm_author",
when="AFTER",
),
),
),
pgtrigger.migrations.AddTrigger(
model_name="book",
trigger=pgtrigger.compiler.Trigger(
name="update_search_vector_on_book_edit",
sql=pgtrigger.compiler.UpsertTriggerSql(
func="WITH author_names AS (SELECT array_to_string(bookwyrm_author.name || bookwyrm_author.aliases, ' ') AS name_and_aliases FROM bookwyrm_author LEFT JOIN bookwyrm_book_authors ON bookwyrm_author.id = bookwyrm_book_authors.author_id WHERE bookwyrm_book_authors.book_id = new.id) SELECT setweight(coalesce(nullif(to_tsvector('english', new.title), ''), to_tsvector('simple', new.title)), 'A') || setweight(to_tsvector('english', coalesce(new.subtitle, '')), 'B') || (SELECT setweight(to_tsvector('simple', coalesce(array_to_string(array_agg(name_and_aliases), ' '), '')), 'C') FROM author_names) || setweight(to_tsvector('english', coalesce(new.series, '')), 'D') INTO new.search_vector;RETURN NEW;",
hash="676d929ce95beff671544b6add09cf9360b6f299",
operation='INSERT OR UPDATE OF "title", "subtitle", "series", "search_vector"',
pgid="pgtrigger_update_search_vector_on_book_edit_bec58",
table="bookwyrm_book",
when="BEFORE",
),
),
),
]

View file

@ -31,7 +31,7 @@ logger = logging.getLogger(__name__)
PropertyField = namedtuple("PropertyField", ("set_activity_from_field")) PropertyField = namedtuple("PropertyField", ("set_activity_from_field"))
# pylint: disable=invalid-name
def set_activity_from_property_field(activity, obj, field): def set_activity_from_property_field(activity, obj, field):
"""assign a model property value to the activity json""" """assign a model property value to the activity json"""
activity[field[1]] = getattr(obj, field[0]) activity[field[1]] = getattr(obj, field[0])

View file

@ -133,7 +133,6 @@ class BookDataModel(ObjectMixin, BookWyrmModel):
related_models = [ related_models = [
(r.remote_field.name, r.related_model) for r in self._meta.related_objects (r.remote_field.name, r.related_model) for r in self._meta.related_objects
] ]
# pylint: disable=protected-access
for related_field, related_model in related_models: for related_field, related_model in related_models:
# Skip the ManyToMany fields that arent auto-created. These # Skip the ManyToMany fields that arent auto-created. These
# should have a corresponding OneToMany field in the model for # should have a corresponding OneToMany field in the model for

View file

@ -27,7 +27,7 @@ logger = logging.getLogger(__name__)
class BookwyrmAwsSession(BotoSession): class BookwyrmAwsSession(BotoSession):
"""a boto session that always uses settings.AWS_S3_ENDPOINT_URL""" """a boto session that always uses settings.AWS_S3_ENDPOINT_URL"""
def client(self, *args, **kwargs): # pylint: disable=arguments-differ def client(self, *args, **kwargs):
kwargs["endpoint_url"] = settings.AWS_S3_ENDPOINT_URL kwargs["endpoint_url"] = settings.AWS_S3_ENDPOINT_URL
return super().client("s3", *args, **kwargs) return super().client("s3", *args, **kwargs)

View file

@ -193,8 +193,7 @@ class UsernameField(ActivitypubFieldMixin, models.CharField):
def __init__(self, activitypub_field="preferredUsername", **kwargs): def __init__(self, activitypub_field="preferredUsername", **kwargs):
self.activitypub_field = activitypub_field self.activitypub_field = activitypub_field
# I don't totally know why pylint is mad at this, but it makes it work super(ActivitypubFieldMixin, self).__init__(
super(ActivitypubFieldMixin, self).__init__( # pylint: disable=bad-super-call
_("username"), _("username"),
max_length=150, max_length=150,
unique=True, unique=True,
@ -234,7 +233,6 @@ class PrivacyField(ActivitypubFieldMixin, models.CharField):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, max_length=255, choices=PrivacyLevels, default="public") super().__init__(*args, max_length=255, choices=PrivacyLevels, default="public")
# pylint: disable=invalid-name
def set_field_from_activity( def set_field_from_activity(
self, instance, data, overwrite=True, allow_external_connections=True self, instance, data, overwrite=True, allow_external_connections=True
): ):
@ -276,7 +274,6 @@ class PrivacyField(ActivitypubFieldMixin, models.CharField):
if hasattr(instance, "mention_users"): if hasattr(instance, "mention_users"):
mentions = [u.remote_id for u in instance.mention_users.all()] mentions = [u.remote_id for u in instance.mention_users.all()]
# this is a link to the followers list # this is a link to the followers list
# pylint: disable=protected-access
followers = instance.user.followers_url followers = instance.user.followers_url
if instance.privacy == "public": if instance.privacy == "public":
activity["to"] = [self.public] activity["to"] = [self.public]
@ -444,7 +441,7 @@ class ImageField(ActivitypubFieldMixin, models.ImageField):
self.alt_field = alt_field self.alt_field = alt_field
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
# pylint: disable=arguments-differ,arguments-renamed,too-many-arguments # pylint: disable=arguments-renamed,too-many-arguments
def set_field_from_activity( def set_field_from_activity(
self, instance, data, save=True, overwrite=True, allow_external_connections=True self, instance, data, save=True, overwrite=True, allow_external_connections=True
): ):

View file

@ -359,7 +359,7 @@ def import_item_task(item_id):
try: try:
item.resolve() item.resolve()
except Exception as err: # pylint: disable=broad-except except Exception as err:
item.fail_reason = _("Error loading book") item.fail_reason = _("Error loading book")
item.save() item.save()
item.update_job() item.update_job()

View file

@ -135,7 +135,7 @@ class UserFollowRequest(ActivitypubMixin, UserRelationship):
status = "follow_request" status = "follow_request"
activity_serializer = activitypub.Follow activity_serializer = activitypub.Follow
def save(self, *args, broadcast=True, **kwargs): # pylint: disable=arguments-differ def save(self, *args, broadcast=True, **kwargs):
"""make sure the follow or block relationship doesn't already exist""" """make sure the follow or block relationship doesn't already exist"""
# if there's a request for a follow that already exists, accept it # if there's a request for a follow that already exists, accept it
# without changing the local database state # without changing the local database state

View file

@ -98,7 +98,7 @@ class Status(OrderedCollectionPageMixin, BookWyrmModel):
self.thread_id = self.id self.thread_id = self.id
super().save(broadcast=False, update_fields=["thread_id"]) super().save(broadcast=False, update_fields=["thread_id"])
def delete(self, *args, **kwargs): # pylint: disable=unused-argument def delete(self, *args, **kwargs):
""" "delete" a status""" """ "delete" a status"""
if hasattr(self, "boosted_status"): if hasattr(self, "boosted_status"):
# okay but if it's a boost really delete it # okay but if it's a boost really delete it
@ -213,7 +213,7 @@ class Status(OrderedCollectionPageMixin, BookWyrmModel):
**kwargs, **kwargs,
).serialize() ).serialize()
def to_activity_dataclass(self, pure=False): # pylint: disable=arguments-differ def to_activity_dataclass(self, pure=False):
"""return tombstone if the status is deleted""" """return tombstone if the status is deleted"""
if self.deleted: if self.deleted:
return activitypub.Tombstone( return activitypub.Tombstone(

View file

@ -409,7 +409,6 @@ class User(OrderedCollectionPageMixin, AbstractUser):
def delete(self, *args, **kwargs): def delete(self, *args, **kwargs):
"""We don't actually delete the database entry""" """We don't actually delete the database entry"""
# pylint: disable=attribute-defined-outside-init
self.is_active = False self.is_active = False
self.allow_reactivation = False self.allow_reactivation = False
self.is_deleted = True self.is_deleted = True
@ -452,7 +451,6 @@ class User(OrderedCollectionPageMixin, AbstractUser):
def deactivate(self): def deactivate(self):
"""Disable the user but allow them to reactivate""" """Disable the user but allow them to reactivate"""
# pylint: disable=attribute-defined-outside-init
self.is_active = False self.is_active = False
self.deactivation_reason = "self_deactivation" self.deactivation_reason = "self_deactivation"
self.allow_reactivation = True self.allow_reactivation = True
@ -460,7 +458,6 @@ class User(OrderedCollectionPageMixin, AbstractUser):
def reactivate(self): def reactivate(self):
"""Now you want to come back, huh?""" """Now you want to come back, huh?"""
# pylint: disable=attribute-defined-outside-init
if not self.allow_reactivation: if not self.allow_reactivation:
return return
self.is_active = True self.is_active = True

View file

@ -420,7 +420,6 @@ def save_and_cleanup(image, instance=None):
return True return True
# pylint: disable=invalid-name
@app.task(queue=IMAGES) @app.task(queue=IMAGES)
def generate_site_preview_image_task(): def generate_site_preview_image_task():
"""generate preview_image for the website""" """generate preview_image for the website"""
@ -445,7 +444,6 @@ def generate_site_preview_image_task():
save_and_cleanup(image, instance=site) save_and_cleanup(image, instance=site)
# pylint: disable=invalid-name
@app.task(queue=IMAGES) @app.task(queue=IMAGES)
def generate_edition_preview_image_task(book_id): def generate_edition_preview_image_task(book_id):
"""generate preview_image for a book""" """generate preview_image for a book"""

View file

@ -6,7 +6,7 @@ from base64 import b64encode, b64decode
from Crypto import Random from Crypto import Random
from Crypto.PublicKey import RSA from Crypto.PublicKey import RSA
from Crypto.Signature import pkcs1_15 # pylint: disable=no-name-in-module from Crypto.Signature import pkcs1_15
from Crypto.Hash import SHA256 from Crypto.Hash import SHA256
MAX_SIGNATURE_AGE = 300 MAX_SIGNATURE_AGE = 300
@ -84,7 +84,6 @@ class Signature:
self.headers = headers self.headers = headers
self.signature = signature self.signature = signature
# pylint: disable=invalid-name
@classmethod @classmethod
def parse(cls, request): def parse(cls, request):
"""extract and parse a signature from an http request""" """extract and parse a signature from an http request"""

View file

@ -34,7 +34,6 @@ class SuggestedUsers(RedisStore):
def get_counts_from_rank(self, rank): # pylint: disable=no-self-use def get_counts_from_rank(self, rank): # pylint: disable=no-self-use
"""calculate mutuals count and shared books count from rank""" """calculate mutuals count and shared books count from rank"""
# pylint: disable=c-extension-no-member
return { return {
"mutuals": math.floor(rank), "mutuals": math.floor(rank),
# "shared_books": int(1 / (-1 * (rank % 1 - 1))) - 1, # "shared_books": int(1 / (-1 * (rank % 1 - 1))) - 1,
@ -128,7 +127,6 @@ def get_annotated_users(viewer, *args, **kwargs):
), ),
distinct=True, distinct=True,
), ),
# pylint: disable=line-too-long
# shared_books=Count( # shared_books=Count(
# "shelfbook", # "shelfbook",
# filter=Q( # filter=Q(
@ -202,7 +200,7 @@ def update_suggestions_on_unfollow(sender, instance, **kwargs):
@receiver(signals.post_save, sender=models.User) @receiver(signals.post_save, sender=models.User)
# pylint: disable=unused-argument, too-many-arguments # pylint: disable=unused-argument
def update_user(sender, instance, created, update_fields=None, **kwargs): def update_user(sender, instance, created, update_fields=None, **kwargs):
"""an updated user, neat""" """an updated user, neat"""
# a new user is found, create suggestions for them # a new user is found, create suggestions for them

View file

@ -71,14 +71,8 @@ def get_landing_books():
"""list of books for the landing page""" """list of books for the landing page"""
return list( return list(
set( set(
models.Edition.objects.filter( models.Edition.objects.exclude(cover__exact="")
review__published_date__isnull=False,
review__deleted=False,
review__user__local=True,
review__privacy__in=["public", "unlisted"],
)
.exclude(cover__exact="")
.distinct() .distinct()
.order_by("-review__published_date")[:6] .order_by("-updated_date")[:6]
) )
) )

View file

@ -115,7 +115,6 @@ class AbstractConnector(TestCase):
@responses.activate @responses.activate
def test_get_or_create_author(self): def test_get_or_create_author(self):
"""load an author""" """load an author"""
# pylint: disable=attribute-defined-outside-init
self.connector.author_mappings = [ self.connector.author_mappings = [
Mapping("id"), Mapping("id"),
Mapping("name"), Mapping("name"),
@ -141,7 +140,6 @@ class AbstractConnector(TestCase):
def test_update_author_from_remote(self): def test_update_author_from_remote(self):
"""trigger the function that looks up the remote data""" """trigger the function that looks up the remote data"""
author = models.Author.objects.create(name="Test", openlibrary_key="OL123A") author = models.Author.objects.create(name="Test", openlibrary_key="OL123A")
# pylint: disable=attribute-defined-outside-init
self.connector.author_mappings = [ self.connector.author_mappings = [
Mapping("id"), Mapping("id"),
Mapping("name"), Mapping("name"),

View file

@ -20,7 +20,7 @@ from bookwyrm.models.activitypub_mixin import (
from bookwyrm.settings import PAGE_LENGTH from bookwyrm.settings import PAGE_LENGTH
# pylint: disable=invalid-name,too-many-public-methods # pylint: disable=too-many-public-methods
@patch("bookwyrm.activitystreams.add_status_task.delay") @patch("bookwyrm.activitystreams.add_status_task.delay")
@patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async") @patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async")
class ActivitypubMixins(TestCase): class ActivitypubMixins(TestCase):

View file

@ -42,7 +42,7 @@ class BaseModel(TestCase):
def test_remote_id(self): def test_remote_id(self):
"""these should be generated""" """these should be generated"""
self.test_model.id = 1 # pylint: disable=invalid-name self.test_model.id = 1
expected = self.test_model.get_remote_id() expected = self.test_model.get_remote_id()
self.assertEqual(expected, f"{BASE_URL}/bookwyrmtestmodel/1") self.assertEqual(expected, f"{BASE_URL}/bookwyrmtestmodel/1")

View file

@ -13,7 +13,7 @@ from bookwyrm.utils.tar import BookwyrmTarFile
from bookwyrm.models import bookwyrm_import_job from bookwyrm.models import bookwyrm_import_job
class BookwyrmImport(TestCase): # pylint: disable=too-many-public-methods class BookwyrmImport(TestCase):
"""testing user import functions""" """testing user import functions"""
def setUp(self): def setUp(self):

View file

@ -6,7 +6,6 @@ from django.test import TestCase
from bookwyrm import models, settings from bookwyrm import models, settings
# pylint: disable=unused-argument
@patch("bookwyrm.suggested_users.rerank_suggestions_task.delay") @patch("bookwyrm.suggested_users.rerank_suggestions_task.delay")
@patch("bookwyrm.activitystreams.populate_stream_task.delay") @patch("bookwyrm.activitystreams.populate_stream_task.delay")
@patch("bookwyrm.lists_stream.populate_lists_task.delay") @patch("bookwyrm.lists_stream.populate_lists_task.delay")

View file

@ -10,7 +10,7 @@ class MergeBookDataModel(TestCase):
"""test merging of subclasses of BookDataModel""" """test merging of subclasses of BookDataModel"""
@classmethod @classmethod
def setUpTestData(cls): # pylint: disable=invalid-name def setUpTestData(cls):
"""shared data""" """shared data"""
models.SiteSettings.objects.create() models.SiteSettings.objects.create()

View file

@ -42,7 +42,7 @@ def validate_html(html):
validator.feed(str(html.content)) validator.feed(str(html.content))
class HtmlValidator(HTMLParser): # pylint: disable=abstract-method class HtmlValidator(HTMLParser):
"""Checks for custom html validation requirements""" """Checks for custom html validation requirements"""
def __init__(self): def __init__(self):

View file

@ -14,7 +14,6 @@ from bookwyrm.tests.validate_html import validate_html
class ImportUserViews(TestCase): class ImportUserViews(TestCase):
"""user import views""" """user import views"""
# pylint: disable=invalid-name
def setUp(self): def setUp(self):
"""we need basic test data and mocks""" """we need basic test data and mocks"""
self.factory = RequestFactory() self.factory = RequestFactory()

View file

@ -11,7 +11,6 @@ from django.test.client import RequestFactory
from bookwyrm import models, views from bookwyrm import models, views
# pylint: disable=too-many-public-methods
class Inbox(TestCase): class Inbox(TestCase):
"""readthrough tests""" """readthrough tests"""

View file

@ -7,7 +7,6 @@ import responses
from bookwyrm import models, views from bookwyrm import models, views
# pylint: disable=too-many-public-methods
class InboxAdd(TestCase): class InboxAdd(TestCase):
"""inbox tests""" """inbox tests"""

View file

@ -7,7 +7,6 @@ import responses
from bookwyrm import models, views from bookwyrm import models, views
# pylint: disable=too-many-public-methods
class InboxActivities(TestCase): class InboxActivities(TestCase):
"""inbox tests""" """inbox tests"""

View file

@ -6,7 +6,6 @@ from django.test import TestCase
from bookwyrm import models, views from bookwyrm import models, views
# pylint: disable=too-many-public-methods
class InboxBlock(TestCase): class InboxBlock(TestCase):
"""inbox tests""" """inbox tests"""

View file

@ -9,7 +9,6 @@ from bookwyrm import models, views
from bookwyrm.activitypub import ActivitySerializerError from bookwyrm.activitypub import ActivitySerializerError
# pylint: disable=too-many-public-methods
class TransactionInboxCreate(TransactionTestCase): class TransactionInboxCreate(TransactionTestCase):
"""readthrough tests""" """readthrough tests"""

View file

@ -7,7 +7,6 @@ from django.test import TestCase
from bookwyrm import models, views from bookwyrm import models, views
# pylint: disable=too-many-public-methods
class InboxActivities(TestCase): class InboxActivities(TestCase):
"""inbox tests""" """inbox tests"""

View file

@ -7,7 +7,6 @@ from django.test import TestCase
from bookwyrm import models, views from bookwyrm import models, views
# pylint: disable=too-many-public-methods
class InboxRelationships(TestCase): class InboxRelationships(TestCase):
"""inbox tests""" """inbox tests"""

View file

@ -6,7 +6,6 @@ from django.test import TestCase
from bookwyrm import models, views from bookwyrm import models, views
# pylint: disable=too-many-public-methods
class InboxActivities(TestCase): class InboxActivities(TestCase):
"""inbox tests""" """inbox tests"""

View file

@ -6,7 +6,6 @@ from django.test import TestCase
from bookwyrm import models, views from bookwyrm import models, views
# pylint: disable=too-many-public-methods
class InboxRemove(TestCase): class InboxRemove(TestCase):
"""inbox tests""" """inbox tests"""

View file

@ -8,7 +8,6 @@ from django.test import TestCase
from bookwyrm import models, views from bookwyrm import models, views
# pylint: disable=too-many-public-methods
class InboxUpdate(TestCase): class InboxUpdate(TestCase):
"""inbox tests""" """inbox tests"""
@ -161,7 +160,6 @@ class InboxUpdate(TestCase):
datafile = pathlib.Path(__file__).parent.joinpath("../../data/bw_edition.json") datafile = pathlib.Path(__file__).parent.joinpath("../../data/bw_edition.json")
bookdata = json.loads(datafile.read_bytes()) bookdata = json.loads(datafile.read_bytes())
del bookdata["authors"] del bookdata["authors"]
# pylint: disable=line-too-long
link_data = { link_data = {
"href": "https://openlibrary.org/books/OL11645413M/Queen_Victoria/daisy", "href": "https://openlibrary.org/books/OL11645413M/Queen_Victoria/daisy",
"mediaType": "Daisy", "mediaType": "Daisy",

View file

@ -11,7 +11,6 @@ from bookwyrm import forms, models, views
from bookwyrm.tests.validate_html import validate_html from bookwyrm.tests.validate_html import validate_html
# pylint: disable=too-many-public-methods
@patch("bookwyrm.suggested_users.rerank_suggestions_task.delay") @patch("bookwyrm.suggested_users.rerank_suggestions_task.delay")
@patch("bookwyrm.activitystreams.populate_stream_task.delay") @patch("bookwyrm.activitystreams.populate_stream_task.delay")
class LoginViews(TestCase): class LoginViews(TestCase):

View file

@ -11,7 +11,6 @@ from bookwyrm import models, views
from bookwyrm.tests.validate_html import validate_html from bookwyrm.tests.validate_html import validate_html
# pylint: disable=unused-argument
class ListViews(TestCase): class ListViews(TestCase):
"""list view""" """list view"""

View file

@ -11,7 +11,6 @@ from bookwyrm import models, views
from bookwyrm.tests.validate_html import validate_html from bookwyrm.tests.validate_html import validate_html
# pylint: disable=unused-argument
class ListViews(TestCase): class ListViews(TestCase):
"""list view""" """list view"""

View file

@ -13,7 +13,6 @@ from bookwyrm.activitypub import ActivitypubResponse
from bookwyrm.tests.validate_html import validate_html from bookwyrm.tests.validate_html import validate_html
# pylint: disable=unused-argument
# pylint: disable=too-many-public-methods # pylint: disable=too-many-public-methods
class ListViews(TestCase): class ListViews(TestCase):
"""list view""" """list view"""

View file

@ -7,8 +7,6 @@ from django.test.client import RequestFactory
from bookwyrm import models, views from bookwyrm import models, views
# pylint: disable=unused-argument
# pylint: disable=too-many-public-methods
class ListItemViews(TestCase): class ListItemViews(TestCase):
"""list view""" """list view"""

View file

@ -11,7 +11,7 @@ from django.test.client import RequestFactory
from bookwyrm import models, views from bookwyrm import models, views
from bookwyrm.tests.validate_html import validate_html from bookwyrm.tests.validate_html import validate_html
# pylint: disable=unused-argument
class ListViews(TestCase): class ListViews(TestCase):
"""lists of lists""" """lists of lists"""

View file

@ -18,7 +18,7 @@ class ExportViews(TestCase):
"""viewing and creating statuses""" """viewing and creating statuses"""
@classmethod @classmethod
def setUpTestData(cls): # pylint: disable=invalid-name def setUpTestData(cls):
"""we need basic test data and mocks""" """we need basic test data and mocks"""
with ( with (
patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"), patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"),
@ -41,7 +41,6 @@ class ExportViews(TestCase):
bnf_id="beep", bnf_id="beep",
) )
# pylint: disable=invalid-name
def setUp(self): def setUp(self):
"""individual test setup""" """individual test setup"""
self.factory = RequestFactory() self.factory = RequestFactory()

View file

@ -11,7 +11,7 @@ from django.test.client import RequestFactory
from bookwyrm import forms, models, views from bookwyrm import forms, models, views
# pylint: disable=too-many-public-methods
@patch("bookwyrm.suggested_users.rerank_suggestions_task.delay") @patch("bookwyrm.suggested_users.rerank_suggestions_task.delay")
@patch("bookwyrm.activitystreams.populate_stream_task.delay") @patch("bookwyrm.activitystreams.populate_stream_task.delay")
class TwoFactorViews(TestCase): class TwoFactorViews(TestCase):

View file

@ -9,7 +9,7 @@ from django.test.client import RequestFactory
from bookwyrm import models, views from bookwyrm import models, views
from bookwyrm.tests.validate_html import validate_html from bookwyrm.tests.validate_html import validate_html
# pylint: disable=unused-argument
class DirectoryViews(TestCase): class DirectoryViews(TestCase):
"""tag views""" """tag views"""

View file

@ -10,7 +10,6 @@ from bookwyrm import models, views
from bookwyrm.settings import USER_AGENT from bookwyrm.settings import USER_AGENT
# pylint: disable=too-many-public-methods
@patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async") @patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async")
class OutboxView(TestCase): class OutboxView(TestCase):
"""sends out activities""" """sends out activities"""

View file

@ -52,7 +52,6 @@ class PartialDate(datetime):
Use subclasses to specify precision. If `dt` is naive, `ValueError` Use subclasses to specify precision. If `dt` is naive, `ValueError`
is raised. is raised.
""" """
# pylint: disable=invalid-name
if timezone.is_naive(dt): if timezone.is_naive(dt):
raise ValueError("naive datetime not accepted") raise ValueError("naive datetime not accepted")
return cls.combine(dt.date(), dt.time(), tzinfo=dt.tzinfo) return cls.combine(dt.date(), dt.time(), tzinfo=dt.tzinfo)

View file

@ -88,6 +88,7 @@ class CeleryStatus(View):
def post(self, request): def post(self, request):
"""Submit form to clear queues""" """Submit form to clear queues"""
form = ClearCeleryForm(request.POST) form = ClearCeleryForm(request.POST)
results = []
if form.is_valid(): if form.is_valid():
if len(celery.control.ping()) != 0: if len(celery.control.ping()) != 0:
return HttpResponse( return HttpResponse(

View file

@ -43,7 +43,6 @@ class Dashboard(View):
) or not re.match(regex.DOMAIN, settings.EMAIL_SENDER_DOMAIN) ) or not re.match(regex.DOMAIN, settings.EMAIL_SENDER_DOMAIN)
data["email_config_error"] = email_config_error data["email_config_error"] = email_config_error
# pylint: disable=line-too-long
data[ data[
"email_sender" "email_sender"
] = f"{settings.EMAIL_SENDER_NAME}@{settings.EMAIL_SENDER_DOMAIN}" ] = f"{settings.EMAIL_SENDER_NAME}@{settings.EMAIL_SENDER_DOMAIN}"

View file

@ -152,7 +152,7 @@ class FederatedServer(View):
} }
return TemplateResponse(request, "settings/federation/instance.html", data) return TemplateResponse(request, "settings/federation/instance.html", data)
def post(self, request, server): # pylint: disable=unused-argument def post(self, request, server):
"""update note""" """update note"""
server = get_object_or_404(models.FederatedServer, id=server) server = get_object_or_404(models.FederatedServer, id=server)
server.notes = request.POST.get("notes") server.notes = request.POST.get("notes")

View file

@ -63,7 +63,6 @@ class ImportList(View):
} }
return TemplateResponse(request, "settings/imports/imports.html", data) return TemplateResponse(request, "settings/imports/imports.html", data)
# pylint: disable=unused-argument
def post(self, request, import_id): def post(self, request, import_id):
"""Mark an import as complete""" """Mark an import as complete"""
import_job = get_object_or_404(models.ImportJob, id=import_id) import_job = get_object_or_404(models.ImportJob, id=import_id)
@ -95,7 +94,6 @@ def enable_imports(request):
@require_POST @require_POST
@permission_required("bookwyrm.edit_instance_settings", raise_exception=True) @permission_required("bookwyrm.edit_instance_settings", raise_exception=True)
# pylint: disable=unused-argument
def set_import_size_limit(request): def set_import_size_limit(request):
"""Limit the amount of books users can import at once""" """Limit the amount of books users can import at once"""
site = models.SiteSettings.objects.get() site = models.SiteSettings.objects.get()
@ -120,7 +118,6 @@ def set_user_import_completed(request, import_id):
@require_POST @require_POST
@permission_required("bookwyrm.edit_instance_settings", raise_exception=True) @permission_required("bookwyrm.edit_instance_settings", raise_exception=True)
# pylint: disable=unused-argument
def set_user_import_limit(request): def set_user_import_limit(request):
"""Limit how ofter users can import and export their account""" """Limit how ofter users can import and export their account"""
site = models.SiteSettings.objects.get() site = models.SiteSettings.objects.get()

View file

@ -204,7 +204,6 @@ def resolve_book(request):
@login_required @login_required
@require_POST @require_POST
@permission_required("bookwyrm.edit_book", raise_exception=True) @permission_required("bookwyrm.edit_book", raise_exception=True)
# pylint: disable=unused-argument
def update_book_from_remote(request, book_id, connector_identifier): def update_book_from_remote(request, book_id, connector_identifier):
"""load the remote data for this book""" """load the remote data for this book"""
connector = connector_manager.load_connector( connector = connector_manager.load_connector(

View file

@ -88,7 +88,6 @@ class CreateBook(View):
data = {"form": forms.EditionForm()} data = {"form": forms.EditionForm()}
return TemplateResponse(request, "book/edit/edit_book.html", data) return TemplateResponse(request, "book/edit/edit_book.html", data)
# pylint: disable=too-many-locals
def post(self, request): def post(self, request):
"""create a new book""" """create a new book"""
# returns None if no match is found # returns None if no match is found

View file

@ -240,7 +240,7 @@ def redirect_to_referer(request, *args, **kwargs):
return redirect(*args or "/", **kwargs) return redirect(*args or "/", **kwargs)
# pylint: disable=redefined-builtin,invalid-name # pylint: disable=redefined-builtin
def get_mergeable_object_or_404(klass, id): def get_mergeable_object_or_404(klass, id):
"""variant of get_object_or_404 that also redirects if id has been merged """variant of get_object_or_404 that also redirects if id has been merged
into another object""" into another object"""

View file

@ -97,7 +97,7 @@ class Register(View):
class ConfirmEmailCode(View): class ConfirmEmailCode(View):
"""confirm email address""" """confirm email address"""
def get(self, request, code): # pylint: disable=unused-argument def get(self, request, code):
"""you got the code! good work""" """you got the code! good work"""
settings = models.SiteSettings.get() settings = models.SiteSettings.get()
if request.user.is_authenticated: if request.user.is_authenticated:
@ -124,7 +124,7 @@ class ConfirmEmailCode(View):
class ConfirmEmail(View): class ConfirmEmail(View):
"""enter code to confirm email address""" """enter code to confirm email address"""
def get(self, request): # pylint: disable=unused-argument def get(self, request):
"""you need a code! keep looking""" """you need a code! keep looking"""
settings = models.SiteSettings.get() settings = models.SiteSettings.get()
if request.user.is_authenticated or not settings.require_confirm_email: if request.user.is_authenticated or not settings.require_confirm_email:

View file

@ -22,7 +22,6 @@ logger = logging.getLogger(__name__)
# pylint: disable=no-self-use # pylint: disable=no-self-use
# pylint: disable=too-many-return-statements
@method_decorator(login_required, name="dispatch") @method_decorator(login_required, name="dispatch")
class ReadingStatus(View): class ReadingStatus(View):
"""consider reading a book""" """consider reading a book"""

View file

@ -7,7 +7,7 @@ from ..models import Review, Quotation, Comment
from .helpers import get_user_from_username from .helpers import get_user_from_username
# pylint: disable=no-self-use, unused-argument # pylint: disable=no-self-use
class RssFeed(Feed): class RssFeed(Feed):
"""serialize user's posts in rss feed""" """serialize user's posts in rss feed"""

View file

@ -119,7 +119,6 @@ class Shelf(View):
return TemplateResponse(request, "shelf/shelf.html", data) return TemplateResponse(request, "shelf/shelf.html", data)
@method_decorator(login_required, name="dispatch") @method_decorator(login_required, name="dispatch")
# pylint: disable=unused-argument
def post(self, request, username, shelf_identifier): def post(self, request, username, shelf_identifier):
"""edit a shelf""" """edit a shelf"""
user = get_user_from_username(request.user, username) user = get_user_from_username(request.user, username)

View file

@ -32,7 +32,7 @@ logger = logging.getLogger(__name__)
class EditStatus(View): class EditStatus(View):
"""the view for *posting*""" """the view for *posting*"""
def get(self, request, status_id): # pylint: disable=unused-argument def get(self, request, status_id):
"""load the edit panel""" """load the edit panel"""
status = get_object_or_404( status = get_object_or_404(
models.Status.objects.select_subclasses(), id=status_id models.Status.objects.select_subclasses(), id=status_id

View file

@ -4,7 +4,7 @@ boto3==1.34.74
bw-file-resubmit==0.6.0rc2 bw-file-resubmit==0.6.0rc2
celery==5.3.6 celery==5.3.6
colorthief==0.2.1 colorthief==0.2.1
Django==4.2.11 Django==4.2.14
django-celery-beat==2.6.0 django-celery-beat==2.6.0
django-compressor==4.4 django-compressor==4.4
django-csp==3.8 django-csp==3.8
@ -37,6 +37,7 @@ redis==5.0.3
requests==2.32.0 requests==2.32.0
responses==0.25.0 responses==0.25.0
s3-tar==0.1.13 s3-tar==0.1.13
sqlparse==0.5.1
# Indirect dependencies with version constraints for security fixes # Indirect dependencies with version constraints for security fixes
grpcio>=1.57.0 grpcio>=1.57.0
@ -48,7 +49,7 @@ black==22.*
celery-types==0.22.0 celery-types==0.22.0
django-stubs[compatible-mypy]==4.2.7 django-stubs[compatible-mypy]==4.2.7
mypy==1.7.1 mypy==1.7.1
pylint==2.17.7 pylint==3.2.6
pytest==8.1.1 pytest==8.1.1
pytest-cov==5.0.0 pytest-cov==5.0.0
pytest-django==4.8.0 pytest-django==4.8.0