diff options
author | Heungsub Lee <sub@subl.ee> | 2016-07-01 19:50:34 +0900 |
---|---|---|
committer | Heungsub Lee <sub@subl.ee> | 2016-07-09 18:36:30 +0900 |
commit | 60d13b090a3a6a6989e520e08bcf86fb70073c72 (patch) | |
tree | 080209038b848c90f19460524ece791a3a947193 | |
parent | 5fe694b57ef730c4154f77a78c5dbe819c6298a5 (diff) | |
download | babel-60d13b090a3a6a6989e520e08bcf86fb70073c72.tar.gz |
Fix #426
Parse compiler flags based on __future__ imports in Python codes.
Evaluate a string literal with the parsed compiler flags.
-rw-r--r-- | babel/messages/extract.py | 8 | ||||
-rw-r--r-- | babel/util.py | 23 | ||||
-rw-r--r-- | tests/messages/test_extract.py | 10 |
3 files changed, 38 insertions, 3 deletions
diff --git a/babel/messages/extract.py b/babel/messages/extract.py index 4dc56a4..db17848 100644 --- a/babel/messages/extract.py +++ b/babel/messages/extract.py @@ -22,7 +22,7 @@ from os.path import relpath import sys from tokenize import generate_tokens, COMMENT, NAME, OP, STRING -from babel.util import parse_encoding, pathmatch +from babel.util import parse_encoding, parse_future_flags, pathmatch from babel._compat import PY2, text_type from textwrap import dedent @@ -399,6 +399,7 @@ def extract_python(fileobj, keywords, comment_tags, options): comment_tag = None encoding = parse_encoding(fileobj) or options.get('encoding', 'UTF-8') + future_flags = parse_future_flags(fileobj, encoding) if PY2: next_line = fileobj.readline @@ -470,8 +471,9 @@ def extract_python(fileobj, keywords, comment_tags, options): # encoding # https://sourceforge.net/tracker/?func=detail&atid=355470& # aid=617979&group_id=5470 - value = eval('# coding=%s\n%s' % (str(encoding), value), - {'__builtins__': {}}, {}) + code = compile('# coding=%s\n%s' % (str(encoding), value), + '<string>', 'eval', future_flags) + value = eval(code, {'__builtins__': {}}, {}) if PY2 and not isinstance(value, text_type): value = value.decode(encoding) buf.append(value) diff --git a/babel/util.py b/babel/util.py index aeb9a5f..996f902 100644 --- a/babel/util.py +++ b/babel/util.py @@ -95,6 +95,29 @@ def parse_encoding(fp): fp.seek(pos) +PYTHON_FUTURE_IMPORT_re = re.compile( + r'from\s+__future__\s+import\s+\(*(.+)\)*') + + +def parse_future_flags(fp, encoding='latin-1'): + """Parse the compiler flags by :mod:`__future__` from the given Python + code. + """ + import __future__ + pos = fp.tell() + fp.seek(0) + flags = 0 + try: + body = fp.read().decode(encoding) + for m in PYTHON_FUTURE_IMPORT_re.finditer(body): + names = [x.strip() for x in m.group(1).split(',')] + for name in names: + flags |= getattr(__future__, name).compiler_flag + finally: + fp.seek(pos) + return flags + + def pathmatch(pattern, filename): """Extended pathname pattern matching. diff --git a/tests/messages/test_extract.py b/tests/messages/test_extract.py index 9d78d92..22ea1cd 100644 --- a/tests/messages/test_extract.py +++ b/tests/messages/test_extract.py @@ -498,3 +498,13 @@ msg = _('') return [(1, None, (), ())] for x in extract.extract(arbitrary_extractor, BytesIO(b"")): assert x[0] == 1 + + def test_future(self): + buf = BytesIO(br""" +# -*- coding: utf-8 -*- +from __future__ import unicode_literals +nbsp = _('\xa0') +""") + messages = list(extract.extract('python', buf, + extract.DEFAULT_KEYWORDS, [], {})) + assert messages[0][1] == u'\xa0' |