From 279af591b58bd257951dfa8392f7367be2d7c406 Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Sat, 9 Jul 2022 15:10:30 +0200 Subject: [PATCH 001/122] Switched to pylint --- .vscode/settings.json | 4 ++++ pyproject.toml | 4 ++++ 2 files changed, 8 insertions(+) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..97dfb56 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,4 @@ +{ + "python.linting.enabled": true, + "cSpell.language": "it-IT,en-GB" +} diff --git a/pyproject.toml b/pyproject.toml index 0ab736b..60261de 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,3 +13,7 @@ ignore = [ in-place = true recursive = true aggressive = 3 + + +[tool.pylint.main] +max-line-length = 120 From 6830a6421e6116c2c4974b8a17a5b768e7281fcb Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Sat, 9 Jul 2022 15:12:33 +0200 Subject: [PATCH 002/122] Migliorato lo stile --- src/pi.py | 4 +- src/rand.py | 138 +++++++++++++++++++++++++++++----------------------- 2 files changed, 78 insertions(+), 64 deletions(-) diff --git a/src/pi.py b/src/pi.py index 7f5c679..9e25aad 100755 --- a/src/pi.py +++ b/src/pi.py @@ -109,7 +109,7 @@ def main(): # Inizializzazione TRG = TrueRandomGenerator(bug=BUG) # Il nostro generatore - LEN = TRG.nRandomNumbers # Numero di valori casuali disponibili + LEN = TRG.n_random_numbers # Numero di valori casuali disponibili N_in: int = 0 # Numero di coordinate casuali all'interno del cerchio # noqa x_in: list[int] = [] # Lista delle coordinate x all'interno del cerchio # noqa y_in: list[int] = [] # Lista delle coordinate y all'interno del cerchio # noqa @@ -179,7 +179,7 @@ def main(): # ------------ Metodo 3: tutte le coordinate possibili, O(n^2) ------------ elif _mode == 2: # Pre-calcolo dei quadrati, per ottimizzazione - nums = [b**2 for b in TRG.randomNumbers] + nums = [b**2 for b in TRG.random_numbers] # `enumerate([a, b, c]) -> [(0, a), (1, b), (2, c)]` # Questo ciclo scorre gli elementi (`x`) del vettore `nums`, diff --git a/src/rand.py b/src/rand.py index 9266522..d7d0707 100755 --- a/src/rand.py +++ b/src/rand.py @@ -31,12 +31,12 @@ class TrueRandomGenerator: # --- Variabili d'istanza --- # pubbliche - deltaTs: list[int] # Differenze dei tempi - randomBits: list[int] # Bit (0|1) casuali - randomNumbers: list[int] # Numeri casuali (da 0 a 255) - nRandomNumbers: int # Numero di numeri casuali + delta_times: list[int] # Differenze dei tempi + random_bits: list[int] # Bit (0|1) casuali + random_numbers: list[int] # Numeri casuali (da 0 a 255) + n_random_numbers: int # Numero di numeri casuali # protette - _i: int # Indice per il metodo `random_number()` + _i: int # Indice per il metodo `random_number()` # --- Metodo di inizializzazione --- @@ -75,8 +75,8 @@ def __init__( # Se invece nemmeno `file=` è stato specificato, non usare alcun file files = ([] if file is None else [file]) if files is None else files.copy() # Apri i file in `files` e leggi l'albero "Data_R", aggiungendo i dati a `t` - for f in files: - events += root.read(f, "Data_R", cls=Event) + for file in files: + events += root.read(file, "Data_R", cls=Event) # Se non ci sono abbastanza eventi, riporta un errore e termina il programma if len(events) < 9: raise ValueError( @@ -86,12 +86,12 @@ def __init__( # --- 1. Calcolo delle differenze dei tempi tra coppie di tempi adiacenti --- if __debug__: print("--> Calculating time differences") - self.deltaTs = [] + self.delta_times = [] for i in range(1, len(events)): - # `dT` = (tempo dell'`i`-esimo evento) - (tempo dell'`i-1`-esimo evento) - dT = events[i].Timestamp - events[i - 1].Timestamp - # Salva `dT` nel vettore dedicato - self.deltaTs.append(dT) + # ∆t = (tempo dell'`i`-esimo evento) - (tempo dell'`i-1`-esimo evento) + delta_time = events[i].Timestamp - events[i - 1].Timestamp + # Salva ∆t (`delta_time`) nel vettore dedicato + self.delta_times.append(delta_time) if __debug__: print(" done.") @@ -100,7 +100,7 @@ def __init__( print("--> Generating random bits") # Applicazione del metodo (statico) `self._rand(...)` alle # differenze dei tempi e salvataggio nel vettore `self.bits` - self.randomBits = list(map(self._rand, self.deltaTs)) + self.random_bits = list(map(self._rand, self.delta_times)) if __debug__: print(" done.") @@ -108,81 +108,82 @@ def __init__( print("--> Generating random numbers") # --- 3. Generazione dei numeri casuali (da 0 a 255) --- - self.randomNumbers = [] - randomNumbers_b = [] + self.random_numbers = [] + random_numbers_b = [] # Inizializza un vettore di lunghezza 8 (pieno di zeri) byte = [0] * 8 if _BYTES_GENERATION_METHOD == 0: # -------------------- Metodo 1 -------------------- # = ⌊ / 8 ⌋ ('//' è la divisione intera) - nbytes = len(self.randomBits) // 8 - for i in range(nbytes): + n_bytes = len(self.random_bits) // 8 + for i in range(n_bytes): for j in range(8): # Prendi 8 elementi da `self.randomBits` e salvali in `byte` - byte[j] = self.randomBits[i * 8 + j] + byte[j] = self.random_bits[i * 8 + j] # Converti `byte` in un numero da 0 a 255 tramite il metodo (statico) `_conv()`; # salva poi il risultato nella variabile di istanza. - self.randomNumbers.append(self._conv(byte)) + self.random_numbers.append(self._conv(byte)) # Se il `bug` è attivo, rifallo con il metodo (statico) `_conv2()` if bug: - randomNumbers_b.append(self._conv2(byte)) + random_numbers_b.append(self._conv2(byte)) else: # -------------------- Metodo 2 -------------------- - for i in range(len(self.randomBits)): + for i, bit in enumerate(self.random_bits): # Copia l'`i`-esimo bit nell'(`i` mod 8)-esima cella di `byte` - byte[i % 8] = self.randomBits[i] + byte[i % 8] = bit if i % 8 == 7: # Il byte è completo: convertilo in numero decimale e salvalo - self.randomNumbers.append(self._conv(byte)) + self.random_numbers.append(self._conv(byte)) if bug: - randomNumbers_b.append(self._conv2(byte)) + random_numbers_b.append(self._conv2(byte)) if bug: - self.randomNumbers += randomNumbers_b + self.random_numbers += random_numbers_b if __debug__: print(" done.") # Salva la lunghezza di "self.randomNumbers" per un accesso più rapido - self.nRandomNumbers = len(self.randomNumbers) + self.n_random_numbers = len(self.random_numbers) # Dichiara la variabile d'istanza che tiene traccia del punto a cui siamo arrivati a leggere i byte casuali self._i = 0 - # Metodo statico: genera un bit dal paramentro "n" + # Metodo statico: genera un bit dal numero che gli viene passato @staticmethod - def _rand(n: int) -> int: - return n % 2 + def _rand(num: int) -> int: + return num % 2 - # Metodo statico: converte il vettore di bit "v" in numero decimale + # Metodo statico: converte il vettore di bit `byte` in numero decimale @staticmethod - def _conv(v: list[int]) -> int: - # indici di `v` (`7-i`): [ 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 ] - # esponenti di 2 (`i`) : [ 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 ] - sum = 0 + def _conv(byte: list[int]) -> int: + # indici di `byte` (`7-i`): [ 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 ] + # esponenti di 2 (`i`) : [ 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 ] + num = 0 for i in range(8): - sum += v[7 - i] * 2**i - return sum + num += byte[7 - i] * 2**i + return num - # Metodo statico: converte fasullamente il vettore di bit "v" in numero decimale + # Metodo statico: converte in modo errato il vettore di bit `byte` in numero decimale @staticmethod - def _conv2(v: list[int]) -> int: - # indici di `v` (`i`): [ 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 ] - # esponenti di 2 (`i`): [ 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 ] - sum = 0 + def _conv2(byte: list[int]) -> int: + # indici di `byte` (`i`): [ 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 ] + # esponenti di 2 (`i`): [ 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 ] + num = 0 for i in range(8): - sum += v[i] * 2**i # <-- il bug è qui, i pesi dei bit sono in ordine inverso - return sum + num += byte[i] * 2**i # <-- il bug è qui, i pesi dei bit sono in ordine inverso + return num # Metodo: restituisce un numero casuale tra 0 e 255 (ogni volta diverso: scorre ciclicamente lungo i byte casuali) def random_number(self) -> int: - n = self.randomNumbers[self._i] + """Restituisce un numero casuale da 0 a 255.""" + num = self.random_numbers[self._i] # Incremento dell'indice, torna a 0 se si raggiunge l'ultimo numero casuale disponibile - self._i = (self._i + 1) % self.nRandomNumbers - return n + self._i = (self._i + 1) % self.n_random_numbers + return num # Classe che contiene le flag per scegliere cosa mostrare nei grafici @@ -195,7 +196,7 @@ class PLOT(Flag): # Distribuzione dei bit BITS_DISTRIBUTION = auto() # Distribuzione dei byte - BYTES_DISTRIBUTION = auto() # isogramma principale + BYTES_DISTRIBUTION = auto() # istogramma principale BYTES_DISTRIBUTION_LOCAL_MEANS = auto() # medie locali @@ -212,35 +213,48 @@ class PLOT(Flag): # Funzione per calcolare le medie locali (ciclicamente) -def cyclic_local_means(v: list[int], spread: int = 5) -> list[float]: - # 'v' è il vettore con i dati - # 'spread' è quanti valori prendere +def cyclic_local_means(data: list[int], spread: int = 5) -> list[float]: + """Calcola ciclicamente le medie locali del vettore `data`, con lo `spread` specificato. + + Esempio + ------- + >>> for i in range(1, 7): + ... print(f"spread={i} --> {cyclic_local_means(list(range(6)), spread=i)}") + spread=1 --> [0.0, 1.0, 2.0, 3.0, 4.0, 5.0] + spread=2 --> [0.5, 1.5, 2.5, 3.5, 4.5, 2.5] + spread=3 --> [2.0, 1.0, 2.0, 3.0, 4.0, 3.0] + spread=4 --> [2.0, 1.5, 2.5, 3.5, 3.0, 2.5] + spread=5 --> [2.4, 2.2, 2.0, 3.0, 2.8, 2.6] + spread=6 --> [2.5, 2.5, 2.5, 2.5, 2.5, 2.5] + """ left = (spread - 1) // 2 - L = len(v) - return [sum([v[(i + j - left) % L] for j in range(spread)]) / spread for i in range(L)] + length = len(data) + return [sum(data[(i + j - left) % length] for j in range(spread)) / spread for i in range(length)] # Funzione per testare il generatore def test(): + """Testa il generatore di numeri veramente casuali.""" + # La libreria `matplotlib` serve soltanto qua: importarla all'inizio di tutto il programma è sconveniente - import matplotlib.pyplot as plt + import matplotlib.pyplot as plt # pylint: disable=import-outside-toplevel gen = TrueRandomGenerator() # Salva alcuni valori utili nel namespace locale # per velocizzare l'accesso - bits = gen.randomBits - nums = gen.randomNumbers + bits = gen.random_bits + nums = gen.random_numbers - _PLOT_ITEM_MESSAGE = " * {}" if __debug__ and TO_PLOT: print("--> Plotting required items:") + _plot_item_message: str = " * {}" # ------------------------ Differenze di tempo ------------------------- if PLOT.TIME_DELTAS in TO_PLOT: if __debug__: - print(_PLOT_ITEM_MESSAGE.format(PLOT.TIME_DELTAS)) - plt.hist(gen.deltaTs, bins=500) + print(_plot_item_message.format(PLOT.TIME_DELTAS)) + plt.hist(gen.delta_times, bins=500) plt.yscale("log") plt.xlabel("Time difference between two conecutive events [Digitizer Clock Periods]") plt.ylabel("Counts") @@ -250,12 +264,12 @@ def test(): # ------------------------ Distribuzione dei bit ------------------------- if PLOT.BITS_DISTRIBUTION in TO_PLOT: if __debug__: - print(_PLOT_ITEM_MESSAGE.format(PLOT.BITS_DISTRIBUTION)) + print(_plot_item_message.format(PLOT.BITS_DISTRIBUTION)) # print(len(gen.deltaT)) # stampa il numero di deltaT disponibili # print(*gen.randomNumbers, sep="\n") # stampa numeri casuali disponibili # # Confronta frequenze di 0 e 1 in bits # n0 = gen.randomBits.count(0) - # print(n0/len(bits), (nbits-n0)/len(bits)) + # print(n0/len(bits), (len(bits)-n0)/len(bits)) plt.hist(bits, bins=2) # istogramma per confrontare 0 e 1 (i bit) plt.xlabel("Bit") plt.ylabel("Counts") @@ -267,7 +281,7 @@ def test(): if PLOT.BYTES_DISTRIBUTION in TO_PLOT: if __debug__: - print(_PLOT_ITEM_MESSAGE.format(PLOT.BYTES_DISTRIBUTION)) + print(_plot_item_message.format(PLOT.BYTES_DISTRIBUTION)) # Numeri casuali plt.hist( nums, @@ -277,7 +291,7 @@ def test(): if PLOT.BYTES_DISTRIBUTION_LOCAL_MEANS in TO_PLOT: if __debug__: - print(_PLOT_ITEM_MESSAGE.format(PLOT.BYTES_DISTRIBUTION_LOCAL_MEANS)) + print(_plot_item_message.format(PLOT.BYTES_DISTRIBUTION_LOCAL_MEANS)) # Conta quanti numeri casuali vengono generati in base al loro valore: # `plt.hist()` lo fa in automatico, ma poiché dobbiamo fare le medie # locali abbiamo bisogno di ottenere questi conteggi “manualmente” From 6f065c58d0b8b6aa7fb06235736a8933c68e2046 Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Sat, 9 Jul 2022 15:42:15 +0200 Subject: [PATCH 003/122] Migliorato root.py Sia come performance che come formattazione --- src/root.py | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/root.py b/src/root.py index b7e6ebf..230ba89 100644 --- a/src/root.py +++ b/src/root.py @@ -1,10 +1,11 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- +# pylint: disable=no-member,used-before-assignment """Modulo che utilizza PyROOT (se installato), o uproot come backend.""" from __future__ import annotations +from typing import Any, NamedTuple, Sequence, TypeVar, get_origin, get_type_hints, overload from collections import namedtuple from pathlib import Path -from typing import Any, NamedTuple, Sequence, TypeVar, get_origin, get_type_hints, overload import os @@ -159,7 +160,7 @@ def read( # Apri l'albero `tree` dal file `file` with uproot.open(f"{file}:{tree}") as t: # Salva i “rami” come mappa - branches = {k: v for k, v in t.iteritems()} + branches = dict(t.iteritems()) for attr in attributes: # Converti l'attributo in lista ove necessario if attr in list_conv: @@ -181,10 +182,7 @@ def read( # data: ### ### ### ### ... # for i in range(len(raw_data[attributes[0]])): - vals.clear() - for attr in raw_data: - vals[attr] = raw_data[attr][i] - data.append(cls(**vals)) # type: ignore + data.append(cls(**{name: val[i] for name, val in raw_data.items()})) # type: ignore if __debug__: print(f" done (read {len(data)} items).") return data @@ -194,11 +192,19 @@ def read( __all__ = ["read"] -if __name__ == "__main__": - # Test +def test(): + """Testa il funzionamento di `read()`""" + class Event(NamedTuple): + """Rappresenta un evento.""" + + # Cosa leggere Timestamp: int Samples: list[int] data = read("src/fondo.root", "Data_R", cls=Event) assert isinstance(data[0], Event) + + +if __name__ == "__main__": + test() From c8d648494f7d5509d71e99ad5b9b83ca2a7967f0 Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Sat, 9 Jul 2022 15:42:43 +0200 Subject: [PATCH 004/122] Update pyproject.toml --- pyproject.toml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 60261de..18ab8a5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,3 +17,12 @@ aggressive = 3 [tool.pylint.main] max-line-length = 120 + +[tool.pylint."MESSAGES CONTROL"] +disable = [ + "W0123", # use of eval + "R0912", # too many branches (*/12) + "R0903", # too few public methods (*/2) + "R1704", # Redefining argument with the local name '*' + "C0103", # Variable name "*" doesn't conform to snake_case naming style +] From 0439eb64abc18be03b5508e2201b72c0949d822e Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Sat, 9 Jul 2022 17:55:06 +0200 Subject: [PATCH 005/122] Update pyproject.toml --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 18ab8a5..52a5c74 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,4 +25,5 @@ disable = [ "R0903", # too few public methods (*/2) "R1704", # Redefining argument with the local name '*' "C0103", # Variable name "*" doesn't conform to snake_case naming style + "W0702", # No exception type(s) specified ] From df34b214184483c7246ba19013e8e08be0c425c1 Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Sat, 9 Jul 2022 18:05:32 +0200 Subject: [PATCH 006/122] Update pyproject.toml --- pyproject.toml | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 52a5c74..003ff8e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,10 +20,15 @@ max-line-length = 120 [tool.pylint."MESSAGES CONTROL"] disable = [ + # Errors + # Warnings "W0123", # use of eval - "R0912", # too many branches (*/12) + "W0702", # No exception type(s) specified + # Conventions + "C0103", # Variable name "*" doesn't conform to snake_case naming style + # Refactoring "R0903", # too few public methods (*/2) + "R0912", # too many branches (*/12) + "R0914", # Too many local variables (21/15) (too-many-locals) "R1704", # Redefining argument with the local name '*' - "C0103", # Variable name "*" doesn't conform to snake_case naming style - "W0702", # No exception type(s) specified ] From 092752d73a5cb48c62736c60603ddfc9635b1a51 Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Sat, 9 Jul 2022 18:06:12 +0200 Subject: [PATCH 007/122] Svolto un po' di refactoring --- src/pi.py | 78 ++++++++++++++++++++++++++----------------------------- 1 file changed, 37 insertions(+), 41 deletions(-) diff --git a/src/pi.py b/src/pi.py index 9e25aad..69d6a0e 100755 --- a/src/pi.py +++ b/src/pi.py @@ -1,13 +1,13 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- """Utilizza il TRNG per stimare π tramite il metodo Monte Carlo.""" -from rand import TrueRandomGenerator -import matplotlib.pyplot as plt +import os +import sys +import random from math import pi as PI from pathlib import Path -import random -import sys -import os +import matplotlib.pyplot as plt +from rand import TrueRandomGenerator # Costanti @@ -27,10 +27,9 @@ def bug(default: bool, /) -> bool: BUG = sys.argv[::-1].index("--bug") < sys.argv[::-1].index("--no-bug") sys.argv = [x for x in sys.argv if x not in ("--no-bug", "--bug")] return BUG - else: - sys.argv = [x for x in sys.argv if x != "--bug"] - return True - elif "--no-bug" in sys.argv: + sys.argv = [x for x in sys.argv if x != "--bug"] + return True + if "--no-bug" in sys.argv: sys.argv = [x for x in sys.argv if x != "--no-bug"] return False return default @@ -45,8 +44,8 @@ def mode() -> int: if len(sys.argv) > 1: # Ci sono almeno 2 valori in sys.argv, quindi è stato inserito almeno un argomento try: - _mode = int(sys.argv[1]) - except BaseException: + _mode = int(eval(sys.argv[1])) + except: # Gestione errori: se il modo selezionato dalla riga di comando # non è valido, continua con la selezione interattiva pass @@ -55,11 +54,9 @@ def mode() -> int: if 0 <= _mode <= 3: # Valido return _mode - else: - # Invalido: continua con la selezione interattiva - pass + # Non valido: continua con la selezione interattiva # Selezione interattiva dell'algoritmo - print(f""" + print(""" >>> Choose an algorithm: [0] Interpret data as sequential (x, y) points. [1] Interpret data as adjacent/linked (x, y) points. @@ -70,12 +67,12 @@ def mode() -> int: _mode: int while True: try: - _mode = int(input("> ")) + _mode = int(eval(input("> "))) # Gestione errori: per terminare il programma except (KeyboardInterrupt, EOFError, OSError): sys.exit(0) # Gestione errori: input non intero (chiede nuovamente) - except BaseException: + except: print("[!] Please type in an integer (0|1|2|3)!") continue # Numero intero: ok @@ -88,8 +85,9 @@ def mode() -> int: return _mode # questo 'return' interrompe il ciclo 'while' e ritorna il valore di '_mode' -# Calcolo di π con metodo Monte Carlo e numeri casuali generati con TrueRandomGenerator +# Funzione principale def main(): + """Calcola π tramite il metodo Monte Carlo, utilizzando il nostro TRNG.""" # Stampa il titolo width = os.get_terminal_size().columns title = " Monte Carlo Method π Approximator " @@ -104,12 +102,12 @@ def main(): print(f"[i] BUG is {'en' if BUG else 'dis'}abled.") # Determina l'algoritmo da utilizzare - _mode: int = mode() # Usa la funzione sopra definita - print(f"[i] Using algorithm [{_mode}].") # Stampa l'algoritmo, per sicurezza + MODE: int = mode() # Usa la funzione sopra definita + print(f"[i] Using algorithm [{MODE}].") # Stampa l'algoritmo, per sicurezza # Inizializzazione TRG = TrueRandomGenerator(bug=BUG) # Il nostro generatore - LEN = TRG.n_random_numbers # Numero di valori casuali disponibili + LEN = TRG.n_random_numbers # Numero di valori casuali disponibili N_in: int = 0 # Numero di coordinate casuali all'interno del cerchio # noqa x_in: list[int] = [] # Lista delle coordinate x all'interno del cerchio # noqa y_in: list[int] = [] # Lista delle coordinate y all'interno del cerchio # noqa @@ -118,16 +116,19 @@ def main(): pi_array: list[float] = [] # Lista delle stime di π nel tempo pi: float = 0 # Stima di π, ricalcolata ad ogni iterazione + # Pre-calcolo dei quadrati, per ottimizzazione + squares = [x**2 for x in TRG.random_numbers] + # ------------------------- Metodo 1: base, O(n) -------------------------- - if _mode == 0: + if MODE == 0: for i in range(LEN // 2): # Generazione di coordinate con due numeri casuali sequenziali x = TRG.random_number() y = TRG.random_number() - # Se il punto di coordinate (x, y) appartiene al 1/4 di cerchio di raggio 255: + # Se il punto di coordinate (x, y) appartiene cerchio di raggio 255: if x**2 + y**2 <= K: - N_in = N_in + 1 # incrementa il numero di coordinate all'interno, + N_in += 1 # incrementa il numero di coordinate all'interno, x_in.append(x) # salva la coordinata x nella lista dedicata, y_in.append(y) # salva la coordinata y nella lista dedicata. else: # Altrimenti, le coordinate (x, y) non appartengono al cerchio: @@ -149,11 +150,11 @@ def main(): plt.show() # -------------- Metodo 2: coppie di valori adiacenti, O(n) --------------- - elif _mode == 1: + elif MODE == 1: y = TRG.random_number() # Assegnazione valore di default (pre-ciclo) - for i in range(LEN - 1): - x = y # Assegnazione a "x" del numero casuale "y" precedentemente utilizzato - y = TRG.random_number() # Creazione nuovo numero casuale + for i in range(LEN): + # L'`y` di prima diventa il nuovo `x`, mentre `y` diventa un nuovo numero casuale + x, y = y, TRG.random_number() if x**2 + y**2 <= K: # Analogo al metodo 1 N_in = N_in + 1 x_in.append(x) @@ -173,19 +174,16 @@ def main(): # Disegna l'andamento della stima di π in funzione del numero di coordinate plt.plot(pi_array) - plt.plot([PI] * (LEN - 1), linestyle="dashed") + plt.plot([PI] * LEN, linestyle="dashed") plt.show() # ------------ Metodo 3: tutte le coordinate possibili, O(n^2) ------------ - elif _mode == 2: - # Pre-calcolo dei quadrati, per ottimizzazione - nums = [b**2 for b in TRG.random_numbers] - + elif MODE == 2: # `enumerate([a, b, c]) -> [(0, a), (1, b), (2, c)]` # Questo ciclo scorre gli elementi (`x`) del vettore `nums`, # associando a ciascuno il proprio indice (`i`) - for i, x in enumerate(nums): - for y in nums: + for i, x in enumerate(squares): + for y in squares: if x + y <= K: # Analogo al metodo 1 N_in += 1 # Stima di π @@ -231,14 +229,12 @@ def main(): plt.show() # --- Stampa la stima finale di π --- - # Converti i numeri in stringhe, rimuovendo il punto decimale (non conta come cifra uguale/diversa) - spi = str(pi).replace(".", "") - SPI = str(PI).replace(".", "") - L = len(SPI) # Per velocizzare i calcoli + # Per velocizzare i calcoli + L = len(str(pi)) - 1 # -1 perché ignoriamo il `.` # Conta quante cifre sono corrette i = 0 - for i in range(L): - if SPI[i] != spi[i]: + for i, (spi, sPI) in enumerate(zip(str(pi).replace(".", ""), str(PI).replace(".", ""))): + if sPI != spi: break # Stampa i valori in un riquadro print(f"""\ From 8d672189fa5a131632f2c09a44aac10f1d5793a3 Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Sat, 9 Jul 2022 18:14:49 +0200 Subject: [PATCH 008/122] Update pi.py --- src/pi.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pi.py b/src/pi.py index 69d6a0e..9c14194 100755 --- a/src/pi.py +++ b/src/pi.py @@ -230,7 +230,7 @@ def main(): # --- Stampa la stima finale di π --- # Per velocizzare i calcoli - L = len(str(pi)) - 1 # -1 perché ignoriamo il `.` + L = len(str(PI)) - 1 # -1 perché ignoriamo il `.` # Conta quante cifre sono corrette i = 0 for i, (spi, sPI) in enumerate(zip(str(pi).replace(".", ""), str(PI).replace(".", ""))): @@ -239,8 +239,8 @@ def main(): # Stampa i valori in un riquadro print(f"""\ ,{'-'*(L+7)}, -| π ≈ {pi} | -| π = {PI} | +| π ≈ {pi:01.{L-1}f} | +| π = {PI:01.{L-1}f} | | {'+' if i else '^'}-{'+'*(i-1) if i else ''}{'^' if i else ''}{'~'*(L-i-1)} | '{'-'*(L+7)}'\ """) From 0d5adcc46cfc75d4af70c92b9dcc6196393a96c1 Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Sat, 9 Jul 2022 20:06:07 +0200 Subject: [PATCH 009/122] Update pyproject.toml --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 003ff8e..65d1398 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,6 +24,7 @@ disable = [ # Warnings "W0123", # use of eval "W0702", # No exception type(s) specified + "W1203", # Use %s formatting in logging functions # Conventions "C0103", # Variable name "*" doesn't conform to snake_case naming style # Refactoring From 41e83ab119ac53a8219b54e3471263768c550542 Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Sun, 10 Jul 2022 11:39:36 +0200 Subject: [PATCH 010/122] Update pyproject.toml --- pyproject.toml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 65d1398..03f83f6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,7 @@ ignore = [ "E221", # multiple spaces before operator "E241", # multiple spaces after ':' "E704", # multiple statements on one line (def) - "W503" # line break before binary operator + "W503", # line break before binary operator ] in-place = true recursive = true @@ -30,6 +30,7 @@ disable = [ # Refactoring "R0903", # too few public methods (*/2) "R0912", # too many branches (*/12) - "R0914", # Too many local variables (21/15) (too-many-locals) + "R0914", # Too many local variables (*/15) + "R0915", # Too many statements (*/50) "R1704", # Redefining argument with the local name '*' ] From 93379b3bc04bd75b4c4a1c1e04927404f60ca6ec Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Sun, 10 Jul 2022 12:37:10 +0200 Subject: [PATCH 011/122] Prima bozza --- src/log.py | 82 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 src/log.py diff --git a/src/log.py b/src/log.py new file mode 100644 index 0000000..baa7225 --- /dev/null +++ b/src/log.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python3 +# -*- coding : utf-8 -*- +"""Utility module: logging support.""" +from __future__ import annotations +import logging +import os +import sys + + +TIMESTAMP: bool = True + + +ICONS = { + logging.NOTSET: "[ ]", + logging.DEBUG: "[#]", + logging.INFO: "[i]", + logging.WARNING: "[!]", + logging.ERROR: "[x]", + logging.CRITICAL: "{x}", +} + + +class ConsoleFormatter(logging.Formatter): + """A customized logging formatter.""" + + def format(self, record: logging.LogRecord) -> str: + # Make the icon available + setattr(record, "x", ICONS[record.levelno]) + if TIMESTAMP: + width = os.get_terminal_size().columns + msg = super().format(record) + asctime = self.formatTime(record, self.datefmt) + rows = msg.split("\n") + first = rows[0] + if len(first) + 1 + len(asctime) <= width: + first += f"{' '*(width-len(first)-len(asctime))}{asctime}" + return "\n".join([first, *rows[1:]]) + return super().format(record) + + +def get_levels() -> list[int]: + """Get the installed levels, as a list, in severity ascending order.""" + name2level: dict[str, int] | None + if sys.version_info >= (3, 11): + name2level = logging.getLevelNamesMapping() # pylint: disable=no-member + name2level = getattr(logging, "_nameToLevel", None) + return sorted(set( + name2level.values() if name2level + else map(logging.getLevelName, 'CRITICAL ERROR WARNING INFO DEBUG NOTSET'.split(" ")) + )) + + +def logging_setup() -> None: + """Setup `logging` based on command-line flags.""" + DEFAULT_LEVEL = logging.WARNING + levels = get_levels() + # Controlla le varie flags che vengono passate al programma + quietness = sys.argv.count("-q") - sys.argv.count("-v") + levels.index(DEFAULT_LEVEL) + sys.argv = [x for x in sys.argv if x not in ("-v", "-q")] + for arg in sys.argv.copy(): + if arg[0] == "-" and arg[1:] and set(arg[1:]) <= {"q", "v"}: + quietness += arg.count("q") - arg.count("v") + sys.argv.remove(arg) + # Determina il livello a partire dalla `silenziosità` richiesta + quietness = max(0, min(len(levels) - 1, quietness)) + level = levels[quietness] + # Configurazione + ch = logging.StreamHandler() + ch.setFormatter(ConsoleFormatter("{x} {message}", style="{", datefmt="[%Y-%m-%d %H:%M:%S]")) + ch.setLevel(logging.NOTSET) + logging.root.addHandler(ch) + logging.root.setLevel(level) + + +if __name__ == "__main__": + logging_setup() + logger = logging.getLogger(__name__) + logger.critical("Message") + logger.error("Message") + logger.warning("Message") + logger.info("Message") + logger.debug("Message") From dd8091084401942a9e0039c16454966f4bcfd24f Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Sun, 10 Jul 2022 12:38:55 +0200 Subject: [PATCH 012/122] =?UTF-8?q?"=C2=A0"=20->=20"=20"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/log.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/log.py b/src/log.py index baa7225..b132fc2 100644 --- a/src/log.py +++ b/src/log.py @@ -54,14 +54,14 @@ def logging_setup() -> None: """Setup `logging` based on command-line flags.""" DEFAULT_LEVEL = logging.WARNING levels = get_levels() - # Controlla le varie flags che vengono passate al programma + # Controlla le varie flags che vengono passate al programma quietness = sys.argv.count("-q") - sys.argv.count("-v") + levels.index(DEFAULT_LEVEL) sys.argv = [x for x in sys.argv if x not in ("-v", "-q")] for arg in sys.argv.copy(): if arg[0] == "-" and arg[1:] and set(arg[1:]) <= {"q", "v"}: quietness += arg.count("q") - arg.count("v") sys.argv.remove(arg) - # Determina il livello a partire dalla `silenziosità` richiesta + # Determina il livello a partire dalla `silenziosità` richiesta quietness = max(0, min(len(levels) - 1, quietness)) level = levels[quietness] # Configurazione From dc24eb2380d7e216cdc8cbe2eb699c7f19bd3cae Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Sun, 10 Jul 2022 12:39:55 +0200 Subject: [PATCH 013/122] Update log.py --- src/log.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/log.py b/src/log.py index b132fc2..0756517 100644 --- a/src/log.py +++ b/src/log.py @@ -9,7 +9,6 @@ TIMESTAMP: bool = True - ICONS = { logging.NOTSET: "[ ]", logging.DEBUG: "[#]", @@ -19,6 +18,8 @@ logging.CRITICAL: "{x}", } +DEFAULT_LEVEL = logging.WARNING + class ConsoleFormatter(logging.Formatter): """A customized logging formatter.""" @@ -52,7 +53,6 @@ def get_levels() -> list[int]: def logging_setup() -> None: """Setup `logging` based on command-line flags.""" - DEFAULT_LEVEL = logging.WARNING levels = get_levels() # Controlla le varie flags che vengono passate al programma quietness = sys.argv.count("-q") - sys.argv.count("-v") + levels.index(DEFAULT_LEVEL) From c4e91b34282221d2931348575cb97d82065b2bb0 Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Sun, 10 Jul 2022 12:45:28 +0200 Subject: [PATCH 014/122] Update log.py --- src/log.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/log.py b/src/log.py index 0756517..68409a6 100644 --- a/src/log.py +++ b/src/log.py @@ -28,12 +28,13 @@ def format(self, record: logging.LogRecord) -> str: # Make the icon available setattr(record, "x", ICONS[record.levelno]) if TIMESTAMP: - width = os.get_terminal_size().columns - msg = super().format(record) - asctime = self.formatTime(record, self.datefmt) - rows = msg.split("\n") + # Right-align the timestamp + width = os.get_terminal_size().columns # Terminal width + asctime = self.formatTime(record, self.datefmt) # Time string + rows = super().format(record).split("\n") first = rows[0] if len(first) + 1 + len(asctime) <= width: + # Don't add the timestamp if the row is too long first += f"{' '*(width-len(first)-len(asctime))}{asctime}" return "\n".join([first, *rows[1:]]) return super().format(record) From 87d6a5de0159175c82d82c1bcde2bc5132a69909 Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Sun, 10 Jul 2022 12:51:34 +0200 Subject: [PATCH 015/122] Update log.py --- src/log.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/log.py b/src/log.py index 68409a6..5a42b6c 100644 --- a/src/log.py +++ b/src/log.py @@ -19,6 +19,7 @@ } DEFAULT_LEVEL = logging.WARNING +_setup_done: bool = False class ConsoleFormatter(logging.Formatter): @@ -28,7 +29,7 @@ def format(self, record: logging.LogRecord) -> str: # Make the icon available setattr(record, "x", ICONS[record.levelno]) if TIMESTAMP: - # Right-align the timestamp + # Right-align the timestamp width = os.get_terminal_size().columns # Terminal width asctime = self.formatTime(record, self.datefmt) # Time string rows = super().format(record).split("\n") @@ -54,6 +55,9 @@ def get_levels() -> list[int]: def logging_setup() -> None: """Setup `logging` based on command-line flags.""" + global _setup_done # pylint: disable=global-statement + if _setup_done: + return levels = get_levels() # Controlla le varie flags che vengono passate al programma quietness = sys.argv.count("-q") - sys.argv.count("-v") + levels.index(DEFAULT_LEVEL) @@ -71,6 +75,7 @@ def logging_setup() -> None: ch.setLevel(logging.NOTSET) logging.root.addHandler(ch) logging.root.setLevel(level) + _setup_done = True if __name__ == "__main__": From 81e28eed7f31edbe07682cd8ac71e37a9fc92153 Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Sun, 10 Jul 2022 13:51:10 +0200 Subject: [PATCH 016/122] =?UTF-8?q?Setup=20automatico:=20per=20attivarlo,?= =?UTF-8?q?=20basta=20importare=20i=20simboli=20da=20l=C3=AC=20e=20non=20d?= =?UTF-8?q?a=20`logger`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/log.py | 35 +++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/src/log.py b/src/log.py index 5a42b6c..e346329 100644 --- a/src/log.py +++ b/src/log.py @@ -2,23 +2,34 @@ # -*- coding : utf-8 -*- """Utility module: logging support.""" from __future__ import annotations +from logging import NOTSET, DEBUG, INFO, WARNING, ERROR, CRITICAL, getLogger import logging import os import sys +__all__ = [ + # Export from `logging` + "NOTSET", "DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL", + "getLogger", + # Defined here + "TIMESTAMP", "DEFAULT_LEVEL", "ICONS", + "cli_configure", +] + + TIMESTAMP: bool = True ICONS = { - logging.NOTSET: "[ ]", - logging.DEBUG: "[#]", - logging.INFO: "[i]", - logging.WARNING: "[!]", - logging.ERROR: "[x]", - logging.CRITICAL: "{x}", + NOTSET: "[ ]", + DEBUG: "[#]", + INFO: "[i]", + WARNING: "[!]", + ERROR: "[x]", + CRITICAL: "{x}", } -DEFAULT_LEVEL = logging.WARNING +DEFAULT_LEVEL = WARNING _setup_done: bool = False @@ -53,7 +64,7 @@ def get_levels() -> list[int]: )) -def logging_setup() -> None: +def cli_configure() -> None: """Setup `logging` based on command-line flags.""" global _setup_done # pylint: disable=global-statement if _setup_done: @@ -78,9 +89,13 @@ def logging_setup() -> None: _setup_done = True +if not eval(os.environ.get("NO_AUTO_LOGGING_CONFIG", "0") or "0"): + cli_configure() + + if __name__ == "__main__": - logging_setup() - logger = logging.getLogger(__name__) + cli_configure() + logger = getLogger(__name__) logger.critical("Message") logger.error("Message") logger.warning("Message") From a1d44be896b092943b99c0617a0fe2ccf9a16032 Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Sun, 10 Jul 2022 13:52:34 +0200 Subject: [PATCH 017/122] Update root.py --- src/root.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/root.py b/src/root.py index 230ba89..3f0238c 100644 --- a/src/root.py +++ b/src/root.py @@ -7,6 +7,7 @@ from collections import namedtuple from pathlib import Path import os +from log import getLogger # ----- 1. Importa la libreria corretta ------ # @@ -32,8 +33,7 @@ if __debug__: - print(f"[i] ROOT backend: {'PyROOT' if ROOT else 'uproot'}") - + getLogger(__name__).info(f"ROOT backend: {'PyROOT' if ROOT else 'uproot'}") # ----- 2. Definisci la funzione di lettura ------ # From 1836720cd68e946840f8037d33e3806992462eed Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Sun, 10 Jul 2022 14:33:51 +0200 Subject: [PATCH 018/122] Aggiunto il supporto per -O MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `python -O file.py` viene trattato più o meno come `python file.py -q` (o, alternativamente, `python file.py` viene trattato più o meno come `python -O file.py -v`) --- src/log.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/log.py b/src/log.py index e346329..0d363a1 100644 --- a/src/log.py +++ b/src/log.py @@ -29,7 +29,7 @@ CRITICAL: "{x}", } -DEFAULT_LEVEL = WARNING +DEFAULT_LEVEL = INFO if __debug__ else WARNING # '-O' works like a '-q' _setup_done: bool = False From fcf1f5fee76b33aef308d2af5c189b36411e1c90 Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Sun, 10 Jul 2022 14:41:40 +0200 Subject: [PATCH 019/122] Migliorati alcuni commenti --- src/spettro.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/spettro.py b/src/spettro.py index 3303994..cee4689 100755 --- a/src/spettro.py +++ b/src/spettro.py @@ -53,14 +53,14 @@ def aree( f"{max_area=}, samples range = [{min_samples}, {max_samples}])" ) - aree: list[float] = [] + aree_calcolate: list[float] = [] for event in events: # Se necessario, calcola la BASELINE per questo evento if BASELINE_CALC_MODE == 1: BASELINE = mean(event.Samples[:BASELINE_CALC_N]) assert BASELINE is not None - # Estrazione dei samples dell'evento tra "min_samples" e "max_samples" + # Estrazione dei samples dell'evento tra `min_samples` e `max_samples` samples = event.Samples[min_samples:max_samples] # Calcolo dell'area: @@ -69,12 +69,12 @@ def aree( # Se non sono stati impostati limiti all'area o area < del limite ... if max_area is None or temp_area < max_area: - # ... salva l'area nel vettore "Aree" - aree.append(temp_area) + # ... salva l'area nel vettore `aree_calcolate` + aree_calcolate.append(temp_area) if __debug__: print(" done.") - return aree + return aree_calcolate # --- Programma principale ---- @@ -144,6 +144,6 @@ def calibrate(x): plt.show() -# Chiama "main()" quando il programma viene eseguito direttamente +# Chiama `main()` quando il programma viene eseguito direttamente if __name__ == "__main__": main() From 4157c2310bfa0af4672228c08277f49ad0e75f03 Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Sun, 10 Jul 2022 17:31:39 +0200 Subject: [PATCH 020/122] Aggiunto un supporto maggiore per la parte destra --- src/log.py | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/log.py b/src/log.py index 0d363a1..b7fbecd 100644 --- a/src/log.py +++ b/src/log.py @@ -36,20 +36,25 @@ class ConsoleFormatter(logging.Formatter): """A customized logging formatter.""" + def __init__(self, lfmt: str, rfmt: str, *args, **kwargs) -> None: + """Left and right formatter strings.""" + lfmt, rfmt = lfmt.replace('\0', ''), rfmt.replace('\0', '') + super().__init__(f"{lfmt}\0{rfmt}", *args, **kwargs) + def format(self, record: logging.LogRecord) -> str: # Make the icon available setattr(record, "x", ICONS[record.levelno]) - if TIMESTAMP: - # Right-align the timestamp + left, right = super().format(record).split("\0") + if right: + # Right-align text width = os.get_terminal_size().columns # Terminal width - asctime = self.formatTime(record, self.datefmt) # Time string - rows = super().format(record).split("\n") + rows = left.split("\n") first = rows[0] - if len(first) + 1 + len(asctime) <= width: - # Don't add the timestamp if the row is too long - first += f"{' '*(width-len(first)-len(asctime))}{asctime}" + if len(first) + 1 + len(right) <= width: + # Don't add the right text if the left one is too long + first += f"{' '*(width-len(first)-len(right))}{right}" return "\n".join([first, *rows[1:]]) - return super().format(record) + return left def get_levels() -> list[int]: @@ -82,7 +87,7 @@ def cli_configure() -> None: level = levels[quietness] # Configurazione ch = logging.StreamHandler() - ch.setFormatter(ConsoleFormatter("{x} {message}", style="{", datefmt="[%Y-%m-%d %H:%M:%S]")) + ch.setFormatter(ConsoleFormatter("{x} {message}", "[{asctime}]", style="{", datefmt="%Y-%m-%d %H:%M:%S")) ch.setLevel(logging.NOTSET) logging.root.addHandler(ch) logging.root.setLevel(level) From 0278650524eaaa4a6eed8dbd2e3cd2c9cea98d50 Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Sun, 10 Jul 2022 17:32:08 +0200 Subject: [PATCH 021/122] Update log.py --- src/log.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/log.py b/src/log.py index b7fbecd..e4a1d4c 100644 --- a/src/log.py +++ b/src/log.py @@ -62,7 +62,8 @@ def get_levels() -> list[int]: name2level: dict[str, int] | None if sys.version_info >= (3, 11): name2level = logging.getLevelNamesMapping() # pylint: disable=no-member - name2level = getattr(logging, "_nameToLevel", None) + else: + name2level = getattr(logging, "_nameToLevel", None) return sorted(set( name2level.values() if name2level else map(logging.getLevelName, 'CRITICAL ERROR WARNING INFO DEBUG NOTSET'.split(" ")) From 96983bc4ad1e148301a1b6178c92fe6236fadbe4 Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Sun, 10 Jul 2022 19:53:29 +0200 Subject: [PATCH 022/122] Update log.py --- src/log.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/log.py b/src/log.py index e4a1d4c..8acadb8 100644 --- a/src/log.py +++ b/src/log.py @@ -89,9 +89,9 @@ def cli_configure() -> None: # Configurazione ch = logging.StreamHandler() ch.setFormatter(ConsoleFormatter("{x} {message}", "[{asctime}]", style="{", datefmt="%Y-%m-%d %H:%M:%S")) - ch.setLevel(logging.NOTSET) logging.root.addHandler(ch) logging.root.setLevel(level) + ch.setLevel(NOTSET) _setup_done = True From e984595bbee28416f24394fbd031ea4b4349dda3 Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Sun, 10 Jul 2022 19:53:59 +0200 Subject: [PATCH 023/122] Update log.py --- src/log.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/log.py b/src/log.py index 8acadb8..43777a9 100644 --- a/src/log.py +++ b/src/log.py @@ -89,9 +89,10 @@ def cli_configure() -> None: # Configurazione ch = logging.StreamHandler() ch.setFormatter(ConsoleFormatter("{x} {message}", "[{asctime}]", style="{", datefmt="%Y-%m-%d %H:%M:%S")) - logging.root.addHandler(ch) - logging.root.setLevel(level) ch.setLevel(NOTSET) + root = getLogger() + root.addHandler(ch) + root.setLevel(level) _setup_done = True From ec7e3dd2c764590c809aac9ef3f81f85762614b8 Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Sun, 10 Jul 2022 21:49:35 +0200 Subject: [PATCH 024/122] Aggiunto il supporto per le `task`s --- src/log.py | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 64 insertions(+), 4 deletions(-) diff --git a/src/log.py b/src/log.py index 43777a9..eee2316 100644 --- a/src/log.py +++ b/src/log.py @@ -2,7 +2,10 @@ # -*- coding : utf-8 -*- """Utility module: logging support.""" from __future__ import annotations -from logging import NOTSET, DEBUG, INFO, WARNING, ERROR, CRITICAL, getLogger +from contextlib import contextmanager +from typing import Iterator, cast +from logging import NOTSET, DEBUG, INFO, WARNING, ERROR, CRITICAL, getLogger as _getLogger +import time import logging import os import sys @@ -18,6 +21,11 @@ ] +def getLogger(name: str = "") -> Logger: + """Get the logger associated with this given name.""" + return cast(Logger, _getLogger(name)) + + TIMESTAMP: bool = True ICONS = { @@ -42,11 +50,18 @@ def __init__(self, lfmt: str, rfmt: str, *args, **kwargs) -> None: super().__init__(f"{lfmt}\0{rfmt}", *args, **kwargs) def format(self, record: logging.LogRecord) -> str: - # Make the icon available + # Make the `icon` available setattr(record, "x", ICONS[record.levelno]) + # Fix `took` missing + if not hasattr(record, "took"): + setattr(record, "took", "") + # Apply indent + record.msg = " " * getattr(record, "indent", 0) * 4 + record.msg + delattr(record, "indent") + # Format and right-align text left, right = super().format(record).split("\0") if right: - # Right-align text + # Right-align text only if needed width = os.get_terminal_size().columns # Terminal width rows = left.split("\n") first = rows[0] @@ -57,6 +72,47 @@ def format(self, record: logging.LogRecord) -> str: return left +class Logger(logging.Logger): + """An enhanced logger.""" + _indent: int + done_extra: str + + def __init__(self, name: str, level: int | str = NOTSET) -> None: + super().__init__(name, level) + self._indent = 0 + self.done_extra = "" + + def makeRecord(self, *args, **kwargs) -> logging.LogRecord: + record = super().makeRecord(*args, **kwargs) + setattr(record, "indent", self._indent + getattr(record, "indent", 0)) + return record + + @contextmanager + def task(self, msg: str) -> Iterator[Logger]: + """Log the fact we're doing something.""" + self.info(f"--> {msg}") + task = self.getChild("task") + task._indent = self._indent + 1 + t0 = time.time_ns() + try: + yield task + finally: + t1 = time.time_ns() + dt = t1 - t0 + # Converti il ∆t in un formato utile + dts: str + if dt < 1_000: + dts = f"{dt} ns" + elif dt < 1_000_000: + dts = f"{dt/1_000} µs" + elif dt < 1_000_000_000: + dts = f"{dt/1_000_000} ms" + else: + dts = time.strftime("%H:%M:%S", time.gmtime(dt/1_000_000_000)) + # Stampa il messaggio + task.info(f"done{f' ({task.done_extra}' if task.done_extra else ''}.", extra=dict(took=f"took {dts} ")) + + def get_levels() -> list[int]: """Get the installed levels, as a list, in severity ascending order.""" name2level: dict[str, int] | None @@ -88,9 +144,11 @@ def cli_configure() -> None: level = levels[quietness] # Configurazione ch = logging.StreamHandler() - ch.setFormatter(ConsoleFormatter("{x} {message}", "[{asctime}]", style="{", datefmt="%Y-%m-%d %H:%M:%S")) + ch.setFormatter(ConsoleFormatter("{x} {message}", "{took}[{asctime}]", style="{", datefmt="%Y-%m-%d %H:%M:%S")) ch.setLevel(NOTSET) + logging.setLoggerClass(Logger) root = getLogger() + root.__class__ = Logger root.addHandler(ch) root.setLevel(level) _setup_done = True @@ -108,3 +166,5 @@ def cli_configure() -> None: logger.warning("Message") logger.info("Message") logger.debug("Message") + with logger.task("Running some serious computation...") as task: + time.sleep(1) From cf3a1a46d94fc830ee484deba2112d888ee4bf17 Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Sun, 10 Jul 2022 21:52:14 +0200 Subject: [PATCH 025/122] Fixed parens --- src/log.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/log.py b/src/log.py index eee2316..d72060b 100644 --- a/src/log.py +++ b/src/log.py @@ -92,7 +92,7 @@ def task(self, msg: str) -> Iterator[Logger]: """Log the fact we're doing something.""" self.info(f"--> {msg}") task = self.getChild("task") - task._indent = self._indent + 1 + task._indent = self._indent + 1 # pylint: disable=protected-access t0 = time.time_ns() try: yield task @@ -110,7 +110,7 @@ def task(self, msg: str) -> Iterator[Logger]: else: dts = time.strftime("%H:%M:%S", time.gmtime(dt/1_000_000_000)) # Stampa il messaggio - task.info(f"done{f' ({task.done_extra}' if task.done_extra else ''}.", extra=dict(took=f"took {dts} ")) + task.info(f"done{f' ({task.done_extra})' if task.done_extra else ''}.", extra=dict(took=f"took {dts} ")) def get_levels() -> list[int]: @@ -166,5 +166,6 @@ def cli_configure() -> None: logger.warning("Message") logger.info("Message") logger.debug("Message") - with logger.task("Running some serious computation...") as task: + with logger.task("Running some serious computation...") as computation: time.sleep(1) + computation.done_extra = "read 10 items." From 1699b830bb11cddb79d97de3d5d48574310e23fe Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Sun, 10 Jul 2022 21:52:33 +0200 Subject: [PATCH 026/122] Update log.py --- src/log.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/log.py b/src/log.py index d72060b..77145d0 100644 --- a/src/log.py +++ b/src/log.py @@ -168,4 +168,4 @@ def cli_configure() -> None: logger.debug("Message") with logger.task("Running some serious computation...") as computation: time.sleep(1) - computation.done_extra = "read 10 items." + computation.done_extra = "wasn't so useful" From b4696c2ca65dc7297196c8e03f460cae85bb4f74 Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Sun, 10 Jul 2022 21:54:23 +0200 Subject: [PATCH 027/122] Update log.py --- src/log.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/log.py b/src/log.py index 77145d0..96c25ef 100644 --- a/src/log.py +++ b/src/log.py @@ -161,11 +161,11 @@ def cli_configure() -> None: if __name__ == "__main__": cli_configure() logger = getLogger(__name__) - logger.critical("Message") - logger.error("Message") - logger.warning("Message") - logger.info("Message") - logger.debug("Message") + logger.critical("Critical") + logger.error("Error") + logger.warning("Warning") + logger.info("Info") + logger.debug("Debug") with logger.task("Running some serious computation...") as computation: time.sleep(1) computation.done_extra = "wasn't so useful" From 04940224068593a4e90e2e25760f5ef3f46fe668 Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Sun, 10 Jul 2022 23:11:55 +0200 Subject: [PATCH 028/122] Esportati alcuni simboli --- src/log.py | 46 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/src/log.py b/src/log.py index 96c25ef..6cd905f 100644 --- a/src/log.py +++ b/src/log.py @@ -3,8 +3,9 @@ """Utility module: logging support.""" from __future__ import annotations from contextlib import contextmanager -from typing import Iterator, cast +from typing import Any, ContextManager, Iterator, cast from logging import NOTSET, DEBUG, INFO, WARNING, ERROR, CRITICAL, getLogger as _getLogger +import inspect import time import logging import os @@ -26,6 +27,14 @@ def getLogger(name: str = "") -> Logger: return cast(Logger, _getLogger(name)) +def moduleLogger(name: str | None = None, /, *, depth: int = 0) -> Logger: + """Get the logger associated with the module that's calling this function.""" + return getLogger( + inspect.stack()[1 + depth].frame.f_globals["__name__"] + if name is None else name + ) + + TIMESTAMP: bool = True ICONS = { @@ -154,6 +163,41 @@ def cli_configure() -> None: _setup_done = True +def task(msg: str) -> ContextManager[Logger]: + """Log an debug message.""" + return moduleLogger(depth=1).task(msg) + + +def debug(msg: Any, *args: Any, extra: dict[str, Any], **kwargs) -> None: + """Log an debug message.""" + moduleLogger(depth=1).debug(msg, *args, extra=extra, **kwargs) + + +def info(msg: Any, *args: Any, extra: dict[str, Any], **kwargs) -> None: + """Log an information.""" + moduleLogger(depth=1).info(msg, *args, extra=extra, **kwargs) + + +def warning(msg: Any, *args: Any, extra: dict[str, Any], **kwargs) -> None: + """Log a warning.""" + moduleLogger(depth=1).warning(msg, *args, extra=extra, **kwargs) + + +def error(msg: Any, *args: Any, extra: dict[str, Any], **kwargs) -> None: + """Log an error.""" + moduleLogger(depth=1).error(msg, *args, extra=extra, **kwargs) + + +def critical(msg: Any, *args: Any, extra: dict[str, Any], **kwargs) -> None: + """Log an error that causes the program's termination.""" + moduleLogger(depth=1).critical(msg, *args, extra=extra, **kwargs) + + +def exception(msg: Any, *args: Any, extra: dict[str, Any], **kwargs) -> None: + """Log an exception.""" + moduleLogger(depth=1).exception(msg, *args, extra=extra, **kwargs) + + if not eval(os.environ.get("NO_AUTO_LOGGING_CONFIG", "0") or "0"): cli_configure() From 290372014e4760d32f4db46d3070930aee6746ef Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Sun, 10 Jul 2022 23:13:46 +0200 Subject: [PATCH 029/122] Update log.py --- src/log.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/log.py b/src/log.py index 6cd905f..4bca2ea 100644 --- a/src/log.py +++ b/src/log.py @@ -100,11 +100,11 @@ def makeRecord(self, *args, **kwargs) -> logging.LogRecord: def task(self, msg: str) -> Iterator[Logger]: """Log the fact we're doing something.""" self.info(f"--> {msg}") - task = self.getChild("task") - task._indent = self._indent + 1 # pylint: disable=protected-access + tsk = self.getChild("task") + tsk._indent = self._indent + 1 # pylint: disable=protected-access t0 = time.time_ns() try: - yield task + yield tsk finally: t1 = time.time_ns() dt = t1 - t0 @@ -119,7 +119,7 @@ def task(self, msg: str) -> Iterator[Logger]: else: dts = time.strftime("%H:%M:%S", time.gmtime(dt/1_000_000_000)) # Stampa il messaggio - task.info(f"done{f' ({task.done_extra})' if task.done_extra else ''}.", extra=dict(took=f"took {dts} ")) + tsk.info(f"done{f' ({tsk.done_extra})' if tsk.done_extra else ''}.", extra=dict(took=f"took {dts} ")) def get_levels() -> list[int]: From 301ff11013585900d24df3bbc7eaea4fe7c44f59 Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Sun, 10 Jul 2022 23:16:13 +0200 Subject: [PATCH 030/122] Update log.py --- src/log.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/log.py b/src/log.py index 4bca2ea..1db0f9b 100644 --- a/src/log.py +++ b/src/log.py @@ -168,32 +168,32 @@ def task(msg: str) -> ContextManager[Logger]: return moduleLogger(depth=1).task(msg) -def debug(msg: Any, *args: Any, extra: dict[str, Any], **kwargs) -> None: +def debug(msg: Any, *args: Any, extra: dict[str, Any] | None = None, **kwargs) -> None: """Log an debug message.""" moduleLogger(depth=1).debug(msg, *args, extra=extra, **kwargs) -def info(msg: Any, *args: Any, extra: dict[str, Any], **kwargs) -> None: +def info(msg: Any, *args: Any, extra: dict[str, Any] | None = None, **kwargs) -> None: """Log an information.""" moduleLogger(depth=1).info(msg, *args, extra=extra, **kwargs) -def warning(msg: Any, *args: Any, extra: dict[str, Any], **kwargs) -> None: +def warning(msg: Any, *args: Any, extra: dict[str, Any] | None = None, **kwargs) -> None: """Log a warning.""" moduleLogger(depth=1).warning(msg, *args, extra=extra, **kwargs) -def error(msg: Any, *args: Any, extra: dict[str, Any], **kwargs) -> None: +def error(msg: Any, *args: Any, extra: dict[str, Any] | None = None, **kwargs) -> None: """Log an error.""" moduleLogger(depth=1).error(msg, *args, extra=extra, **kwargs) -def critical(msg: Any, *args: Any, extra: dict[str, Any], **kwargs) -> None: +def critical(msg: Any, *args: Any, extra: dict[str, Any] | None = None, **kwargs) -> None: """Log an error that causes the program's termination.""" moduleLogger(depth=1).critical(msg, *args, extra=extra, **kwargs) -def exception(msg: Any, *args: Any, extra: dict[str, Any], **kwargs) -> None: +def exception(msg: Any, *args: Any, extra: dict[str, Any] | None = None, **kwargs) -> None: """Log an exception.""" moduleLogger(depth=1).exception(msg, *args, extra=extra, **kwargs) From 6a7e6557cb891c0e648602ba8cbb0359cfc91fe5 Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Sun, 10 Jul 2022 23:32:58 +0200 Subject: [PATCH 031/122] Update root.py --- src/root.py | 120 ++++++++++++++++++++++++++-------------------------- 1 file changed, 59 insertions(+), 61 deletions(-) diff --git a/src/root.py b/src/root.py index 3f0238c..0707eaa 100644 --- a/src/root.py +++ b/src/root.py @@ -7,7 +7,7 @@ from collections import namedtuple from pathlib import Path import os -from log import getLogger +from log import info, task # ----- 1. Importa la libreria corretta ------ # @@ -32,8 +32,7 @@ ROOT = True -if __debug__: - getLogger(__name__).info(f"ROOT backend: {'PyROOT' if ROOT else 'uproot'}") +info(f"ROOT backend: {'PyROOT' if ROOT else 'uproot'}") # ----- 2. Definisci la funzione di lettura ------ # @@ -128,63 +127,62 @@ def read( # In `vals` vengono salvati i parametri da passare alla classe nella costruzione dell'oggetto vals: dict[str, Any] = {} - if __debug__: - print(f"--> Reading tree {tree!r} from file {file!r}") - - if ROOT: # --- PyROOT --- - # Termina il loop degli eventi di PyROOT, in modo che non interferisca con matplotlib - PyROOT.keeppolling = 0 # type: ignore - # Apri il file - f = PyROOT.TFile(file) # type: ignore - # Leggi l'albero - t = f.Get(tree) - # Leggi e salva i dati di interesse - for x in t: - vals.clear() # Svuota i parametri - for attr in attributes: - # Converti l'attributo in lista ove necessario - if attr in list_conv: - vals[attr] = [*getattr(x, attr)] - else: - vals[attr] = getattr(x, attr) - # Crea l'oggetto e aggiungilo a `data` - data.append(cls(**vals)) # type: ignore - # Chiudi il file - f.Close() - - else: # --- uproot --- - - # Mappa vuota per i dati grezzi - # (associa al nome dell'attributo la lista dei valori, ancora da combinare negli oggetti) - raw_data: dict[str, Any] = {} - # Apri l'albero `tree` dal file `file` - with uproot.open(f"{file}:{tree}") as t: - # Salva i “rami” come mappa - branches = dict(t.iteritems()) - for attr in attributes: - # Converti l'attributo in lista ove necessario - if attr in list_conv: - raw_data[attr] = list(map(list, branches[attr].array())) - else: - raw_data[attr] = list(branches[attr].array()) - - # Converti i dati grezzi in lista di oggetti: - # scorri gli indici e associa gli attributi corrispondenti, creando l'oggetto - # - # i: 0 1 2 3 ... - # | | | | - # V V V V - # attr0: x00 x01 x02 x03 ... ¯| - # attr1: x10 x11 x12 x13 ... |--> raw_data - # attr2: x20 x21 x22 x23 ... _| - # | | | | - # V V V V - # data: ### ### ### ### ... - # - for i in range(len(raw_data[attributes[0]])): - data.append(cls(**{name: val[i] for name, val in raw_data.items()})) # type: ignore - if __debug__: - print(f" done (read {len(data)} items).") + with task(f"Reading tree {tree!r} from file {file!r}...") as _task: + + if ROOT: # --- PyROOT --- + # Termina il loop degli eventi di PyROOT, in modo che non interferisca con matplotlib + PyROOT.keeppolling = 0 # type: ignore + # Apri il file + f = PyROOT.TFile(file) # type: ignore + # Leggi l'albero + t = f.Get(tree) + # Leggi e salva i dati di interesse + for x in t: + vals.clear() # Svuota i parametri + for attr in attributes: + # Converti l'attributo in lista ove necessario + if attr in list_conv: + vals[attr] = [*getattr(x, attr)] + else: + vals[attr] = getattr(x, attr) + # Crea l'oggetto e aggiungilo a `data` + data.append(cls(**vals)) # type: ignore + # Chiudi il file + f.Close() + + else: # --- uproot --- + + # Mappa vuota per i dati grezzi + # (associa al nome dell'attributo la lista dei valori, ancora da combinare negli oggetti) + raw_data: dict[str, Any] = {} + # Apri l'albero `tree` dal file `file` + with uproot.open(f"{file}:{tree}") as t: + # Salva i “rami” come mappa + branches = dict(t.iteritems()) + for attr in attributes: + # Converti l'attributo in lista ove necessario + if attr in list_conv: + raw_data[attr] = list(map(list, branches[attr].array())) + else: + raw_data[attr] = list(branches[attr].array()) + + # Converti i dati grezzi in lista di oggetti: + # scorri gli indici e associa gli attributi corrispondenti, creando l'oggetto + # + # i: 0 1 2 3 ... + # | | | | + # V V V V + # attr0: x00 x01 x02 x03 ... ¯| + # attr1: x10 x11 x12 x13 ... |--> raw_data + # attr2: x20 x21 x22 x23 ... _| + # | | | | + # V V V V + # data: ### ### ### ### ... + # + for i in range(len(raw_data[attributes[0]])): + data.append(cls(**{name: val[i] for name, val in raw_data.items()})) # type: ignore + + _task.done_extra = f"read {len(data)} items" return data @@ -202,7 +200,7 @@ class Event(NamedTuple): Timestamp: int Samples: list[int] - data = read("src/fondo.root", "Data_R", cls=Event) + data = read("src/data.root", "Data_R", cls=Event) assert isinstance(data[0], Event) From e921a3f4939846459e9841e9455ae335db54c55a Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Sun, 10 Jul 2022 23:33:19 +0200 Subject: [PATCH 032/122] Update spettro.py --- src/spettro.py | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/src/spettro.py b/src/spettro.py index cee4689..82f3be7 100755 --- a/src/spettro.py +++ b/src/spettro.py @@ -5,6 +5,7 @@ from typing import Literal, NamedTuple import matplotlib.pyplot as plt import root +from log import task # --- Costanti --- @@ -32,8 +33,8 @@ class Event(NamedTuple): # --- Utility ---- -# Calcolo della media degli elementi contenuti nel vettore "v" -def mean(v): +def mean(v: list[float] | list[int]) -> float: + """Calcola la media degli elementi nel vettore `v`""" return sum(v) / len(v) @@ -90,18 +91,15 @@ def main(): # ------------------------ Calcolo della baseline ------------------------- if BASELINE_CALC_MODE == 0: - if __debug__: - print("--> calculating baseline") - medie = [] - for event in t: - # Calcola della media dei primi `BASELINE_CALC_N` samples richiamando la funzione "mean" - # Salva la media nel vettore "medie" - medie.append(mean(event.Samples[:BASELINE_CALC_N])) - # Salva la media del vettore "medie" come "BASELINE" - if __debug__: - print(" done.") - BASELINE = mean(medie) - # BASELINE = 13313.683338704632 # già calcolata, all'occorrenza + with task("Calculating baseline..."): + medie = [] + for event in t: + # Calcola della media dei primi `BASELINE_CALC_N` samples richiamando la funzione "mean" + # Salva la media nel vettore "medie" + medie.append(mean(event.Samples[:BASELINE_CALC_N])) + # Salva la media del vettore "medie" come "BASELINE" + BASELINE = mean(medie) + # BASELINE = 13313.683338704632 # già calcolata, all'occorrenza else: BASELINE = None From 00d61f1170fec92610f9add9f322077689097c05 Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Sun, 10 Jul 2022 23:35:13 +0200 Subject: [PATCH 033/122] Update log.py --- src/log.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/log.py b/src/log.py index 1db0f9b..ff85e2c 100644 --- a/src/log.py +++ b/src/log.py @@ -117,7 +117,7 @@ def task(self, msg: str) -> Iterator[Logger]: elif dt < 1_000_000_000: dts = f"{dt/1_000_000} ms" else: - dts = time.strftime("%H:%M:%S", time.gmtime(dt/1_000_000_000)) + dts = time.strftime("%H:%M:%S", time.gmtime(dt / 1_000_000_000)) # Stampa il messaggio tsk.info(f"done{f' ({tsk.done_extra})' if tsk.done_extra else ''}.", extra=dict(took=f"took {dts} ")) From c120f5e8e8ec02bd24f5049a2bbfba5979a47363 Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Sun, 10 Jul 2022 23:59:59 +0200 Subject: [PATCH 034/122] Risolto un problema con `_indent` e aggiornato `__all__` --- src/log.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/log.py b/src/log.py index ff85e2c..dad3d3b 100644 --- a/src/log.py +++ b/src/log.py @@ -13,12 +13,13 @@ __all__ = [ - # Export from `logging` + # Exported from `logging` "NOTSET", "DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL", - "getLogger", + # Overridden from `logging` + "getLogger", "debug", "info", "warning", "error", "critical", "exception", # Defined here "TIMESTAMP", "DEFAULT_LEVEL", "ICONS", - "cli_configure", + "moduleLogger", "task", "cli_configure", ] @@ -160,6 +161,7 @@ def cli_configure() -> None: root.__class__ = Logger root.addHandler(ch) root.setLevel(level) + root._indent = 0 # pylint: disable=protected-access _setup_done = True From 656c88fb82262579618bec8dcae46644d4b954bb Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Mon, 11 Jul 2022 00:14:47 +0200 Subject: [PATCH 035/122] Update pi.py --- src/pi.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pi.py b/src/pi.py index 9c14194..e8e1349 100755 --- a/src/pi.py +++ b/src/pi.py @@ -8,6 +8,7 @@ from pathlib import Path import matplotlib.pyplot as plt from rand import TrueRandomGenerator +from log import info # Costanti @@ -97,13 +98,12 @@ def main(): # Determina il valore di "BUG", tenendo conto della riga di comando BUG = bug(True) # Di default è attivo - if __debug__: - # Comunica che BUG è attivo (per sicurezza) - print(f"[i] BUG is {'en' if BUG else 'dis'}abled.") + # Comunica che BUG è attivo (per sicurezza) + info(f"BUG is {'en' if BUG else 'dis'}abled.") # Determina l'algoritmo da utilizzare MODE: int = mode() # Usa la funzione sopra definita - print(f"[i] Using algorithm [{MODE}].") # Stampa l'algoritmo, per sicurezza + info(f"Using algorithm [{MODE}].") # Stampa l'algoritmo, per sicurezza # Inizializzazione TRG = TrueRandomGenerator(bug=BUG) # Il nostro generatore From 0ff7e769b4bae117ba5c7a32599d768a7f663139 Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Tue, 12 Jul 2022 11:18:28 +0200 Subject: [PATCH 036/122] Update log.py --- src/log.py | 65 +++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 55 insertions(+), 10 deletions(-) diff --git a/src/log.py b/src/log.py index dad3d3b..24de96f 100644 --- a/src/log.py +++ b/src/log.py @@ -12,6 +12,18 @@ import sys +# Prova ad importare `rich`, se possibile +RICH: bool +try: + import rich.markup + import rich.logging + import rich.highlighter +except ModuleNotFoundError: + RICH = False +else: + RICH = True + + __all__ = [ # Exported from `logging` "NOTSET", "DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL", @@ -47,7 +59,25 @@ def moduleLogger(name: str | None = None, /, *, depth: int = 0) -> Logger: CRITICAL: "{x}", } +STYLES = { + NOTSET: "normal", + DEBUG: "dim", + INFO: "cyan", + WARNING: "yellow", + ERROR: "red", + CRITICAL: "bold red", +} + DEFAULT_LEVEL = INFO if __debug__ else WARNING # '-O' works like a '-q' + +if RICH: + MESSAGE_FORMAT = ( + "{x} {message}", + "[/][dim][bold]{took}[/bold][{asctime}]" + ) +else: + MESSAGE_FORMAT = ("{x} {message}", "{took}[{asctime}]") + _setup_done: bool = False @@ -60,26 +90,35 @@ def __init__(self, lfmt: str, rfmt: str, *args, **kwargs) -> None: super().__init__(f"{lfmt}\0{rfmt}", *args, **kwargs) def format(self, record: logging.LogRecord) -> str: - # Make the `icon` available - setattr(record, "x", ICONS[record.levelno]) + # Make the console markup working + if RICH: + setattr(record, "markup", True) + # Make the `icon` available and escape it if necessary + icon = ICONS[record.levelno] + if RICH: + icon = rich.markup.escape(icon) + setattr(record, "x", icon) # Fix `took` missing if not hasattr(record, "took"): setattr(record, "took", "") # Apply indent - record.msg = " " * getattr(record, "indent", 0) * 4 + record.msg - delattr(record, "indent") + if hasattr(record, "indent"): + record.msg = " " * getattr(record, "indent") * 4 + record.msg + delattr(record, "indent") # Format and right-align text - left, right = super().format(record).split("\0") + text = (f"[{STYLES[record.levelno]}]" + super().format(record) + "[/]") + styles_len = len(text) - len(rich.markup.render(text)) if RICH else 0 + left, right = text.split("\0") if right: # Right-align text only if needed width = os.get_terminal_size().columns # Terminal width rows = left.split("\n") first = rows[0] - if len(first) + 1 + len(right) <= width: + if len(first) + 1 + len(right) - styles_len <= width: # Don't add the right text if the left one is too long - first += f"{' '*(width-len(first)-len(right))}{right}" + first += f"{' '*(width - len(first) - len(right) + styles_len)}{right}" return "\n".join([first, *rows[1:]]) - return left + return left + "[/]" class Logger(logging.Logger): @@ -153,8 +192,14 @@ def cli_configure() -> None: quietness = max(0, min(len(levels) - 1, quietness)) level = levels[quietness] # Configurazione - ch = logging.StreamHandler() - ch.setFormatter(ConsoleFormatter("{x} {message}", "{took}[{asctime}]", style="{", datefmt="%Y-%m-%d %H:%M:%S")) + ch = rich.logging.RichHandler( + highlighter=rich.highlighter.NullHighlighter(), + show_level=False, + show_time=False, + rich_tracebacks=True, + show_path=False, + ) if RICH else logging.StreamHandler() + ch.setFormatter(ConsoleFormatter(*MESSAGE_FORMAT, style="{", datefmt="%Y-%m-%d %H:%M:%S")) ch.setLevel(NOTSET) logging.setLoggerClass(Logger) root = getLogger() From f50ab67261c59d3daa15bda313fe1655d7b53193 Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Tue, 12 Jul 2022 11:22:45 +0200 Subject: [PATCH 037/122] Aggiunta la variabile d'ambiente NO_RICH --- src/log.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/log.py b/src/log.py index 24de96f..24f2ecc 100644 --- a/src/log.py +++ b/src/log.py @@ -13,8 +13,11 @@ # Prova ad importare `rich`, se possibile +NO_RICH = bool(eval(os.environ.get("NO_RICH", "") or "0")) RICH: bool try: + if NO_RICH: + raise ModuleNotFoundError import rich.markup import rich.logging import rich.highlighter @@ -106,7 +109,9 @@ def format(self, record: logging.LogRecord) -> str: record.msg = " " * getattr(record, "indent") * 4 + record.msg delattr(record, "indent") # Format and right-align text - text = (f"[{STYLES[record.levelno]}]" + super().format(record) + "[/]") + text = super().format(record) + if RICH: + text = f"[{STYLES[record.levelno]}]{text}[/]" styles_len = len(text) - len(rich.markup.render(text)) if RICH else 0 left, right = text.split("\0") if right: From 482d2d6ec1be9ed74be765c0998098a44984996b Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Tue, 12 Jul 2022 12:14:03 +0200 Subject: [PATCH 038/122] Aggiunto __slots__ a Logger --- src/log.py | 39 ++++++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/src/log.py b/src/log.py index 24f2ecc..2d26381 100644 --- a/src/log.py +++ b/src/log.py @@ -4,10 +4,10 @@ from __future__ import annotations from contextlib import contextmanager from typing import Any, ContextManager, Iterator, cast -from logging import NOTSET, DEBUG, INFO, WARNING, ERROR, CRITICAL, getLogger as _getLogger +from logging import NOTSET, DEBUG, INFO, WARNING, ERROR, CRITICAL +import logging import inspect import time -import logging import os import sys @@ -30,17 +30,19 @@ __all__ = [ # Exported from `logging` "NOTSET", "DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL", - # Overridden from `logging` - "getLogger", "debug", "info", "warning", "error", "critical", "exception", # Defined here - "TIMESTAMP", "DEFAULT_LEVEL", "ICONS", - "moduleLogger", "task", "cli_configure", + "TIMESTAMP", "DEFAULT_LEVEL", "ICONS", "STYLES", + "ConsoleFormatter", "Logger", + "cli_configure", "getLogger", "moduleLogger", + "debug", "info", "warning", "error", "critical", "exception", "task", ] def getLogger(name: str = "") -> Logger: """Get the logger associated with this given name.""" - return cast(Logger, _getLogger(name)) + if not name: + raise ValueError("You should not use the root logger!") + return cast(Logger, logging.getLogger(name)) def moduleLogger(name: str | None = None, /, *, depth: int = 0) -> Logger: @@ -128,13 +130,22 @@ def format(self, record: logging.LogRecord) -> str: class Logger(logging.Logger): """An enhanced logger.""" + __slots__ = ("_indent", "result", "_result_logged", "_timestamp") _indent: int - done_extra: str + result: str + _result_logged: bool + _timestamp: int | None def __init__(self, name: str, level: int | str = NOTSET) -> None: super().__init__(name, level) self._indent = 0 - self.done_extra = "" + self._task_reset() + + def _task_reset(self): + """Resetta gli attributi relativi alla `task`.""" + self.result = "" + self._result_logged = False + self._timestamp = None def makeRecord(self, *args, **kwargs) -> logging.LogRecord: record = super().makeRecord(*args, **kwargs) @@ -142,12 +153,12 @@ def makeRecord(self, *args, **kwargs) -> logging.LogRecord: return record @contextmanager - def task(self, msg: str) -> Iterator[Logger]: + def task(self, msg: str, level: int = INFO) -> Iterator[Logger]: """Log the fact we're doing something.""" - self.info(f"--> {msg}") + self.log(level, f"--> {msg}") tsk = self.getChild("task") tsk._indent = self._indent + 1 # pylint: disable=protected-access - t0 = time.time_ns() + tsk.save_timestamp() try: yield tsk finally: @@ -207,11 +218,9 @@ def cli_configure() -> None: ch.setFormatter(ConsoleFormatter(*MESSAGE_FORMAT, style="{", datefmt="%Y-%m-%d %H:%M:%S")) ch.setLevel(NOTSET) logging.setLoggerClass(Logger) - root = getLogger() - root.__class__ = Logger + root = logging.getLogger() root.addHandler(ch) root.setLevel(level) - root._indent = 0 # pylint: disable=protected-access _setup_done = True From df92cbeb1dc0d13d3695e94609c54e8525728a3f Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Tue, 12 Jul 2022 12:18:36 +0200 Subject: [PATCH 039/122] Aggiunte altre opzioni per `task` --- src/log.py | 82 +++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 65 insertions(+), 17 deletions(-) diff --git a/src/log.py b/src/log.py index 2d26381..a7c9603 100644 --- a/src/log.py +++ b/src/log.py @@ -162,20 +162,49 @@ def task(self, msg: str, level: int = INFO) -> Iterator[Logger]: try: yield tsk finally: - t1 = time.time_ns() - dt = t1 - t0 - # Converti il ∆t in un formato utile - dts: str - if dt < 1_000: - dts = f"{dt} ns" - elif dt < 1_000_000: - dts = f"{dt/1_000} µs" - elif dt < 1_000_000_000: - dts = f"{dt/1_000_000} ms" - else: - dts = time.strftime("%H:%M:%S", time.gmtime(dt / 1_000_000_000)) # Stampa il messaggio - tsk.info(f"done{f' ({tsk.done_extra})' if tsk.done_extra else ''}.", extra=dict(took=f"took {dts} ")) + if not tsk._result_logged: # pylint: disable=protected-access + tsk.done() + # Resetta questo logger + tsk._task_reset() # pylint: disable=protected-access + + def save_timestamp(self) -> None: + """Salva il tempo attuale in nanosecondi.""" + self._timestamp = time.time_ns() + + @staticmethod + def _repr_dt(dt: int) -> str: + # Converti il ∆t in un formato utile + if dt < 1_000: + return f"{dt} ns" + if dt < 1_000_000: + return f"{dt/1_000} µs" + if dt < 1_000_000_000: + return f"{dt/1_000_000} ms" + return time.strftime("%H:%M:%S", time.gmtime(dt / 1_000_000_000)) + + def done(self, result: str | None = None, level: int = INFO) -> None: + """Log a 'done (...)' message.""" + t = time.time_ns() + if self._timestamp: + extra = dict(took=f"took {self._repr_dt(t - self._timestamp)} ") + else: + extra = {} + result = self.result if result is None else result + self.log(level, f"done{f' ({result})' if result else ''}.", extra=extra) + self._result_logged = True + + def fail(self, result: str | None = None, level: int = ERROR) -> None: + """Log a 'fail (...)' message.""" + t = time.time_ns() + if self._timestamp: + extra = dict(took=f"took {self._repr_dt(t - self._timestamp)} ") + else: + extra = {} + result = self.result if result is None else result + self.log(level, f"failed{f' ({result})' if result else ''}.", extra=extra) + self._result_logged = True + def get_levels() -> list[int]: @@ -224,9 +253,9 @@ def cli_configure() -> None: _setup_done = True -def task(msg: str) -> ContextManager[Logger]: +def task(msg: str, level: int = INFO) -> ContextManager[Logger]: """Log an debug message.""" - return moduleLogger(depth=1).task(msg) + return moduleLogger(depth=1).task(msg, level=level) def debug(msg: Any, *args: Any, extra: dict[str, Any] | None = None, **kwargs) -> None: @@ -271,6 +300,25 @@ def exception(msg: Any, *args: Any, extra: dict[str, Any] | None = None, **kwarg logger.warning("Warning") logger.info("Info") logger.debug("Debug") - with logger.task("Running some serious computation...") as computation: + with logger.task("Null task #1") as computation: + pass + with logger.task("Null task #2") as computation: + computation.done() + with logger.task("Null task #3") as computation: + computation.done("explicit") + with logger.task("Null task #4") as computation: + computation.fail("explicit") + with logger.task("Null task #5") as computation: + computation.result = "custom result" + with logger.task("Sleep task #1 (1s)") as computation: time.sleep(1) - computation.done_extra = "wasn't so useful" + computation.fail() + with logger.task("Sleep task #2 (10s, loop)") as computation: + for _ in range(10): + time.sleep(1) + computation.done() + with logger.task("Sleep task #3 (10s, loop, log at every iteration)") as computation: + for _ in range(10): + time.sleep(1) + computation.info("Just slept 1s.") + computation.done() From 187910dcc495cd275218027475e5ab2bb3258831 Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Tue, 12 Jul 2022 12:18:52 +0200 Subject: [PATCH 040/122] Update root.py --- src/root.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/root.py b/src/root.py index 0707eaa..809f988 100644 --- a/src/root.py +++ b/src/root.py @@ -10,7 +10,7 @@ from log import info, task -# ----- 1. Importa la libreria corretta ------ # +# ----- 1. Importa la libreria corretta ------ # # Variabile che controlla la libreria da usare: True per PyROOT, False per uproot. # Il valore iniziale è determinato a partire variabile d'ambiente FORCE_UPROOT. @@ -34,7 +34,7 @@ info(f"ROOT backend: {'PyROOT' if ROOT else 'uproot'}") -# ----- 2. Definisci la funzione di lettura ------ # +# ----- 2. Definisci la funzione di lettura ------ # _T = TypeVar("_T", bound=NamedTuple) @@ -182,7 +182,7 @@ def read( for i in range(len(raw_data[attributes[0]])): data.append(cls(**{name: val[i] for name, val in raw_data.items()})) # type: ignore - _task.done_extra = f"read {len(data)} items" + _task.result = f"read {len(data)} items" return data From c05b38ed51cee0bdcefb07d855e44bc94461c0ec Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Tue, 12 Jul 2022 12:22:54 +0200 Subject: [PATCH 041/122] Update root.py --- src/root.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/root.py b/src/root.py index 809f988..2cfe19f 100644 --- a/src/root.py +++ b/src/root.py @@ -6,6 +6,7 @@ from typing import Any, NamedTuple, Sequence, TypeVar, get_origin, get_type_hints, overload from collections import namedtuple from pathlib import Path +import sys import os from log import info, task @@ -200,7 +201,15 @@ class Event(NamedTuple): Timestamp: int Samples: list[int] - data = read("src/data.root", "Data_R", cls=Event) + SRC = Path(__file__).parent + DEFAULT = SRC/"fondo.root" + if len(sys.argv) > 1: + file = Path(sys.argv[1]) + if not file.exists(): + file = DEFAULT + else: + file = DEFAULT + data = read(file, "Data_R", cls=Event) assert isinstance(data[0], Event) From 2e7c0a8293ea74585caf3164ec6eeec9e2993080 Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Tue, 12 Jul 2022 12:31:52 +0200 Subject: [PATCH 042/122] Migliorati il colore degli errori e il prompt delle task --- src/log.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/log.py b/src/log.py index a7c9603..78ae58e 100644 --- a/src/log.py +++ b/src/log.py @@ -69,7 +69,7 @@ def moduleLogger(name: str | None = None, /, *, depth: int = 0) -> Logger: DEBUG: "dim", INFO: "cyan", WARNING: "yellow", - ERROR: "red", + ERROR: "red1", CRITICAL: "bold red", } @@ -80,8 +80,10 @@ def moduleLogger(name: str | None = None, /, *, depth: int = 0) -> Logger: "{x} {message}", "[/][dim][bold]{took}[/bold][{asctime}]" ) + TASK_MESSAGE = "[bold]--> {}[/bold]" else: MESSAGE_FORMAT = ("{x} {message}", "{took}[{asctime}]") + TASK_MESSAGE = "--> {}" _setup_done: bool = False @@ -155,7 +157,7 @@ def makeRecord(self, *args, **kwargs) -> logging.LogRecord: @contextmanager def task(self, msg: str, level: int = INFO) -> Iterator[Logger]: """Log the fact we're doing something.""" - self.log(level, f"--> {msg}") + self.log(level, TASK_MESSAGE.format(msg)) tsk = self.getChild("task") tsk._indent = self._indent + 1 # pylint: disable=protected-access tsk.save_timestamp() From 2ba3aaf29ce48f3d1dff4fceb70042d5be807dda Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Tue, 12 Jul 2022 12:32:57 +0200 Subject: [PATCH 043/122] Update log.py --- src/log.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/log.py b/src/log.py index 78ae58e..0475a26 100644 --- a/src/log.py +++ b/src/log.py @@ -157,7 +157,7 @@ def makeRecord(self, *args, **kwargs) -> logging.LogRecord: @contextmanager def task(self, msg: str, level: int = INFO) -> Iterator[Logger]: """Log the fact we're doing something.""" - self.log(level, TASK_MESSAGE.format(msg)) + self.log(level, TASK_MESSAGE.format(msg)) # pylint: disable=logging-format-interpolation tsk = self.getChild("task") tsk._indent = self._indent + 1 # pylint: disable=protected-access tsk.save_timestamp() From 816e43b0caf20da25412686b3a427406daf44dfa Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Tue, 12 Jul 2022 12:33:22 +0200 Subject: [PATCH 044/122] Update log.py --- src/log.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/log.py b/src/log.py index 0475a26..f2df93d 100644 --- a/src/log.py +++ b/src/log.py @@ -208,7 +208,6 @@ def fail(self, result: str | None = None, level: int = ERROR) -> None: self._result_logged = True - def get_levels() -> list[int]: """Get the installed levels, as a list, in severity ascending order.""" name2level: dict[str, int] | None From 6bc4862796cf93d38a7cd776e42e2860f0a1ab1a Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Tue, 12 Jul 2022 12:33:42 +0200 Subject: [PATCH 045/122] Update root.py --- src/root.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/root.py b/src/root.py index 2cfe19f..cc0d8c1 100644 --- a/src/root.py +++ b/src/root.py @@ -202,7 +202,7 @@ class Event(NamedTuple): Samples: list[int] SRC = Path(__file__).parent - DEFAULT = SRC/"fondo.root" + DEFAULT = SRC / "fondo.root" if len(sys.argv) > 1: file = Path(sys.argv[1]) if not file.exists(): From 750f3162a51f7451ef35397594dc899b3b288cc8 Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Tue, 12 Jul 2022 12:34:21 +0200 Subject: [PATCH 046/122] Update .pep8speaks.yml --- .pep8speaks.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.pep8speaks.yml b/.pep8speaks.yml index c9ed0d2..7dd3a5c 100644 --- a/.pep8speaks.yml +++ b/.pep8speaks.yml @@ -9,6 +9,7 @@ pycodestyle: - E241 # multiple spaces after ':' - E704 # multiple statements on one line (def) - W503 # line break before binary operator + - E722 # do not use bare 'except' # - E402 # module level import not at top of file # - E731 # do not assign a lambda expression, use a def # - C406 # Unnecessary list literal - rewrite as a dict literal. From 81b33ca4e8a11b9718eb5e4c5236b18d0ff1b6cc Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Tue, 12 Jul 2022 12:34:40 +0200 Subject: [PATCH 047/122] Update pi.py --- src/pi.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/pi.py b/src/pi.py index e8e1349..e4240e7 100755 --- a/src/pi.py +++ b/src/pi.py @@ -18,11 +18,11 @@ def bug(default: bool, /) -> bool: """Determina se è stato attivato il “bug” da riga di comando.""" - # $ python pi.py # --> di default + # $ python pi.py # --> di default # $ python pi.py --bug # --> attivo - # $ python pi.py --no-bug # --> disattivato - # $ python pi.py --no-bug --bug # --> attivo (--bug sovrascrive --no-bug) - # $ python pi.py --bug --no-bug # --> disattivato (--no-bug sovrascrive --bug) + # $ python pi.py --no-bug # --> disattivato + # $ python pi.py --no-bug --bug # --> attivo (--bug sovrascrive --no-bug) + # $ python pi.py --bug --no-bug # --> disattivato (--no-bug sovrascrive --bug) if "--bug" in sys.argv: if "--no-bug" in sys.argv: BUG = sys.argv[::-1].index("--bug") < sys.argv[::-1].index("--no-bug") @@ -55,7 +55,7 @@ def mode() -> int: if 0 <= _mode <= 3: # Valido return _mode - # Non valido: continua con la selezione interattiva + # Non valido: continua con la selezione interattiva # Selezione interattiva dell'algoritmo print(""" >>> Choose an algorithm: @@ -88,7 +88,7 @@ def mode() -> int: # Funzione principale def main(): - """Calcola π tramite il metodo Monte Carlo, utilizzando il nostro TRNG.""" + """Calcola π tramite il metodo Monte Carlo, utilizzando il nostro TRNG.""" # Stampa il titolo width = os.get_terminal_size().columns title = " Monte Carlo Method π Approximator " @@ -153,7 +153,7 @@ def main(): elif MODE == 1: y = TRG.random_number() # Assegnazione valore di default (pre-ciclo) for i in range(LEN): - # L'`y` di prima diventa il nuovo `x`, mentre `y` diventa un nuovo numero casuale + # L'`y` di prima diventa il nuovo `x`, mentre `y` diventa un nuovo numero casuale x, y = y, TRG.random_number() if x**2 + y**2 <= K: # Analogo al metodo 1 N_in = N_in + 1 From fb5b03ea670faf5e0637a7f130962f252f8c6644 Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Tue, 12 Jul 2022 12:51:13 +0200 Subject: [PATCH 048/122] Update pi.py --- src/pi.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pi.py b/src/pi.py index e4240e7..ce0c8dd 100755 --- a/src/pi.py +++ b/src/pi.py @@ -8,7 +8,7 @@ from pathlib import Path import matplotlib.pyplot as plt from rand import TrueRandomGenerator -from log import info +from log import info, warning # Costanti @@ -74,13 +74,13 @@ def mode() -> int: sys.exit(0) # Gestione errori: input non intero (chiede nuovamente) except: - print("[!] Please type in an integer (0|1|2|3)!") + warning("Algorithm index has to be an integer (0|1|2|3)!") continue # Numero intero: ok else: # Troppo grande o troppo piccolo (chiede nuovamente) if _mode > 3 or _mode < 0: - print("[!] Invalid integer (has to be in [0, 3])!") + warning(f"Invalid integer `{_mode}` (has to be in [0, 3])!") continue # Tutto ok: "_mode" è impostato e si continua col programma return _mode # questo 'return' interrompe il ciclo 'while' e ritorna il valore di '_mode' From ecdea7427d9f108c7154f8f728886bacb13b46dd Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Tue, 12 Jul 2022 13:05:19 +0200 Subject: [PATCH 049/122] Update log.py --- src/log.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/log.py b/src/log.py index f2df93d..3bb8935 100644 --- a/src/log.py +++ b/src/log.py @@ -42,6 +42,8 @@ def getLogger(name: str = "") -> Logger: """Get the logger associated with this given name.""" if not name: raise ValueError("You should not use the root logger!") + if name == "root": + name = "root_" return cast(Logger, logging.getLogger(name)) From 0e313daab573728b2feb1fb800ec135d179b312d Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Tue, 12 Jul 2022 13:32:30 +0200 Subject: [PATCH 050/122] Aggiunte due funzioni per scrivere a console --- src/log.py | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/src/log.py b/src/log.py index 3bb8935..5fc4e41 100644 --- a/src/log.py +++ b/src/log.py @@ -2,9 +2,10 @@ # -*- coding : utf-8 -*- """Utility module: logging support.""" from __future__ import annotations -from contextlib import contextmanager from typing import Any, ContextManager, Iterator, cast from logging import NOTSET, DEBUG, INFO, WARNING, ERROR, CRITICAL +from contextlib import contextmanager +from io import StringIO import logging import inspect import time @@ -18,6 +19,7 @@ try: if NO_RICH: raise ModuleNotFoundError + import rich import rich.markup import rich.logging import rich.highlighter @@ -291,6 +293,27 @@ def exception(msg: Any, *args: Any, extra: dict[str, Any] | None = None, **kwarg moduleLogger(depth=1).exception(msg, *args, extra=extra, **kwargs) +def style(message: str, style: str) -> str: # pylint: disable=redefined-outer-name + """Apply the given `style` to `message` only if `rich` is available.""" + if RICH: + return f"[{style}]{message}[/{style}]" + return message + + +def sprint(*values, sep: str = " ", end: str = "\n", style: str = ""): # pylint: disable=redefined-outer-name + """Print styled text to console.""" + if RICH: + if style: + io = StringIO() + print(*values, sep=sep, end=end, file=io, flush=True) + io.seek(0) + rich.print(f"[{style}]{io.read()}[/{style}]", end="", flush=True) + else: + rich.print(*values, sep=sep, end=end, flush=True) + else: + print(*values, sep=sep, end=end, flush=True) + + if not eval(os.environ.get("NO_AUTO_LOGGING_CONFIG", "0") or "0"): cli_configure() From f17c528635247166ee3f3c6340c5e5eb72f2a1a3 Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Tue, 12 Jul 2022 13:32:47 +0200 Subject: [PATCH 051/122] Semplificato il codice che scrive il titolo --- src/pi.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/pi.py b/src/pi.py index ce0c8dd..0dc79dc 100755 --- a/src/pi.py +++ b/src/pi.py @@ -8,7 +8,7 @@ from pathlib import Path import matplotlib.pyplot as plt from rand import TrueRandomGenerator -from log import info, warning +from log import info, warning, sprint # Costanti @@ -92,8 +92,7 @@ def main(): # Stampa il titolo width = os.get_terminal_size().columns title = " Monte Carlo Method π Approximator " - around = "=" * (max(0, width - len(title)) // 2) - print(around, title, around, sep="") + sprint(f"{title:=^{width}}", style="bold") # see https://pyformat.info/ for why this works # Determina il valore di "BUG", tenendo conto della riga di comando BUG = bug(True) # Di default è attivo From b0cb565c289958e78d9cb0f63c16ce3dc223adc2 Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Tue, 12 Jul 2022 13:37:53 +0200 Subject: [PATCH 052/122] Silenzia matplotlib --- src/log.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/log.py b/src/log.py index 5fc4e41..6433119 100644 --- a/src/log.py +++ b/src/log.py @@ -316,6 +316,8 @@ def sprint(*values, sep: str = " ", end: str = "\n", style: str = ""): # pylint if not eval(os.environ.get("NO_AUTO_LOGGING_CONFIG", "0") or "0"): cli_configure() + # Silenzia i messaggi di debug di alcune librerie + getLogger("matplotlib").setLevel(WARNING) if __name__ == "__main__": From 8a81d88e0efe23566b50390614ac745c24752c7f Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Tue, 12 Jul 2022 13:42:41 +0200 Subject: [PATCH 053/122] Update root.py --- src/root.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/root.py b/src/root.py index cc0d8c1..b751559 100644 --- a/src/root.py +++ b/src/root.py @@ -8,23 +8,27 @@ from pathlib import Path import sys import os -from log import info, task +from log import debug, info, task # ----- 1. Importa la libreria corretta ------ # # Variabile che controlla la libreria da usare: True per PyROOT, False per uproot. # Il valore iniziale è determinato a partire variabile d'ambiente FORCE_UPROOT. -ROOT: bool = not eval(os.environ.get("FORCE_UPROOT", "0") or "0") +FORCE_UPROOT = eval(os.environ.get("FORCE_UPROOT", "0") or "0") # Prova a importare PyROOT; se fallisci, prova con uproot. # Imposta la variabile `ROOT` di conseguenza. +ROOT: bool try: - if not ROOT: + debug(f"Environment variable `FORCE_UPROOT` is {'' if FORCE_UPROOT else 'not '}set.") + if FORCE_UPROOT: raise ModuleNotFoundError + debug("Trying to import `PyROOT`") import ROOT as PyROOT except ModuleNotFoundError: # Non c'è PyROOT: usiamo uproot + debug("Trying to import `uproot`") import uproot ROOT = False From 267778cd4c4e284e83f7fd0073764b42f2652a64 Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Tue, 12 Jul 2022 13:45:59 +0200 Subject: [PATCH 054/122] Update root.py --- src/root.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/root.py b/src/root.py index b751559..005d11e 100644 --- a/src/root.py +++ b/src/root.py @@ -132,7 +132,7 @@ def read( # In `vals` vengono salvati i parametri da passare alla classe nella costruzione dell'oggetto vals: dict[str, Any] = {} - with task(f"Reading tree {tree!r} from file {file!r}...") as _task: + with task(f"Reading tree {tree!r} from file {file!r}...") as reading: if ROOT: # --- PyROOT --- # Termina il loop degli eventi di PyROOT, in modo che non interferisca con matplotlib @@ -187,7 +187,7 @@ def read( for i in range(len(raw_data[attributes[0]])): data.append(cls(**{name: val[i] for name, val in raw_data.items()})) # type: ignore - _task.result = f"read {len(data)} items" + reading.result = f"read {len(data)} items" return data From 4376c6e4bef03f82f0c088847df7a0b61eaef39e Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Tue, 12 Jul 2022 14:09:02 +0200 Subject: [PATCH 055/122] =?UTF-8?q?`task`=20pu=C3=B2=20essere=20usato=20an?= =?UTF-8?q?che=20come=20decoratore?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/log.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/log.py b/src/log.py index 6433119..2722fd6 100644 --- a/src/log.py +++ b/src/log.py @@ -2,9 +2,9 @@ # -*- coding : utf-8 -*- """Utility module: logging support.""" from __future__ import annotations -from typing import Any, ContextManager, Iterator, cast +from typing import Any, Iterator, cast from logging import NOTSET, DEBUG, INFO, WARNING, ERROR, CRITICAL -from contextlib import contextmanager +from contextlib import contextmanager, _GeneratorContextManager from io import StringIO import logging import inspect @@ -258,7 +258,7 @@ def cli_configure() -> None: _setup_done = True -def task(msg: str, level: int = INFO) -> ContextManager[Logger]: +def task(msg: str, level: int = INFO) -> _GeneratorContextManager[Logger]: """Log an debug message.""" return moduleLogger(depth=1).task(msg, level=level) @@ -350,3 +350,12 @@ def sprint(*values, sep: str = " ", end: str = "\n", style: str = ""): # pylint time.sleep(1) computation.info("Just slept 1s.") computation.done() + logger.debug("About to define function `_foo` with `@task` decorator") + + @task("Foo task") + def _foo(x: int) -> None: + for __ in range(x): + time.sleep(1) + + logger.debug("After defining function `_foo` with `@task` decorator") + _foo(2) From 14b8d9944901cf556cd06acca44f35fd8faa9398 Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Tue, 12 Jul 2022 14:20:06 +0200 Subject: [PATCH 056/122] Aggiunta una funzione per ottenere il logger della task attuale --- src/log.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/log.py b/src/log.py index 2722fd6..a45ac4f 100644 --- a/src/log.py +++ b/src/log.py @@ -57,6 +57,11 @@ def moduleLogger(name: str | None = None, /, *, depth: int = 0) -> Logger: ) +def taskLogger(module: str | None = None, /, *, depth: int = 0) -> Logger: + """Get the task logger for the module that's calling this function.""" + return moduleLogger(module, depth=1 + depth).getChild("task") + + TIMESTAMP: bool = True ICONS = { From 7ecd021e2ee8b20a9810948bd32b9783a8728b0d Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Tue, 12 Jul 2022 14:20:26 +0200 Subject: [PATCH 057/122] Update spettro.py --- src/spettro.py | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/spettro.py b/src/spettro.py index 82f3be7..91efe71 100755 --- a/src/spettro.py +++ b/src/spettro.py @@ -1,11 +1,12 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- +"""Analisi dello spettro del segnale.""" from __future__ import annotations from pathlib import Path from typing import Literal, NamedTuple import matplotlib.pyplot as plt import root -from log import task +from log import moduleLogger, task, taskLogger # --- Costanti --- @@ -39,6 +40,7 @@ def mean(v: list[float] | list[int]) -> float: # Calcolo delle aree per ogni evento +@task(f"Calculating {'BASELINES and ' if BASELINE_CALC_MODE == 1 else ''}areas") def aree( events: list[Event], BASELINE: float | None = None, @@ -46,13 +48,9 @@ def aree( min_samples: int = 0, max_samples: int | None = None, ) -> list[float]: - - if __debug__: - print( - f"--> calculating {'BASELINES and ' if BASELINE_CALC_MODE == 1 else ''}" - f"areas({f'BASELINE={BASELINE}, ' if BASELINE_CALC_MODE == 0 else ''}" - f"{max_area=}, samples range = [{min_samples}, {max_samples}])" - ) + """Calcola l'area di ogni evento.""" + logger = taskLogger() + logger.debug(f"{max_area=}, samples range = [{min_samples}, {max_samples}])") aree_calcolate: list[float] = [] for event in events: @@ -73,8 +71,6 @@ def aree( # ... salva l'area nel vettore `aree_calcolate` aree_calcolate.append(temp_area) - if __debug__: - print(" done.") return aree_calcolate From 12883c1f066a988dd03b9f20b638f11de19cb21f Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Tue, 12 Jul 2022 14:20:41 +0200 Subject: [PATCH 058/122] Update spettro.py --- src/spettro.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/spettro.py b/src/spettro.py index 91efe71..66b0c79 100755 --- a/src/spettro.py +++ b/src/spettro.py @@ -6,7 +6,7 @@ from typing import Literal, NamedTuple import matplotlib.pyplot as plt import root -from log import moduleLogger, task, taskLogger +from log import task, taskLogger # --- Costanti --- From f1c84d869ed3959de45a354db0d9a156e072e61e Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Tue, 12 Jul 2022 14:33:46 +0200 Subject: [PATCH 059/122] Update spettro.py --- src/spettro.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/spettro.py b/src/spettro.py index 66b0c79..bd47796 100755 --- a/src/spettro.py +++ b/src/spettro.py @@ -87,7 +87,7 @@ def main(): # ------------------------ Calcolo della baseline ------------------------- if BASELINE_CALC_MODE == 0: - with task("Calculating baseline..."): + with task("Calculating baseline...") as calc: medie = [] for event in t: # Calcola della media dei primi `BASELINE_CALC_N` samples richiamando la funzione "mean" @@ -96,6 +96,7 @@ def main(): # Salva la media del vettore "medie" come "BASELINE" BASELINE = mean(medie) # BASELINE = 13313.683338704632 # già calcolata, all'occorrenza + calc.result = f"it's {BASELINE}" else: BASELINE = None From 7277776daa1acb5648b7c05fdaa834541d1e7f87 Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Tue, 12 Jul 2022 14:43:59 +0200 Subject: [PATCH 060/122] Update log.py --- src/log.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/log.py b/src/log.py index a45ac4f..2af0114 100644 --- a/src/log.py +++ b/src/log.py @@ -264,7 +264,7 @@ def cli_configure() -> None: def task(msg: str, level: int = INFO) -> _GeneratorContextManager[Logger]: - """Log an debug message.""" + """Start logging a task.""" return moduleLogger(depth=1).task(msg, level=level) From 8797558ec2598eaad911b4c291259c1023c7fad0 Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Tue, 12 Jul 2022 14:48:07 +0200 Subject: [PATCH 061/122] Update pyproject.toml --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 03f83f6..00293a1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,6 +23,7 @@ disable = [ # Errors # Warnings "W0123", # use of eval + "W0622", # Redefining built-in '*' "W0702", # No exception type(s) specified "W1203", # Use %s formatting in logging functions # Conventions From 36861806c266e606edb8aa65482ed53586ea445f Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Tue, 12 Jul 2022 14:48:26 +0200 Subject: [PATCH 062/122] Aggiunto il parametro `id` a `task()` --- src/log.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/log.py b/src/log.py index 2af0114..2d37d68 100644 --- a/src/log.py +++ b/src/log.py @@ -164,20 +164,23 @@ def makeRecord(self, *args, **kwargs) -> logging.LogRecord: return record @contextmanager - def task(self, msg: str, level: int = INFO) -> Iterator[Logger]: + def task(self, msg: str, level: int = INFO, id: str | None = None) -> Iterator[Logger]: """Log the fact we're doing something.""" + # pylint: disable=protected-access self.log(level, TASK_MESSAGE.format(msg)) # pylint: disable=logging-format-interpolation tsk = self.getChild("task") - tsk._indent = self._indent + 1 # pylint: disable=protected-access + if id: + tsk = tsk.getChild(id) + tsk._indent = self._indent + 1 tsk.save_timestamp() try: yield tsk finally: # Stampa il messaggio - if not tsk._result_logged: # pylint: disable=protected-access + if not tsk._result_logged: tsk.done() # Resetta questo logger - tsk._task_reset() # pylint: disable=protected-access + tsk._task_reset() def save_timestamp(self) -> None: """Salva il tempo attuale in nanosecondi.""" @@ -263,9 +266,9 @@ def cli_configure() -> None: _setup_done = True -def task(msg: str, level: int = INFO) -> _GeneratorContextManager[Logger]: +def task(msg: str, level: int = INFO, id: str | None = "") -> _GeneratorContextManager[Logger]: """Start logging a task.""" - return moduleLogger(depth=1).task(msg, level=level) + return moduleLogger(depth=1).task(msg, level=level, id=id) def debug(msg: Any, *args: Any, extra: dict[str, Any] | None = None, **kwargs) -> None: From 81b6d75f93bafb20c733c8f136901164da155457 Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Tue, 12 Jul 2022 14:49:43 +0200 Subject: [PATCH 063/122] Update log.py --- src/log.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/log.py b/src/log.py index 2d37d68..0d129cf 100644 --- a/src/log.py +++ b/src/log.py @@ -57,9 +57,12 @@ def moduleLogger(name: str | None = None, /, *, depth: int = 0) -> Logger: ) -def taskLogger(module: str | None = None, /, *, depth: int = 0) -> Logger: +def taskLogger(module: str | None = None, /, id: str = "", *, depth: int = 0) -> Logger: """Get the task logger for the module that's calling this function.""" - return moduleLogger(module, depth=1 + depth).getChild("task") + logger = moduleLogger(module, depth=1 + depth).getChild("task") + if id: + return logger.getChild(id) + return logger TIMESTAMP: bool = True From 71a38a8fc4750fc26217ad083f2e902c249f5170 Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Tue, 12 Jul 2022 14:50:33 +0200 Subject: [PATCH 064/122] Update log.py --- src/log.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/log.py b/src/log.py index 0d129cf..eaa1d7e 100644 --- a/src/log.py +++ b/src/log.py @@ -59,10 +59,10 @@ def moduleLogger(name: str | None = None, /, *, depth: int = 0) -> Logger: def taskLogger(module: str | None = None, /, id: str = "", *, depth: int = 0) -> Logger: """Get the task logger for the module that's calling this function.""" - logger = moduleLogger(module, depth=1 + depth).getChild("task") + tl = moduleLogger(module, depth=1 + depth).getChild("task") if id: - return logger.getChild(id) - return logger + return tl.getChild(id) + return tl TIMESTAMP: bool = True From c29d661febe93e9796098b9fb77ce4d2a853e4fc Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Tue, 12 Jul 2022 14:50:59 +0200 Subject: [PATCH 065/122] Update pyproject.toml --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 00293a1..4582791 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,6 +23,7 @@ disable = [ # Errors # Warnings "W0123", # use of eval + "W0621", # Redefining name '*' from outer scope "W0622", # Redefining built-in '*' "W0702", # No exception type(s) specified "W1203", # Use %s formatting in logging functions From 66c562663f357cb8c1ea11a35654c8bfb22e4fa8 Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Tue, 12 Jul 2022 14:52:00 +0200 Subject: [PATCH 066/122] Update log.py --- src/log.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/log.py b/src/log.py index eaa1d7e..72c5ef1 100644 --- a/src/log.py +++ b/src/log.py @@ -304,14 +304,14 @@ def exception(msg: Any, *args: Any, extra: dict[str, Any] | None = None, **kwarg moduleLogger(depth=1).exception(msg, *args, extra=extra, **kwargs) -def style(message: str, style: str) -> str: # pylint: disable=redefined-outer-name +def style(message: str, style: str) -> str: """Apply the given `style` to `message` only if `rich` is available.""" if RICH: return f"[{style}]{message}[/{style}]" return message -def sprint(*values, sep: str = " ", end: str = "\n", style: str = ""): # pylint: disable=redefined-outer-name +def sprint(*values, sep: str = " ", end: str = "\n", style: str = ""): """Print styled text to console.""" if RICH: if style: From 62f022e00a80cfb75ac46255a107907477e01614 Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Tue, 12 Jul 2022 15:07:56 +0200 Subject: [PATCH 067/122] Migliorato un commento --- src/root.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/root.py b/src/root.py index 005d11e..d24d2dd 100644 --- a/src/root.py +++ b/src/root.py @@ -14,7 +14,7 @@ # ----- 1. Importa la libreria corretta ------ # # Variabile che controlla la libreria da usare: True per PyROOT, False per uproot. -# Il valore iniziale è determinato a partire variabile d'ambiente FORCE_UPROOT. +# Il valore iniziale è determinato a partire dalla variabile d'ambiente `FORCE_UPROOT`. FORCE_UPROOT = eval(os.environ.get("FORCE_UPROOT", "0") or "0") # Prova a importare PyROOT; se fallisci, prova con uproot. From c0dde05a5bb59988ff23d0288aa1110757040e58 Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Tue, 12 Jul 2022 15:08:13 +0200 Subject: [PATCH 068/122] Semplificata la definizione di TASK_MESSAGE --- src/log.py | 51 +++++++++++++++++++++++++++------------------------ 1 file changed, 27 insertions(+), 24 deletions(-) diff --git a/src/log.py b/src/log.py index 72c5ef1..2c72ccd 100644 --- a/src/log.py +++ b/src/log.py @@ -40,6 +40,27 @@ ] +def style(message: str, style: str) -> str: + """Apply the given `style` to `message` only if `rich` is available.""" + if RICH: + return f"[{style}]{message}[/{style}]" + return message + + +def sprint(*values, sep: str = " ", end: str = "\n", style: str = ""): + """Print styled text to console.""" + if RICH: + if style: + io = StringIO() + print(*values, sep=sep, end=end, file=io, flush=True) + io.seek(0) + rich.print(f"[{style}]{io.read()}[/{style}]", end="", flush=True) + else: + rich.print(*values, sep=sep, end=end, flush=True) + else: + print(*values, sep=sep, end=end, flush=True) + + def getLogger(name: str = "") -> Logger: """Get the logger associated with this given name.""" if not name: @@ -92,10 +113,13 @@ def taskLogger(module: str | None = None, /, id: str = "", *, depth: int = 0) -> "{x} {message}", "[/][dim][bold]{took}[/bold][{asctime}]" ) - TASK_MESSAGE = "[bold]--> {}[/bold]" else: - MESSAGE_FORMAT = ("{x} {message}", "{took}[{asctime}]") - TASK_MESSAGE = "--> {}" + MESSAGE_FORMAT = ( + "{x} {message}", + "{took}[{asctime}]" + ) + +TASK_MESSAGE = style("--> {}", "bold") _setup_done: bool = False @@ -304,27 +328,6 @@ def exception(msg: Any, *args: Any, extra: dict[str, Any] | None = None, **kwarg moduleLogger(depth=1).exception(msg, *args, extra=extra, **kwargs) -def style(message: str, style: str) -> str: - """Apply the given `style` to `message` only if `rich` is available.""" - if RICH: - return f"[{style}]{message}[/{style}]" - return message - - -def sprint(*values, sep: str = " ", end: str = "\n", style: str = ""): - """Print styled text to console.""" - if RICH: - if style: - io = StringIO() - print(*values, sep=sep, end=end, file=io, flush=True) - io.seek(0) - rich.print(f"[{style}]{io.read()}[/{style}]", end="", flush=True) - else: - rich.print(*values, sep=sep, end=end, flush=True) - else: - print(*values, sep=sep, end=end, flush=True) - - if not eval(os.environ.get("NO_AUTO_LOGGING_CONFIG", "0") or "0"): cli_configure() # Silenzia i messaggi di debug di alcune librerie From 70b683b2ee2e30be70b3074bde3eeac54074267d Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Tue, 12 Jul 2022 15:10:40 +0200 Subject: [PATCH 069/122] Update log.py --- src/log.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/log.py b/src/log.py index 2c72ccd..15358c2 100644 --- a/src/log.py +++ b/src/log.py @@ -133,7 +133,7 @@ def __init__(self, lfmt: str, rfmt: str, *args, **kwargs) -> None: super().__init__(f"{lfmt}\0{rfmt}", *args, **kwargs) def format(self, record: logging.LogRecord) -> str: - # Make the console markup working + # Activate console markup if RICH: setattr(record, "markup", True) # Make the `icon` available and escape it if necessary From fb44c6ed08c58477b5a3ddb2c72bcf07edb31583 Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Tue, 12 Jul 2022 15:22:00 +0200 Subject: [PATCH 070/122] Aggiunte le `task` a `rand.py` --- src/rand.py | 217 ++++++++++++++++++++++++---------------------------- 1 file changed, 102 insertions(+), 115 deletions(-) diff --git a/src/rand.py b/src/rand.py index d7d0707..ec3d35d 100755 --- a/src/rand.py +++ b/src/rand.py @@ -5,6 +5,7 @@ from pathlib import Path from typing import Literal, NamedTuple, overload from enum import Flag, auto +from log import task import root # Determina la cartella dove si trova questo file @@ -84,67 +85,60 @@ def __init__( ) # --- 1. Calcolo delle differenze dei tempi tra coppie di tempi adiacenti --- - if __debug__: - print("--> Calculating time differences") - self.delta_times = [] - for i in range(1, len(events)): - # ∆t = (tempo dell'`i`-esimo evento) - (tempo dell'`i-1`-esimo evento) - delta_time = events[i].Timestamp - events[i - 1].Timestamp - # Salva ∆t (`delta_time`) nel vettore dedicato - self.delta_times.append(delta_time) - if __debug__: - print(" done.") + with task("Calculating time differences"): + + self.delta_times = [] + for i in range(1, len(events)): + # ∆t = (tempo dell'`i`-esimo evento) - (tempo dell'`i-1`-esimo evento) + delta_time = events[i].Timestamp - events[i - 1].Timestamp + # Salva ∆t (`delta_time`) nel vettore dedicato + self.delta_times.append(delta_time) # --- 2. Generazione dei bit casuali --- - if __debug__: - print("--> Generating random bits") - # Applicazione del metodo (statico) `self._rand(...)` alle - # differenze dei tempi e salvataggio nel vettore `self.bits` - self.random_bits = list(map(self._rand, self.delta_times)) - if __debug__: - print(" done.") + with task("Generating random bits"): + # Applicazione del metodo (statico) `self._rand(...)` alle + # differenze dei tempi e salvataggio nel vettore `self.bits` + self.random_bits = list(map(self._rand, self.delta_times)) - if __debug__: - print("--> Generating random numbers") # --- 3. Generazione dei numeri casuali (da 0 a 255) --- - self.random_numbers = [] - random_numbers_b = [] - # Inizializza un vettore di lunghezza 8 (pieno di zeri) - byte = [0] * 8 - - if _BYTES_GENERATION_METHOD == 0: - # -------------------- Metodo 1 -------------------- - # = ⌊ / 8 ⌋ ('//' è la divisione intera) - n_bytes = len(self.random_bits) // 8 - for i in range(n_bytes): - for j in range(8): - # Prendi 8 elementi da `self.randomBits` e salvali in `byte` - byte[j] = self.random_bits[i * 8 + j] - - # Converti `byte` in un numero da 0 a 255 tramite il metodo (statico) `_conv()`; - # salva poi il risultato nella variabile di istanza. - self.random_numbers.append(self._conv(byte)) - # Se il `bug` è attivo, rifallo con il metodo (statico) `_conv2()` - if bug: - random_numbers_b.append(self._conv2(byte)) - - else: - # -------------------- Metodo 2 -------------------- - for i, bit in enumerate(self.random_bits): - # Copia l'`i`-esimo bit nell'(`i` mod 8)-esima cella di `byte` - byte[i % 8] = bit - if i % 8 == 7: - # Il byte è completo: convertilo in numero decimale e salvalo + with task("Generating random numbers"): + + self.random_numbers = [] + random_numbers_b = [] + # Inizializza un vettore di lunghezza 8 (pieno di zeri) + byte = [0] * 8 + + if _BYTES_GENERATION_METHOD == 0: + # -------------------- Metodo 1 -------------------- + # = ⌊ / 8 ⌋ ('//' è la divisione intera) + n_bytes = len(self.random_bits) // 8 + for i in range(n_bytes): + for j in range(8): + # Prendi 8 elementi da `self.randomBits` e salvali in `byte` + byte[j] = self.random_bits[i * 8 + j] + + # Converti `byte` in un numero da 0 a 255 tramite il metodo (statico) `_conv()`; + # salva poi il risultato nella variabile di istanza. self.random_numbers.append(self._conv(byte)) + # Se il `bug` è attivo, rifallo con il metodo (statico) `_conv2()` if bug: random_numbers_b.append(self._conv2(byte)) - if bug: - self.random_numbers += random_numbers_b + else: + # -------------------- Metodo 2 -------------------- + for i, bit in enumerate(self.random_bits): + # Copia l'`i`-esimo bit nell'(`i` mod 8)-esima cella di `byte` + byte[i % 8] = bit + if i % 8 == 7: + # Il byte è completo: convertilo in numero decimale e salvalo + self.random_numbers.append(self._conv(byte)) + if bug: + random_numbers_b.append(self._conv2(byte)) + + if bug: + self.random_numbers += random_numbers_b - if __debug__: - print(" done.") # Salva la lunghezza di "self.randomNumbers" per un accesso più rapido self.n_random_numbers = len(self.random_numbers) @@ -246,70 +240,63 @@ def test(): bits = gen.random_bits nums = gen.random_numbers - if __debug__ and TO_PLOT: - print("--> Plotting required items:") - _plot_item_message: str = " * {}" - - # ------------------------ Differenze di tempo ------------------------- - if PLOT.TIME_DELTAS in TO_PLOT: - if __debug__: - print(_plot_item_message.format(PLOT.TIME_DELTAS)) - plt.hist(gen.delta_times, bins=500) - plt.yscale("log") - plt.xlabel("Time difference between two conecutive events [Digitizer Clock Periods]") - plt.ylabel("Counts") - plt.title("Time difference between two conecutive events") - plt.show() - - # ------------------------ Distribuzione dei bit ------------------------- - if PLOT.BITS_DISTRIBUTION in TO_PLOT: - if __debug__: - print(_plot_item_message.format(PLOT.BITS_DISTRIBUTION)) - # print(len(gen.deltaT)) # stampa il numero di deltaT disponibili - # print(*gen.randomNumbers, sep="\n") # stampa numeri casuali disponibili - # # Confronta frequenze di 0 e 1 in bits - # n0 = gen.randomBits.count(0) - # print(n0/len(bits), (len(bits)-n0)/len(bits)) - plt.hist(bits, bins=2) # istogramma per confrontare 0 e 1 (i bit) - plt.xlabel("Bit") - plt.ylabel("Counts") - plt.ylim(bottom=0) - plt.title("Bits distribution") - plt.show() - - # ------------------------ Distribuzione dei byte ------------------------- - - if PLOT.BYTES_DISTRIBUTION in TO_PLOT: - if __debug__: - print(_plot_item_message.format(PLOT.BYTES_DISTRIBUTION)) - # Numeri casuali - plt.hist( - nums, - bins=256, - alpha=0.75 if PLOT.BYTES_DISTRIBUTION_LOCAL_MEANS in TO_PLOT else 1, - ) - - if PLOT.BYTES_DISTRIBUTION_LOCAL_MEANS in TO_PLOT: - if __debug__: - print(_plot_item_message.format(PLOT.BYTES_DISTRIBUTION_LOCAL_MEANS)) - # Conta quanti numeri casuali vengono generati in base al loro valore: - # `plt.hist()` lo fa in automatico, ma poiché dobbiamo fare le medie - # locali abbiamo bisogno di ottenere questi conteggi “manualmente” - vals = [0] * 256 - for x in nums: - vals[x] += 1 - # Disegna le medie locali - plt.plot(cyclic_local_means(vals, spread=32)) - - if PLOT.BYTES_DISTRIBUTION in TO_PLOT or PLOT.BYTES_DISTRIBUTION_LOCAL_MEANS in TO_PLOT: - plt.xlabel("Bytes") - plt.ylabel("Counts") - plt.ylim(0, 85) - plt.title("Bytes distribution") - plt.show() - - if __debug__: - print(" done.") + if TO_PLOT: + with task("Plotting required items") as plotting: + _plot_item_message: str = " * {}" + + # ------------------------ Differenze di tempo ------------------------- + if PLOT.TIME_DELTAS in TO_PLOT: + plotting.info(_plot_item_message.format(PLOT.TIME_DELTAS)) + plt.hist(gen.delta_times, bins=500) + plt.yscale("log") + plt.xlabel("Time difference between two conecutive events [Digitizer Clock Periods]") + plt.ylabel("Counts") + plt.title("Time difference between two conecutive events") + plt.show() + + # ------------------------ Distribuzione dei bit ------------------------- + if PLOT.BITS_DISTRIBUTION in TO_PLOT: + plotting.info(_plot_item_message.format(PLOT.BITS_DISTRIBUTION)) + # print(len(gen.deltaT)) # stampa il numero di deltaT disponibili + # print(*gen.randomNumbers, sep="\n") # stampa numeri casuali disponibili + # # Confronta frequenze di 0 e 1 in bits + # n0 = gen.randomBits.count(0) + # print(n0/len(bits), (len(bits)-n0)/len(bits)) + plt.hist(bits, bins=2) # istogramma per confrontare 0 e 1 (i bit) + plt.xlabel("Bit") + plt.ylabel("Counts") + plt.ylim(bottom=0) + plt.title("Bits distribution") + plt.show() + + # ------------------------ Distribuzione dei byte ------------------------- + + if PLOT.BYTES_DISTRIBUTION in TO_PLOT: + plotting.info(_plot_item_message.format(PLOT.BYTES_DISTRIBUTION)) + # Numeri casuali + plt.hist( + nums, + bins=256, + alpha=0.75 if PLOT.BYTES_DISTRIBUTION_LOCAL_MEANS in TO_PLOT else 1, + ) + + if PLOT.BYTES_DISTRIBUTION_LOCAL_MEANS in TO_PLOT: + plotting.info(_plot_item_message.format(PLOT.BYTES_DISTRIBUTION_LOCAL_MEANS)) + # Conta quanti numeri casuali vengono generati in base al loro valore: + # `plt.hist()` lo fa in automatico, ma poiché dobbiamo fare le medie + # locali abbiamo bisogno di ottenere questi conteggi “manualmente” + vals = [0] * 256 + for x in nums: + vals[x] += 1 + # Disegna le medie locali + plt.plot(cyclic_local_means(vals, spread=32)) + + if PLOT.BYTES_DISTRIBUTION in TO_PLOT or PLOT.BYTES_DISTRIBUTION_LOCAL_MEANS in TO_PLOT: + plt.xlabel("Bytes") + plt.ylabel("Counts") + plt.ylim(0, 85) + plt.title("Bytes distribution") + plt.show() # Chiama "test()" quando il programma viene eseguito direttamente From 452a8ca439c34234f8a6cdbf147818b60e9afbf8 Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Tue, 12 Jul 2022 15:27:39 +0200 Subject: [PATCH 071/122] Update root.py --- src/root.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/root.py b/src/root.py index d24d2dd..ee88220 100644 --- a/src/root.py +++ b/src/root.py @@ -8,7 +8,7 @@ from pathlib import Path import sys import os -from log import debug, info, task +from log import critical, debug, info, task # ----- 1. Importa la libreria corretta ------ # @@ -27,11 +27,16 @@ debug("Trying to import `PyROOT`") import ROOT as PyROOT except ModuleNotFoundError: - # Non c'è PyROOT: usiamo uproot - debug("Trying to import `uproot`") - import uproot - - ROOT = False + try: + # Non c'è PyROOT: usiamo uproot + debug("Trying to import `uproot`") + import uproot + except ModuleNotFoundError: + # Non c'è né PyROOT né uproot: + critical("No ROOT backend available: please install either PyROOT (`root`) or `uproot`.") + sys.exit(1) + else: + ROOT = False else: # nessun errore: PyROOT c'è. ROOT = True From f9bd70459e2992c70cf3b4c31e6f45b621638c09 Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Tue, 12 Jul 2022 16:37:42 +0200 Subject: [PATCH 072/122] =?UTF-8?q?=CF=80=20colorato?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pi.py | 46 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 38 insertions(+), 8 deletions(-) diff --git a/src/pi.py b/src/pi.py index 0dc79dc..3ac4dd9 100755 --- a/src/pi.py +++ b/src/pi.py @@ -8,7 +8,7 @@ from pathlib import Path import matplotlib.pyplot as plt from rand import TrueRandomGenerator -from log import info, warning, sprint +from log import info, style, warning, sprint # Costanti @@ -58,7 +58,7 @@ def mode() -> int: # Non valido: continua con la selezione interattiva # Selezione interattiva dell'algoritmo print(""" ->>> Choose an algorithm: +>>> Please choose an algorithm: [0] Interpret data as sequential (x, y) points. [1] Interpret data as adjacent/linked (x, y) points. [2] Generate every possible (x, y) combination. @@ -230,21 +230,51 @@ def main(): # --- Stampa la stima finale di π --- # Per velocizzare i calcoli L = len(str(PI)) - 1 # -1 perché ignoriamo il `.` + # + spi = f"{pi:01.{L-1}f}" + sPI = f"{PI:01.{L-1}f}" # Conta quante cifre sono corrette i = 0 - for i, (spi, sPI) in enumerate(zip(str(pi).replace(".", ""), str(PI).replace(".", ""))): - if sPI != spi: + for i, (digit, DIGIT) in enumerate(zip(spi.replace(".", ""), sPI.replace(".", ""))): + if DIGIT != digit: break # Stampa i valori in un riquadro - print(f"""\ + PI_STYLE = "green" # il valore vero di π + OK_STYLE = "bold green" # le cifre corrette + K0_STYLE = "bold yellow" # la prima cifra errata + KO_STYLE = "bright_red" # le altre cifre errate + sprint(f"""\ ,{'-'*(L+7)}, -| π ≈ {pi:01.{L-1}f} | -| π = {PI:01.{L-1}f} | -| {'+' if i else '^'}-{'+'*(i-1) if i else ''}{'^' if i else ''}{'~'*(L-i-1)} | +| π ≈ {style_pi(spi, i, OK_STYLE, K0_STYLE, KO_STYLE)} | +| π = {style_pi(sPI, i, PI_STYLE, OK_STYLE, PI_STYLE)} | +| { + style('+', OK_STYLE) if i else style('^', K0_STYLE) +}-{ + style('+', OK_STYLE)*(i-1) if i else '' +}{ + style('^', K0_STYLE) if i else '' +}{ + style('~', KO_STYLE)*(L-i-1) +} | '{'-'*(L+7)}'\ """) +def style_pi(pi: str, i: int, OK: str, K0: str, KO: str) -> str: + # i è il numero di cifre corrette + s = "" + for j, c in enumerate(pi.replace(".", "")): + if j < i: + s += style(c, OK) + elif j == i: + s += style(c, K0) + else: + s += style(c, KO) + if j == 0: + s += "." + return s + + # Chiama "main()" quando il programma viene eseguito direttamente if __name__ == "__main__": main() From 950fdfef2b633f35506dc381dc961bf0e5777267 Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Tue, 12 Jul 2022 16:37:50 +0200 Subject: [PATCH 073/122] Update log.py --- src/log.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/log.py b/src/log.py index 15358c2..93f9297 100644 --- a/src/log.py +++ b/src/log.py @@ -19,7 +19,7 @@ try: if NO_RICH: raise ModuleNotFoundError - import rich + import rich.console import rich.markup import rich.logging import rich.highlighter @@ -43,20 +43,21 @@ def style(message: str, style: str) -> str: """Apply the given `style` to `message` only if `rich` is available.""" if RICH: - return f"[{style}]{message}[/{style}]" + return f"[{style}]{message}[/]" return message def sprint(*values, sep: str = " ", end: str = "\n", style: str = ""): """Print styled text to console.""" + console = rich.console.Console(highlight=False) if RICH: if style: io = StringIO() print(*values, sep=sep, end=end, file=io, flush=True) io.seek(0) - rich.print(f"[{style}]{io.read()}[/{style}]", end="", flush=True) + console.print(f"[{style}]{io.read()}[/{style}]", end="") else: - rich.print(*values, sep=sep, end=end, flush=True) + console.print(*values, sep=sep, end=end) else: print(*values, sep=sep, end=end, flush=True) From b491fa3cc2aef097abb185c0e20c590cfcd85f01 Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Tue, 12 Jul 2022 16:38:38 +0200 Subject: [PATCH 074/122] Update pi.py --- src/pi.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pi.py b/src/pi.py index 3ac4dd9..027422c 100755 --- a/src/pi.py +++ b/src/pi.py @@ -261,6 +261,7 @@ def main(): def style_pi(pi: str, i: int, OK: str, K0: str, KO: str) -> str: + """Colora `pi` in base al numero di cifre corrette (`i`) e agli stili specificati.""" # i è il numero di cifre corrette s = "" for j, c in enumerate(pi.replace(".", "")): From 5384535bf96ff5a8d5d1326b44031ba6b77120d3 Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Tue, 12 Jul 2022 16:38:46 +0200 Subject: [PATCH 075/122] Update pi.py --- src/pi.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pi.py b/src/pi.py index 027422c..6ce2bff 100755 --- a/src/pi.py +++ b/src/pi.py @@ -262,7 +262,6 @@ def main(): def style_pi(pi: str, i: int, OK: str, K0: str, KO: str) -> str: """Colora `pi` in base al numero di cifre corrette (`i`) e agli stili specificati.""" - # i è il numero di cifre corrette s = "" for j, c in enumerate(pi.replace(".", "")): if j < i: From 86e603d69a30914ebcea48f4b55625986509fb4d Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Tue, 12 Jul 2022 17:09:29 +0200 Subject: [PATCH 076/122] Unicode box --- src/pi.py | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/pi.py b/src/pi.py index 6ce2bff..3c8fc33 100755 --- a/src/pi.py +++ b/src/pi.py @@ -15,6 +15,9 @@ K = 255**2 SRC = Path(__file__).parent # Cartella di questo file +# Se l'output è formattato male, imposta questa flag a `False` +UNICODE_BOX: bool = True # False + def bug(default: bool, /) -> bool: """Determina se è stato attivato il “bug” da riga di comando.""" @@ -243,11 +246,18 @@ def main(): OK_STYLE = "bold green" # le cifre corrette K0_STYLE = "bold yellow" # la prima cifra errata KO_STYLE = "bright_red" # le altre cifre errate + # Margini del riquadro + UL = "┌" if UNICODE_BOX else "," + UR = "┐" if UNICODE_BOX else "," + DL = "└" if UNICODE_BOX else "'" + DR = "┘" if UNICODE_BOX else "'" + H = "─" if UNICODE_BOX else "-" + V = "│" if UNICODE_BOX else "|" sprint(f"""\ -,{'-'*(L+7)}, -| π ≈ {style_pi(spi, i, OK_STYLE, K0_STYLE, KO_STYLE)} | -| π = {style_pi(sPI, i, PI_STYLE, OK_STYLE, PI_STYLE)} | -| { +{UL}{H*(L+7)}{UR} +{V} π ≈ {style_pi(spi, i, OK_STYLE, K0_STYLE, KO_STYLE)} {V} +{V} π = {style_pi(sPI, i, PI_STYLE, OK_STYLE, PI_STYLE)} {V} +{V} { style('+', OK_STYLE) if i else style('^', K0_STYLE) }-{ style('+', OK_STYLE)*(i-1) if i else '' @@ -255,8 +265,8 @@ def main(): style('^', K0_STYLE) if i else '' }{ style('~', KO_STYLE)*(L-i-1) -} | -'{'-'*(L+7)}'\ +} {V} +{DL}{H*(L+7)}{DR}\ """) From 849899444a31e0c56b6dfdaebacd647a495546b6 Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Tue, 12 Jul 2022 17:21:46 +0200 Subject: [PATCH 077/122] Update spettro.py --- src/spettro.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/spettro.py b/src/spettro.py index bd47796..0fd7a89 100755 --- a/src/spettro.py +++ b/src/spettro.py @@ -50,7 +50,7 @@ def aree( ) -> list[float]: """Calcola l'area di ogni evento.""" logger = taskLogger() - logger.debug(f"{max_area=}, samples range = [{min_samples}, {max_samples}])") + logger.debug(f"{max_area=}, samples range = [{min_samples}, {max_samples}]") aree_calcolate: list[float] = [] for event in events: @@ -76,10 +76,8 @@ def aree( # --- Programma principale ---- -# Funzione principale def main(): - if __debug__: - print("START") + """Funzione principale.""" # ----------------------------- Apertura file ----------------------------- SRC = Path(__file__).parent From e27f7e5722efc400f9436ad5f63c6591f4334550 Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Tue, 12 Jul 2022 17:24:43 +0200 Subject: [PATCH 078/122] Update classe.py --- src/classe.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/classe.py b/src/classe.py index 7da75bc..9cf60b8 100755 --- a/src/classe.py +++ b/src/classe.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- """Per comprendere il funzionamento delle classi.""" +# pylint: disable=protected-access,no-member from __future__ import annotations @@ -64,7 +65,8 @@ def money(self): def pay(self, price: float) -> None: """Paga (= rimuovi dal conto) €`price`.""" - # Senza `abs(...)` un prezzo negativo aumenterebbe i soldi sul conto. Così, il segno viene semplicemente ignorato. + # Senza `abs(...)` un prezzo negativo aumenterebbe i soldi sul conto. + # Così, il segno viene semplicemente ignorato. self.__money -= abs(price) def __repr__(self) -> str: @@ -74,6 +76,7 @@ def __repr__(self) -> str: def privatevar_example(): """Dimostrazione delle variabili private in Python.""" + # type: ignore # print(f"{x:.2f}") -> stampa a schermo `x` con 2 cifre decimali a = Account() print(f"€{a.money:.2f}") # €200.00 @@ -104,7 +107,8 @@ def inheritance_example(): print(Quadrato(10)) # Scrive la stringa ritornata da `Quadrato.__repr__(...)` print(Quadrato(10)*2) # Equivale a `Quadrato(10).__mul__(2)` - print(Quadrato(10)*Quadrato(10)) # TypeError! (`__mul__(...)`, ovvero l'operazione `*`, è definito solo per interi o decimali, non altri quadrati) + print(Quadrato(10)*Quadrato(10)) # TypeError! + # (`__mul__(...)`, ovvero l'operazione `*`, è definito solo per interi o decimali, non altri quadrati) if __name__ == "__main__": From 709bb4ae4d60e06f26d54084db252749d3d5684e Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Tue, 12 Jul 2022 17:27:13 +0200 Subject: [PATCH 079/122] Update classe.py --- src/classe.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/classe.py b/src/classe.py index 9cf60b8..44ec66c 100755 --- a/src/classe.py +++ b/src/classe.py @@ -7,6 +7,7 @@ class Rettangolo: + """Rappresenta un rettangolo.""" a: float b: float @@ -16,10 +17,12 @@ def __init__(self, a: float, b: float) -> None: @property def area(self) -> float: + """Calcola l'area del rettangolo.""" return self.a * self.b @property def perimetro(self) -> float: + """Calcola il perimetro del rettangolo.""" return (self.a + self.b) * 2 def __mul__(self, i: int | float) -> Rettangolo: @@ -47,6 +50,7 @@ def __mul__(self, i: int | float) -> Quadrato: @property def lato(self): + """Il lato di questo quadrato.""" return self.a # o `self.b`, in realtà è indifferente @@ -74,7 +78,7 @@ def __repr__(self) -> str: -def privatevar_example(): +def private_var_example(): """Dimostrazione delle variabili private in Python.""" # type: ignore # print(f"{x:.2f}") -> stampa a schermo `x` con 2 cifre decimali @@ -93,6 +97,7 @@ def privatevar_example(): def inheritance_example(): + """Dimostrazione dell'«albero» delle classi.""" a = Quadrato(3) print(f"{a.lato=}") print(f"{a.perimetro=}") From a76800b28ffe99d567fe6124aaa6e734e55d13cd Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Tue, 12 Jul 2022 17:29:12 +0200 Subject: [PATCH 080/122] Update ideal.py --- src/ideal.py | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/ideal.py b/src/ideal.py index 1448673..b9fb256 100755 --- a/src/ideal.py +++ b/src/ideal.py @@ -1,10 +1,12 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -import matplotlib.pyplot as plt -from math import pi as PI +"""Ciò che succederebbe con un dataset ideale.""" +# from math import pi as PI +# import matplotlib.pyplot as plt -def grid(N): +def grid(N: int) -> float: + """Calcola π sia per eccesso e per difetto su una griglia di lato `N`.""" TOT = N**2 K = (N - 1) ** 2 N_in = 0 @@ -26,15 +28,15 @@ def grid(N): return pi -def theoretical(N, case): - TOT = N**2 - N_in = int(PI * TOT / 4) - if case == 0: - print(N_in) - elif case == 1: - print(N_in * 4 / TOT) - print(abs(PI - N_in * 4 / TOT)) - return N_in +# def theoretical(N, case): +# TOT = N**2 +# N_in = int(PI * TOT / 4) +# if case == 0: +# print(N_in) +# elif case == 1: +# print(N_in * 4 / TOT) +# print(abs(PI - N_in * 4 / TOT)) +# return N_in if __name__ == "__main__": From 3b1f83706286123bb6ce50c9ae0780650bd29ce2 Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Tue, 12 Jul 2022 17:30:11 +0200 Subject: [PATCH 081/122] Update stagisti.py --- src/stagisti.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/stagisti.py b/src/stagisti.py index adea725..511c173 100755 --- a/src/stagisti.py +++ b/src/stagisti.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- +"""Distribuisci le parti della presentazione casualmente fra gli stagisti.""" from rand import TrueRandomGenerator g = TrueRandomGenerator() From 4f88662c752f08c8fd8a5810ecf99e424fc24e55 Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Tue, 12 Jul 2022 17:32:23 +0200 Subject: [PATCH 082/122] =?UTF-8?q?Rimossi=20i=20`=C2=A0`=20(non-breaking?= =?UTF-8?q?=20spaces)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/classe.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/classe.py b/src/classe.py index 44ec66c..3d5af54 100755 --- a/src/classe.py +++ b/src/classe.py @@ -69,7 +69,7 @@ def money(self): def pay(self, price: float) -> None: """Paga (= rimuovi dal conto) €`price`.""" - # Senza `abs(...)` un prezzo negativo aumenterebbe i soldi sul conto. + # Senza `abs(...)` un prezzo negativo aumenterebbe i soldi sul conto. # Così, il segno viene semplicemente ignorato. self.__money -= abs(price) @@ -118,8 +118,8 @@ def inheritance_example(): if __name__ == "__main__": print("~~~ classe.py ~~~") - # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # Decommenta la riga di interesse qua sotto # - # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # #privatevar_example() #inheritance_example() From 01f453c3f63bcac48f864f0cb3a73bcc9d18901d Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Tue, 12 Jul 2022 17:34:29 +0200 Subject: [PATCH 083/122] PEP8 fixes --- src/classe.py | 8 +++----- src/rand.py | 2 -- src/stagisti.py | 2 +- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/classe.py b/src/classe.py index 3d5af54..78e27af 100755 --- a/src/classe.py +++ b/src/classe.py @@ -77,7 +77,6 @@ def __repr__(self) -> str: return f"" - def private_var_example(): """Dimostrazione delle variabili private in Python.""" # type: ignore @@ -95,7 +94,6 @@ def private_var_example(): print(f"€{a.money:.2f}") # €296.86 - def inheritance_example(): """Dimostrazione dell'«albero» delle classi.""" a = Quadrato(3) @@ -110,9 +108,9 @@ def inheritance_example(): print(f"{Quadrato(10).area=}") - print(Quadrato(10)) # Scrive la stringa ritornata da `Quadrato.__repr__(...)` - print(Quadrato(10)*2) # Equivale a `Quadrato(10).__mul__(2)` - print(Quadrato(10)*Quadrato(10)) # TypeError! + print(Quadrato(10)) # Scrive la stringa ritornata da `Quadrato.__repr__(...)` + print(Quadrato(10) * 2) # Equivale a `Quadrato(10).__mul__(2)` + print(Quadrato(10) * Quadrato(10)) # TypeError! # (`__mul__(...)`, ovvero l'operazione `*`, è definito solo per interi o decimali, non altri quadrati) diff --git a/src/rand.py b/src/rand.py index ec3d35d..17b0122 100755 --- a/src/rand.py +++ b/src/rand.py @@ -100,7 +100,6 @@ def __init__( # differenze dei tempi e salvataggio nel vettore `self.bits` self.random_bits = list(map(self._rand, self.delta_times)) - # --- 3. Generazione dei numeri casuali (da 0 a 255) --- with task("Generating random numbers"): @@ -139,7 +138,6 @@ def __init__( if bug: self.random_numbers += random_numbers_b - # Salva la lunghezza di "self.randomNumbers" per un accesso più rapido self.n_random_numbers = len(self.random_numbers) diff --git a/src/stagisti.py b/src/stagisti.py index 511c173..777afb0 100755 --- a/src/stagisti.py +++ b/src/stagisti.py @@ -10,6 +10,6 @@ risultati = [] while len(risultati) < 4: stagista = stagisti[g.random_number() % 4] - if not stagista in risultati: + if stagista not in risultati: risultati.append(stagista) print(risultati) From 595c317ba028c5ab3733b66b9b782cd6c8639deb Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Tue, 12 Jul 2022 17:35:25 +0200 Subject: [PATCH 084/122] More PEP8 fixes --- src/classe.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/classe.py b/src/classe.py index 78e27af..f1d583b 100755 --- a/src/classe.py +++ b/src/classe.py @@ -85,13 +85,13 @@ def private_var_example(): print(f"€{a.money:.2f}") # €200.00 a.pay(3.14) print(f"€{a.money:.2f}") # €196.86 - #print(a.__money) # AttributeError! - #a.__money += 100 # AttributeError! + # print(a.__money) # AttributeError! + # a.__money += 100 # AttributeError! # Purtroppo, però, in Python le variabili “private” non sono veramente private... # per accedervi, utilizzare .___: - print(f"€{a._Account__money:.2f}") # €196.86 + print(f"€{a._Account__money:.2f}") # €196.86 a._Account__money += 100 # Modifico la variabile “privata” - print(f"€{a.money:.2f}") # €296.86 + print(f"€{a.money:.2f}") # €296.86 def inheritance_example(): From 4cbc296e065addf86cafa1ffd41efd1b7b3fe717 Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Tue, 12 Jul 2022 17:36:12 +0200 Subject: [PATCH 085/122] Update classe.py --- src/classe.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/classe.py b/src/classe.py index f1d583b..f7fd13a 100755 --- a/src/classe.py +++ b/src/classe.py @@ -119,5 +119,5 @@ def inheritance_example(): # # # # # # # # # # # # # # # # # # # # # # # # Decommenta la riga di interesse qua sotto # # # # # # # # # # # # # # # # # # # # # # # # - #privatevar_example() - #inheritance_example() + # privatevar_example() + # inheritance_example() From 9110b04eec4f9617564c78794ef85606d8478417 Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Thu, 14 Jul 2022 13:32:58 +0200 Subject: [PATCH 086/122] Migliorata la documentazione --- README.md | 28 +++++++++++++++++++++++----- src/ideal.py | 12 +++++++----- src/pi.py | 4 ++-- 3 files changed, 32 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 0953c43..6616b82 100644 --- a/README.md +++ b/README.md @@ -145,18 +145,36 @@ python -O src/nome-del-file.py ``` Di default, la libreria per leggere i dati è `PyROOT` (quando installata); altrimenti, viene utilizzata `uproot`. -Per forzare l'utilizzo di `uproot` anche quando `PyROOT` è installata, impostare la variabile d'ambiente `FORCE_UPROOT`. +Per forzare l'utilizzo di `uproot` anche quando `PyROOT` è installata, impostare la variabile d'ambiente `FORCE_UPROOT` (il valore assegnato non è importante, basta che in Python si auto-converta in `True` – per esempio, `1`, `42`, `__import__("math").pi` \[sconsigliato], o `True` stesso). +Per disabilitare `FORCE_UPROOT`, assegnare un valore che in Python si auto-converta in `False`, come `0`, `list()` \[sconsigliato] o `False` stesso. Alternativamente, rimuovere la variabile d'ambiente (assegnandole un valore nullo, `FORCE_UPROOT=`). + Su UNIX: ```bash -export FORCE_UPROOT=1 # Anche '=True' va bene +# Imposta la variabile d'ambiente per tutta la sessione +export FORCE_UPROOT=1 python src/file.py -``` +# Più avanti, per disattivarla, si dovrà rimuoverla... +export FORCE_UPROOT="" +# ... o impostarla a `False` +export FORCE_UPROOT=0 -O, per evitare di usare `export`: +# --- oppure ---- -```bash +# Imposta la variabile d'ambiente solo per questo comando FORCE_UPROOT=1 python src/file.py +# Dopo che il comando è stato eseguito, la variabile d'ambiente *non* è più impostata. +``` + +Su Windows: + +```powershell +# Imposta la variabile d'ambiente +set FORCE_UPROOT=1 +# Più avanti, la si dovrà rimuovere... +set FORCE_UPROOT= +# ... o impostare a `False` +set FORCE_UPROOT=0 ``` ### Stagisti diff --git a/src/ideal.py b/src/ideal.py index b9fb256..aa199b9 100755 --- a/src/ideal.py +++ b/src/ideal.py @@ -8,12 +8,14 @@ def grid(N: int) -> float: """Calcola π sia per eccesso e per difetto su una griglia di lato `N`.""" TOT = N**2 - K = (N - 1) ** 2 + squares = [x**2 for x in range(N)] + # K = (N - 1) ** 2 + K = squares[-1] # l'ultimo valore di `squares` è in effetti K N_in = 0 N_out = 0 - for x in range(N): - for y in range(N): - v = x**2 + y**2 - K + for X in squares: + for Y in squares: + v = X + Y - K if v == 0: N_in += 1 N_out += 1 @@ -24,7 +26,7 @@ def grid(N: int) -> float: pim = N_in * 4 / TOT piM = (TOT - N_out) * 4 / TOT pi = (pim + piM) / 2 - print(N, pim, piM, pi, sep=" \t") + print(f"{N}\t{pim:01.15f}\t{piM:01.15f}\t{pi:01.15f}") return pi diff --git a/src/pi.py b/src/pi.py index 3c8fc33..704d0b7 100755 --- a/src/pi.py +++ b/src/pi.py @@ -97,10 +97,10 @@ def main(): title = " Monte Carlo Method π Approximator " sprint(f"{title:=^{width}}", style="bold") # see https://pyformat.info/ for why this works - # Determina il valore di "BUG", tenendo conto della riga di comando + # Determina il valore di `BUG`, tenendo conto delle flag da riga di comando BUG = bug(True) # Di default è attivo - # Comunica che BUG è attivo (per sicurezza) + # Comunica se BUG è attivo (per sicurezza) info(f"BUG is {'en' if BUG else 'dis'}abled.") # Determina l'algoritmo da utilizzare From c04f2cab6216293d4c25eefd5bcbb4d187c053bd Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Thu, 14 Jul 2022 13:37:17 +0200 Subject: [PATCH 087/122] Migliorata la documentazione --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 6616b82..08801ba 100644 --- a/README.md +++ b/README.md @@ -153,6 +153,7 @@ Su UNIX: ```bash # Imposta la variabile d'ambiente per tutta la sessione export FORCE_UPROOT=1 +# Esegui normalmente i programmi python src/file.py # Più avanti, per disattivarla, si dovrà rimuoverla... export FORCE_UPROOT="" @@ -171,6 +172,8 @@ Su Windows: ```powershell # Imposta la variabile d'ambiente set FORCE_UPROOT=1 +# Esegui normalmente i programmi +python src/file.py # Più avanti, la si dovrà rimuovere... set FORCE_UPROOT= # ... o impostare a `False` From 29c4f503a3e6a424159d595ba16a2ed7104d1dae Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Thu, 14 Jul 2022 13:42:26 +0200 Subject: [PATCH 088/122] Update ideal.py --- src/ideal.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ideal.py b/src/ideal.py index aa199b9..e56114b 100755 --- a/src/ideal.py +++ b/src/ideal.py @@ -42,8 +42,10 @@ def grid(N: int) -> float: if __name__ == "__main__": - for N in range(1, 1024): + N = 1 + while True: grid(N) + N += 1 # theoretical(N, case) # diff = [] # for i in range(500): From f4f1df3d1177887db1b6e94824ed75e186cb3b27 Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Thu, 14 Jul 2022 16:04:35 +0200 Subject: [PATCH 089/122] Update .gitignore --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index 4c9b6b7..3e26125 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,10 @@ __pycache__/ *.py[cod] *$py.class +# Cython-generated C/C++ files +*.c +*.cpp + # C extensions *.so From b812625eb0c39c25af5c0751d4dcdcf156082c88 Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Thu, 14 Jul 2022 16:55:21 +0200 Subject: [PATCH 090/122] First stub of compile.py --- compile.py | 108 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 compile.py diff --git a/compile.py b/compile.py new file mode 100644 index 0000000..03d74da --- /dev/null +++ b/compile.py @@ -0,0 +1,108 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +"""Un programma di utility che compila in Cython i moduli richiesti.""" +import os +from pathlib import Path +import sys +from typing import NoReturn +import subprocess +import shutil + + +CYTHON_VERSION = "3.0.0a10" + +def _cython_dep_error() -> NoReturn: + print(f"""\ +ERROR: No compatible Cython version found. + +Please install this Cython version: + pip install Cython={CYTHON_VERSION} +""", file=sys.stderr) + sys.exit(1) + +try: + import cython + from Cython.Build.Cythonize import main as cythonize +except ModuleNotFoundError: + _cython_dep_error() +else: + if cython.__version__ != CYTHON_VERSION: + _cython_dep_error() + + +SRC = Path(__file__).parent/"src" + + +def build(*targets: str) -> None: + for target in targets: + sources = [str(f.resolve()) for f in [SRC/f"{target}.py", SRC/f"{target}.pxd"] if f.exists()] + if not sources: + print(f"--> Skipping {target} (no sources found)") + continue + print(f"--> Building {target} ({', '.join(sources)})") + cythonize(["-3i", *sources]) + + +def rm(*paths: str | Path): + for path in paths: + if not isinstance(path, Path): + path = Path(path) + if not path.exists(): + continue + print(f"Removing {path.relative_to(SRC.parent)}") + if path.is_dir(): + shutil.rmtree(path) + else: + os.unlink(path) + + +def clean() -> None: + rm(*SRC.glob("*.c")) + rm(*SRC.glob("*.html")) + rm(*SRC.glob("*.so")) + rm(*SRC.glob("*.pyd")) + rm(SRC/"build") + + +def run(*argv: str) -> int: + args = list(argv) + target = "" + for arg in args: + if not arg.startswith("-"): + target = arg + break + if not target: + raise ValueError("A target must be specified!") + build(target) + os.chdir(SRC) + index = args.index(target) + args[index] = f"__import__('{target}').main()" + args.insert(index, "-c") + return subprocess.run( + [sys.executable, *args], + ).returncode + + +COMMANDS = dict( + run=run, + build=build, + clean=clean +) + + +def cli(argv: list[str]) -> int | None: + """Interfaccia da riga di comando.""" + if len(argv) < 1: + raise ValueError("Please specify an argument.") + first = argv.pop(0) + if first in COMMANDS: + cmd = COMMANDS[first] + else: + cmd = build + argv = [first] + argv + print(f"{cmd.__name__}({', '.join(argv)})") + return cmd(*argv) + + +if __name__ == "__main__": + sys.exit(cli(sys.argv[1:]) or 0) From 967b068e2ea9765496c63f4808c8bf80474dc55f Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Thu, 14 Jul 2022 18:39:27 +0200 Subject: [PATCH 091/122] Adapted `ideal.py` for cython compilation --- src/ideal.pxd | 16 ++++++++++++++++ src/ideal.py | 18 ++++++++++++++---- src/ideal.pyi | 8 ++++++++ 3 files changed, 38 insertions(+), 4 deletions(-) create mode 100644 src/ideal.pxd create mode 100644 src/ideal.pyi diff --git a/src/ideal.pxd b/src/ideal.pxd new file mode 100644 index 0000000..d4b1121 --- /dev/null +++ b/src/ideal.pxd @@ -0,0 +1,16 @@ +from libc.limits cimport ULLONG_MAX +import cython + + +@cython.locals( + TOT=cython.ulonglong, + K=cython.ulonglong, + N_in=cython.ulonglong, + N_out=cython.ulonglong, + X=cython.ulonglong, + Y=cython.ulonglong, + v=cython.ulonglong, +) +cpdef double grid(unsigned long long N) + +cpdef void main() diff --git a/src/ideal.py b/src/ideal.py index e56114b..645be42 100755 --- a/src/ideal.py +++ b/src/ideal.py @@ -1,11 +1,13 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- """Ciò che succederebbe con un dataset ideale.""" +from __future__ import annotations +import sys # from math import pi as PI # import matplotlib.pyplot as plt -def grid(N: int) -> float: +def grid(N): """Calcola π sia per eccesso e per difetto su una griglia di lato `N`.""" TOT = N**2 squares = [x**2 for x in range(N)] @@ -41,11 +43,15 @@ def grid(N: int) -> float: # return N_in -if __name__ == "__main__": +def main(): + """Main program.""" N = 1 while True: - grid(N) - N += 1 + try: + grid(N) + N += 1 + except KeyboardInterrupt: + sys.exit(0) # theoretical(N, case) # diff = [] # for i in range(500): @@ -54,3 +60,7 @@ def grid(N: int) -> float: # print(diff) # plt.plot(diff) # plt.show() + + +if __name__ == "__main__": + main() diff --git a/src/ideal.pyi b/src/ideal.pyi new file mode 100644 index 0000000..9beab6b --- /dev/null +++ b/src/ideal.pyi @@ -0,0 +1,8 @@ +# -*- coding: utf-8 -*- +from __future__ import annotations + +def grid(N: int) -> float: + ... + +def main() -> None: + ... From d0f9fe7c755161259aa6ea04ad7774e7f2117fc4 Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Fri, 15 Jul 2022 12:05:05 +0200 Subject: [PATCH 092/122] Merged `moduleLogger` and `getLogger` in one function --- src/log.py | 61 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 32 insertions(+), 29 deletions(-) diff --git a/src/log.py b/src/log.py index 93f9297..74e4434 100644 --- a/src/log.py +++ b/src/log.py @@ -35,7 +35,7 @@ # Defined here "TIMESTAMP", "DEFAULT_LEVEL", "ICONS", "STYLES", "ConsoleFormatter", "Logger", - "cli_configure", "getLogger", "moduleLogger", + "cli_configure", "getLogger", "debug", "info", "warning", "error", "critical", "exception", "task", ] @@ -62,26 +62,25 @@ def sprint(*values, sep: str = " ", end: str = "\n", style: str = ""): print(*values, sep=sep, end=end, flush=True) -def getLogger(name: str = "") -> Logger: - """Get the logger associated with this given name.""" +def getLogger(name: str | None = None, /, *, depth: int = 0) -> Logger: + """Get the logger associated with this given name. + + If no name is specified, get the logger of the module + that called this function. + """ if not name: - raise ValueError("You should not use the root logger!") + return getLogger( + inspect.stack()[1 + depth].frame.f_globals["__name__"] + if name is None else name + ) if name == "root": name = "root_" return cast(Logger, logging.getLogger(name)) -def moduleLogger(name: str | None = None, /, *, depth: int = 0) -> Logger: - """Get the logger associated with the module that's calling this function.""" - return getLogger( - inspect.stack()[1 + depth].frame.f_globals["__name__"] - if name is None else name - ) - - def taskLogger(module: str | None = None, /, id: str = "", *, depth: int = 0) -> Logger: """Get the task logger for the module that's calling this function.""" - tl = moduleLogger(module, depth=1 + depth).getChild("task") + tl = getLogger(module, depth=1 + depth).getChild("task") if id: return tl.getChild(id) return tl @@ -130,8 +129,8 @@ class ConsoleFormatter(logging.Formatter): def __init__(self, lfmt: str, rfmt: str, *args, **kwargs) -> None: """Left and right formatter strings.""" - lfmt, rfmt = lfmt.replace('\0', ''), rfmt.replace('\0', '') - super().__init__(f"{lfmt}\0{rfmt}", *args, **kwargs) + lfmt, rfmt = lfmt.replace("\0", ""), rfmt.replace("\0", "") + super().__init__(lfmt + "\0" + rfmt, *args, **kwargs) def format(self, record: logging.LogRecord) -> str: # Activate console markup @@ -296,37 +295,37 @@ def cli_configure() -> None: def task(msg: str, level: int = INFO, id: str | None = "") -> _GeneratorContextManager[Logger]: """Start logging a task.""" - return moduleLogger(depth=1).task(msg, level=level, id=id) + return getLogger(depth=1).task(msg, level=level, id=id) def debug(msg: Any, *args: Any, extra: dict[str, Any] | None = None, **kwargs) -> None: """Log an debug message.""" - moduleLogger(depth=1).debug(msg, *args, extra=extra, **kwargs) + getLogger(depth=1).debug(msg, *args, extra=extra, **kwargs) def info(msg: Any, *args: Any, extra: dict[str, Any] | None = None, **kwargs) -> None: """Log an information.""" - moduleLogger(depth=1).info(msg, *args, extra=extra, **kwargs) + getLogger(depth=1).info(msg, *args, extra=extra, **kwargs) def warning(msg: Any, *args: Any, extra: dict[str, Any] | None = None, **kwargs) -> None: """Log a warning.""" - moduleLogger(depth=1).warning(msg, *args, extra=extra, **kwargs) + getLogger(depth=1).warning(msg, *args, extra=extra, **kwargs) def error(msg: Any, *args: Any, extra: dict[str, Any] | None = None, **kwargs) -> None: """Log an error.""" - moduleLogger(depth=1).error(msg, *args, extra=extra, **kwargs) + getLogger(depth=1).error(msg, *args, extra=extra, **kwargs) def critical(msg: Any, *args: Any, extra: dict[str, Any] | None = None, **kwargs) -> None: """Log an error that causes the program's termination.""" - moduleLogger(depth=1).critical(msg, *args, extra=extra, **kwargs) + getLogger(depth=1).critical(msg, *args, extra=extra, **kwargs) def exception(msg: Any, *args: Any, extra: dict[str, Any] | None = None, **kwargs) -> None: """Log an exception.""" - moduleLogger(depth=1).exception(msg, *args, extra=extra, **kwargs) + getLogger(depth=1).exception(msg, *args, extra=extra, **kwargs) if not eval(os.environ.get("NO_AUTO_LOGGING_CONFIG", "0") or "0"): @@ -335,7 +334,7 @@ def exception(msg: Any, *args: Any, extra: dict[str, Any] | None = None, **kwarg getLogger("matplotlib").setLevel(WARNING) -if __name__ == "__main__": +def main(): cli_configure() logger = getLogger(__name__) logger.critical("Critical") @@ -356,21 +355,25 @@ def exception(msg: Any, *args: Any, extra: dict[str, Any] | None = None, **kwarg with logger.task("Sleep task #1 (1s)") as computation: time.sleep(1) computation.fail() - with logger.task("Sleep task #2 (10s, loop)") as computation: - for _ in range(10): + with logger.task("Sleep task #2 (3s, loop)") as computation: + for _ in range(3): time.sleep(1) computation.done() - with logger.task("Sleep task #3 (10s, loop, log at every iteration)") as computation: - for _ in range(10): + with logger.task("Sleep task #3 (3s, loop, log at every iteration)") as computation: + for _ in range(3): time.sleep(1) computation.info("Just slept 1s.") computation.done() logger.debug("About to define function `_foo` with `@task` decorator") - @task("Foo task") + @logger.task("Sleep task #4 (3s, via function)") def _foo(x: int) -> None: for __ in range(x): time.sleep(1) logger.debug("After defining function `_foo` with `@task` decorator") - _foo(2) + _foo(3) + + +if __name__ == "__main__": + main() From fc458ee7cccba859aa3ff2eb6576c2a86c0b2a0e Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Fri, 15 Jul 2022 12:26:48 +0200 Subject: [PATCH 093/122] Switched to explicit module logger --- src/root.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/root.py b/src/root.py index ee88220..bf5077a 100644 --- a/src/root.py +++ b/src/root.py @@ -8,7 +8,10 @@ from pathlib import Path import sys import os -from log import critical, debug, info, task +from log import getLogger + + +L = getLogger(__name__) # ----- 1. Importa la libreria corretta ------ # @@ -21,19 +24,19 @@ # Imposta la variabile `ROOT` di conseguenza. ROOT: bool try: - debug(f"Environment variable `FORCE_UPROOT` is {'' if FORCE_UPROOT else 'not '}set.") + L.debug(f"Environment variable `FORCE_UPROOT` is {'' if FORCE_UPROOT else 'not '}set.") if FORCE_UPROOT: raise ModuleNotFoundError - debug("Trying to import `PyROOT`") + L.debug("Trying to import `PyROOT`") import ROOT as PyROOT except ModuleNotFoundError: try: # Non c'è PyROOT: usiamo uproot - debug("Trying to import `uproot`") + L.debug("Trying to import `uproot`") import uproot except ModuleNotFoundError: # Non c'è né PyROOT né uproot: - critical("No ROOT backend available: please install either PyROOT (`root`) or `uproot`.") + L.critical("No ROOT backend available: please install either PyROOT (`root`) or `uproot`.") sys.exit(1) else: ROOT = False @@ -42,7 +45,7 @@ ROOT = True -info(f"ROOT backend: {'PyROOT' if ROOT else 'uproot'}") +L.info(f"ROOT backend: {'PyROOT' if ROOT else 'uproot'}") # ----- 2. Definisci la funzione di lettura ------ # @@ -137,7 +140,7 @@ def read( # In `vals` vengono salvati i parametri da passare alla classe nella costruzione dell'oggetto vals: dict[str, Any] = {} - with task(f"Reading tree {tree!r} from file {file!r}...") as reading: + with L.task(f"Reading tree {tree!r} from file {file!r}...") as reading: if ROOT: # --- PyROOT --- # Termina il loop degli eventi di PyROOT, in modo che non interferisca con matplotlib From 22cb5129c8c39e113f5e7845f6b9dc9140addd16 Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Fri, 15 Jul 2022 12:27:08 +0200 Subject: [PATCH 094/122] More improvements --- compile.py | 50 +++++++++++++++++++++++++++++++++++++------------- 1 file changed, 37 insertions(+), 13 deletions(-) diff --git a/compile.py b/compile.py index 03d74da..fcb633f 100644 --- a/compile.py +++ b/compile.py @@ -31,16 +31,38 @@ def _cython_dep_error() -> NoReturn: SRC = Path(__file__).parent/"src" +TARGETS = [f.stem for f in SRC.glob("*.py")] +PYTHON_FRAMES: bool = True -def build(*targets: str) -> None: +def list_targets() -> None: + print(*TARGETS, sep=", ") + + +def build(*targets: str) -> int: + if "all" in targets: + return build(*(f.stem for f in SRC.glob("*.py"))) for target in targets: sources = [str(f.resolve()) for f in [SRC/f"{target}.py", SRC/f"{target}.pxd"] if f.exists()] if not sources: print(f"--> Skipping {target} (no sources found)") continue print(f"--> Building {target} ({', '.join(sources)})") - cythonize(["-3i", *sources]) + try: + args = [ + "-3ia", + "-j", str(os.cpu_count()), + "-X", f"linetrace={PYTHON_FRAMES}", + "-X", f"profile={PYTHON_FRAMES}", + # "-s", f"trace={PYTHON_FRAMES}", + "--lenient", + *sources, + ] + print(f"$ cythonize {' '.join(args)}") + cythonize(args) + except SystemExit as e: + return e.code + return 0 def rm(*paths: str | Path): @@ -57,11 +79,13 @@ def rm(*paths: str | Path): def clean() -> None: - rm(*SRC.glob("*.c")) - rm(*SRC.glob("*.html")) - rm(*SRC.glob("*.so")) - rm(*SRC.glob("*.pyd")) - rm(SRC/"build") + rm( + *SRC.glob("*.c"), + *SRC.glob("*.html"), + *SRC.glob("*.so"), + *SRC.glob("*.pyd"), + SRC/"build", + ) def run(*argv: str) -> int: @@ -76,17 +100,18 @@ def run(*argv: str) -> int: build(target) os.chdir(SRC) index = args.index(target) - args[index] = f"__import__('{target}').main()" + args[index] = f"import {target}; getattr({target}, 'main', getattr({target}, 'test', lambda: None))()" args.insert(index, "-c") - return subprocess.run( - [sys.executable, *args], - ).returncode + args.insert(0, sys.executable) + print(f"--> Running {target} ({' '.join([repr(x) for x in args])})") + return subprocess.run(args, check=False).returncode COMMANDS = dict( run=run, build=build, - clean=clean + clean=clean, + list=list_targets, ) @@ -100,7 +125,6 @@ def cli(argv: list[str]) -> int | None: else: cmd = build argv = [first] + argv - print(f"{cmd.__name__}({', '.join(argv)})") return cmd(*argv) From 086ecfb9b150e7410e0c326f94894aeefa0806f5 Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Fri, 15 Jul 2022 12:29:42 +0200 Subject: [PATCH 095/122] Update .gitignore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 3e26125..76e7a0b 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,9 @@ __pycache__/ *.c *.cpp +# Cython-generated compilation annotations +*.html + # C extensions *.so From be2192bc325bca3bb031d563a982778a3cfeb400 Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Fri, 15 Jul 2022 12:30:11 +0200 Subject: [PATCH 096/122] Update log.py --- src/log.py | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/log.py b/src/log.py index 74e4434..72d4e23 100644 --- a/src/log.py +++ b/src/log.py @@ -336,42 +336,42 @@ def exception(msg: Any, *args: Any, extra: dict[str, Any] | None = None, **kwarg def main(): cli_configure() - logger = getLogger(__name__) - logger.critical("Critical") - logger.error("Error") - logger.warning("Warning") - logger.info("Info") - logger.debug("Debug") - with logger.task("Null task #1") as computation: + L = getLogger(__name__) + L.critical("Critical") + L.error("Error") + L.warning("Warning") + L.info("Info") + L.debug("Debug") + with L.task("Null task #1") as computation: pass - with logger.task("Null task #2") as computation: + with L.task("Null task #2") as computation: computation.done() - with logger.task("Null task #3") as computation: + with L.task("Null task #3") as computation: computation.done("explicit") - with logger.task("Null task #4") as computation: + with L.task("Null task #4") as computation: computation.fail("explicit") - with logger.task("Null task #5") as computation: + with L.task("Null task #5") as computation: computation.result = "custom result" - with logger.task("Sleep task #1 (1s)") as computation: + with L.task("Sleep task #1 (1s)") as computation: time.sleep(1) computation.fail() - with logger.task("Sleep task #2 (3s, loop)") as computation: + with L.task("Sleep task #2 (3s, loop)") as computation: for _ in range(3): time.sleep(1) computation.done() - with logger.task("Sleep task #3 (3s, loop, log at every iteration)") as computation: + with L.task("Sleep task #3 (3s, loop, log at every iteration)") as computation: for _ in range(3): time.sleep(1) computation.info("Just slept 1s.") computation.done() - logger.debug("About to define function `_foo` with `@task` decorator") + L.debug("About to define function `_foo` with `@task` decorator") - @logger.task("Sleep task #4 (3s, via function)") + @L.task("Sleep task #4 (3s, via function)") def _foo(x: int) -> None: for __ in range(x): time.sleep(1) - logger.debug("After defining function `_foo` with `@task` decorator") + L.debug("After defining function `_foo` with `@task` decorator") _foo(3) From ec3c3c25a528d6ab5ed56f0911502e3eca596da3 Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Fri, 15 Jul 2022 13:17:49 +0200 Subject: [PATCH 097/122] Update compile.py --- compile.py | 61 +++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 53 insertions(+), 8 deletions(-) diff --git a/compile.py b/compile.py index fcb633f..dafe097 100644 --- a/compile.py +++ b/compile.py @@ -1,6 +1,11 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -"""Un programma di utility che compila in Cython i moduli richiesti.""" +"""Un programma di utility che compila in Cython i moduli richiesti. + + python compile.py [COMMAND] [ARGUMENTS] + python compile.py help + python compile.py commands +""" import os from pathlib import Path import sys @@ -36,25 +41,32 @@ def _cython_dep_error() -> NoReturn: def list_targets() -> None: + """Ottieni una lista di tutti i moduli disponibili. + + python compile.py list + """ print(*TARGETS, sep=", ") def build(*targets: str) -> int: + """Compila con Cython i moduli specificati. + + python compile.py build *[TARGETS] + python compile.py build log root stagisti + """ if "all" in targets: return build(*(f.stem for f in SRC.glob("*.py"))) for target in targets: - sources = [str(f.resolve()) for f in [SRC/f"{target}.py", SRC/f"{target}.pxd"] if f.exists()] - if not sources: - print(f"--> Skipping {target} (no sources found)") + if not target in TARGETS: continue + sources = [str(f.resolve()) for f in [SRC/f"{target}.py", SRC/f"{target}.pxd"] if f.exists()] print(f"--> Building {target} ({', '.join(sources)})") try: args = [ "-3ia", "-j", str(os.cpu_count()), - "-X", f"linetrace={PYTHON_FRAMES}", - "-X", f"profile={PYTHON_FRAMES}", - # "-s", f"trace={PYTHON_FRAMES}", + # "-X", f"linetrace={PYTHON_FRAMES}", + # "-X", f"profile={PYTHON_FRAMES}", "--lenient", *sources, ] @@ -66,6 +78,7 @@ def build(*targets: str) -> int: def rm(*paths: str | Path): + """Elimina i file e le cartelle in `paths`.""" for path in paths: if not isinstance(path, Path): path = Path(path) @@ -79,6 +92,10 @@ def rm(*paths: str | Path): def clean() -> None: + """Rimuovi gli elementi creati durante la `build`. + + python compile.py clean + """ rm( *SRC.glob("*.c"), *SRC.glob("*.html"), @@ -89,6 +106,11 @@ def clean() -> None: def run(*argv: str) -> int: + """Compila ed esegui il modulo dato con gli argomenti dati. + + python compile.py run *[OPZIONI PYTHON] [PROGRAMMA] *[ARGOMENTI/OPZIONI PROGRAMMA] + python compile.py run -O root -vv data.root + """ args = list(argv) target = "" for arg in args: @@ -107,18 +129,41 @@ def run(*argv: str) -> int: return subprocess.run(args, check=False).returncode +def help(cmd: str | None = None, /) -> None: + """Get help for a given command. + + python compile.py help [COMMAND] + python compile.py help commands + """ + if cmd is None: + print(__doc__) + help("help") + else: + print(COMMANDS.get(cmd, help).__doc__) + + +def list_commands() -> None: + """List the available commands. + + python compile.py commands + """ + print(*COMMANDS, sep=" ") + + COMMANDS = dict( run=run, build=build, clean=clean, list=list_targets, + help=help, + commands=list_commands, ) def cli(argv: list[str]) -> int | None: """Interfaccia da riga di comando.""" if len(argv) < 1: - raise ValueError("Please specify an argument.") + return help() first = argv.pop(0) if first in COMMANDS: cmd = COMMANDS[first] From 8cb392ef2044a92dc0564ede1d04ba162b14c6e6 Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Fri, 15 Jul 2022 13:24:55 +0200 Subject: [PATCH 098/122] Update log.py --- src/log.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/log.py b/src/log.py index 72d4e23..f033e7f 100644 --- a/src/log.py +++ b/src/log.py @@ -69,10 +69,20 @@ def getLogger(name: str | None = None, /, *, depth: int = 0) -> Logger: that called this function. """ if not name: - return getLogger( - inspect.stack()[1 + depth].frame.f_globals["__name__"] - if name is None else name - ) + try: + return getLogger( + inspect.stack()[1 + depth].frame.f_globals["__name__"] + if name is None else name + ) + except IndexError: + getLogger(__name__).error( + "Could not resolve `__name__` from an outer frame." + "There may be a problem with the interpreter frame stack," + "most probably due to the caller module being Cython-compiled." + "Please either switch from `(...)` to `getLogger(__name__).(...)` syntax," + "or avoid Cython-compiling that module." + ) + sys.exit(1) if name == "root": name = "root_" return cast(Logger, logging.getLogger(name)) From 872b165f18f21b67fa0b202c75d7d38f2c26f257 Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Fri, 15 Jul 2022 13:30:52 +0200 Subject: [PATCH 099/122] Update compile.py --- compile.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compile.py b/compile.py index dafe097..dd26a8f 100644 --- a/compile.py +++ b/compile.py @@ -45,7 +45,7 @@ def list_targets() -> None: python compile.py list """ - print(*TARGETS, sep=", ") + print("all", *TARGETS, sep=", ") def build(*targets: str) -> int: @@ -55,7 +55,7 @@ def build(*targets: str) -> int: python compile.py build log root stagisti """ if "all" in targets: - return build(*(f.stem for f in SRC.glob("*.py"))) + return build(*TARGETS) for target in targets: if not target in TARGETS: continue From be6dc95a7478df9825e507cd4e270a5853c42bb7 Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Fri, 15 Jul 2022 18:49:39 +0200 Subject: [PATCH 100/122] Improved launching script --- compile.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/compile.py b/compile.py index dd26a8f..e06267e 100644 --- a/compile.py +++ b/compile.py @@ -122,10 +122,15 @@ def run(*argv: str) -> int: build(target) os.chdir(SRC) index = args.index(target) - args[index] = f"import {target}; getattr({target}, 'main', getattr({target}, 'test', lambda: None))()" + args[index] = ( + f"import {target}; " + f"func = getattr({target}, 'main', getattr({target}, 'test', lambda: None)); " + f"print('-->', 'Running', '{target}.' + func.__name__ + '()', 'from', {target}.__file__); " + f"func()" + ) args.insert(index, "-c") args.insert(0, sys.executable) - print(f"--> Running {target} ({' '.join([repr(x) for x in args])})") + print(f"--> Running {target}") return subprocess.run(args, check=False).returncode From b7dd61e9b2abb2b27142f8651eea7fdb9de41846 Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Fri, 15 Jul 2022 18:55:48 +0200 Subject: [PATCH 101/122] Added [multiple] targets support to `clean` --- compile.py | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/compile.py b/compile.py index e06267e..749278e 100644 --- a/compile.py +++ b/compile.py @@ -91,18 +91,30 @@ def rm(*paths: str | Path): os.unlink(path) -def clean() -> None: +def clean(*targets) -> None: """Rimuovi gli elementi creati durante la `build`. + python compile.py clean *[TARGETS] + python compile.py clean root log + python compile.py clean all python compile.py clean """ - rm( - *SRC.glob("*.c"), - *SRC.glob("*.html"), - *SRC.glob("*.so"), - *SRC.glob("*.pyd"), - SRC/"build", - ) + if not targets or "all" in targets: + rm( + *SRC.glob("*.c"), + *SRC.glob("*.html"), + *SRC.glob("*.so"), + *SRC.glob("*.pyd"), + SRC/"build", + ) + return + for target in targets: + rm( + SRC/f"{target}.c", + SRC/f"{target}.html", + *SRC.glob(f"{target}.*.so"), + *SRC.glob(f"build/lib.*/{target}.*.so"), + ) def run(*argv: str) -> int: From 389e062519c49c77689d29ab8432f6e69bd9086a Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Fri, 15 Jul 2022 19:00:54 +0200 Subject: [PATCH 102/122] Better support for lambdas --- compile.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/compile.py b/compile.py index 749278e..34fbaf2 100644 --- a/compile.py +++ b/compile.py @@ -134,12 +134,13 @@ def run(*argv: str) -> int: build(target) os.chdir(SRC) index = args.index(target) - args[index] = ( - f"import {target}; " - f"func = getattr({target}, 'main', getattr({target}, 'test', lambda: None)); " - f"print('-->', 'Running', '{target}.' + func.__name__ + '()', 'from', {target}.__file__); " - f"func()" - ) + args[index] = (f"""\ +import {target} +func = getattr({target}, 'main', getattr({target}, 'test', None)) +if func: + print('-->', 'Running', '{target}.' + func.__name__ + '()', 'from', {target}.__file__) + func() +""") args.insert(index, "-c") args.insert(0, sys.executable) print(f"--> Running {target}") From feb45e1ec8a6a1106217116e7d8fb90dcc260cc4 Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Fri, 15 Jul 2022 19:04:57 +0200 Subject: [PATCH 103/122] Update compile.py --- compile.py | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/compile.py b/compile.py index 34fbaf2..6ab6c96 100644 --- a/compile.py +++ b/compile.py @@ -117,6 +117,18 @@ def clean(*targets) -> None: ) + +RUN = r"""\ +print(f'\n--> Importing $$') +import $$ +func = getattr($$, 'main', getattr($$, 'test', None)) +if func: + print(f'\n--> Running $$.{func.__name__}() from {$$.__file__}') + func() +""" + + + def run(*argv: str) -> int: """Compila ed esegui il modulo dato con gli argomenti dati. @@ -134,16 +146,9 @@ def run(*argv: str) -> int: build(target) os.chdir(SRC) index = args.index(target) - args[index] = (f"""\ -import {target} -func = getattr({target}, 'main', getattr({target}, 'test', None)) -if func: - print('-->', 'Running', '{target}.' + func.__name__ + '()', 'from', {target}.__file__) - func() -""") + args[index] = RUN.replace("$$", target) args.insert(index, "-c") args.insert(0, sys.executable) - print(f"--> Running {target}") return subprocess.run(args, check=False).returncode From c4d8b0c1a2376c5bb21d70aab54cd32986e2bb7d Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Fri, 15 Jul 2022 19:06:12 +0200 Subject: [PATCH 104/122] Update compile.py --- compile.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compile.py b/compile.py index 6ab6c96..f9ac3b5 100644 --- a/compile.py +++ b/compile.py @@ -122,8 +122,9 @@ def clean(*targets) -> None: print(f'\n--> Importing $$') import $$ func = getattr($$, 'main', getattr($$, 'test', None)) +print(f'\n--> $$ has been imported from {$$.__file__}') if func: - print(f'\n--> Running $$.{func.__name__}() from {$$.__file__}') + print(f'--> Running $$.{func.__name__}()') func() """ From 41f3cb59629f1b356842d3d4872c646601b8f90a Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Fri, 15 Jul 2022 19:07:28 +0200 Subject: [PATCH 105/122] Fixed non-breaking spaces --- compile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compile.py b/compile.py index f9ac3b5..8c0c3cc 100644 --- a/compile.py +++ b/compile.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- """Un programma di utility che compila in Cython i moduli richiesti. python compile.py [COMMAND] [ARGUMENTS] From 1071f4098f4765f59366fe94e8b6d113a3e8ad7f Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Fri, 15 Jul 2022 19:09:31 +0200 Subject: [PATCH 106/122] PEP-8 fixes --- compile.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/compile.py b/compile.py index 8c0c3cc..12da473 100644 --- a/compile.py +++ b/compile.py @@ -16,6 +16,7 @@ CYTHON_VERSION = "3.0.0a10" + def _cython_dep_error() -> NoReturn: print(f"""\ ERROR: No compatible Cython version found. @@ -25,6 +26,7 @@ def _cython_dep_error() -> NoReturn: """, file=sys.stderr) sys.exit(1) + try: import cython from Cython.Build.Cythonize import main as cythonize @@ -35,7 +37,7 @@ def _cython_dep_error() -> NoReturn: _cython_dep_error() -SRC = Path(__file__).parent/"src" +SRC = Path(__file__).parent / "src" TARGETS = [f.stem for f in SRC.glob("*.py")] PYTHON_FRAMES: bool = True @@ -57,9 +59,9 @@ def build(*targets: str) -> int: if "all" in targets: return build(*TARGETS) for target in targets: - if not target in TARGETS: + if target not in TARGETS: continue - sources = [str(f.resolve()) for f in [SRC/f"{target}.py", SRC/f"{target}.pxd"] if f.exists()] + sources = [str(f.resolve()) for f in [SRC / f"{target}.py", SRC / f"{target}.pxd"] if f.exists()] print(f"--> Building {target} ({', '.join(sources)})") try: args = [ @@ -105,19 +107,18 @@ def clean(*targets) -> None: *SRC.glob("*.html"), *SRC.glob("*.so"), *SRC.glob("*.pyd"), - SRC/"build", + SRC / "build", ) return for target in targets: rm( - SRC/f"{target}.c", - SRC/f"{target}.html", + SRC / f"{target}.c", + SRC / f"{target}.html", *SRC.glob(f"{target}.*.so"), *SRC.glob(f"build/lib.*/{target}.*.so"), ) - RUN = r"""\ print(f'\n--> Importing $$') import $$ @@ -129,7 +130,6 @@ def clean(*targets) -> None: """ - def run(*argv: str) -> int: """Compila ed esegui il modulo dato con gli argomenti dati. From 8a67889a3b616ab225ec006a23a7c1850ca2df7e Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Fri, 15 Jul 2022 19:14:15 +0200 Subject: [PATCH 107/122] Update spettro.py --- src/spettro.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/spettro.py b/src/spettro.py index 0fd7a89..ead2720 100755 --- a/src/spettro.py +++ b/src/spettro.py @@ -84,6 +84,7 @@ def main(): t = root.read(SRC / "data.root", "Data_R", cls=Event) # ------------------------ Calcolo della baseline ------------------------- + BASELINE = None if BASELINE_CALC_MODE == 0: with task("Calculating baseline...") as calc: medie = [] @@ -95,8 +96,6 @@ def main(): BASELINE = mean(medie) # BASELINE = 13313.683338704632 # già calcolata, all'occorrenza calc.result = f"it's {BASELINE}" - else: - BASELINE = None # ---------------------- Calibrazione spettro in keV ---------------------- X1 = 118900 # picco a 1436 keV From b93fcf7f526634cc651c275089f3342b8e88bda5 Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Fri, 15 Jul 2022 19:17:22 +0200 Subject: [PATCH 108/122] Fixed logger --- src/spettro.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/spettro.py b/src/spettro.py index ead2720..c98ffee 100755 --- a/src/spettro.py +++ b/src/spettro.py @@ -6,10 +6,12 @@ from typing import Literal, NamedTuple import matplotlib.pyplot as plt import root -from log import task, taskLogger +from log import getLogger, taskLogger # --- Costanti --- +# Logger per questo programma +L = getLogger(__name__) # Distanza temporale tra due samples T: float = 4 # µs # Numero di samples da prendere per calcolare la baseline @@ -40,7 +42,7 @@ def mean(v: list[float] | list[int]) -> float: # Calcolo delle aree per ogni evento -@task(f"Calculating {'BASELINES and ' if BASELINE_CALC_MODE == 1 else ''}areas") +@L.task(f"Calculating {'BASELINES and ' if BASELINE_CALC_MODE == 1 else ''}areas") def aree( events: list[Event], BASELINE: float | None = None, @@ -49,7 +51,7 @@ def aree( max_samples: int | None = None, ) -> list[float]: """Calcola l'area di ogni evento.""" - logger = taskLogger() + logger = taskLogger(__name__) logger.debug(f"{max_area=}, samples range = [{min_samples}, {max_samples}]") aree_calcolate: list[float] = [] @@ -86,7 +88,7 @@ def main(): # ------------------------ Calcolo della baseline ------------------------- BASELINE = None if BASELINE_CALC_MODE == 0: - with task("Calculating baseline...") as calc: + with L.task("Calculating baseline...") as calc: medie = [] for event in t: # Calcola della media dei primi `BASELINE_CALC_N` samples richiamando la funzione "mean" From 193529e6178773d18769398797b0048f0cae9c7c Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Sat, 16 Jul 2022 16:27:39 +0200 Subject: [PATCH 109/122] Improved `root` cythonization --- pyproject.toml | 1 + src/root.pxd | 3 +++ 2 files changed, 4 insertions(+) create mode 100644 src/root.pxd diff --git a/pyproject.toml b/pyproject.toml index 4582791..f4c10fe 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -35,4 +35,5 @@ disable = [ "R0914", # Too many local variables (*/15) "R0915", # Too many statements (*/50) "R1704", # Redefining argument with the local name '*' + "E0611", # No name * in module * ] diff --git a/src/root.pxd b/src/root.pxd new file mode 100644 index 0000000..7f641b5 --- /dev/null +++ b/src/root.pxd @@ -0,0 +1,3 @@ +cdef: + bint FORCE_UPROOT + bint ROOT From c577f8f621b23e84e9da3dd0f07fd33d007ff6be Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Mon, 18 Jul 2022 18:13:21 +0200 Subject: [PATCH 110/122] Improved Cythonization of `root,py` --- src/root.pxd | 21 ++++++++ src/root.py | 137 ++++++++++++++++++++++++++++----------------------- 2 files changed, 95 insertions(+), 63 deletions(-) diff --git a/src/root.pxd b/src/root.pxd index 7f641b5..1c712d8 100644 --- a/src/root.pxd +++ b/src/root.pxd @@ -1,3 +1,24 @@ +import cython cdef: bint FORCE_UPROOT bint ROOT + + +@cython.locals( + data=list, + vals=dict, + f=object, + t=object, + x=object, + raw_data=dict, + branches=dict, + attr=str, + i=cython.long, +) +cdef list[object] _read( + object file, + type cls, + str tree, + list attributes, + list list_conv, +) diff --git a/src/root.py b/src/root.py index bf5077a..c7ec2b6 100644 --- a/src/root.py +++ b/src/root.py @@ -49,9 +49,82 @@ # ----- 2. Definisci la funzione di lettura ------ # + _T = TypeVar("_T", bound=NamedTuple) +def _read( + file: str | Path, + cls: type[_T], + tree: str, + attributes: list[str], + list_conv: list[str], +) -> list[_T]: + # Inizializzazione variabili + file = str(Path(file).expanduser().resolve()) + data: list[_T] = [] # Questo sarà il risultato della funzione + # In `vals` vengono salvati i parametri da passare alla classe nella costruzione dell'oggetto + vals: dict[str, Any] = {} + + with L.task(f"Reading tree {tree!r} from file {file!r}...") as reading: + + if ROOT: # --- PyROOT --- + # Termina il loop degli eventi di PyROOT, in modo che non interferisca con matplotlib + PyROOT.keeppolling = 0 # type: ignore + # Apri il file + f = PyROOT.TFile(file) # type: ignore + # Leggi l'albero + t = f.Get(tree) + # Leggi e salva i dati di interesse + for x in t: + vals.clear() # Svuota i parametri + for attr in attributes: + # Converti l'attributo in lista ove necessario + if attr in list_conv: + vals[attr] = [*getattr(x, attr)] + else: + vals[attr] = getattr(x, attr) + # Crea l'oggetto e aggiungilo a `data` + data.append(cls(**vals)) # type: ignore + # Chiudi il file + f.Close() + + else: # --- uproot --- + + # Mappa vuota per i dati grezzi + # (associa al nome dell'attributo la lista dei valori, ancora da combinare negli oggetti) + raw_data: dict[str, Any] = {} + # Apri l'albero `tree` dal file `file` + with uproot.open(f"{file}:{tree}") as t: + # Salva i “rami” come mappa + branches = dict(t.iteritems()) + for attr in attributes: + # Converti l'attributo in lista ove necessario + if attr in list_conv: + raw_data[attr] = list(map(list, branches[attr].array())) + else: + raw_data[attr] = list(branches[attr].array()) + + # Converti i dati grezzi in lista di oggetti: + # scorri gli indici e associa gli attributi corrispondenti, creando l'oggetto + # + # i: 0 1 2 3 ... + # | | | | + # V V V V + # attr0: x00 x01 x02 x03 ... ¯| + # attr1: x10 x11 x12 x13 ... |--> raw_data + # attr2: x20 x21 x22 x23 ... _| + # | | | | + # V V V V + # data: ### ### ### ### ... + # + for i in range(len(raw_data[attributes[0]])): + data.append(cls(**{name: val[i] for name, val in raw_data.items()})) # type: ignore + + reading.result = f"read {len(data)} items" + return data + + # O si specifica la classe tramite il parametro `cls`... @overload def read(file: Path | str, tree: str, /, *, cls: type[_T]) -> list[_T]: @@ -134,69 +207,7 @@ def read( if issubclass(get_origin(t) or t, list) ] - # Inizializzazione variabili - file = str(Path(file).expanduser().resolve()) - data: list[_T] = [] # Questo sarà il risultato della funzione - # In `vals` vengono salvati i parametri da passare alla classe nella costruzione dell'oggetto - vals: dict[str, Any] = {} - - with L.task(f"Reading tree {tree!r} from file {file!r}...") as reading: - - if ROOT: # --- PyROOT --- - # Termina il loop degli eventi di PyROOT, in modo che non interferisca con matplotlib - PyROOT.keeppolling = 0 # type: ignore - # Apri il file - f = PyROOT.TFile(file) # type: ignore - # Leggi l'albero - t = f.Get(tree) - # Leggi e salva i dati di interesse - for x in t: - vals.clear() # Svuota i parametri - for attr in attributes: - # Converti l'attributo in lista ove necessario - if attr in list_conv: - vals[attr] = [*getattr(x, attr)] - else: - vals[attr] = getattr(x, attr) - # Crea l'oggetto e aggiungilo a `data` - data.append(cls(**vals)) # type: ignore - # Chiudi il file - f.Close() - - else: # --- uproot --- - - # Mappa vuota per i dati grezzi - # (associa al nome dell'attributo la lista dei valori, ancora da combinare negli oggetti) - raw_data: dict[str, Any] = {} - # Apri l'albero `tree` dal file `file` - with uproot.open(f"{file}:{tree}") as t: - # Salva i “rami” come mappa - branches = dict(t.iteritems()) - for attr in attributes: - # Converti l'attributo in lista ove necessario - if attr in list_conv: - raw_data[attr] = list(map(list, branches[attr].array())) - else: - raw_data[attr] = list(branches[attr].array()) - - # Converti i dati grezzi in lista di oggetti: - # scorri gli indici e associa gli attributi corrispondenti, creando l'oggetto - # - # i: 0 1 2 3 ... - # | | | | - # V V V V - # attr0: x00 x01 x02 x03 ... ¯| - # attr1: x10 x11 x12 x13 ... |--> raw_data - # attr2: x20 x21 x22 x23 ... _| - # | | | | - # V V V V - # data: ### ### ### ### ... - # - for i in range(len(raw_data[attributes[0]])): - data.append(cls(**{name: val[i] for name, val in raw_data.items()})) # type: ignore - - reading.result = f"read {len(data)} items" - return data + return _read(file, cls, tree, list(attributes), list_conv) # type: ignore # "Esporta" i simboli di interesse From 66f8688d8a61d23f6ec82b5aac608bc0132ce87b Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Mon, 18 Jul 2022 19:59:32 +0200 Subject: [PATCH 111/122] Include full C/C++ code in annotation HTMLs --- compile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compile.py b/compile.py index 12da473..f7c18b4 100644 --- a/compile.py +++ b/compile.py @@ -65,7 +65,7 @@ def build(*targets: str) -> int: print(f"--> Building {target} ({', '.join(sources)})") try: args = [ - "-3ia", + "-3i", "--annotate-fullc", "-j", str(os.cpu_count()), # "-X", f"linetrace={PYTHON_FRAMES}", # "-X", f"profile={PYTHON_FRAMES}", From 4dc523646488de9d3399e334b35e699dedc48fef Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Mon, 18 Jul 2022 20:00:05 +0200 Subject: [PATCH 112/122] Critical, not error --- src/log.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/log.py b/src/log.py index f033e7f..3c09069 100644 --- a/src/log.py +++ b/src/log.py @@ -75,11 +75,11 @@ def getLogger(name: str | None = None, /, *, depth: int = 0) -> Logger: if name is None else name ) except IndexError: - getLogger(__name__).error( - "Could not resolve `__name__` from an outer frame." - "There may be a problem with the interpreter frame stack," - "most probably due to the caller module being Cython-compiled." - "Please either switch from `(...)` to `getLogger(__name__).(...)` syntax," + getLogger(__name__).critical( + "Could not resolve `__name__` from an outer frame.\n" + "There may be a problem with the interpreter frame stack, " + "most probably due to the caller module being Cython-compiled. " + "Please either switch from `(...)` to `getLogger(__name__).(...)` syntax, " "or avoid Cython-compiling that module." ) sys.exit(1) From 44f52e6d7be7e076e8cff46563d55158c7a058a2 Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Mon, 18 Jul 2022 20:01:01 +0200 Subject: [PATCH 113/122] Fixed logging via explicit module logger --- src/pi.py | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/pi.py b/src/pi.py index 704d0b7..59e37d3 100755 --- a/src/pi.py +++ b/src/pi.py @@ -8,12 +8,13 @@ from pathlib import Path import matplotlib.pyplot as plt from rand import TrueRandomGenerator -from log import info, style, warning, sprint +from log import getLogger, style, sprint # Costanti K = 255**2 SRC = Path(__file__).parent # Cartella di questo file +L = getLogger(__name__) # Logger per questo file # Se l'output è formattato male, imposta questa flag a `False` UNICODE_BOX: bool = True # False @@ -77,13 +78,13 @@ def mode() -> int: sys.exit(0) # Gestione errori: input non intero (chiede nuovamente) except: - warning("Algorithm index has to be an integer (0|1|2|3)!") + L.warning("Algorithm index has to be an integer (0|1|2|3)!") continue # Numero intero: ok else: # Troppo grande o troppo piccolo (chiede nuovamente) if _mode > 3 or _mode < 0: - warning(f"Invalid integer `{_mode}` (has to be in [0, 3])!") + L.warning(f"Invalid integer `{_mode}` (has to be in [0, 3])!") continue # Tutto ok: "_mode" è impostato e si continua col programma return _mode # questo 'return' interrompe il ciclo 'while' e ritorna il valore di '_mode' @@ -101,11 +102,11 @@ def main(): BUG = bug(True) # Di default è attivo # Comunica se BUG è attivo (per sicurezza) - info(f"BUG is {'en' if BUG else 'dis'}abled.") + L.info(f"BUG is {'en' if BUG else 'dis'}abled.") # Determina l'algoritmo da utilizzare MODE: int = mode() # Usa la funzione sopra definita - info(f"Using algorithm [{MODE}].") # Stampa l'algoritmo, per sicurezza + L.info(f"Using algorithm [{MODE}].") # Stampa l'algoritmo, per sicurezza # Inizializzazione TRG = TrueRandomGenerator(bug=BUG) # Il nostro generatore @@ -232,10 +233,10 @@ def main(): # --- Stampa la stima finale di π --- # Per velocizzare i calcoli - L = len(str(PI)) - 1 # -1 perché ignoriamo il `.` + l = len(str(PI)) - 1 # -1 perché ignoriamo il `.` # - spi = f"{pi:01.{L-1}f}" - sPI = f"{PI:01.{L-1}f}" + spi = f"{pi:01.{l-1}f}" + sPI = f"{PI:01.{l-1}f}" # Conta quante cifre sono corrette i = 0 for i, (digit, DIGIT) in enumerate(zip(spi.replace(".", ""), sPI.replace(".", ""))): @@ -254,7 +255,7 @@ def main(): H = "─" if UNICODE_BOX else "-" V = "│" if UNICODE_BOX else "|" sprint(f"""\ -{UL}{H*(L+7)}{UR} +{UL}{H*(l+7)}{UR} {V} π ≈ {style_pi(spi, i, OK_STYLE, K0_STYLE, KO_STYLE)} {V} {V} π = {style_pi(sPI, i, PI_STYLE, OK_STYLE, PI_STYLE)} {V} {V} { @@ -264,9 +265,9 @@ def main(): }{ style('^', K0_STYLE) if i else '' }{ - style('~', KO_STYLE)*(L-i-1) + style('~', KO_STYLE)*(l-i-1) } {V} -{DL}{H*(L+7)}{DR}\ +{DL}{H*(l+7)}{DR}\ """) From 2b1b577da456843c06e3f3f2e6bcb4c1c0c5d052 Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Mon, 18 Jul 2022 20:01:25 +0200 Subject: [PATCH 114/122] Fixed logging via explicit module logger --- src/rand.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/rand.py b/src/rand.py index 17b0122..c6b4015 100755 --- a/src/rand.py +++ b/src/rand.py @@ -5,11 +5,12 @@ from pathlib import Path from typing import Literal, NamedTuple, overload from enum import Flag, auto -from log import task +from log import getLogger import root # Determina la cartella dove si trova questo file SRC = Path(__file__).parent +L = getLogger(__name__) # Il logger associato a questo modulo class Event(NamedTuple): @@ -85,7 +86,7 @@ def __init__( ) # --- 1. Calcolo delle differenze dei tempi tra coppie di tempi adiacenti --- - with task("Calculating time differences"): + with L.task("Calculating time differences"): self.delta_times = [] for i in range(1, len(events)): @@ -95,13 +96,13 @@ def __init__( self.delta_times.append(delta_time) # --- 2. Generazione dei bit casuali --- - with task("Generating random bits"): + with L.task("Generating random bits"): # Applicazione del metodo (statico) `self._rand(...)` alle # differenze dei tempi e salvataggio nel vettore `self.bits` self.random_bits = list(map(self._rand, self.delta_times)) # --- 3. Generazione dei numeri casuali (da 0 a 255) --- - with task("Generating random numbers"): + with L.task("Generating random numbers"): self.random_numbers = [] random_numbers_b = [] @@ -239,7 +240,7 @@ def test(): nums = gen.random_numbers if TO_PLOT: - with task("Plotting required items") as plotting: + with L.task("Plotting required items") as plotting: _plot_item_message: str = " * {}" # ------------------------ Differenze di tempo ------------------------- From fe459785aa63a863abc59428df70aceb6848f574 Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Mon, 18 Jul 2022 20:01:55 +0200 Subject: [PATCH 115/122] Added `pi.pxd` to optimize Cython compilation --- src/pi.pxd | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100755 src/pi.pxd diff --git a/src/pi.pxd b/src/pi.pxd new file mode 100755 index 0000000..1d8652f --- /dev/null +++ b/src/pi.pxd @@ -0,0 +1,57 @@ +import cython + +cdef: + long K + object SRC + object L + bint UNICODE_BOX + + +@cython.locals(BUG=bint) +cdef bint bug(bint default) + +@cython.locals(_mode=int) +cdef int mode() + +@cython.locals( + s=str, + j=int, + c=str +) +cdef str style_pi(str pi, int i, str OK, str K0, str KO) + +@cython.locals( + width=int, + title=str, + BUG=bint, + MODE=bint, + TRG=object, + LEN=int, + N_in=int, + x_in=list, + x_out=list, + y_in=list, + y_out=list, + pi_array=list, + squares=list, + i=int, + x=int, + y=int, + pi=object, + l=int, + spi=str, + sPI=str, + digit=str, + DIGIT=str, + PI_STYLE=str, + OK_STYLE=str, + K0_STYLE=str, + KO_STYLE=str, + UL=str, + UR=str, + DL=str, + DR=str, + H=str, + V=str, +) +cpdef void main() From 6b640a2a3b31cde02e22deaef134a68367587804 Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Tue, 19 Jul 2022 16:54:08 +0200 Subject: [PATCH 116/122] Update pi.py --- src/pi.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pi.py b/src/pi.py index 59e37d3..2707aee 100755 --- a/src/pi.py +++ b/src/pi.py @@ -69,7 +69,7 @@ def mode() -> int: [3] Use pseudo-random (x, y) points.\ """) # Richiede all'utente l'algoritmo da utilizzare (il valore di "_mode") - _mode: int + _mode: int = 0 while True: try: _mode = int(eval(input("> "))) @@ -254,7 +254,7 @@ def main(): DR = "┘" if UNICODE_BOX else "'" H = "─" if UNICODE_BOX else "-" V = "│" if UNICODE_BOX else "|" - sprint(f"""\ + sprint(f""" {UL}{H*(l+7)}{UR} {V} π ≈ {style_pi(spi, i, OK_STYLE, K0_STYLE, KO_STYLE)} {V} {V} π = {style_pi(sPI, i, PI_STYLE, OK_STYLE, PI_STYLE)} {V} @@ -267,7 +267,7 @@ def main(): }{ style('~', KO_STYLE)*(l-i-1) } {V} -{DL}{H*(l+7)}{DR}\ +{DL}{H*(l+7)}{DR} """) From 1d44f6729b1010424b6a8bbd740356b683c6cee1 Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Tue, 19 Jul 2022 19:09:40 +0200 Subject: [PATCH 117/122] Aggiunto `setup.cfg` con la configurazione di `pycodestyle` --- pyproject.toml | 3 ++- setup.cfg | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 setup.cfg diff --git a/pyproject.toml b/pyproject.toml index f4c10fe..70398eb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,6 +8,7 @@ ignore = [ "E221", # multiple spaces before operator "E241", # multiple spaces after ':' "E704", # multiple statements on one line (def) + "E722", # bare except "W503", # line break before binary operator ] in-place = true @@ -27,7 +28,7 @@ disable = [ "W0622", # Redefining built-in '*' "W0702", # No exception type(s) specified "W1203", # Use %s formatting in logging functions - # Conventions + # Conventions "C0103", # Variable name "*" doesn't conform to snake_case naming style # Refactoring "R0903", # too few public methods (*/2) diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..d1b2896 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,5 @@ +[pycodestyle] +max-line-length = 120 +ignore = E221,E241,E704,E722,W503 +statistics = True + From 3181b59b4e501e111811733963caa23633de10a5 Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Tue, 19 Jul 2022 19:09:56 +0200 Subject: [PATCH 118/122] Stringhe di documentazione per `classe.py` --- src/classe.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/classe.py b/src/classe.py index f7fd13a..f414eea 100755 --- a/src/classe.py +++ b/src/classe.py @@ -7,7 +7,8 @@ class Rettangolo: - """Rappresenta un rettangolo.""" + """Classe che “simula” un rettangolo.""" + a: float b: float @@ -26,11 +27,13 @@ def perimetro(self) -> float: return (self.a + self.b) * 2 def __mul__(self, i: int | float) -> Rettangolo: + """Operazione di moltiplicazione.""" if not isinstance(i, (int, float)): return NotImplemented return Rettangolo(self.a * i, self.b * i) def __repr__(self) -> str: + """Rappresentazione dell'oggetto come stringa.""" return f"" @@ -42,9 +45,11 @@ def __init__(self, lato: float) -> None: super().__init__(lato, lato) def __repr__(self) -> str: + """Rappresentazione dell'oggetto come stringa.""" return f"" def __mul__(self, i: int | float) -> Quadrato: + """Operazione di moltiplicazione.""" # Va ridefinito perché deve ritornare un `Quadrato`, non un `Rettangolo`. return Quadrato(self.lato * i) @@ -56,6 +61,7 @@ def lato(self): class Account: """Una classe per dimostrare l'utilizzo di `@property` e le variabili “private”.""" + __money: float def __init__(self) -> None: @@ -74,6 +80,7 @@ def pay(self, price: float) -> None: self.__money -= abs(price) def __repr__(self) -> str: + """Rappresentazione dell'oggetto come stringa.""" return f"" From 96f361d78090cd0c463b812396e92a8ab08bf48b Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Tue, 19 Jul 2022 19:10:04 +0200 Subject: [PATCH 119/122] Code cleanup --- src/ideal.pxd | 1 - src/ideal.pyi | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ideal.pxd b/src/ideal.pxd index d4b1121..36cec79 100644 --- a/src/ideal.pxd +++ b/src/ideal.pxd @@ -1,4 +1,3 @@ -from libc.limits cimport ULLONG_MAX import cython diff --git a/src/ideal.pyi b/src/ideal.pyi index 9beab6b..fcd3984 100644 --- a/src/ideal.pyi +++ b/src/ideal.pyi @@ -1,8 +1,10 @@ # -*- coding: utf-8 -*- from __future__ import annotations + def grid(N: int) -> float: ... + def main() -> None: ... From 466da1daa1c64741ab2b872981d1cf09afe9e9cf Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Tue, 19 Jul 2022 19:10:25 +0200 Subject: [PATCH 120/122] Stringhe di documentazione --- src/log.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/log.py b/src/log.py index 3c09069..691bd64 100644 --- a/src/log.py +++ b/src/log.py @@ -143,6 +143,7 @@ def __init__(self, lfmt: str, rfmt: str, *args, **kwargs) -> None: super().__init__(lfmt + "\0" + rfmt, *args, **kwargs) def format(self, record: logging.LogRecord) -> str: + """Correctly format `record`.""" # Activate console markup if RICH: setattr(record, "markup", True) @@ -178,6 +179,7 @@ def format(self, record: logging.LogRecord) -> str: class Logger(logging.Logger): """An enhanced logger.""" + __slots__ = ("_indent", "result", "_result_logged", "_timestamp") _indent: int result: str @@ -196,6 +198,7 @@ def _task_reset(self): self._timestamp = None def makeRecord(self, *args, **kwargs) -> logging.LogRecord: + """Create a `logging.LogRecord` instance.""" record = super().makeRecord(*args, **kwargs) setattr(record, "indent", self._indent + getattr(record, "indent", 0)) return record @@ -271,7 +274,7 @@ def get_levels() -> list[int]: def cli_configure() -> None: - """Setup `logging` based on command-line flags.""" + """Set up `logging` based on command-line flags.""" global _setup_done # pylint: disable=global-statement if _setup_done: return From cb181a7e310968f999afd1dc6ee8dc669fa4ccf9 Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Tue, 19 Jul 2022 19:10:35 +0200 Subject: [PATCH 121/122] Piccoli miglioramenti --- src/pi.pxd | 8 +++++++- src/pi.py | 7 ++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/pi.pxd b/src/pi.pxd index 1d8652f..0e2d8b0 100755 --- a/src/pi.pxd +++ b/src/pi.pxd @@ -7,12 +7,17 @@ cdef: bint UNICODE_BOX -@cython.locals(BUG=bint) +@cython.locals( + BUG=bint, + rargv=list, +) cdef bint bug(bint default) + @cython.locals(_mode=int) cdef int mode() + @cython.locals( s=str, j=int, @@ -20,6 +25,7 @@ cdef int mode() ) cdef str style_pi(str pi, int i, str OK, str K0, str KO) + @cython.locals( width=int, title=str, diff --git a/src/pi.py b/src/pi.py index 2707aee..c74c666 100755 --- a/src/pi.py +++ b/src/pi.py @@ -14,7 +14,7 @@ # Costanti K = 255**2 SRC = Path(__file__).parent # Cartella di questo file -L = getLogger(__name__) # Logger per questo file +L = getLogger(__name__) # Logger per questo file # Se l'output è formattato male, imposta questa flag a `False` UNICODE_BOX: bool = True # False @@ -29,7 +29,8 @@ def bug(default: bool, /) -> bool: # $ python pi.py --bug --no-bug # --> disattivato (--no-bug sovrascrive --bug) if "--bug" in sys.argv: if "--no-bug" in sys.argv: - BUG = sys.argv[::-1].index("--bug") < sys.argv[::-1].index("--no-bug") + rargv = sys.argv[::-1] # Crea una copia invertita di `sys.argv` + BUG = rargv.index("--bug") < rargv.index("--no-bug") sys.argv = [x for x in sys.argv if x not in ("--no-bug", "--bug")] return BUG sys.argv = [x for x in sys.argv if x != "--bug"] @@ -41,7 +42,7 @@ def bug(default: bool, /) -> bool: def mode() -> int: - """Determina l'algoritmo da utilizzare""" + """Determina l'algoritmo da utilizzare.""" # Controlla se l'algoritmo è stato selezionato da riga di comando. # Struttura del vettore "sys.argv": # $ python /path/to/script.py a1 a2 a3 From fc41a9085f5784296873838c87b910761ca76ee9 Mon Sep 17 00:00:00 2001 From: RBerga06 <78449715+RBerga06@users.noreply.github.com> Date: Tue, 19 Jul 2022 20:06:59 +0200 Subject: [PATCH 122/122] Update ideal.pxd --- src/ideal.pxd | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ideal.pxd b/src/ideal.pxd index 36cec79..84e4349 100644 --- a/src/ideal.pxd +++ b/src/ideal.pxd @@ -12,4 +12,5 @@ import cython ) cpdef double grid(unsigned long long N) + cpdef void main()