0.6.1: la Home non si apriva (doppio Component.onCompleted); controllo automatico delle assegnazioni duplicate
This commit is contained in:
@@ -0,0 +1,118 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Assegnazioni duplicate di proprieta' negli stessi oggetti QML.
|
||||
|
||||
Perche' esiste: in Qt 5.6 assegnare due volte la stessa proprieta' di un oggetto
|
||||
QML (tipico: due `Component.onCompleted` nella stessa pagina) non e' un errore
|
||||
innocuo: il file compila con un warning "Property value set multiple times" e
|
||||
l'oggetto NON si crea piu'. Dall'esterno si vede solo
|
||||
|
||||
harbour-aira.qml:9:30: Type MainPage unavailable
|
||||
|
||||
cioe' l'app che si apre senza la pagina iniziale, senza dire il perche'.
|
||||
Il kit Silica non vede niente di tutto questo (qmllint conosce il QML, non il
|
||||
significato di questi binding), quindi il controllo sta qui e gira in pochi
|
||||
millisecondi.
|
||||
|
||||
Uso:
|
||||
python3 tests/qml_check_bindings.py [cartella_qml]
|
||||
python3 tests/qml_check_bindings.py --dettagli # elenca anche l'ok
|
||||
Esce 1 se trova duplicati, 0 se e' pulito.
|
||||
"""
|
||||
|
||||
import pathlib
|
||||
import re
|
||||
import sys
|
||||
|
||||
# Riga che apre un oggetto QML: un componente (maiuscola) oppure un gruppo di
|
||||
# proprieta' noto. Il corpo di un gestore (onClicked:) o di una funzione e' JS,
|
||||
# e li' dentro i "duplicati" sono normali (ternari, oggetti letterali, rami).
|
||||
APRE_OGGETTO = re.compile(r"^\s*([A-Z][A-Za-z0-9_]*)\s*\{")
|
||||
APRE_GRUPPO = re.compile(r"^\s*(anchors|font|Layout\.[A-Za-z]+)\s*\{")
|
||||
APRE_JS = re.compile(r"^\s*(function\s+\w+|if\b|else\b|for\b|while\b|switch\b|do\b|try\b|catch\b)\s*[\({]")
|
||||
APRE_GESTORE = re.compile(r"^\s*on[A-Z][A-Za-z0-9_]*\s*:")
|
||||
|
||||
PROPRIETA = re.compile(r"^\s*((?:readonly\s+)?property\s+\S+\s+([A-Za-z_]\w*)|signal\s+(\w+)|function\s+(\w+)|([A-Za-z_][\w.]*))\s*:")
|
||||
SENZA_COMMENTO = re.compile(r"//.*$")
|
||||
|
||||
|
||||
def chiave(riga: str):
|
||||
"""Nome della proprieta' assegnata dalla riga, o None."""
|
||||
m = PROPRIETA.match(SENZA_COMMENTO.sub("", riga))
|
||||
if not m:
|
||||
return None
|
||||
return m.group(2) or m.group(3) or m.group(4) or m.group(5)
|
||||
|
||||
|
||||
def controlla(testo: str):
|
||||
"""Ritorna la lista di (riga, chiave) duplicate nello stesso oggetto."""
|
||||
pila = [("qml", {})] # (tipo ambiente, chiavi viste)
|
||||
problemi = []
|
||||
for numero, riga in enumerate(testo.splitlines(), 1):
|
||||
pulita = SENZA_COMMENTO.sub("", riga)
|
||||
if not pulita.strip():
|
||||
continue
|
||||
|
||||
# Chiusura: si esce dall'ambiente corrente.
|
||||
while pulita.strip().startswith("}") and len(pila) > 1:
|
||||
pila.pop()
|
||||
pulita = pulita.strip()[1:]
|
||||
if not pulita.strip():
|
||||
break
|
||||
if not pulita.strip():
|
||||
continue
|
||||
|
||||
ambiente, chiavi = pila[-1]
|
||||
if ambiente == "qml":
|
||||
k = chiave(pulita)
|
||||
if k:
|
||||
if k in chiavi:
|
||||
problemi.append((numero, k))
|
||||
else:
|
||||
chiavi[k] = numero
|
||||
|
||||
# Apertura di un nuovo ambiente: il contenuto e' QML solo se la riga
|
||||
# apre un oggetto o un gruppo; gestori e funzioni aprono JS.
|
||||
apre_js = bool(APRE_JS.match(pulita)) or bool(APRE_GESTORE.match(pulita))
|
||||
apre_qml = (bool(APRE_OGGETTO.match(pulita)) or bool(APRE_GRUPPO.match(pulita))) \
|
||||
and not apre_js
|
||||
# Una proprieta' il cui valore e' un oggetto letterale JS non e' QML.
|
||||
if ":" in pulita and "{" in pulita and not apre_qml and not apre_js:
|
||||
apre_js = True
|
||||
if pulita.count("{") > pulita.count("}"):
|
||||
pila.append(("qml" if apre_qml else "js", {}))
|
||||
return problemi
|
||||
|
||||
|
||||
def main(argv):
|
||||
argomenti = [a for a in argv[1:] if not a.startswith("--")]
|
||||
radice = pathlib.Path(argomenti[0] if argomenti else "qml")
|
||||
dettagli = "--dettagli" in argv
|
||||
|
||||
file_qml = sorted(radice.rglob("*.qml"))
|
||||
if not file_qml:
|
||||
print("nessun file QML in", radice)
|
||||
return 1
|
||||
|
||||
totale = 0
|
||||
for percorso in file_qml:
|
||||
problemi = controlla(percorso.read_text(encoding="utf-8"))
|
||||
relative = percorso.relative_to(radice.parent if radice.name == "qml" else radice)
|
||||
if problemi:
|
||||
totale += len(problemi)
|
||||
print("%s: %d assegnazioni duplicate" % (relative, len(problemi)))
|
||||
for numero, k in problemi:
|
||||
print(" riga %d: '%s' gia' assegnata in questo oggetto" % (numero, k))
|
||||
elif dettagli:
|
||||
print("%s: pulito" % relative)
|
||||
|
||||
print("---")
|
||||
if totale:
|
||||
print("ESITO: %d assegnazioni duplicate (l'oggetto non si crea: "
|
||||
"vedi Qt 5.6, 'Property value set multiple times')" % totale)
|
||||
return 1
|
||||
print("ESITO: nessuna assegnazione duplicata in %d file" % len(file_qml))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main(sys.argv))
|
||||
Reference in New Issue
Block a user