From 8a3885e1467e4c441721eaae608755a9fd10b33f Mon Sep 17 00:00:00 2001 From: Radek Czajka Date: Tue, 10 Nov 2026 13:00:14 +0100 Subject: [PATCH] Python 3.11, relevant upgrades --- Dockerfile | 2 +- docker-compose.yml | 1 + requirements/requirements.txt | 19 +++---- src/catalogue/fields.py | 4 +- src/wolnelektury/settings/auth.py | 2 +- src/wolnelektury/settings/static.py | 9 +++- src/wolnelektury/utils.py | 78 ----------------------------- 7 files changed, 23 insertions(+), 92 deletions(-) diff --git a/Dockerfile b/Dockerfile index f6debff91..434dade33 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,7 +12,7 @@ # Global build arguments. Stages using UID/GID must redeclare them # (bare `ARG UID`) to bring them into scope; the defaults are set here. -ARG PYTHON_IMAGE=python:3.10-trixie +ARG PYTHON_IMAGE=python:3.11-trixie ARG UID=8100 ARG GID=8100 diff --git a/docker-compose.yml b/docker-compose.yml index 81df2031d..afa178665 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -17,6 +17,7 @@ x-app-base: &app-base - ./var/counters/:/app/var/counters - ${LIBRARIAN:-/nonexistent}:/${LIBRARIAN:+edita}ble/librarian - ipython:/app/.ipython + - ./var/.bash_history:/app/.bash_history depends_on: - db - redis diff --git a/requirements/requirements.txt b/requirements/requirements.txt index 9c3aa7959..0743b99a6 100644 --- a/requirements/requirements.txt +++ b/requirements/requirements.txt @@ -11,13 +11,13 @@ django-pipeline==4.1.0 jsmin==3.0.1 fnp-django-pagination==2.2.12 -django-modeltranslation==0.18.12 -django-allauth[socialaccount,openid]==0.63.6 -django-extensions==3.2.3 -djangorestframework==3.15.1 -django-filter==23.5 +django-modeltranslation==0.19.17 +django-allauth[socialaccount,openid]==65.21.1 +django-extensions==4.1 +djangorestframework==3.17.2 +django-filter==25.1 djangorestframework-xml==2.0.0 -django-admin-ordering==0.18 +django-admin-ordering==0.22 django-countries==7.6.1 django-forms-builder @ git+https://github.com/rczajka/django-forms-builder@6533f9442f004d44ed362202b6bdab82b73dd5e9 @@ -33,9 +33,9 @@ django-honeypot==1.2.1 Feedparser==6.0.14 -Pillow==12.3.0 +Pillow==13.0.0 mutagen==1.48.1 -sorl-thumbnail==12.10.0 +sorl-thumbnail==13.0.0 zipstream-ng==1.9.3 @@ -46,11 +46,12 @@ librarian==26.11 # celery tasks celery[redis]==5.6.2 -oaipmh==3.2.1 +oaipmh==4.0.0 sentry-sdk==2.74.0 requests==2.34.2 +requests_oauthlib==2.0.0 paypalrestsdk diff --git a/src/catalogue/fields.py b/src/catalogue/fields.py index 2d35357eb..ee64ee4d1 100644 --- a/src/catalogue/fields.py +++ b/src/catalogue/fields.py @@ -1,9 +1,9 @@ # This file is part of Wolne Lektury, licensed under GNU Affero GPLv3 or later. # Copyright © Fundacja Wolne Lektury. See NOTICE for more information. # +from importlib.metadata import version import io import os -import pkg_resources import random import time from urllib.request import urlopen @@ -151,7 +151,7 @@ class EbookField(models.FileField): def get_current_etag(self): MediaInsertSet = apps.get_model('annoy', 'MediaInsertSet') - librarian_version = pkg_resources.get_distribution("librarian").version + librarian_version = version("librarian") etag = librarian_version mis = MediaInsertSet.get_for_format(self.ext) if mis is not None: diff --git a/src/wolnelektury/settings/auth.py b/src/wolnelektury/settings/auth.py index 2fe844c6e..0acfa9041 100644 --- a/src/wolnelektury/settings/auth.py +++ b/src/wolnelektury/settings/auth.py @@ -6,7 +6,7 @@ AUTHENTICATION_BACKENDS = [ 'allauth.account.auth_backends.AuthenticationBackend', ] ACCOUNT_EMAIL_CONFIRMATION_EXPIRE_DAYS = 2 -ACCOUNT_AUTHENTICATION_METHOD = "username_email" +ACCOUNT_LOGIN_METHODS = {'email', 'username'} FEATURE_CONFIRM_USER = False LOGIN_URL = '/uzytkownik/login/' diff --git a/src/wolnelektury/settings/static.py b/src/wolnelektury/settings/static.py index 9c2960fbd..505d13916 100644 --- a/src/wolnelektury/settings/static.py +++ b/src/wolnelektury/settings/static.py @@ -144,7 +144,14 @@ PIPELINE = { 'CSSMIN_ARGUMENTS': '--stdin --style=compressed --no-source-map', } -STATICFILES_STORAGE = 'fnpdjango.pipeline_storage.GzipPipelineManifestStorage' +STORAGES = { + 'default': { + 'BACKEND': 'django.core.files.storage.FileSystemStorage' + }, + 'staticfiles': { + 'BACKEND': 'fnpdjango.pipeline_storage.GzipPipelineManifestStorage' + }, +} STATICFILES_FINDERS = [ diff --git a/src/wolnelektury/utils.py b/src/wolnelektury/utils.py index dfa8522f8..245c076b1 100644 --- a/src/wolnelektury/utils.py +++ b/src/wolnelektury/utils.py @@ -4,9 +4,7 @@ import codecs import csv from functools import wraps -from inspect import getargspec from io import BytesIO -import json import os import re @@ -14,7 +12,6 @@ from django.conf import settings from django.contrib import admin from django.core.cache import cache from django.core.mail import send_mail -from django.http import HttpResponse from django.template.loader import render_to_string from django.utils.translation import get_language from django.conf import settings @@ -30,81 +27,6 @@ def makedirs(path): os.makedirs(path) -def stringify_keys(dictionary): - return dict((keyword.encode('ascii'), value) - for keyword, value in dictionary.items()) - - -def json_encode(obj, sort_keys=True, ensure_ascii=False): - return json.dumps(obj, sort_keys=sort_keys, ensure_ascii=ensure_ascii) - - -def json_decode(obj): - return json.loads(obj) - - -def json_decode_fallback(value): - try: - return json_decode(value) - except ValueError: - return value - - -class AjaxError(Exception): - pass - - -def ajax(login_required=False, method=None, template=None, permission_required=None): - def decorator(fun): - @wraps(fun) - def ajax_view(request): - kwargs = {} - request_params = None - if method == 'post': - request_params = request.POST - elif method == 'get': - request_params = request.GET - fun_params, xx, fun_kwargs, defaults = getargspec(fun) - if defaults: - required_params = fun_params[1:-len(defaults)] - else: - required_params = fun_params[1:] - missing_params = set(required_params) - set(request_params) - if missing_params: - res = { - 'result': 'missing params', - 'missing': ', '.join(missing_params), - } - else: - if request_params: - request_params = dict( - (key, json_decode_fallback(value)) - for key, value in request_params.items() - if fun_kwargs or key in fun_params) - kwargs.update(stringify_keys(request_params)) - res = None - if login_required and not request.user.is_authenticated: - res = {'result': 'logout'} - if (permission_required and - not request.user.has_perm(permission_required)): - res = {'result': 'access denied'} - if not res: - try: - res = fun(request, **kwargs) - if res and template: - res = {'html': render_to_string(template, res, request=request)} - except AjaxError as e: - res = {'result': e.args[0]} - if 'result' not in res: - res['result'] = 'ok' - return HttpResponse(json_encode(res), content_type='application/json; charset=utf-8', - status=200 if res['result'] == 'ok' else 400) - - return ajax_view - - return decorator - - def send_noreply_mail(subject, message, recipient_list, **kwargs): send_mail( '[WolneLektury] ' + subject, -- 2.20.1