style: formater le depot avec ruff format

QUA-002, premiere moitie. **Ce commit ne fait que reformater** : aucun
changement de comportement, aucune ligne de logique touchee. 72 fichiers,
4 restaient deja conformes. Il est isole exprès, pour que `git log -p` sur
les commits voisins reste lisible.

`quote-style = "preserve"` etait deja pose dans pyproject.toml, ce qui
evite le brassage guillemets simples / doubles : le diff porte sur les
retours a la ligne, l indentation des appels longs et les virgules
finales, pas sur le style de chaine.

Verification : 263 tests passent avant et apres, ruff check propre.

L activation en CI arrive dans le commit suivant, separement, pour que ce
diff-ci ne contienne rien d autre.

Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
GGThed
2026-08-08 15:53:10 -04:00
co-authored by Claude Opus 5
parent 2f40290f00
commit 7cec18c139
72 changed files with 2658 additions and 1449 deletions
+64 -52
View File
@@ -100,19 +100,21 @@ def build_csp(*, allow_inline_script, nonce=None):
else:
script_src = f"'self' 'nonce-{nonce}' https://cdn.jsdelivr.net"
return '; '.join([
"default-src 'self'",
f'script-src {script_src}',
# style-src is a separate migration: inline style="" attributes are
# spread across the templates and are not an XSS vector on their own.
"style-src 'self' 'unsafe-inline' https://cdnjs.cloudflare.com https://cdn.jsdelivr.net",
"font-src 'self' https://cdnjs.cloudflare.com",
"img-src 'self' data: https://cdn.discordapp.com",
"connect-src 'self'",
"frame-ancestors 'none'",
"base-uri 'self'",
"form-action 'self'",
])
return '; '.join(
[
"default-src 'self'",
f'script-src {script_src}',
# style-src is a separate migration: inline style="" attributes are
# spread across the templates and are not an XSS vector on their own.
"style-src 'self' 'unsafe-inline' https://cdnjs.cloudflare.com https://cdn.jsdelivr.net",
"font-src 'self' https://cdnjs.cloudflare.com",
"img-src 'self' data: https://cdn.discordapp.com",
"connect-src 'self'",
"frame-ancestors 'none'",
"base-uri 'self'",
"form-action 'self'",
]
)
def create_app(config=None):
@@ -170,12 +172,8 @@ def create_app(config=None):
# Side effects of create_app(), both on by default so that production and
# development behave exactly as before. Tests turn them off.
app.config['AUTO_CREATE_TABLES'] = (
os.getenv('AUTO_CREATE_TABLES', 'true').lower() == 'true'
)
app.config['ENABLE_DISCORD_BOT'] = (
os.getenv('ENABLE_DISCORD_BOT', 'true').lower() == 'true'
)
app.config['AUTO_CREATE_TABLES'] = os.getenv('AUTO_CREATE_TABLES', 'true').lower() == 'true'
app.config['ENABLE_DISCORD_BOT'] = os.getenv('ENABLE_DISCORD_BOT', 'true').lower() == 'true'
# --- caller overrides win ---------------------------------------------
if config:
@@ -198,7 +196,9 @@ def create_app(config=None):
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024
# Secure session cookie settings
app.config['SESSION_COOKIE_SECURE'] = os.getenv('SESSION_COOKIE_SECURE', 'true').lower() == 'true'
app.config['SESSION_COOKIE_SECURE'] = (
os.getenv('SESSION_COOKIE_SECURE', 'true').lower() == 'true'
)
app.config['SESSION_COOKIE_HTTPONLY'] = True
app.config['SESSION_COOKIE_SAMESITE'] = 'Lax'
app.config['PERMANENT_SESSION_LIFETIME'] = 3600 # 1 hour session timeout
@@ -245,12 +245,14 @@ def create_app(config=None):
@app.context_processor
def inject_csp_nonce():
return {'csp_nonce': '' if app.config['CSP_ALLOW_INLINE_SCRIPT']
else g.get('csp_nonce', '')}
return {
'csp_nonce': '' if app.config['CSP_ALLOW_INLINE_SCRIPT'] else g.get('csp_nonce', '')
}
@app.context_processor
def inject_locales():
from flask_babel import get_locale
return {
'current_locale': str(get_locale() or i18n.DEFAULT_LOCALE),
'supported_locales': i18n.SUPPORTED_LOCALES,
@@ -259,6 +261,7 @@ def create_app(config=None):
# Configure structured logging
from app.logging_config import configure_logging
configure_logging(app)
from app.routes.auth import auth_bp
@@ -303,8 +306,7 @@ def create_app(config=None):
response.headers['X-Frame-Options'] = 'DENY'
response.headers['Referrer-Policy'] = 'strict-origin-when-cross-origin'
response.headers['Permissions-Policy'] = (
'camera=(), microphone=(), geolocation=(), '
'interest-cohort=(), payment=(), usb=()'
'camera=(), microphone=(), geolocation=(), interest-cohort=(), payment=(), usb=()'
)
response.headers['Cross-Origin-Opener-Policy'] = 'same-origin'
response.headers['Content-Security-Policy'] = build_csp(
@@ -383,9 +385,11 @@ def create_app(config=None):
Returns:
Response: Rendered error page or JSON for API requests.
"""
if request.path.startswith('/users/disponibilities') or \
request.path.startswith('/users/coach-availability') or \
request.path.startswith('/users/api/'):
if (
request.path.startswith('/users/disponibilities')
or request.path.startswith('/users/coach-availability')
or request.path.startswith('/users/api/')
):
return jsonify({'error': 'Bad request', 'message': str(error)}), 400
return render_template('errors/400.html', error=error), 400
@@ -399,10 +403,12 @@ def create_app(config=None):
Returns:
Response: Redirect to login for pages, JSON for API.
"""
if request.path.startswith('/users/disponibilities') or \
request.path.startswith('/users/api/'):
if request.path.startswith('/users/disponibilities') or request.path.startswith(
'/users/api/'
):
return jsonify({'error': 'Unauthorized'}), 401
from flask import flash as _flash
_flash('Please log in to access this page.', 'warning')
return redirect(url_for('auth.login'))
@@ -416,8 +422,9 @@ def create_app(config=None):
Returns:
Response: Rendered error page or JSON for API requests.
"""
if request.path.startswith('/users/disponibilities') or \
request.path.startswith('/users/api/'):
if request.path.startswith('/users/disponibilities') or request.path.startswith(
'/users/api/'
):
return jsonify({'error': 'Forbidden', 'message': str(error)}), 403
return render_template('errors/403.html', error=error), 403
@@ -431,8 +438,9 @@ def create_app(config=None):
Returns:
Response: Rendered error page or JSON for API requests.
"""
if request.path.startswith('/users/disponibilities') or \
request.path.startswith('/users/api/'):
if request.path.startswith('/users/disponibilities') or request.path.startswith(
'/users/api/'
):
return jsonify({'error': 'Not found'}), 404
return render_template('errors/404.html', error=error), 404
@@ -446,12 +454,12 @@ def create_app(config=None):
Returns:
Response: JSON error for API or rendered page.
"""
if request.path.startswith('/users/disponibilities') or \
request.path.startswith('/users/api/'):
return jsonify({
'error': 'Too many requests',
'message': 'Please try again later.'
}), 429
if request.path.startswith('/users/disponibilities') or request.path.startswith(
'/users/api/'
):
return jsonify(
{'error': 'Too many requests', 'message': 'Please try again later.'}
), 429
return render_template('errors/429.html', error=error), 429
@app.errorhandler(500)
@@ -472,12 +480,15 @@ def create_app(config=None):
# Roll back any failed database session
db.session.rollback()
if request.path.startswith('/users/disponibilities') or \
request.path.startswith('/users/api/'):
return jsonify({
'error': 'Internal server error',
'message': 'An unexpected error occurred. Please try again later.'
}), 500
if request.path.startswith('/users/disponibilities') or request.path.startswith(
'/users/api/'
):
return jsonify(
{
'error': 'Internal server error',
'message': 'An unexpected error occurred. Please try again later.',
}
), 500
return render_template('errors/500.html'), 500
@app.errorhandler(HTTPException)
@@ -490,13 +501,12 @@ def create_app(config=None):
Returns:
Response: JSON error for API, re-raises for others.
"""
if request.path.startswith('/users/disponibilities') or \
request.path.startswith('/users/api/'):
return jsonify({
'error': error.name,
'message': error.description,
'code': error.code
}), error.code
if request.path.startswith('/users/disponibilities') or request.path.startswith(
'/users/api/'
):
return jsonify(
{'error': error.name, 'message': error.description, 'code': error.code}
), error.code
return error
# =========================================================================
@@ -504,6 +514,7 @@ def create_app(config=None):
# =========================================================================
with app.app_context():
import app.models as models # noqa: F401 — registers all models with SQLAlchemy
# NOTE: create_all() only ever creates missing tables. It never adds a
# column to an existing one, so a model change is silently absent from
# any database that already has the table. Replacing this with Alembic
@@ -515,6 +526,7 @@ def create_app(config=None):
if app.config['ENABLE_DISCORD_BOT']:
try:
from app.discord_bot import start_bot
start_bot(flask_app=app)
except Exception as e:
app.logger.warning('Could not start Discord bot: %s', e)