This commit is contained in:
Antoine Pitrou 2012-07-13 20:59:35 +02:00
commit c287423cb0
7 changed files with 4113 additions and 4098 deletions

View File

@ -163,9 +163,14 @@ Importing Modules
.. c:function:: PyObject* PyImport_ExecCodeModuleWithPathnames(char *name, PyObject *co, char *pathname, char *cpathname) .. c:function:: PyObject* PyImport_ExecCodeModuleWithPathnames(char *name, PyObject *co, char *pathname, char *cpathname)
Like :c:func:`PyImport_ExecCodeModuleObject`, but *name*, *pathname* and Like :c:func:`PyImport_ExecCodeModuleObject`, but *name*, *pathname* and
*cpathname* are UTF-8 encoded strings. *cpathname* are UTF-8 encoded strings. Attempts are also made to figure out
what the value for *pathname* should be from *cpathname* if the former is
set to ``NULL``.
.. versionadded:: 3.2 .. versionadded:: 3.2
.. versionchanged:: 3.3
Uses :func:`imp.source_from_cache()` in calculating the source path if
only the bytecode path is provided.
.. c:function:: long PyImport_GetMagicNumber() .. c:function:: long PyImport_GetMagicNumber()

View File

@ -13,7 +13,7 @@ from _imp import (lock_held, acquire_lock, release_lock,
# Directly exposed by this module # Directly exposed by this module
from importlib._bootstrap import new_module from importlib._bootstrap import new_module
from importlib._bootstrap import cache_from_source from importlib._bootstrap import cache_from_source, source_from_cache
from importlib import _bootstrap from importlib import _bootstrap
@ -58,29 +58,6 @@ def get_suffixes():
return extensions + source + bytecode return extensions + source + bytecode
def source_from_cache(path):
"""Given the path to a .pyc./.pyo file, return the path to its .py file.
The .pyc/.pyo file does not need to exist; this simply returns the path to
the .py file calculated to correspond to the .pyc/.pyo file. If path does
not conform to PEP 3147 format, ValueError will be raised. If
sys.implementation.cache_tag is None then NotImplementedError is raised.
"""
if sys.implementation.cache_tag is None:
raise NotImplementedError('sys.implementation.cache_tag is None')
head, pycache_filename = os.path.split(path)
head, pycache = os.path.split(head)
if pycache != _bootstrap._PYCACHE:
raise ValueError('{} not bottom-level directory in '
'{!r}'.format(_bootstrap._PYCACHE, path))
if pycache_filename.count('.') != 2:
raise ValueError('expected only 2 dots in '
'{!r}'.format(pycache_filename))
base_filename = pycache_filename.partition('.')[0]
return os.path.join(head, base_filename + machinery.SOURCE_SUFFIXES[0])
class NullImporter: class NullImporter:
"""Null import object.""" """Null import object."""

View File

@ -428,6 +428,50 @@ def cache_from_source(path, debug_override=None):
return _path_join(head, _PYCACHE, filename) return _path_join(head, _PYCACHE, filename)
def source_from_cache(path):
"""Given the path to a .pyc./.pyo file, return the path to its .py file.
The .pyc/.pyo file does not need to exist; this simply returns the path to
the .py file calculated to correspond to the .pyc/.pyo file. If path does
not conform to PEP 3147 format, ValueError will be raised. If
sys.implementation.cache_tag is None then NotImplementedError is raised.
"""
if sys.implementation.cache_tag is None:
raise NotImplementedError('sys.implementation.cache_tag is None')
head, pycache_filename = _path_split(path)
head, pycache = _path_split(head)
if pycache != _PYCACHE:
raise ValueError('{} not bottom-level directory in '
'{!r}'.format(_PYCACHE, path))
if pycache_filename.count('.') != 2:
raise ValueError('expected only 2 dots in '
'{!r}'.format(pycache_filename))
base_filename = pycache_filename.partition('.')[0]
return _path_join(head, base_filename + SOURCE_SUFFIXES[0])
def _get_sourcefile(bytecode_path):
"""Convert a bytecode file path to a source path (if possible).
This function exists purely for backwards-compatibility for
PyImport_ExecCodeModuleWithFilenames() in the C API.
"""
if len(bytecode_path) == 0:
return None
rest, _, extension = bytecode_path.rparition('.')
if not rest or extension.lower()[-3:-1] != '.py':
return bytecode_path
try:
source_path = source_from_cache(bytecode_path)
except (NotImplementedError, ValueError):
source_path = bytcode_path[-1:]
return source_path if _path_isfile(source_stats) else bytecode_path
def _verbose_message(message, *args): def _verbose_message(message, *args):
"""Print the message to stderr if -v/PYTHONVERBOSE is turned on.""" """Print the message to stderr if -v/PYTHONVERBOSE is turned on."""
if sys.flags.verbose: if sys.flags.verbose:

View File

@ -92,6 +92,15 @@ Library
- Issue 10924: Fixed mksalt() to use a RNG that is suitable for cryptographic - Issue 10924: Fixed mksalt() to use a RNG that is suitable for cryptographic
purpose. purpose.
C API
-----
- Issues #15169, #14599: Strip out the C implementation of
imp.source_from_cache() used by PyImport_ExecCodeModuleWithPathnames() and
used the Python code instead. Leads to PyImport_ExecCodeModuleObject() to not
try to infer the source path from the bytecode path as
PyImport_ExecCodeModuleWithPathnames() does.
Extension Modules Extension Modules
----------------- -----------------

View File

@ -630,8 +630,6 @@ remove_module(PyObject *name)
"sys.modules failed"); "sys.modules failed");
} }
static PyObject * get_sourcefile(PyObject *filename);
static PyObject *make_source_pathname(PyObject *pathname);
/* Execute a code object in a module and return the module object /* Execute a code object in a module and return the module object
* WITH INCREMENTED REFERENCE COUNT. If an error occurs, name is * WITH INCREMENTED REFERENCE COUNT. If an error occurs, name is
@ -668,18 +666,37 @@ PyImport_ExecCodeModuleWithPathnames(char *name, PyObject *co, char *pathname,
if (nameobj == NULL) if (nameobj == NULL)
return NULL; return NULL;
if (pathname != NULL) {
pathobj = PyUnicode_DecodeFSDefault(pathname);
if (pathobj == NULL)
goto error;
} else
pathobj = NULL;
if (cpathname != NULL) { if (cpathname != NULL) {
cpathobj = PyUnicode_DecodeFSDefault(cpathname); cpathobj = PyUnicode_DecodeFSDefault(cpathname);
if (cpathobj == NULL) if (cpathobj == NULL)
goto error; goto error;
} else }
else
cpathobj = NULL; cpathobj = NULL;
if (pathname != NULL) {
pathobj = PyUnicode_DecodeFSDefault(pathname);
if (pathobj == NULL)
goto error;
}
else if (cpathobj != NULL) {
PyInterpreterState *interp = PyThreadState_GET()->interp;
_Py_IDENTIFIER(_get_sourcefile);
if (interp == NULL) {
Py_FatalError("PyImport_ExecCodeModuleWithPathnames: "
"no interpreter!");
}
pathobj = _PyObject_CallMethodObjIdArgs(interp->importlib,
&PyId__get_sourcefile, cpathobj,
NULL);
if (pathobj == NULL)
PyErr_Clear();
}
else
pathobj = NULL;
m = PyImport_ExecCodeModuleObject(nameobj, co, pathobj, cpathobj); m = PyImport_ExecCodeModuleObject(nameobj, co, pathobj, cpathobj);
error: error:
Py_DECREF(nameobj); Py_DECREF(nameobj);
@ -706,18 +723,13 @@ PyImport_ExecCodeModuleObject(PyObject *name, PyObject *co, PyObject *pathname,
PyEval_GetBuiltins()) != 0) PyEval_GetBuiltins()) != 0)
goto error; goto error;
} }
/* Remember the filename as the __file__ attribute */
if (pathname != NULL) { if (pathname != NULL) {
v = get_sourcefile(pathname); v = pathname;
if (v == NULL)
PyErr_Clear();
} }
else else {
v = NULL;
if (v == NULL) {
v = ((PyCodeObject *)co)->co_filename; v = ((PyCodeObject *)co)->co_filename;
Py_INCREF(v);
} }
Py_INCREF(v);
if (PyDict_SetItemString(d, "__file__", v) != 0) if (PyDict_SetItemString(d, "__file__", v) != 0)
PyErr_Clear(); /* Not important enough to report */ PyErr_Clear(); /* Not important enough to report */
Py_DECREF(v); Py_DECREF(v);
@ -752,100 +764,6 @@ PyImport_ExecCodeModuleObject(PyObject *name, PyObject *co, PyObject *pathname,
} }
/* Like rightmost_sep, but operate on unicode objects. */
static Py_ssize_t
rightmost_sep_obj(PyObject* o, Py_ssize_t start, Py_ssize_t end)
{
Py_ssize_t found, i;
Py_UCS4 c;
for (found = -1, i = start; i < end; i++) {
c = PyUnicode_READ_CHAR(o, i);
if (c == SEP
#ifdef ALTSEP
|| c == ALTSEP
#endif
)
{
found = i;
}
}
return found;
}
/* Given a pathname to a Python byte compiled file, return the path to the
source file, if the path matches the PEP 3147 format. This does not check
for any file existence, however, if the pyc file name does not match PEP
3147 style, NULL is returned. buf must be at least as big as pathname;
the resulting path will always be shorter.
(...)/__pycache__/foo.<tag>.pyc -> (...)/foo.py */
static PyObject*
make_source_pathname(PyObject *path)
{
Py_ssize_t left, right, dot0, dot1, len;
Py_ssize_t i, j;
PyObject *result;
int kind;
void *data;
len = PyUnicode_GET_LENGTH(path);
if (len > MAXPATHLEN)
return NULL;
/* Look back two slashes from the end. In between these two slashes
must be the string __pycache__ or this is not a PEP 3147 style
path. It's possible for there to be only one slash.
*/
right = rightmost_sep_obj(path, 0, len);
if (right == -1)
return NULL;
left = rightmost_sep_obj(path, 0, right);
if (left == -1)
left = 0;
else
left++;
if (right-left != sizeof(CACHEDIR)-1)
return NULL;
for (i = 0; i < sizeof(CACHEDIR)-1; i++)
if (PyUnicode_READ_CHAR(path, left+i) != CACHEDIR[i])
return NULL;
/* Now verify that the path component to the right of the last slash
has two dots in it.
*/
dot0 = PyUnicode_FindChar(path, '.', right+1, len, 1);
if (dot0 < 0)
return NULL;
dot1 = PyUnicode_FindChar(path, '.', dot0+1, len, 1);
if (dot1 < 0)
return NULL;
/* Too many dots? */
if (PyUnicode_FindChar(path, '.', dot1+1, len, 1) != -1)
return NULL;
/* This is a PEP 3147 path. Start by copying everything from the
start of pathname up to and including the leftmost slash. Then
copy the file's basename, removing the magic tag and adding a .py
suffix.
*/
result = PyUnicode_New(left + (dot0-right) + 2,
PyUnicode_MAX_CHAR_VALUE(path));
if (!result)
return NULL;
kind = PyUnicode_KIND(result);
data = PyUnicode_DATA(result);
PyUnicode_CopyCharacters(result, 0, path, 0, (i = left));
PyUnicode_CopyCharacters(result, left, path, right+1,
(j = dot0-right));
PyUnicode_WRITE(kind, data, i+j, 'p');
PyUnicode_WRITE(kind, data, i+j+1, 'y');
assert(_PyUnicode_CheckConsistency(result, 1));
return result;
}
static void static void
update_code_filenames(PyCodeObject *co, PyObject *oldname, PyObject *newname) update_code_filenames(PyCodeObject *co, PyObject *oldname, PyObject *newname)
{ {
@ -911,61 +829,6 @@ imp_fix_co_filename(PyObject *self, PyObject *args)
} }
/* Get source file -> unicode or None
* Returns the path to the py file if available, else the given path
*/
static PyObject *
get_sourcefile(PyObject *filename)
{
Py_ssize_t len;
PyObject *py;
struct stat statbuf;
int err;
void *data;
unsigned int kind;
len = PyUnicode_GET_LENGTH(filename);
if (len == 0)
Py_RETURN_NONE;
/* don't match *.pyc or *.pyo? */
data = PyUnicode_DATA(filename);
kind = PyUnicode_KIND(filename);
if (len < 5
|| PyUnicode_READ(kind, data, len-4) != '.'
|| (PyUnicode_READ(kind, data, len-3) != 'p'
&& PyUnicode_READ(kind, data, len-3) != 'P')
|| (PyUnicode_READ(kind, data, len-2) != 'y'
&& PyUnicode_READ(kind, data, len-2) != 'Y'))
goto unchanged;
/* Start by trying to turn PEP 3147 path into source path. If that
* fails, just chop off the trailing character, i.e. legacy pyc path
* to py.
*/
py = make_source_pathname(filename);
if (py == NULL) {
PyErr_Clear();
py = PyUnicode_Substring(filename, 0, len - 1);
}
if (py == NULL)
goto error;
err = _Py_stat(py, &statbuf);
if (err == -2)
goto error;
if (err == 0 && S_ISREG(statbuf.st_mode))
return py;
Py_DECREF(py);
goto unchanged;
error:
PyErr_Clear();
unchanged:
Py_INCREF(filename);
return filename;
}
/* Forward */ /* Forward */
static struct _frozen * find_frozen(PyObject *); static struct _frozen * find_frozen(PyObject *);

File diff suppressed because it is too large Load Diff

View File

@ -4,12 +4,16 @@
__author__ = 'Raymond Hettinger' __author__ = 'Raymond Hettinger'
import keyword, tokenize, cgi, re, functools import keyword, tokenize, cgi, re, functools
try:
import builtins
except ImportError:
import __builtin__ as builtins
#### Analyze Python Source ################################# #### Analyze Python Source #################################
def is_builtin(s): def is_builtin(s):
'Return True if s is the name of a builtin' 'Return True if s is the name of a builtin'
return hasattr(__builtins__, s) return hasattr(builtins, s)
def combine_range(lines, start, end): def combine_range(lines, start, end):
'Join content from a range of lines between start and end' 'Join content from a range of lines between start and end'
@ -21,9 +25,7 @@ def combine_range(lines, start, end):
def analyze_python(source): def analyze_python(source):
'''Generate and classify chunks of Python for syntax highlighting. '''Generate and classify chunks of Python for syntax highlighting.
Yields tuples in the form: (leadin_text, category, categorized_text). Yields tuples in the form: (category, categorized_text).
The final tuple has empty strings for the category and categorized text.
''' '''
lines = source.splitlines(True) lines = source.splitlines(True)
lines.append('') lines.append('')
@ -37,7 +39,7 @@ def analyze_python(source):
kind = '' kind = ''
if tok_type == tokenize.COMMENT: if tok_type == tokenize.COMMENT:
kind = 'comment' kind = 'comment'
elif tok_type == tokenize.OP and tok_str[:1] not in '{}[](),.:;': elif tok_type == tokenize.OP and tok_str[:1] not in '{}[](),.:;@':
kind = 'operator' kind = 'operator'
elif tok_type == tokenize.STRING: elif tok_type == tokenize.STRING:
kind = 'string' kind = 'string'
@ -53,22 +55,20 @@ def analyze_python(source):
elif is_builtin(tok_str) and prev_tok_str != '.': elif is_builtin(tok_str) and prev_tok_str != '.':
kind = 'builtin' kind = 'builtin'
if kind: if kind:
line_upto_token, written = combine_range(lines, written, (srow, scol)) text, written = combine_range(lines, written, (srow, scol))
line_thru_token, written = combine_range(lines, written, (erow, ecol)) yield '', text
yield line_upto_token, kind, line_thru_token text, written = combine_range(lines, written, (erow, ecol))
yield kind, text
line_upto_token, written = combine_range(lines, written, (erow, ecol)) line_upto_token, written = combine_range(lines, written, (erow, ecol))
yield line_upto_token, '', '' yield '', line_upto_token
#### Raw Output ########################################### #### Raw Output ###########################################
def raw_highlight(classified_text): def raw_highlight(classified_text):
'Straight text display of text classifications' 'Straight text display of text classifications'
result = [] result = []
for line_upto_token, kind, line_thru_token in classified_text: for kind, text in classified_text:
if line_upto_token: result.append('%15s: %r\n' % (kind or 'plain', text))
result.append(' plain: %r\n' % line_upto_token)
if line_thru_token:
result.append('%15s: %r\n' % (kind, line_thru_token))
return ''.join(result) return ''.join(result)
#### ANSI Output ########################################### #### ANSI Output ###########################################
@ -88,9 +88,9 @@ def ansi_highlight(classified_text, colors=default_ansi):
'Add syntax highlighting to source code using ANSI escape sequences' 'Add syntax highlighting to source code using ANSI escape sequences'
# http://en.wikipedia.org/wiki/ANSI_escape_code # http://en.wikipedia.org/wiki/ANSI_escape_code
result = [] result = []
for line_upto_token, kind, line_thru_token in classified_text: for kind, text in classified_text:
opener, closer = colors.get(kind, ('', '')) opener, closer = colors.get(kind, ('', ''))
result += [line_upto_token, opener, line_thru_token, closer] result += [opener, text, closer]
return ''.join(result) return ''.join(result)
#### HTML Output ########################################### #### HTML Output ###########################################
@ -98,16 +98,13 @@ def ansi_highlight(classified_text, colors=default_ansi):
def html_highlight(classified_text,opener='<pre class="python">\n', closer='</pre>\n'): def html_highlight(classified_text,opener='<pre class="python">\n', closer='</pre>\n'):
'Convert classified text to an HTML fragment' 'Convert classified text to an HTML fragment'
result = [opener] result = [opener]
for line_upto_token, kind, line_thru_token in classified_text: for kind, text in classified_text:
if kind: if kind:
result += [cgi.escape(line_upto_token), result.append('<span class="%s">' % kind)
'<span class="%s">' % kind, result.append(cgi.escape(text))
cgi.escape(line_thru_token), if kind:
'</span>'] result.append('</span>')
else: result.append(closer)
result += [cgi.escape(line_upto_token),
cgi.escape(line_thru_token)]
result += [closer]
return ''.join(result) return ''.join(result)
default_css = { default_css = {
@ -188,15 +185,12 @@ def latex_highlight(classified_text, title = 'python',
document = default_latex_document): document = default_latex_document):
'Create a complete LaTeX document with colorized source code' 'Create a complete LaTeX document with colorized source code'
result = [] result = []
for line_upto_token, kind, line_thru_token in classified_text: for kind, text in classified_text:
if kind: if kind:
result += [latex_escape(line_upto_token), result.append(r'{\color{%s}' % colors[kind])
r'{\color{%s}' % colors[kind], result.append(latex_escape(text))
latex_escape(line_thru_token), if kind:
'}'] result.append('}')
else:
result += [latex_escape(line_upto_token),
latex_escape(line_thru_token)]
return default_latex_document % dict(title=title, body=''.join(result)) return default_latex_document % dict(title=title, body=''.join(result))