File size: 1,302 Bytes
380a7fc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
"""Ancrage des citations : une URL citée en commentaire doit venir des notes de recherche web de la session.

Un candidat signalé passe derrière les autres à résultat de tests égal ; il n'est jamais rejeté si ses tests
passent. Les URL du code lui-même (chaînes, constantes) ne sont pas concernées : seules les citations le sont.
"""
from __future__ import annotations

import io
import re
import tokenize

_URL = re.compile(r"https?://[^\s)\]>'\"`]+")


def _clean(url: str) -> str:
    return url.rstrip(".,;:")


def comment_urls(code: str) -> list[str]:
    urls: list[str] = []
    try:
        for tok in tokenize.generate_tokens(io.StringIO(code).readline):
            if tok.type == tokenize.COMMENT:
                urls += [_clean(u) for u in _URL.findall(tok.string)]
    except (tokenize.TokenError, IndentationError, SyntaxError):
        return []
    return urls


class CitationCheck:
    def check(self, files, notes=None) -> list[str]:
        allowed = {_clean(u) for n in (notes or []) for u in _URL.findall(n)}
        issues = []
        for name, code in sorted(files.items()):
            for url in comment_urls(code):
                if url not in allowed:
                    issues.append(f"citation non sourcée dans {name} : {url[:120]}")
        return issues