2016-07-14 54 views
6

Se specifico la codifica dei caratteri (come suggerito da PEP 263) nella "linea magica" o shebang di un modulo Python comevengo codifica specificata nella magia linea/shebang (dall'interno di modulo)

# -*- coding: utf-8 -*- 

posso recuperare questa codifica da quel modulo?

(Lavorare su Windows 7 x64 con Python 2.7.9)


ho cercato (senza successo) per recuperare la codifica di default o la baracca

# -*- coding: utf-8 -*- 

import sys 
from shebang import shebang 

print "sys.getdefaultencoding():", sys.getdefaultencoding() 
print "shebang:", shebang(__file__.rstrip("oc")) 

sarà resa:

sys.getdefaultencoding(): ascii

shebang: None

(stesso per iso-8859-1)

+1

E 'molto probabile che le informazioni di codifica si perde dopo la compilazione a 'pyc'. Potrebbe essere necessario analizzare direttamente il file 'py'. – gdlmx

+2

Si noti che 'sys.getdefaultencoding()' non ha * * nulla a che fare con la decodifica del codice sorgente Python. –

risposta

5

Prenderò in prestito Python 3 tokenize.detect_encoding() function in Python 2, leggermente aggiustato per soddisfare le aspettative di Python 2. Ho modificato la firma della funzione per accettare un nome file e ho eliminato l'inclusione delle righe lette finora; non hai bisogno di quelle per il vostro caso d'uso:

import re 
from codecs import lookup, BOM_UTF8 

cookie_re = re.compile(r'^[ \t\f]*#.*?coding[:=][ \t]*([-\w.]+)') 
blank_re = re.compile(br'^[ \t\f]*(?:[#\r\n]|$)') 

def _get_normal_name(orig_enc): 
    """Imitates get_normal_name in tokenizer.c.""" 
    # Only care about the first 12 characters. 
    enc = orig_enc[:12].lower().replace("_", "-") 
    if enc == "utf-8" or enc.startswith("utf-8-"): 
     return "utf-8" 
    if enc in ("latin-1", "iso-8859-1", "iso-latin-1") or \ 
     enc.startswith(("latin-1-", "iso-8859-1-", "iso-latin-1-")): 
     return "iso-8859-1" 
    return orig_enc 

def detect_encoding(filename): 
    bom_found = False 
    encoding = None 
    default = 'ascii' 

    def find_cookie(line): 
     match = cookie_re.match(line) 
     if not match: 
      return None 
     encoding = _get_normal_name(match.group(1)) 
     try: 
      codec = lookup(encoding) 
     except LookupError: 
      # This behaviour mimics the Python interpreter 
      raise SyntaxError(
       "unknown encoding for {!r}: {}".format(
        filename, encoding)) 

     if bom_found: 
      if encoding != 'utf-8': 
       # This behaviour mimics the Python interpreter 
       raise SyntaxError(
        'encoding problem for {!r}: utf-8'.format(filename)) 
      encoding += '-sig' 
     return encoding 

    with open(filename, 'rb') as fileobj:   
     first = next(fileobj, '') 
     if first.startswith(BOM_UTF8): 
      bom_found = True 
      first = first[3:] 
      default = 'utf-8-sig' 
     if not first: 
      return default 

     encoding = find_cookie(first) 
     if encoding: 
      return encoding 
     if not blank_re.match(first): 
      return default 

     second = next(fileobj, '') 

    if not second: 
     return default  
    return find_cookie(second) or default 

Come la funzione originaria, la funzione di cui sopra legge due linee max dal file di origine, e solleverà un'eccezione SyntaxError se la codifica nel cookie non è valido o non è UTF-8 mentre è presente una BOM UTF-8.

Demo:

>>> import tempfile 
>>> def test(contents): 
...  with tempfile.NamedTemporaryFile() as f: 
...   f.write(contents) 
...   f.flush() 
...   return detect_encoding(f.name) 
... 
>>> test('# -*- coding: utf-8 -*-\n') 
'utf-8' 
>>> test('#!/bin/env python\n# -*- coding: latin-1 -*-\n') 
'iso-8859-1' 
>>> test('import this\n') 
'ascii' 
>>> import codecs 
>>> test(codecs.BOM_UTF8 + 'import this\n') 
'utf-8-sig' 
>>> test(codecs.BOM_UTF8 + '# encoding: latin-1\n') 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "<stdin>", line 5, in test 
    File "<string>", line 37, in detect_encoding 
    File "<string>", line 24, in find_cookie 
SyntaxError: encoding problem for '/var/folders/w0/nl1bwj6163j2pvxswf84xcsjh2pc5g/T/tmpxsqH8L': utf-8 
>>> test('# encoding: foobarbaz\n') 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "<stdin>", line 5, in test 
    File "<string>", line 37, in detect_encoding 
    File "<string>", line 18, in find_cookie 
SyntaxError: unknown encoding for '/var/folders/w0/nl1bwj6163j2pvxswf84xcsjh2pc5g/T/tmpHiHdG3': foobarbaz 
+0

Grande codice, anche se su Windows mi dà un 'IOError: [Errno 13] Autorizzazione negata: 'c: \\ users \\ maggyero \\ appdata \\ local \\ temp \\ tmp6qsaxo'' (su Linux funziona bene). Ho una domanda correlata [qui] (https://stackoverflow.com/questions/48984214/python-2-assumes-different-source-code-encodings) e penso che tu sia uno dei più qualificati qui intorno a cui rispondere. – Maggyero

+1

@Maggyero: questa è una limitazione della classe ['NamedTemporaryFile'] (https://docs.python.org/3/library/tempfile.html#tempfile.NamedTemporaryFile) su Windows: * Se il nome può essere utilizzato per aprire il file una seconda volta, mentre il file temporaneo nominato è ancora aperto, varia su piattaforme diverse (può essere usato su Unix, non su Windows NT o successivo). * –

Problemi correlati