2016-07-14 7 views
6

Wenn ich die Zeichenkodierung angeben, in der "magic line" oder shebang eines Python-Modul wieerhalten Codierung in magische Linie/shebang angegeben (aus Modul)

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

(wie PEP 263 vorgeschlagen) kann ich diese Kodierung von innerhalb dieses Moduls abrufen?

(Arbeiten an Windows 7 x64 mit Python 2.7.9)


I (ohne Erfolg) versucht, die Standard-Kodierung oder shebang

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

import sys 
from shebang import shebang 

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

nachgeben abzurufen:

sys.getdefaultencoding(): ascii

shebang: None

(gleich für iso-8859-1)

+1

Es ist sehr wahrscheinlich, dass die Codierung Informationen nach der Kompilierung zu 'pyc' verloren gehen. Möglicherweise müssen Sie die 'py'-Datei direkt analysieren. – gdlmx

+2

Beachten Sie, dass 'sys.getdefaultencoding()' nichts mit der Decodierung von Python-Quellcode zu tun hat. –

Antwort

5

Ich würde das Python 3 tokenize.detect_encoding() function in Python 2 ausleihen, ein wenig angepasst, um den Erwartungen von Python 2 zu entsprechen. Ich habe die Funktionssignatur geändert, um einen Dateinamen zu akzeptieren, und den Einschluss der bisher gelesenen Zeilen gelöscht; Sie stimmen nicht mit denen für Ihre usecase brauchen:

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 

Wie die ursprüngliche Funktion, die obige Funktion liest zwei Linien max aus der Quelldatei, und wirft eine SyntaxError Ausnahme, wenn die Kodierung im Cookie ungültig oder ist nicht UTF-8, während eine UTF-8-Stückliste vorhanden ist.

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

Großartiger Code, obwohl es unter Windows einen 'IOError: [Errno 13] Berechtigung verweigert:' c: \\ users \\ maggyero \\ appdata \\ local \\ temp \ tmp6qsaxo '(unter Linux funktioniert es gut). Ich habe eine verwandte Frage [hier] (https://stackoverflow.com/questions/48984214/python-2-assumes-different-source-code-encodings) und ich denke, Sie sind einer der qualifiziertesten hier zu beantworten. – Maggyero

+1

@Maggyero: das ist eine Einschränkung der ['NamedTemporaryFile'-Klasse] (https://docs.python.org/3/library/tempfile.html#tempfile.NamedTemporaryFile) unter Windows: * Ob der Name zum Öffnen verwendet werden kann die Datei ein zweites Mal, während die benannte temporäre Datei noch offen ist, variiert zwischen den Plattformen (sie kann so unter Unix verwendet werden; unter Windows NT oder höher nicht). * –