Source

diveintopython3-it / caso-di-studio-convertire-chardet-verso-python-3.html

Full commit
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
<!DOCTYPE html>
<meta charset=utf-8>
<title>Caso di studio: convertire chardet verso Python 3 - Immersione in Python 3</title>
<!--[if IE]><script src=j/html5.js></script><![endif]-->
<link rel=stylesheet href=dip3.css>
<style>
body{counter-reset:h1 15}
ins,del{line-height:2.154;text-decoration:none;font-style:normal;display:inline-block;width:100%}
ins{background:#9f9}
del{background:#f87}
</style>
<link rel=stylesheet media='only screen and (max-device-width: 480px)' href=mobile.css>
<link rel=stylesheet media=print href=print.css>
<meta name=viewport content='initial-scale=1.0'>
<form action=http://www.google.com/cse><div><input type=hidden name=cx value=014021643941856155761:l5eihuescdw><input type=hidden name=ie value=UTF-8>&nbsp;<input type=search name=q size=25 placeholder="powered by Google&trade;">&nbsp;<input type=submit name=sa value=Search></div></form>
<p>Voi siete qui: <a href=index.html>Inizio</a> <span class=u>&#8227;</span> <a href=indice.html#caso-di-studio-convertire-chardet-verso-python3>Immersione in Python 3</a> <span class=u>&#8227;</span>
<p id=level>Livello di difficoltà: <span class=u title=professionale>&#x2666;&#x2666;&#x2666;&#x2666;&#x2666;</span>
<h1>Caso di studio: convertire <code>chardet</code> verso Python 3</h1>
<blockquote class=q>
<p><span class=u>&#x275D;</span> Parole, non abbiamo altro che parole per andare avanti. <span class=u>&#x275E;</span><br>&mdash; <a href=http://www.imdb.com/title/tt0100519/quotes>Rosencrantz e Guildenstern sono morti</a>
</blockquote>
<p id=toc>&nbsp;
<h2 id=divingin>Immersione!</h2>
<p class=f>Si deve principalmente a una codifica di caratteri scorretta o sconosciuta la presenza di testo inintelleggibile sul web, nella vostra casella di posta e in effetti attraverso ogni sistema computerizzato mai realizzato. Nel capitolo <a href=stringhe.html>Stringhe</a> ho parlato della storia delle codifiche di carattere e della creazione di Unicode, &#8220;una codifica per dominarle tutte&#8221;. Mi piacerebbe non dover mai più vedere un carattere incomprensibile su una pagina web, perché tutti i sistemi d&#8217;autore memorizzerebbero informazioni di codifica accurate, tutti i protocolli di trasferimento conoscerebbero Unicode e ogni sistema che gestisce testo manterrebbe una fedeltà perfetta nella conversione tra diverse codifiche.
<p>Mi piacerebbe anche avere un pony.
<p>Un pony Unicode.
<p>Uno Unipony, per così dire.
<p>Mi accontenterò del riconoscimento automatico delle codifiche di carattere.

<p class=a>&#x2042;

<h2 id=faq.what>Che cos&#8217;è il riconoscimento automatico delle codifiche di carattere?</h2>
<p>Significa prendere una sequenza di byte in una codifica di carattere sconosciuta e cercare di determinare la codifica in modo da poter leggere il testo. &Egrave; come decifrare un codice quando non avete la chiave di cifratura.

<h3 id=faq.impossible>Ma non è impossibile?</h3>
<p>In generale, sì. Comunque, alcune codifiche sono ottimizzate per lingue specifiche, e le lingue non sono casuali. Alcune sequenze di caratteri compaiono tutte le volte, mentre altre sequenze non hanno senso. Una persona capace di parlare l&#8217;inglese correntemente che apre un giornale e vi trova &#8220;txzqJv 2!dasd0a QqdKjvz&#8221; riconoscerà istantaneamente che quello non è inglese (sebbene sia composto interamente da lettere inglesi). Analizzando grandi quantità di testo &#8220;tipico&#8221;, un algoritmo per computer può simulare questa capacità e formulare un&#8217;ipotesi fondata sulla lingua in cui è scritto un testo.
<p>In altre parole, il riconoscimento di una codifica è in realtà il riconoscimento di una lingua combinato con la conoscenza di quali lingue tendono a usare determinate codifiche di carattere.

<h3 id=faq.who>Esiste un algoritmo di questo tipo?</h3>
<p>A quanto pare, sì. Tutti browser più diffusi usano il riconoscimento automatico delle codifiche di carattere, perché il web è pieno di pagine che non hanno alcuna informazione sulla loro codifica. <a href=http://lxr.mozilla.org/seamonkey/source/extensions/universalchardet/src/base/>Mozilla Firefox contiene una libreria per il riconoscimento automatico delle codifiche</a> distribuita sotto licenza open source. <a href=http://chardet.feedparser.org/>Io ho convertito questa libreria verso Python 2</a> e ho chiamato il modulo <code>chardet</code>. Questo capitolo vi illustrerà passo per passo il processo di conversione del modulo <code>chardet</code> da Python 2 verso Python 3.

<p class=a>&#x2042;

<h2 id=divingin2>Una introduzione al modulo <code>chardet</code></h2>
<p>Prima di cominciare la conversione, potrebbe essere d&#8217;aiuto capire come funziona il codice! Questa sezione vuole essere una breve guida alla struttura e al funzionamento del modulo <code>chardet</code>. I sorgenti della libreria sono troppo grandi per riportarne l&#8217;intero contenuto qui di seguito, ma potete <a href=http://chardet.feedparser.org/download/>scaricarli dal sito <code>chardet.feedparser.org</code></a>. 
<aside>Il riconoscimento di codifica è in realtà il riconoscimento di una lingua travestito.</aside>
<p>Il punto d&#8217;ingresso principale per l&#8217;algoritmo di riconoscimento è <code>universaldetector.py</code>, dove viene definita la classe <code>UniversalDetector</code>. (Potreste pensare che il punto d&#8217;entrata principale sia la funzione <code>detect()</code> in <code>chardet/__init__.py</code>, ma in realtà quella è solo una funzione di convenienza che crea un oggetto di tipo <code>UniversalDetector</code>, lo invoca e ne restituisce il risultato.)
<p>Ci sono 5 categorie di codifiche che <code>UniversalDetector</code> gestisce.
<ol>
<li><abbr>UTF-n</abbr> con <abbr>BOM</abbr>. Questa categoria comprende <abbr>UTF-8</abbr>, sia le varianti Big-Endian che Little-Endian di <abbr>UTF-16</abbr>, e tutte e 4 le varianti di <abbr>UTF-32</abbr> con ordine di byte differente.
<li>Codifiche con escape, che sono interamente compatibili con la codifica <abbr>ASCII</abbr> a 7 bit, dove i caratteri non-<abbr>ASCII</abbr> cominciano con una sequenza di escape. Esempi: <abbr>ISO-2022-JP</abbr> (giapponese) e <abbr>HZ-GB-2312</abbr> (cinese).
<li>Codifiche multibyte, dove ogni carattere è rappresentato da un numero variabile di byte. Esempi: <abbr>Big5</abbr> (cinese), <abbr>SHIFT_JIS</abbr> (giapponese), <abbr>EUC-KR</abbr> (coreano) e <abbr>UTF-8</abbr> senza <abbr>BOM</abbr>.
<li>Codifiche a singolo byte, dove ogni carattere è rappresentato da un byte. Esempi: <abbr>KOI8-R</abbr> (russo), <abbr>windows-1255</abbr> (ebraico) e <abbr>TIS-620</abbr> (thai).
<li><abbr>windows-1252</abbr>, che viene usato principalmente su Microsoft Windows da direttori d&#8217;azienda che non distinguerebbero una codifica di carattere da un buco in terra.
</ol>
<h3 id=how.bom><abbr>UTF-n</abbr> con <abbr>BOM</abbr></h3>
<p>Se il testo comincia con un <abbr>BOM</abbr>, possiamo ragionevolmente presumere che sia codificato in <abbr>UTF-8</abbr>, <abbr>UTF-16</abbr>, o <abbr>UTF-32</abbr>. (Il <abbr>BOM</abbr> ci dirà esattamente quale; questo è quello a cui serve.) Questa categoria viene gestita direttamente da <code>UniversalDetector</code>, che restituisce il risultato immediatamente senza ulteriori elaborazioni.
<h3 id=how.esc>Codifiche con escape</h3>
<p>Se il testo contiene una sequenza di escape riconoscibile che potrebbe indicare una codifica con escape, <code>UniversalDetector</code> crea un oggetto <code>EscCharSetProber</code> (definito in <code>escprober.py</code>) e gli passa il testo.
<p><code>EscCharSetProber</code> crea una serie di macchine a stati finiti, basate sui modelli di <abbr>HZ-GB-2312</abbr>, <abbr>ISO-2022-CN</abbr>, <abbr>ISO-2022-JP</abbr> e <abbr>ISO-2022-KR</abbr> (definiti in <code>escsm.py</code>). <code>EscCharSetProber</code> passa il testo a ognuna di queste macchine a stati, un byte alla volta. Se una qualsiasi di queste macchine a stati riesce a identificare univocamente la codifica, <code>EscCharSetProber</code> restituisce immediatamente il risultato positivo a <code>UniversalDetector</code>, che lo restituisce al chiamante. Se una qualsiasi delle macchine a stati trova una sequenza illegale, viene scartata e l&#8217;elaborazione continua con le altre macchine a stati.
<h3 id=how.mb>Codifiche multibyte</h3>
<p>Presumendo che non ci sia un <abbr>BOM</abbr>, <code>UniversalDetector</code> controlla se il testo contiene un carattere <i>high-bit</i>, cioè un carattere codificato con un byte il cui bit più significativo sia impostato a 1. Se è così, crea una serie di &#8220;sonde&#8221; per riconoscere le codifiche multibyte, a singolo byte e, come ultima risorsa, <abbr>windows-1252</abbr>.
<p>La sonda per le codifiche multibyte, <code>MBCSGroupProber</code> (definita in <code>mbcsgroupprober.py</code>), in realtà si occupa solo di gestire un gruppo di altre sonde, una per ogni codifica multibyte: <abbr>Big5</abbr>, <abbr>GB2312</abbr>, <abbr>EUC-TW</abbr>, <abbr>EUC-KR</abbr>, <abbr>EUC-JP</abbr>, <abbr>SHIFT_JIS</abbr> e <abbr>UTF-8</abbr>. <code>MBCSGroupProber</code> passa il testo a ognuna di queste specifiche sonde e controlla i risultati. Se una sonda riferisce di aver trovato una sequenza illegale di byte, viene scartata da ulteriori elaborazioni (in modo che, per esempio, ogni chiamata successiva a <code>UniversalDetector</code>.<code>feed()</code> eviterà di utilizzare quella sonda). Se una sonda riferisce di essere ragionevolmente sicura di aver riconosciuto la codifica, <code>MBCSGroupProber</code> restituisce questo risultato positivo a <code>UniversalDetector</code>, che restituisce a sua volta il risultato al chiamante.
<p>La maggior parte delle sonde per le codifiche multibyte eredita da <code>MultiByteCharSetProber</code> (definita in <code>mbcharsetprober.py</code>) e si occupa semplicemente di agganciare la macchina a stati e l&#8217;analizzatore di distribuzione appropriati, lasciando che sia <code>MultiByteCharSetProber</code> a fare il resto del lavoro. <code>MultiByteCharSetProber</code> fa scorrere il testo attraverso la macchina a stati della singola codifica, un byte alla volta, per cercare sequenze di byte che indicherebbero un risultato conclusivo positivo o negativo. Allo stesso tempo, <code>MultiByteCharSetProber</code> passa il testo a un analizzatore di distribuzione specifico per quella codifica.
<p>Gli analizzatori di distribuzione (tutti definiti in <code>chardistribution.py</code>) si basano su modelli, specifici per ogni lingua, che descrivono quali caratteri vengono usati più frequentemente in una lingua. Una volta che <code>MultiByteCharSetProber</code> ha passato abbastanza testo all&#8217;analizzatore di distribuzione, questo calcola una stima di confidenza basata sul numero di caratteri frequentemente usati, sul numero totale di caratteri e su un rapporto di distribuzione specifico per la lingua. Se la confidenza è abbastanza alta, <code>MultiByteCharSetProber</code> restituisce il risultato a <code>MBCSGroupProber</code>, che lo restituisce a <code>UniversalDetector</code>, che lo restituisce al chiamante.
<p>Il caso del giapponese è più difficile. Gli analizzatori di distribuzione a singolo carattere non sono sempre sufficienti per distinguere tra <abbr>EUC-JP</abbr> e <abbr>SHIFT_JIS</abbr>, quindi la classe <code>SJISProber</code> (definita in <code>sjisprober.py</code>) sfrutta anche l&#8217;analisi di distribuzione a 2 caratteri. <code>SJISContextAnalysis</code> ed <code>EUCJPContextAnalysis</code> (entrambe definite in <code>jpcntx.py</code> ed entrambe estensioni della classe <code>JapaneseContextAnalysis</code>) controllano la frequenza dei caratteri provenienti dal sillabario Hiragana all&#8217;interno del testo. Una volta che è stato elaborato testo a sufficienza, restituiscono un livello di confidenza a <code>SJISProber</code>, che controlla il risultato di entrambi gli analizzatori e restituisce il livello di confidenza più alto a <code>MBCSGroupProber</code>.
<h3 id=how.sb>Codifiche a singolo byte</h3>
<aside>Seriamente, dov&#8217;è il mio pony Unicode?</aside>
<p>La sonda per le codifiche a singolo byte, <code>SBCSGroupProber</code> (definita in <code>sbcsgroupprober.py</code>), si occupa anch&#8217;essa di gestire un gruppo di altre sonde, una per ogni combinazione di lingua e codifica a singolo byte: <abbr>windows-1251</abbr>, <abbr>KOI8-R</abbr>, <abbr>ISO-8859-5</abbr>, <abbr>MacCyrillic</abbr>, <abbr>IBM855</abbr> e <abbr>IBM866</abbr> (russo); <abbr>ISO-8859-7</abbr> e <abbr>windows-1253</abbr> (greco); <abbr>ISO-8859-5</abbr> e <abbr>windows-1251</abbr> (bulgaro); <abbr>ISO-8859-2</abbr> e <abbr>windows-1250</abbr> (ungherese); <abbr>TIS-620</abbr> (thai); <abbr>windows-1255</abbr> e <abbr>ISO-8859-8</abbr> (ebraico).
<p><code>SBCSGroupProber</code> passa il testo a queste sonde, specifiche per coppie di lingua e codifica, e controlla i risultati. Queste sonde sono tutte implementate come una singola classe, <code>SingleByteCharSetProber</code> (definita in <code>sbcharsetprober.py</code>), che prende un modello di lingua come argomento. Il modello di lingua definisce la frequenza con cui differenti sequenze di 2 caratteri appaiono nel testo tipico di quella lingua. <code>SingleByteCharSetProber</code> elabora il testo in ingresso e conta le sequenze di 2 caratteri più utilizzate. Una volta che ha elaborato testo a sufficienza, calcola un livello di confidenza basato sul numero delle sequenze più utilizzate, sul numero totale di caratteri e su un rapporto di distribuzione specifico per lingua.
<p>L&#8217;ebraico viene trattato come un caso a parte. Se il testo sembra essere ebraico sulla base dell&#8217;analisi di distribuzione a 2 caratteri, la sonda <code>HebrewProber</code> (definita in <code>hebrewprober.py</code>) prova a distinguere tra ebraico visuale (dove il testo sorgente viene in realtà memorizzato &#8220;all&#8217;indietro&#8221; riga per riga e poi visualizzato così com&#8217;è in modo da poter essere letto da destra verso sinistra) ed ebraico logico (dove il testo sorgente viene memorizzato nell&#8217;ordine di lettura e poi riportato da destra verso sinistra dal programma che lo visualizza). Dato che alcuni caratteri sono codificati in maniera differente a seconda che compaiano nel mezzo o alla fine di una parola, possiamo fare un&#8217;ipotesi ragionevole a proposito della direzione del testo sorgente e restituire la codifica appropriata (<abbr>windows-1255</abbr> per l&#8217;ebraico logico oppure <abbr>ISO-8859-8</abbr> per l&#8217;ebraico visuale).
<h3 id=how.windows1252><abbr>windows-1252</abbr></h3>
<p>Se <code>UniversalDetector</code> riconosce un carattere high-bit nel testo, ma nessuna delle altre sonde per codifiche multibyte o a singolo byte restituisce un risultato di confidenza, allora crea una sonda <code>Latin1Prober</code> (definita in <code>latin1prober.py</code>) per cercare di riconoscere testo inglese in una codifica <abbr>windows-1252</abbr>. Questo riconscimento è intrinsecamente inaffidabile, perché le lettere inglesi sono rappresentate allo stesso modo in molte codifiche differenti. L&#8217;unico modo di distinguere <abbr>windows-1252</abbr> è attraverso simboli comunemente usati come virgolette e apostrofi tipografici, simboli di copyright e simili. <code>Latin1Prober</code> riduce automaticamente la propria stima di confidenza per permettere a sonde più accurate di prevalere nel caso sia possibile.

<p class=a>&#x2042;

<h2 id=running2to3>Eseguire <code>2to3</code></h2>
<p>Ora effettueremo la migrazione del modulo <code>chardet</code> da Python 2 a Python 3. Python 3 viene distribuito con uno script di utilità chiamato <code>2to3</code>, che prende in ingresso il vostro codice sorgente in Python 2 e lo converte automaticamente verso Python 3 tanto quanto gli è possibile. In alcuni casi le modifiche sono semplici&nbsp;&mdash;&nbsp;una funzione è stata rinominata oppure spostata in un modulo differente&nbsp;&mdash;&nbsp;ma in altri casi possono diventare piuttosto complesse. Per avere un&#8217;idea di tutto quello che lo script <em>può</em> fare, fate riferimento all&#8217;appendice <a href=convertire-codice-verso-python-3-con-2to3.html>Convertire codice verso Python 3 con <code>2to3</code></a>. In questo capitolo, cominceremo col lanciare <code>2to3</code> sul pacchetto <code>chardet</code>, ma come vedrete ci sarà ancora molto lavoro da fare dopo che gli strumenti automatici avranno eseguito i loro incantesimi.
<p>Il pacchetto <code>chardet</code> è suddiviso in molti file diversi, tutti nella stessa directory. Lo script <code>2to3</code> facilita la conversione di più file in una volta: basta passargli una directory come argomento dalla riga di comando e <code>2to3</code> convertirà ognuno dei file a turno.
<pre class=screen><samp class=p>C:\home\chardet> </samp><kbd>python c:\Python30\Tools\Scripts\2to3.py -w chardet\</kbd>
<samp>RefactoringTool: Salto il correttore implicito: buffer
RefactoringTool: Salto il correttore implicito: idioms
RefactoringTool: Salto il correttore implicito: set_literal
RefactoringTool: Salto il correttore implicito: ws_comma
--- chardet\__init__.py (originale)
+++ chardet\__init__.py (modificato)
@@ -18,7 +18,7 @@
 __version__ = "1.0.1"

 def detect(aBuf):
<del>-    import universaldetector</del>
<ins>+    from . import universaldetector</ins>
     u = universaldetector.UniversalDetector()
     u.reset()
     u.feed(aBuf)
--- chardet\big5prober.py (originale)
+++ chardet\big5prober.py (modificato)
@@ -25,10 +25,10 @@
 # 02110-1301  USA
 ######################### END LICENSE BLOCK #########################

<del>-from mbcharsetprober import MultiByteCharSetProber</del>
<del>-from codingstatemachine import CodingStateMachine</del>
<del>-from chardistribution import Big5DistributionAnalysis</del>
<del>-from mbcssm import Big5SMModel</del>
<ins>+from .mbcharsetprober import MultiByteCharSetProber</ins>
<ins>+from .codingstatemachine import CodingStateMachine</ins>
<ins>+from .chardistribution import Big5DistributionAnalysis</ins>
<ins>+from .mbcssm import Big5SMModel</ins>

 class Big5Prober(MultiByteCharSetProber):
     def __init__(self):
--- chardet\chardistribution.py (originale)
+++ chardet\chardistribution.py (modificato)
@@ -25,12 +25,12 @@
 # 02110-1301  USA
 ######################### END LICENSE BLOCK #########################

<del>-import constants</del>
<del>-from euctwfreq import EUCTWCharToFreqOrder, EUCTW_TABLE_SIZE, EUCTW_TYPICAL_DISTRIBUTION_RATIO</del>
<del>-from euckrfreq import EUCKRCharToFreqOrder, EUCKR_TABLE_SIZE, EUCKR_TYPICAL_DISTRIBUTION_RATIO</del>
<del>-from gb2312freq import GB2312CharToFreqOrder, GB2312_TABLE_SIZE, GB2312_TYPICAL_DISTRIBUTION_RATIO</del>
<del>-from big5freq import Big5CharToFreqOrder, BIG5_TABLE_SIZE, BIG5_TYPICAL_DISTRIBUTION_RATIO</del>
<del>-from jisfreq import JISCharToFreqOrder, JIS_TABLE_SIZE, JIS_TYPICAL_DISTRIBUTION_RATIO</del>
<ins>+from . import constants</ins>
<ins>+from .euctwfreq import EUCTWCharToFreqOrder, EUCTW_TABLE_SIZE, EUCTW_TYPICAL_DISTRIBUTION_RATIO</ins>
<ins>+from .euckrfreq import EUCKRCharToFreqOrder, EUCKR_TABLE_SIZE, EUCKR_TYPICAL_DISTRIBUTION_RATIO</ins>
<ins>+from .gb2312freq import GB2312CharToFreqOrder, GB2312_TABLE_SIZE, GB2312_TYPICAL_DISTRIBUTION_RATIO</ins>
<ins>+from .big5freq import Big5CharToFreqOrder, BIG5_TABLE_SIZE, BIG5_TYPICAL_DISTRIBUTION_RATIO</ins>
<ins>+from .jisfreq import JISCharToFreqOrder, JIS_TABLE_SIZE, JIS_TYPICAL_DISTRIBUTION_RATIO</ins>

 ENOUGH_DATA_THRESHOLD = 1024
 SURE_YES = 0.99
.
.
<mark>. (va avanti così per un po&#8217;)</mark>
.
.
RefactoringTool: File che sono stati modificati:
RefactoringTool: chardet\__init__.py
RefactoringTool: chardet\big5prober.py
RefactoringTool: chardet\chardistribution.py
RefactoringTool: chardet\charsetgroupprober.py
RefactoringTool: chardet\codingstatemachine.py
RefactoringTool: chardet\constants.py
RefactoringTool: chardet\escprober.py
RefactoringTool: chardet\escsm.py
RefactoringTool: chardet\eucjpprober.py
RefactoringTool: chardet\euckrprober.py
RefactoringTool: chardet\euctwprober.py
RefactoringTool: chardet\gb2312prober.py
RefactoringTool: chardet\hebrewprober.py
RefactoringTool: chardet\jpcntx.py
RefactoringTool: chardet\langbulgarianmodel.py
RefactoringTool: chardet\langcyrillicmodel.py
RefactoringTool: chardet\langgreekmodel.py
RefactoringTool: chardet\langhebrewmodel.py
RefactoringTool: chardet\langhungarianmodel.py
RefactoringTool: chardet\langthaimodel.py
RefactoringTool: chardet\latin1prober.py
RefactoringTool: chardet\mbcharsetprober.py
RefactoringTool: chardet\mbcsgroupprober.py
RefactoringTool: chardet\mbcssm.py
RefactoringTool: chardet\sbcharsetprober.py
RefactoringTool: chardet\sbcsgroupprober.py
RefactoringTool: chardet\sjisprober.py
RefactoringTool: chardet\universaldetector.py
RefactoringTool: chardet\utf8prober.py</samp></pre>
<p>Ora eseguiamo lo script <code>2to3</code> sul programma di collaudo, <code>test.py</code>.
<pre class='nd screen'><samp class=p>C:\home\chardet> </samp><kbd>python c:\Python30\Tools\Scripts\2to3.py -w test.py</kbd>
<samp>RefactoringTool: Salto il correttore implicito: buffer
RefactoringTool: Salto il correttore implicito: idioms
RefactoringTool: Salto il correttore implicito: set_literal
RefactoringTool: Salto il correttore implicito: ws_comma
--- test.py (originale)
+++ test.py (modificato)
@@ -4,7 +4,7 @@
 count = 0
 u = UniversalDetector()
 for f in glob.glob(sys.argv[1]):
<del>-    print f.ljust(60),</del>
<ins>+    print(f.ljust(60), end=' ')</ins>
     u.reset()
     for line in file(f, 'rb'):
         u.feed(line)
@@ -12,8 +12,8 @@
     u.close()
     result = u.result
     if result['encoding']:
<del>-        print result['encoding'], 'con confidenza', result['confidence']</del>
<ins>+        print(result['encoding'], 'con confidenza', result['confidence'])</ins>
     else:
<del>-        print '******** nessun risultato'</del>
<ins>+        print('******** nessun risultato')</ins>
     count += 1
<del>-print count, 'test'</del>
<ins>+print(count, 'test')</ins>
RefactoringTool: File che sono stati modificati:
RefactoringTool: test.py</samp></pre>
<p>Be&#8217;, non è stato così difficile. Ci sono state solo alcune istruzioni <code>print</code> e <code>import</code> da convertire. Parlando di queste ultime, qual <em>era</em> il problema con tutte quelle istruzioni di importazione? Per rispondere a questa domanda, avete bisogno di capire come il modulo <code>chardet</code> è diviso in molteplici file.

<p class=a>&#x2042;

<h2 id=multifile-modules>Una breve digressione sui moduli multifile</h2>

<p><code>chardet</code> è un <i>modulo multifile</i>. Avrei potuto scegliere di mettere tutto il codice in un unico file (chiamato <code>chardet.py</code>), ma non l&#8217;ho fatto. Invece, ho creato una directory (chiamata <code>chardet</code>) e poi ho creato un file <code>__init__.py</code> in quella directory. <em>Se Python vede un file <code>__init__.py</code> in una directory, assume che tutti i file in quella directory siano parte dello stesso modulo.</em> Il nome del modulo è il nome della directory. I file nella directory possono fare riferimento ad altri file nella stessa directory, o persino all&#8217;interno di sottodirectory. (Ne parlerò più in dettaglio fra un minuto.) Ma l&#8217;intera collezione di file viene presentata ad altro codice Python come un singolo modulo&nbsp;&mdash;&nbsp;come se tutte le funzioni e le classi fossero in un singolo file <code>.py</code>.

<p>Che cosa va messo in un file <code>__init__.py</code>? Niente. Tutto. Qualcosa. Il file <code>__init__.py</code> non ha bisogno di definire nulla, può letteralmente essere un file vuoto. Oppure potete usarlo per definire le funzioni dei vostri punti d&#8217;ingresso principali. O potete metterci tutte le vostre funzioni. O tutte tranne una.

<blockquote class=note>
<p><span class=u>&#x261E;</span>Una directory contenente un file <code>__init__.py</code> viene sempre trattata come un modulo multifile. Senza un file <code>__init__.py</code>, una directory è semplicemente una directory contenente file <code>.py</code> non correlati tra loro.
</blockquote>

<p>Vediamo come questi moduli funzionano in pratica.

<pre class=screen>
<samp class=p>>>> </samp><kbd class=pp>import chardet</kbd>
<a><samp class=p>>>> </samp><kbd class=pp>dir(chardet)</kbd>             <span class=u>&#x2460;</span></a>
<samp class=pp>['__builtins__', '__doc__', '__file__', '__name__',
 '__package__', '__path__', '__version__', 'detect']</samp>
<a><samp class=p>>>> </samp><kbd class=pp>chardet</kbd>                  <span class=u>&#x2461;</span></a>
<samp>&lt;module 'chardet' from 'C:\Python31\lib\site-packages\chardet\__init__.py'></samp></pre>
<ol>
<li>A parte i soliti attributi di classe, l&#8217;unica cosa contenuta nel modulo <code>chardet</code> è la funzione <code>detect()</code>.
<li>Ecco il vostro primo indizio che il modulo <code>chardet</code> sia più di un semplice file: il &#8220;modulo&#8221; viene mostrato come il file <code>__init__.py</code> nella directory <code>chardet</code>.
</ol>

<p>Diamo uno sguardo a quel file <code>__init__.py</code>.

<pre class=pp><code><a>def detect(aBuf):                              <span class=u>&#x2460;</span></a>
<a>    from . import universaldetector            <span class=u>&#x2461;</span></a>
    u = universaldetector.UniversalDetector()
    u.reset()
    u.feed(aBuf)
    u.close()
    return u.result</code></pre>
<ol>
<li>Il file <code>__init__.py</code> definisce la funzione <code>detect()</code>, che è il punto d&#8217;ingresso principale per la libreria <code>chardet</code>.
<li>Ma la funzione <code>detect()</code> contiene pochissimo codice! In effetti, tutto quello che fa è importare il modulo <code>universaldetector</code> e cominciare a usarlo. Ma dov&#8217;è definito <code>universaldetector</code>?
</ol>

<p>La risposta si trova in quella strana istruzione <code>import</code>:

<pre class='nd pp'><code>from . import universaldetector</code></pre>

<p>Tradotta in italiano, quella istruzione significa &#8220;importa il modulo <code>universaldetector</code> che si trova nella stessa directory in cui sono io&#8221;, dove &#8220;io&#8221; è il file <code>chardet/__init__.py</code>. Questa viene chiamata <i>importazione relativa</i> ed è il modo in cui i file all&#8217;interno di un modulo multifile possono fare riferimento gli uni agli altri, senza preoccuparsi di eventuali conflitti di nomi con gli altri moduli che potreste avere installato nel <a href=il-vostro-primo-programma-python.html#importsearchpath>vostro percorso di ricerca per le importazioni</a>. Questa istruzione <code>import</code> cercherà il modulo <code>universaldetector</code> <em>solo</em> all&#8217;interno della directory <code>chardet</code>.

<p>Questi due concetti&nbsp;&mdash;&nbsp;<code>__init__.py</code> e le importazioni relative&nbsp;&mdash;&nbsp;significano che potete suddividere il vostro modulo in tutte le parti che preferite. Il modulo <code>chardet</code> comprende 36 file <code>.py</code>&nbsp;&mdash;&nbsp;36! Eppure tutto quello che avete bisogno di fare per cominciare a usarlo è scrivere <code>import chardet</code>, poi potete chiamare la funzione principale <code>chardet.detect()</code>. All&#8217;insaputa del vostro codice, la funzione <code>detect()</code> è in realtà definita nel file <code>chardet/__init__.py</code>. E a vostra insaputa, la funzione <code>detect()</code> usa un&#8217;importazione relativa per fare riferimento a una classe definita in <code>chardet/universaldetector.py</code>, che a sua volta usa importazioni relative per altri cinque file, tutti contenuti nella directory <code>chardet/</code>.

<blockquote class=note>
<p><span class=u>&#x261E;</span>Se vi siete mai trovati a scrivere una libreria Python di grandi dimensioni (o, più probabilmente, quando realizzate che le dimensioni della vostra piccola libreria sono cresciute fino a diventare grandi), prendetevi il tempo di riorganizzarla in un modulo multifile. Questa è una delle molte cose che Python è capace di fare bene, quindi avvantaggiatevene.
</blockquote>

<p class=a>&#x2042;

<h2 id=manual>Risolvere quello che <code>2to3</code> non può</h2>
<h3 id=falseisinvalidsyntax><code>False</code> è sintassi non valida</h3>
<aside>Avete i test, giusto?</aside>
<p>E ora, la verifica vera e propria: lanciamo il programma di collaudo con i nostri test. Dato che i test sono stati progettati per coprire tutti i possibili percorsi di esecuzione nel codice, questo è un buon modo di collaudare il codice convertito per assicurarsi che non ci sia alcun bug in agguato da qualche parte.
<pre class='nd screen'><samp class=p>C:\home\chardet> </samp><kbd>python test.py tests\*\*</kbd>
<samp class=traceback>Traceback (most recent call last):
  File "test.py", line 1, in &lt;module>
    from chardet.universaldetector import UniversalDetector
  File "C:\home\chardet\chardet\universaldetector.py", line 51
    self.done = constants.False
                              ^
SyntaxError: invalid syntax</samp></pre>
<p>Hmm, un piccolo intoppo. In Python 3, <code>False</code> è una parola riservata e quindi non potete usarla come nome di variabile. Diamo un&#8217;occhiata a <code>constants.py</code> per vedere dov&#8217;è definita. Ecco la versione orginale della parte rilevante di <code>constants.py</code>, prima che lo script <code>2to3</code> la modificasse:
<pre class='nd pp'><code>import __builtin__
if not hasattr(__builtin__, 'False'):
    False = 0
    True = 1
else:
    False = __builtin__.False
    True = __builtin__.True</code></pre>
<p>Questo frammento di codice è progettato per permettere a questa libreria di venire eseguita dalle versioni più vecchie di Python 2. Prima della versione 2.3, Python non aveva alcun tipo <code>bool</code>. Questo codice riconosce l&#8217;assenza delle costanti built-in <code>True</code> e <code>False</code> e le definisce nel caso sia necessario.
<p>Comunque, Python 3 avrà sempre un tipo <code>bool</code>, quindi questo intero frammento di codice è superfluo. La soluzione più semplice consiste nel sostituire tutte le istanze di <code>constants.True</code> e <code>constants.False</code> con <code>True</code> e <code>False</code> rispettivamente e poi cancellare questo codice ormai inutile da <code>constants.py</code>.
<p>Così questa riga in <code>universaldetector.py</code>:
<pre class='nd pp'><code>self.done = constants.False</code></pre>
<p>Diventa:
<pre class='nd pp'><code>self.done = False</code></pre>
<p>Aaah, non siete soddisfatti? Il codice è più corto e già più leggibile.
<h3 id=nomodulenamedconstants>Nessun modulo chiamato <code>constants</code></h3>
<p>&Egrave; il momento di eseguire nuovamente <code>test.py</code> e vedere fino a dove riesce ad arrivare.
<pre class='nd screen'><samp class=p>C:\home\chardet> </samp><kbd>python test.py tests\*\*</kbd>
<samp class=traceback>Traceback (most recent call last):
  File "test.py", line 1, in &lt;module>
    from chardet.universaldetector import UniversalDetector
  File "C:\home\chardet\chardet\universaldetector.py", line 29, in &lt;module>
    import constants, sys
ImportError: No module named constants</samp></pre>
<p>Cosa dice? Nessun modulo chiamato <code>constants</code>? Ma certo che c&#8217;è un modulo chiamato <code>constants</code>. &Egrave; proprio lì, in <code>chardet/constants.py</code>.

<p>Ricordate quando lo script <code>2to3</code> ha corretto tutte quelle istruzioni <code>import</code>? Questa libreria contiene un sacco di <code>import</code> relativi&nbsp;&mdash;&nbsp;cioè, <a href=#multifile-modules>moduli che importano altri moduli nell&#8217;ambito della libreria</a>&nbsp;&mdash;&nbsp;ma <em>la logica dietro il funzionamento delle importazioni relative è cambiata in Python 3</em>. In Python 2, potevate semplicemente scrivere <code>import constants</code> e l&#8217;interprete avrebbe guardato nella direcotry <code>chardet</code> come prima cosa. In Python 3, <a href=http://www.python.org/dev/peps/pep-0328/>tutte le istruzioni <code>import</code> sono assolute per default</a>. Se volete importare un modulo in maniera relativa in Python 3, dovete farlo esplicitamente:
<pre class='nd pp'><code>from . import constants</code></pre>
<p>Ma aspettate. Non doveva essere lo script <code>2to3</code> a curarsi di queste cose per voi? Be&#8217;, lo ha fatto, ma la particolare istruzione <code>import</code> che ha generato l&#8217;errore combina due differenti tipi di importazione in una sola riga: un&#8217;importazione relativa del modulo <code>constants</code> all&#8217;interno della libreria e un&#8217;importazione assoluta del modulo <code>sys</code> che è preinstallato nella libreria standard di Python. In Python 2, potevate combinare questi due tipi in un&#8217;unica istruzione <code>import</code>. In Python 3 non potete, ma lo script <code>2to3</code> non è abbastanza scaltro da dividere l&#8217;istruzione <code>import</code> in due.
<p>La soluzione consiste nel dividere l&#8217;istruzione <code>import</code> manualmente. Così questa doppia istruzione <code>import</code>:
<pre class='nd pp'><code>import constants, sys</code></pre>
<p>Deve diventare due istruzioni <code>import</code> separate:
<pre class='nd pp'><code>from . import constants
import sys</code></pre>
<p>Ci sono variazioni di questo problema sparse in tutta la libreria <code>chardet</code>. In alcuni posti è &#8220;<code>import constants, sys</code>&#8221;; in altri posti è &#8220;<code>import constants, re</code>&#8221;. La soluzione è la stessa: dividete manualmente l&#8217;istruzione <code>import</code> in due righe, una per quella relativa, l&#8217;altra per quella assoluta.
<p>Procediamo!
<h3 id=namefileisnotdefined>Il nome <var>'file'</var> non è definito</h3>
<aside>open() è il nuovo file(). PapayaWhip è il nuovo nero.</aside>
<p>Ed eccoci ancora qui, a lanciare <code>test.py</code> per provare a eseguire i nostri test&hellip;
<pre class='nd screen'><samp class=p>C:\home\chardet> </samp><kbd>python test.py tests\*\*</kbd>
<samp>tests\ascii\howto.diveintomark.org.xml</samp>
<samp class=traceback>Traceback (most recent call last):
  File "test.py", line 9, in &lt;module>
    for line in file(f, 'rb'):
NameError: name 'file' is not defined</samp></pre>
<p>Questo errore mi ha sorpreso, perché ho usato questo idioma fin da quando riesco a ricordare. In Python 2, la funzione globale <code>file()</code> era un alias per <code>open()</code>, che era la funzione standard da usare per <a href=file.html#reading>aprire i file e leggerli</a>. In Python 3, la funzione globale <code>file()</code> non esiste più, ma la funzione <code>open()</code> esiste ancora.
<p>Quindi, la soluzione più semplice al problema della mancanza di <code>file()</code> è chiamare <code>open()</code> al suo posto:
<pre class='nd pp'><code>for line in open(f, 'rb'):</code></pre>
<p>E questo è tutto quello che ho da dire al riguardo.
<h3 id=cantuseastringpattern>Impossibile usare un pattern di stringa su un oggetto tipo <code>bytes</code></h3>
<p>Ora le cose cominciano a diventare interessanti. E per &#8220;interessanti&#8221; voglio dire &#8220;incasinate come l&#8217;inferno&#8221;.
<pre class='nd screen'><samp class=p>C:\home\chardet> </samp><kbd>python test.py tests\*\*</kbd>
<samp>tests\ascii\howto.diveintomark.org.xml</samp>
<samp class=traceback>Traceback (most recent call last):
  File "test.py", line 10, in &lt;module>
    u.feed(line)
  File "C:\home\chardet\chardet\universaldetector.py", line 98, in feed
    if self._highBitDetector.search(aBuf):
TypeError: can't use a string pattern on a bytes-like object</samp></pre>
<p>Per correggere questo errore, diamo un&#8217;occhiata a che cos&#8217;è <var>self._highBitDetector</var>. &Egrave; definito nel metodo <code>__init__()</code> della classe <var>UniversalDetector</var>:
<pre class='nd pp'><code>class UniversalDetector:
    def __init__(self):
        self._highBitDetector = re.compile(r'[\x80-\xFF]')</code></pre>
<p>Questo codice precompila un&#8217;espressione regolare progettata per trovare caratteri di tipo non-<abbr>ASCII</abbr> nell&#8217;intervallo 128&ndash;255 (0x80&ndash;0xFF). Aspettate, non è proprio esatto; devo essere più preciso nella mia terminologia. Questo pattern è utilizzato per trovare <em>byte</em> di tipo non-<abbr>ASCII</abbr> nell&#8217;intervallo 128&ndash;255.
<p>E il problema è proprio qui.
<p>In Python 2, una stringa era un array di byte la cui codifica di carattere veniva memorizzata separatamente. Se volevate che Python 2 tenesse traccia della codifica di carattere, dovevate usare una stringa di tipo Unicode (<code>u''</code>). Ma in Python 3 una stringa è sempre ciò che Python 2 chiamava una stringa di tipo Unicode&nbsp;&mdash;&nbsp;cioè un array di caratteri Unicode (eventualmente con lunghezze diverse in termini di byte). Dato che questa espressione regolare è definita come un pattern di stringa, può essere usata solo per fare ricerche in una stringa&nbsp;&mdash;&nbsp;ancora, un array di caratteri. Ma ciò in cui stiamo cercando non è una stringa, bensì un array di byte. La <i>traceback</i>, cioè la traccia dello stack di esecuzione, ci dice che questo errore è apparso in <code>universaldetector.py</code>:
<pre class='nd pp'><code>def feed(self, aBuf):
    .
    .
    .
    if self._mInputState == ePureAscii:
        if self._highBitDetector.search(aBuf):</code></pre>
<p>E che cos&#8217;è <var>aBuf</var>? Torniamo ancora più indietro al punto in cui <code>UniversalDetector.feed()</code> viene invocata. Quel punto si trova nel programma di collaudo, <code>test.py</code>.
<pre class='nd pp'><code>u = UniversalDetector()
.
.
.
for line in open(f, 'rb'):
    u.feed(line)</code></pre>
<aside>Non un array di caratteri, ma un array di byte.</aside>
<p>E qui troviamo la nostra risposta: nel metodo <code>UniversalDetector.feed()</code>, <var>aBuf</var> è una riga letta da un file su disco. Guardate attentamente i parametri utilizzati per aprire il file: <code>'rb'</code>. <code>'r'</code> sta per &#8220;read&#8221;, lettura; va bene, grazie tante, stiamo leggendo il file. Ah, ma <a href=file.html#binary><code>'b'</code> sta per &#8220;binary&#8221;, binario</a>. Senza il flag <code>'b'</code>, questo ciclo <code>for</code> leggerebbe il file riga per riga e convertirebbe ogni riga in una stringa&nbsp;&mdash;&nbsp;un array di caratteri Unicode&nbsp;&mdash;&nbsp;in accordo con la codifica di carattere predefinita del sistema. Ma con il flag <code>'b'</code> questo ciclo <code>for</code> legge il file riga per riga e memorizza ogni riga esattamente come appare nel file, sotto forma di un array di byte. Quell&#8217;array di byte viene passato a <code>UniversalDetector.feed()</code> e alla fine viene passato all&#8217;espressione regolare precompilata <var>self._highBitDetector</var> per cercare i&hellip; caratteri high-bit. Ma non abbiamo caratteri. Abbiamo byte. Oops.
<p>Quello su cui abbiamo bisogno che l&#8217;espressione regolare effettui la ricerca non è un array di caratteri, ma un array di byte.
<p>Una volta che realizzate questo, la soluzione non è difficile. Le espressioni regolari definite come stringhe possono effettuare ricerche sulle stringhe. Le espressioni regolari definite come array di byte possono effettuare ricerche sugli array di byte. Per definire un pattern come un array di byte, cambiamo semplicemente il tipo dell&#8217;argomento che usiamo per definire l&#8217;espressione regolare ad array di byte. (C&#8217;è un altro caso di questo stesso problema, proprio nella riga successiva.)
<pre class='nd pp'><code>  class UniversalDetector:
      def __init__(self):
<del>-         self._highBitDetector = re.compile(r'[\x80-\xFF]')</del>
<del>-         self._escDetector = re.compile(r'(\033|~{)')</del>
<ins>+         self._highBitDetector = re.compile(b'[\x80-\xFF]')</ins>
<ins>+         self._escDetector = re.compile(b'(\033|~{)')</ins>
          self._mEscCharSetProber = None
          self._mCharSetProbers = []
          self.reset()</code></pre>
<p>Una ricerca sull&#8217;intera base di codice rivela altri due utilizzi del modulo <code>re</code>, in <code>charsetprober.py</code>. Ancora, il codice definisce espressioni regolari come stringhe ma le utilizza su <var>aBuf</var> che è un array di byte. La soluzione è la stessa: definire i pattern di espressione regolare come array di byte.
<pre class='nd pp'><code>  class CharSetProber:
      .
      .
      .
      def filter_high_bit_only(self, aBuf):
<del>-         aBuf = re.sub(r'([\x00-\x7F])+', ' ', aBuf)</del>
<ins>+         aBuf = re.sub(b'([\x00-\x7F])+', b' ', aBuf)</ins>
          return aBuf
    
      def filter_without_english_letters(self, aBuf):
<del>-         aBuf = re.sub(r'([A-Za-z])+', ' ', aBuf)</del>
<ins>+         aBuf = re.sub(b'([A-Za-z])+', b' ', aBuf)</ins>
          return aBuf</code></pre>
        
<h3 id=cantconvertbytesobject>Impossibile convertire implicitamente un oggetto <code>'bytes'</code> in <code>str</code></h3>
<p>Sempre più stranissimo&hellip;
<pre class='nd screen'><samp class=p>C:\home\chardet> </samp><kbd>python test.py tests\*\*</kbd>
<samp>tests\ascii\howto.diveintomark.org.xml</samp>
<samp class=traceback>Traceback (most recent call last):
  File "test.py", line 10, in &lt;module>
    u.feed(line)
  File "C:\home\chardet\chardet\universaldetector.py", line 100, in feed
    elif (self._mInputState == ePureAscii) and self._escDetector.search(self._mLastChar + aBuf):
TypeError: Can't convert 'bytes' object to str implicitly</samp></pre>
<p>Qui c&#8217;è una sfortunata collisione tra stile di codifica e interprete Python. L&#8217;errore di tipo <code>TypeError</code> potrebbe essere ovunque su quella riga, ma la traceback non vi dice esattamente dov&#8217;è. Potrebbe essere nella prima condizione o nella seconda, ma la traceback sarebbe la stessa. Per circoscrivere l&#8217;errore dovete spezzare la riga a metà in questo modo:
<pre class='nd pp'><code>elif (self._mInputState == ePureAscii) and \
    self._escDetector.search(self._mLastChar + aBuf):</code></pre>
<p>E rieseguire il test:
<pre class='nd screen'><samp class=p>C:\home\chardet> </samp><kbd>python test.py tests\*\*</kbd>
<samp>tests\ascii\howto.diveintomark.org.xml</samp>
<samp class=traceback>Traceback (most recent call last):
  File "test.py", line 10, in &lt;module>
    u.feed(line)
  File "C:\home\chardet\chardet\universaldetector.py", line 101, in feed
    self._escDetector.search(self._mLastChar + aBuf):
TypeError: Can't convert 'bytes' object to str implicitly</samp></pre>
<p>Aha! Il problema non era nella prima condizione (<code>self._mInputState == ePureAscii</code>) ma nella seconda. Quindi cosa potrebbe causare un <code>TypeError</code> in quel punto? Forse state pensando che il metodo <code>search()</code> si aspetti un valore di tipo differente, ma quel problema non genererebbe questa traceback. Le funzioni Python possono accettare qualsiasi valore; se passate il numero corretto di argomenti, la funzione verrà eseguita. Potrebbe <em>fallire</em> se le passate un valore di tipo differente rispetto a quello che si aspetta, ma se questo fosse successo la traceback punterebbe da qualche parte all&#8217;interno della funzione. Invece questa traceback dice che l&#8217;interprete non è mai arrivato tanto lontano da chiamare il metodo <code>search()</code>. Quindi il problema deve essere in quella operazione <code>+</code>, dato che sta cercando di costruire un valore che alla fine verrà passato al metodo <code>search()</code>.
<p>Sappiamo da una <a href=#cantuseastringpattern>correzione precedente</a> che <var>aBuf</var> è un array di byte. Quindi cos&#8217;è <code>self._mLastChar</code>? &Egrave; una variabile di istanza, definita nel metodo <code>reset()</code>, che viene effettivamente chiamato dal metodo <code>__init__()</code>.
<pre class='nd pp'><code>class UniversalDetector:
    def __init__(self):
        self._highBitDetector = re.compile(b'[\x80-\xFF]')
        self._escDetector = re.compile(b'(\033|~{)')
        self._mEscCharSetProber = None
        self._mCharSetProbers = []
<mark>        self.reset()</mark>

    def reset(self):
        self.result = {'encoding': None, 'confidence': 0.0}
        self.done = False
        self._mStart = True
        self._mGotData = False
        self._mInputState = ePureAscii
<mark>        self._mLastChar = ''</mark></code></pre>
<p>E ora abbiamo la nostra risposta. La vedete? <var>self._mLastChar</var> è una stringa, ma <var>aBuf</var> è un array di byte. E non potete concatenare una stringa a un array di byte&nbsp;&mdash;&nbsp;nemmeno una stringa di lunghezza zero.
<p>Quindi cos&#8217;è <var>self._mLastChar</var> ad ogni modo? Guardiamo nel metodo <code>feed()</code>, giusto qualche riga più in basso rispetto a dove la traceback si è generata.
<pre class='nd pp'><code>if self._mInputState == ePureAscii:
    if self._highBitDetector.search(aBuf):
        self._mInputState = eHighbyte
    elif (self._mInputState == ePureAscii) and \
            self._escDetector.search(self._mLastChar + aBuf):
        self._mInputState = eEscAscii

<mark>self._mLastChar = aBuf[-1]</mark></code></pre>
<p>La funzione chiamante invoca continuamente questo metodo <code>feed()</code> con pochi byte alla volta. Il metodo elabora i byte che gli sono stati dati (passati come <var>aBuf</var>), poi memorizza l&#8217;ultimo byte in <var>self._mLastChar</var> nel caso ce ne sia bisogno durante l&#8217;invocazione successiva. (In una codifica multibyte, il metodo <code>feed()</code> potrebbe venire invocato con metà di un carattere e poi invocato ancora con l&#8217;altra metà.) Ma dato che ora <var>aBuf</var> è un array di byte invece di essere una stringa, anche <var>self._mLastChar</var> deve essere un array di byte. Perciò:
<pre class='nd pp'><code>  def reset(self):
      .
      .
      .
<del>-     self._mLastChar = ''</del>
<ins>+     self._mLastChar = b''</ins></code></pre>
<p>Una ricerca su tutta la base di codice per &#8220;<code>mLastChar</code>&#8221; rivela un problema simile in <code>mbcharsetprober.py</code>, dove invece di memorizzare l&#8217;ultimo carattere vengono memorizzati gli ultimi <em>due</em> caratteri. La classe <code>MultiByteCharSetProber</code> usa una lista di stringhe di 1 carattere per tenere traccia degli ultimi due caratteri; in Python 3 deve usare una lista di interi, perché non sta realmente tenendo traccia di caratteri, ma di byte. (I byte sono semplicemente interi nell&#8217;intervallo 0&ndash;255.)
<pre class='nd pp'><code>  class MultiByteCharSetProber(CharSetProber):
      def __init__(self):
          CharSetProber.__init__(self)
          self._mDistributionAnalyzer = None
          self._mCodingSM = None
<del>-         self._mLastChar = ['\x00', '\x00']</del>
<ins>+         self._mLastChar = [0, 0]</ins>

      def reset(self):
          CharSetProber.reset(self)
          if self._mCodingSM:
              self._mCodingSM.reset()
          if self._mDistributionAnalyzer:
              self._mDistributionAnalyzer.reset()
<del>-         self._mLastChar = ['\x00', '\x00']</del>
<ins>+         self._mLastChar = [0, 0]</ins></code></pre>
<h3 id=unsupportedoperandtypeforplus>Tipi di operando non supportati per +: <code>'int'</code> e <code>'bytes'</code></h3>
<p>Ho una buona notizia e una cattiva notizia. La buona notizia è che stiamo facendo passi avanti&hellip;
<pre class='nd screen'><samp class=p>C:\home\chardet> </samp><kbd>python test.py tests\*\*</kbd>
<samp>tests\ascii\howto.diveintomark.org.xml</samp>
<samp class=traceback>Traceback (most recent call last):
  File "test.py", line 10, in &lt;module>
    u.feed(line)
  File "C:\home\chardet\chardet\universaldetector.py", line 101, in feed
    self._escDetector.search(self._mLastChar + aBuf):
TypeError: unsupported operand type(s) for +: 'int' and 'bytes'</samp></pre>
<p>&hellip;La cattiva notizia è che questo non sempre sembra un passo avanti.
<p>Ma questo è un passo avanti! Davvero! Anche se la traceback evidenzia la stessa riga di codice, questo è un errore diverso da quello di prima. Un passo avanti! Quindi qual è ora il problema? L&#8217;ultima volta che ho controllato, questa riga di codice non provava a concatenare un <code>int</code> con un array di byte (<code>bytes</code>). Infatti, avete appena speso un sacco di tempo per <a href=#cantconvertbytesobject>assicurarvi che <var>self._mLastChar</var> fosse un array di byte</a>. Come si è trasformato in un <code>int</code>?
<p>La risposta non è nelle righe di codice precedenti, ma nelle righe seguenti.
<pre class='nd pp'><code>if self._mInputState == ePureAscii:
    if self._highBitDetector.search(aBuf):
        self._mInputState = eHighbyte
    elif (self._mInputState == ePureAscii) and \
            self._escDetector.search(self._mLastChar + aBuf):
        self._mInputState = eEscAscii

<mark>self._mLastChar = aBuf[-1]</mark></code></pre>
<aside>Ogni elemento in una stringa è una stringa. Ogni elemento in un array di byte è un intero.</aside>
<p>Questo errore non avviene la prima volta che il metodo <code>feed()</code> viene chiamato; avviene la <em>seconda volta</em>, dopo che a <var>self._mLastChar</var> è stato assegnato l&#8217;ultimo byte di <var>aBuf</var>. E quindi, qual è il problema con quella operazione? Recuperare un singolo elemento da un array di byte produce un intero, non un array di byte. Per vedere la differenza, seguitemi nella shell interattiva:
<pre class=screen>
<a><samp class=p>>>> </samp><kbd class=pp>aBuf = b'\xEF\xBB\xBF'</kbd>         <span class=u>&#x2460;</span></a>
<samp class=p>>>> </samp><kbd class=pp>len(aBuf)</kbd>
<samp class=pp>3</samp>
<samp class=p>>>> </samp><kbd class=pp>mLastChar = aBuf[-1]</kbd>
<a><samp class=p>>>> </samp><kbd class=pp>mLastChar</kbd>                      <span class=u>&#x2461;</span></a>
<samp class=pp>191</samp>
<a><samp class=p>>>> </samp><kbd class=pp>type(mLastChar)</kbd>                <span class=u>&#x2462;</span></a>
<samp>&lt;class 'int'></samp>
<a><samp class=p>>>> </samp><kbd class=pp>mLastChar + aBuf</kbd>               <span class=u>&#x2463;</span></a>
<samp class=traceback>Traceback (most recent call last):
  File "&lt;stdin>", line 1, in &lt;module>
TypeError: unsupported operand type(s) for +: 'int' and 'bytes'</samp>
<a><samp class=p>>>> </samp><kbd class=pp>mLastChar = aBuf[-1:]</kbd>          <span class=u>&#x2464;</span></a>
<samp class=p>>>> </samp><kbd class=pp>mLastChar</kbd>
<samp class=pp>b'\xbf'</samp>
<a><samp class=p>>>> </samp><kbd class=pp>mLastChar + aBuf</kbd>               <span class=u>&#x2465;</span></a>
<samp class=pp>b'\xbf\xef\xbb\xbf'</samp></pre>
<ol>
<li>Definisce un array di byte di lunghezza 3.
<li>L&#8217;ultimo elemento dell&#8217;array di byte è 191.
<li>Quell&#8217;ultimo elemento è un intero.
<li>Concatenare un intero a un array di byte non funziona. Avete appena riprodotto l&#8217;errore che è stato trovato in <code>universaldetector.py</code>.
<li>Ah, questa è la soluzione. Invece di prendere l&#8217;ultimo elemento dell&#8217;array di byte, <a href=tipi-di-dato-nativi.html#slicinglists>affettate la lista</a> per creare un nuovo array di byte contenente solo l&#8217;ultimo elemento. Cioè, cominciate con l&#8217;ultimo elemento e prolungate la fetta fino alla fine dell&#8217;array di byte. Ora <var>mLastChar</var> è un array di byte di lunghezza 1.
<li>Concatenare un array di byte di lunghezza 1 con un array di byte di lunghezza 3 restituisce un nuovo array di byte di lunghezza 4.
</ol>
<p>Quindi, per assicurarvi che il metodo <code>feed()</code> in <code>universaldetector.py</code> continui a lavorare a prescindere da quante volte viene invocato, dovete <a href=#cantconvertbytesobject>inizializzare <var>self._mLastChar</var> come un array di byte di lunghezza 0</a> e poi <em>assicurarvi che rimanga un array di byte</em>.
<pre class='nd pp'><code>              self._escDetector.search(self._mLastChar + aBuf):
          self._mInputState = eEscAscii

<del>- self._mLastChar = aBuf[-1]</del>
<ins>+ self._mLastChar = aBuf[-1:]</ins></code></pre>
<h3 id=ordexpectedstring><code>ord()</code> si aspetta una stringa di lunghezza 1, ma trova un <code>int</code></h3>
<p>Già stanchi? Ci siete quasi&hellip;
<pre class='nd screen'><samp class=p>C:\home\chardet> </samp><kbd>python test.py tests\*\*</kbd>
<samp>tests\ascii\howto.diveintomark.org.xml                       ascii con confidenza 1.0
tests\Big5\0804.blogspot.com.xml</samp>
<samp class=traceback>Traceback (most recent call last):
  File "test.py", line 10, in &lt;module>
    u.feed(line)
  File "C:\home\chardet\chardet\universaldetector.py", line 116, in feed
    if prober.feed(aBuf) == constants.eFoundIt:
  File "C:\home\chardet\chardet\charsetgroupprober.py", line 60, in feed
    st = prober.feed(aBuf)
  File "C:\home\chardet\chardet\utf8prober.py", line 53, in feed
    codingState = self._mCodingSM.next_state(c)
  File "C:\home\chardet\chardet\codingstatemachine.py", line 43, in next_state
    byteCls = self._mModel['classTable'][ord(c)]
TypeError: ord() expected string of length 1, but int found</samp></pre>
<p>Bene, quindi <var>c</var> è un <code>int</code> ma la funzione <code>ord()</code> si aspetta una stringa di 1 carattere. Abbastanza onesto. Dove viene definito <var>c</var>?
<pre class='nd pp'><code># codingstatemachine.py
def next_state(self, c):
    # recuperiamo la classe di ogni byte
    # se è il primo byte, ne recuperiamo anche la lunghezza
    byteCls = self._mModel['classTable'][ord(c)]</code></pre>
<p>Questo frammento di codice non è di aiuto; il parametro viene semplicemente passato dentro la funzione. Risaliamo la traccia dello stack di esecuzione.
<pre class='nd pp'><code># utf8prober.py
def feed(self, aBuf):
    for c in aBuf:
        codingState = self._mCodingSM.next_state(c)</code></pre>
<p>Vedete? In Python 2 <var>aBuf</var> era una stringa, quindi <var>c</var> era una stringa di 1 carattere. (Questo è quello che si ottiene quando si itera su una stringa&nbsp;&mdash;&nbsp;tutti i caratteri, uno a uno.) Ma ora <var>aBuf</var> è un vettore di byte, quindi <var>c</var> è un <code>int</code>, non una stringa di 1 carattere. In altre parole, non c&#8217;è alcun bisogno di chiamare la funzione <code>ord()</code> perché <var>c</var> è già un <code>int</code>!
<p>Perciò:
<pre class='nd pp'><code>  def next_state(self, c):
      # recuperiamo la classe di ogni byte
      # se è il primo byte, ne recuperiamo anche la lunghezza
<del>-     byteCls = self._mModel['classTable'][ord(c)]</del>
<ins>+     byteCls = self._mModel['classTable'][c]</ins></code></pre>
<p>Una ricerca sull&#8217;intera base di codice per istanze di &#8220;<code>ord(c)</code>&#8221; rivela problemi simili in <code>sbcharsetprober.py</code>&hellip;
<pre class='nd pp'><code># sbcharsetprober.py
def feed(self, aBuf):
    if not self._mModel['keepEnglishLetter']:
        aBuf = self.filter_without_english_letters(aBuf)
    aLen = len(aBuf)
    if not aLen:
        return self.get_state()
    for c in aBuf:
<mark>        order = self._mModel['charToOrderMap'][ord(c)]</mark></code></pre>
<p>&hellip;e in <code>latin1prober.py</code>&hellip;
<pre class='nd pp'><code># latin1prober.py
def feed(self, aBuf):
    aBuf = self.filter_with_english_letters(aBuf)
    for c in aBuf:
<mark>        charClass = Latin1_CharToClass[ord(c)]</mark></code></pre>
<p><var>c</var> sta iterando su <var>aBuf</var>, il che significa che è un intero, non una stringa di 1 carattere. La soluzione è la stessa: sostituire <code>ord(c)</code> con il solo <code>c</code>.
<pre class='nd pp'><code>  # sbcharsetprober.py
  def feed(self, aBuf):
      if not self._mModel['keepEnglishLetter']:
          aBuf = self.filter_without_english_letters(aBuf)
      aLen = len(aBuf)
      if not aLen:
          return self.get_state()
      for c in aBuf:
<del>-         order = self._mModel['charToOrderMap'][ord(c)]</del>
<ins>+         order = self._mModel['charToOrderMap'][c]</ins>

  # latin1prober.py
  def feed(self, aBuf):
      aBuf = self.filter_with_english_letters(aBuf)
      for c in aBuf:
<del>-         charClass = Latin1_CharToClass[ord(c)]</del>
<ins>+         charClass = Latin1_CharToClass[c]</ins>
</code></pre>
<h3 id=unorderabletypes>Tipi non ordinabili: <code>int()</code> >= <code>str()</code></h3>
<p>Continuiamo ancora.
<pre class='nd screen'><samp class=p>C:\home\chardet> </samp><kbd>python test.py tests\*\*</kbd>
<samp>tests\ascii\howto.diveintomark.org.xml                       ascii con confidenza 1.0
tests\Big5\0804.blogspot.com.xml</samp>
<samp class=traceback>Traceback (most recent call last):
  File "test.py", line 10, in &lt;module>
    u.feed(line)
  File "C:\home\chardet\chardet\universaldetector.py", line 116, in feed
    if prober.feed(aBuf) == constants.eFoundIt:
  File "C:\home\chardet\chardet\charsetgroupprober.py", line 60, in feed
    st = prober.feed(aBuf)
  File "C:\home\chardet\chardet\sjisprober.py", line 68, in feed
    self._mContextAnalyzer.feed(self._mLastChar[2 - charLen :], charLen)
  File "C:\home\chardet\chardet\jpcntx.py", line 145, in feed
    order, charLen = self.get_order(aBuf[i:i+2])
  File "C:\home\chardet\chardet\jpcntx.py", line 176, in get_order
    if ((aStr[0] >= '\x81') and (aStr[0] &lt;= '\x9F')) or \
TypeError: unorderable types: int() >= str()</samp></pre>
<p>Quindi qual è l&#8217;errore in questo caso? &#8220;Unorderable types&#8221;? Tipi non ordinabili? Ancora una volta, la differenza tra array di byte e stringhe sta rialzando la sua ripugnante testa. Date un&#8217;occhiata al codice:
<pre class='nd pp'><code>class SJISContextAnalysis(JapaneseContextAnalysis):
    def get_order(self, aStr):
        if not aStr: return -1, 1
        # trova la lunghezza in byte del carattere corrente
<mark>        if ((aStr[0] >= '\x81') and (aStr[0] &lt;= '\x9F')) or \</mark>
           ((aStr[0] >= '\xE0') and (aStr[0] &lt;= '\xFC')):
            charLen = 2
        else:
            charLen = 1</code></pre>
<p>E da dove viene <var>aStr</var>? Risaliamo la traccia dello stack di esecuzione:
<pre class='nd pp'><code>def feed(self, aBuf, aLen):
    .
    .
    .
    i = self._mNeedToSkipCharNum
    while i &lt; aLen:
<mark>        order, charLen = self.get_order(aBuf[i:i+2])</mark></code></pre>
<p>Oh, guardate, è il nostro vecchio amico <var>aBuf</var>. Come avrete potuto intuire da tutti gli altri problemi che abbiamo incontrato in questo capitolo, <var>aBuf</var> è un array di byte. Qui il metodo <code>feed()</code> non sta semplicemente passandolo tutto intero, ma lo sta affettando. Però, come avete visto <a href=#unsupportedoperandtypeforplus>precedentemente in questo capitolo</a>, affettare un array di byte restituisce un array di byte, quindi il parametro <var>aStr</var> che viene passato al metodo <code>get_order()</code> è ancora un array di byte.
<p>E cos&#8217;è che questo codice sta cercando di fare con <var>aStr</var>? Prende il primo elemento dell&#8217;array di byte e lo confronta con una stringa di lunghezza 1. In Python 2 questo funzionava, perché <var>aStr</var> e <var>aBuf</var> erano stringhe, e <var>aStr[0]</var> sarebbe stata una stringa, e potevate confrontare le stringhe per la disuguaglianza. Ma in Python 3 <var>aStr</var> e <var>aBuf</var> sono array di byte, <var>aStr[0]</var> è un intero e non potete effettuare un confronto di disuguaglianza tra interi e stringhe senza convertire esplicitamente gli uni nel tipo delle altre o viceversa.
<p>In questo caso non c&#8217;è bisogno di rendere il codice più complicato aggiungendo una conversione esplicita. <var>aStr[0]</var> produce un intero e le cose con le quali state effettuando il confronto sono tutte costanti. Modifichiamole da stringhe di 1 carattere a interi. E visto che ci siamo, cambiamo il nome di <var>aStr</var> in <var>aBuf</var>, dato che in realtà non è una stringa.
<pre class='nd pp'><code>  class SJISContextAnalysis(JapaneseContextAnalysis):
<del>-     def get_order(self, aStr):</del>
<del>-         if not aStr: return -1, 1</del>
<ins>+     def get_order(self, aBuf):</ins>
<ins>+         if not aBuf: return -1, 1</ins>
          # trova la lunghezza in byte del carattere corrente
<del>-         if ((aStr[0] >= '\x81') and (aStr[0] &lt;= '\x9F')) or \</del>
<del>-            ((aStr[0] >= '\xE0') and (aStr[0] &lt;= '\xFC')):</del>
<ins>+         if ((aBuf[0] >= 0x81) and (aBuf[0] &lt;= 0x9F)) or \</ins>
<ins>+            ((aBuf[0] >= 0xE0) and (aBuf[0] &lt;= 0xFC)):</ins>
              charLen = 2
          else:
              charLen = 1

          # restituisce il suo ordinale se è hiragana
<del>-          if len(aStr) > 1:</del>
<del>-              if (aStr[0] == '\202') and \</del>
<del>-                 (aStr[1] >= '\x9F') and \</del>
<del>-                 (aStr[1] &lt;= '\xF1'):</del>
<del>-                  return ord(aStr[1]) - 0x9F, charLen</del>
<ins>+          if len(aBuf) > 1:</ins>
<ins>+              if (aBuf[0] == 202) and \</ins>
<ins>+                 (aBuf[1] >= 0x9F) and \</ins>
<ins>+                 (aBuf[1] &lt;= 0xF1):</ins>
<ins>+                  return aBuf[1] - 0x9F, charLen</ins>

          return -1, charLen

  class EUCJPContextAnalysis(JapaneseContextAnalysis):
<del>-     def get_order(self, aStr):</del>
<del>-         if not aStr: return -1, 1</del>
<ins>+     def get_order(self, aBuf):</ins>
<ins>+         if not aBuf: return -1, 1</ins>
          # trova la lunghezza in byte del carattere corrente
<del>-         if (aStr[0] == '\x8E') or \</del>
<del>-            ((aStr[0] >= '\xA1') and (aStr[0] &lt;= '\xFE')):</del>
<ins>+         if (aBuf[0] == 0x8E) or \</ins>
<ins>+            ((aBuf[0] >= 0xA1) and (aBuf[0] &lt;= 0xFE)):</ins>
              charLen = 2
<del>-         elif aStr[0] == '\x8F':</del>
<ins>+         elif aBuf[0] == 0x8F:</ins>
              charLen = 3
          else:
              charLen = 1

        # restituisce il suo ordinale se è hiragana
<del>-        if len(aStr) > 1:</del>
<del>-            if (aStr[0] == '\xA4') and \</del>
<del>-               (aStr[1] >= '\xA1') and \</del>
<del>-               (aStr[1] &lt;= '\xF3'):</del>
<del>-                return ord(aStr[1]) - 0xA1, charLen</del>
<ins>+        if len(aBuf) > 1:</ins>
<ins>+            if (aBuf[0] == 0xA4) and \</ins>
<ins>+               (aBuf[1] >= 0xA1) and \</ins>
<ins>+               (aBuf[1] &lt;= 0xF3):</ins>
<ins>+                return aBuf[1] - 0xA1, charLen</ins>

        return -1, charLen</code></pre>
<p>Una ricerca sull&#8217;intera base di codice per occorrenze della funzione <code>ord()</code> rivela lo stesso problema in <code>chardistribution.py</code> (più precisamente, nelle classi <code>EUCTWDistributionAnalysis</code>, <code>EUCKRDistributionAnalysis</code>, <code>GB2312DistributionAnalysis</code>, <code>Big5DistributionAnalysis</code>, <code>SJISDistributionAnalysis</code> e <code>EUCJPDistributionAnalysis</code>). In ogni caso, la correzione è simile alla modifica che abbiamo fatto alle classi <code>EUCJPContextAnalysis</code> e <code>SJISContextAnalysis</code> in <code>jpcntx.py</code>.
<h3 id=reduceisnotdefined>Il nome globale <code>'reduce'</code> non è definito</h3>
<p>Ancora una volta sulla breccia&hellip;
<pre class='nd screen'><samp class=p>C:\home\chardet> </samp><kbd>python test.py tests\*\*</kbd>
<samp>tests\ascii\howto.diveintomark.org.xml                       ascii con confidenza 1.0
tests\Big5\0804.blogspot.com.xml</samp>
<samp class=traceback>Traceback (most recent call last):
  File "test.py", line 12, in &lt;module>
    u.close()
  File "C:\home\chardet\chardet\universaldetector.py", line 141, in close
    proberConfidence = prober.get_confidence()
  File "C:\home\chardet\chardet\latin1prober.py", line 126, in get_confidence
    total = reduce(operator.add, self._mFreqCounter)
NameError: global name 'reduce' is not defined</samp></pre>
<p>Secondo <a href=http://docs.python.org/3.0/whatsnew/3.0.html#builtins>la guida ufficiale alle novità di Python 3</a>, la funzione <code>reduce()</code> è stata spostata dallo spazio di nomi globale al modulo <code>functools</code>. Per citare la guida: &#8220;Usate <code>functools.reduce()</code> se proprio ne avete bisogno; comunque, il 99 per cento delle volte un esplicito ciclo <code>for</code> è più leggibile.&#8221; Potete approfondire le motivazioni di questa decisione sul blog di Guido van Rossum: <a href='http://www.artima.com/weblogs/viewpost.jsp?thread=98196'>Il destino di reduce() in Python 3000</a>.
<pre class='nd pp'><code>def get_confidence(self):
    if self.get_state() == constants.eNotMe:
        return 0.01
  
<mark>    total = reduce(operator.add, self._mFreqCounter)</mark></code></pre>
<p>La funzione <code>reduce()</code> prende due argomenti&nbsp;&mdash;&nbsp;una funzione e una lista (strettamente parlando, qualsiasi oggetto iterabile potrebbe andare)&nbsp;&mdash;&nbsp;e applica una funzione cumulativamente a ogni elemento della lista. In altre parole, questo è un modo elaborato e tortuoso di sommare tutti gli elementi di una lista e restituire il risultato.
<p>Questa mostruosità era così comune che a Python è stata aggiunta una funzione globale <code>sum()</code>.
<pre class='nd pp'><code>  def get_confidence(self):
      if self.get_state() == constants.eNotMe:
          return 0.01
  
<del>-     total = reduce(operator.add, self._mFreqCounter)</del>
<ins>+     total = sum(self._mFreqCounter)</ins></code></pre>
<p>Dato che non state più usando il modulo <code>operator</code>, potete anche rimuovere quella istruzione <code>import</code> dall&#8217;inizio del file.
<pre class='nd pp'><code>  from .charsetprober import CharSetProber
  from . import constants
<del>- import operator</del></code></pre>
<p>I CAN HAZ TESTZ?
<pre class='nd screen'><samp class=p>C:\home\chardet> </samp><kbd>python test.py tests\*\*</kbd>
<samp>tests\ascii\howto.diveintomark.org.xml                       ascii con confidenza 1.0
tests\Big5\0804.blogspot.com.xml                             Big5 con confidenza 0.99
tests\Big5\blog.worren.net.xml                               Big5 con confidenza 0.99
tests\Big5\carbonxiv.blogspot.com.xml                        Big5 con confidenza 0.99
tests\Big5\catshadow.blogspot.com.xml                        Big5 con confidenza 0.99
tests\Big5\coolloud.org.tw.xml                               Big5 con confidenza 0.99
tests\Big5\digitalwall.com.xml                               Big5 con confidenza 0.99
tests\Big5\ebao.us.xml                                       Big5 con confidenza 0.99
tests\Big5\fudesign.blogspot.com.xml                         Big5 con confidenza 0.99
tests\Big5\kafkatseng.blogspot.com.xml                       Big5 con confidenza 0.99
tests\Big5\ke207.blogspot.com.xml                            Big5 con confidenza 0.99
tests\Big5\leavesth.blogspot.com.xml                         Big5 con confidenza 0.99
tests\Big5\letterlego.blogspot.com.xml                       Big5 con confidenza 0.99
tests\Big5\linyijen.blogspot.com.xml                         Big5 con confidenza 0.99
tests\Big5\marilynwu.blogspot.com.xml                        Big5 con confidenza 0.99
tests\Big5\myblog.pchome.com.tw.xml                          Big5 con confidenza 0.99
tests\Big5\oui-design.com.xml                                Big5 con confidenza 0.99
tests\Big5\sanwenji.blogspot.com.xml                         Big5 con confidenza 0.99
tests\Big5\sinica.edu.tw.xml                                 Big5 con confidenza 0.99
tests\Big5\sylvia1976.blogspot.com.xml                       Big5 con confidenza 0.99
tests\Big5\tlkkuo.blogspot.com.xml                           Big5 con confidenza 0.99
tests\Big5\tw.blog.xubg.com.xml                              Big5 con confidenza 0.99
tests\Big5\unoriginalblog.com.xml                            Big5 con confidenza 0.99
tests\Big5\upsaid.com.xml                                    Big5 con confidenza 0.99
tests\Big5\willythecop.blogspot.com.xml                      Big5 con confidenza 0.99
tests\Big5\ytc.blogspot.com.xml                              Big5 con confidenza 0.99
tests\EUC-JP\aivy.co.jp.xml                                  EUC-JP con confidenza 0.99
tests\EUC-JP\akaname.main.jp.xml                             EUC-JP con confidenza 0.99
tests\EUC-JP\arclamp.jp.xml                                  EUC-JP con confidenza 0.99
.
.
.
316 test</samp></pre>
<p>Porca puttana, funziona davvero! <em><a href=http://www.hampsterdance.com/>/me fa una piccola danza</a></em>

<p class=a>&#x2042;

<h2 id=summary>Riepilogo</h2>
<p>Che cosa abbiamo imparato?
<ol>
<li>Convertire una qualunque quantità non banale di codice da Python 2 verso Python 3 sarà doloroso. Non c&#8217;è alcun modo di evitarlo. &Egrave; difficile.
<li>Lo <a href=convertire-codice-verso-python-3-con-2to3.html>stumento automatico <code>2to3</code></a> è utile per quello che può fare, ma si occuperà solo della parte facile&nbsp;&mdash;&nbsp;rinominerà le funzioni e i moduli, modificherà la sintassi. &Egrave; un impressionante esempio di ingegneria, ma in fondo non è altro che un automa intelligente per la ricerca e la sostituzione.
<li>Il problema n°1 nella conversione di questa libreria era la differenza tra stringhe e byte. In questo caso sembrava ovvio, dato che lo scopo stesso della libreria <code>chardet</code> è convertire un flusso di byte in una stringa. Ma &#8220;un flusso di byte&#8221; scorre molto più spesso di quanto possiate immaginare. Leggete un file in modalità &#8220;binaria&#8221;? Avrete un flusso di byte. Prelevate una pagina web? Chiamate una <abbr>API</abbr> web? Anche quelle restituiscono un flusso di byte.
<li><em>Voi</em> avete bisogno di capire il vostro programma. A fondo. Preferibilmente perché lo avete scritto, ma è necessario almeno che vi troviate a vostro agio con tutte le sue stranezze e i suoi angoli ammuffiti. I bug si annidano ovunque.
<li>I test sono essenziali. Non convertite nulla senza di loro. L&#8217;<em>unica</em> ragione per cui ho una qualche confidenza che <code>chardet</code> funzioni in Python 3 è perché ho cominciato con una serie di test che esercitava tutti i percorsi principali all&#8217;interno del codice. Se non avete nessun test, scrivetene alcuni prima di cominciare a convertire verso Python 3. Se avete alcuni test, scrivetene altri. Se avete molti test, allora il vero divertimento può cominciare.
</ol>
<p class=v><a href=servizi-web-http.html rel=prev title='indietro a &#8220;Servizi web HTTP&#8221;'><span class=u>&#x261C;</span></a> <a href=distribuire-librerie-python.html rel=next title='avanti a &#8220;Distribuire librerie Python&#8221;'><span class=u>&#x261E;</span></a>
<p class=c>&copy; 2001&ndash;10 <a href=informazioni-sul-libro.html>Mark Pilgrim</a><br>
&copy; 2009&ndash;10 <a href=informazioni-sulla-traduzione.html>Giulio Piancastelli</a> per la traduzione italiana
<script src=j/jquery.js></script>
<script src=j/prettify.js></script>
<script src=j/dip3.js></script>