Kaydet (Commit) 7c47894a authored tarafından Eric Smith's avatar Eric Smith

Backport of the print function, using a __future__ import.

This work is substantially Anthony Baxter's, from issue
1633807.  I just freshened it, made a few minor tweaks,
and added the test cases.  I also created issue 2412,
which is to check for 2to3's behavior with the print
function.  I also added myself to ACKS.
üst 6c0ff8aa
...@@ -48,11 +48,12 @@ typedef struct { ...@@ -48,11 +48,12 @@ typedef struct {
#define CO_FUTURE_DIVISION 0x2000 #define CO_FUTURE_DIVISION 0x2000
#define CO_FUTURE_ABSOLUTE_IMPORT 0x4000 /* do absolute imports by default */ #define CO_FUTURE_ABSOLUTE_IMPORT 0x4000 /* do absolute imports by default */
#define CO_FUTURE_WITH_STATEMENT 0x8000 #define CO_FUTURE_WITH_STATEMENT 0x8000
#define CO_FUTURE_PRINT_FUNCTION 0x10000
/* This should be defined if a future statement modifies the syntax. /* This should be defined if a future statement modifies the syntax.
For example, when a keyword is added. For example, when a keyword is added.
*/ */
#if 0 #if 1
#define PY_PARSER_REQUIRES_FUTURE_KEYWORD #define PY_PARSER_REQUIRES_FUTURE_KEYWORD
#endif #endif
......
...@@ -24,6 +24,8 @@ typedef struct { ...@@ -24,6 +24,8 @@ typedef struct {
#define FUTURE_DIVISION "division" #define FUTURE_DIVISION "division"
#define FUTURE_ABSOLUTE_IMPORT "absolute_import" #define FUTURE_ABSOLUTE_IMPORT "absolute_import"
#define FUTURE_WITH_STATEMENT "with_statement" #define FUTURE_WITH_STATEMENT "with_statement"
#define FUTURE_PRINT_FUNCTION "print_function"
struct _mod; /* Declare the existence of this type */ struct _mod; /* Declare the existence of this type */
PyAPI_FUNC(PyCodeObject *) PyAST_Compile(struct _mod *, const char *, PyAPI_FUNC(PyCodeObject *) PyAST_Compile(struct _mod *, const char *,
......
...@@ -27,6 +27,10 @@ typedef struct { ...@@ -27,6 +27,10 @@ typedef struct {
#define PyPARSE_WITH_IS_KEYWORD 0x0003 #define PyPARSE_WITH_IS_KEYWORD 0x0003
#endif #endif
#define PyPARSE_PRINT_IS_FUNCTION 0x0004
PyAPI_FUNC(node *) PyParser_ParseString(const char *, grammar *, int, PyAPI_FUNC(node *) PyParser_ParseString(const char *, grammar *, int,
perrdetail *); perrdetail *);
PyAPI_FUNC(node *) PyParser_ParseFile (FILE *, const char *, grammar *, int, PyAPI_FUNC(node *) PyParser_ParseFile (FILE *, const char *, grammar *, int,
......
...@@ -8,7 +8,7 @@ extern "C" { ...@@ -8,7 +8,7 @@ extern "C" {
#endif #endif
#define PyCF_MASK (CO_FUTURE_DIVISION | CO_FUTURE_ABSOLUTE_IMPORT | \ #define PyCF_MASK (CO_FUTURE_DIVISION | CO_FUTURE_ABSOLUTE_IMPORT | \
CO_FUTURE_WITH_STATEMENT) CO_FUTURE_WITH_STATEMENT|CO_FUTURE_PRINT_FUNCTION)
#define PyCF_MASK_OBSOLETE (CO_NESTED) #define PyCF_MASK_OBSOLETE (CO_NESTED)
#define PyCF_SOURCE_IS_UTF8 0x0100 #define PyCF_SOURCE_IS_UTF8 0x0100
#define PyCF_DONT_IMPLY_DEDENT 0x0200 #define PyCF_DONT_IMPLY_DEDENT 0x0200
......
...@@ -53,6 +53,7 @@ all_feature_names = [ ...@@ -53,6 +53,7 @@ all_feature_names = [
"division", "division",
"absolute_import", "absolute_import",
"with_statement", "with_statement",
"print_function",
] ]
__all__ = ["all_feature_names"] + all_feature_names __all__ = ["all_feature_names"] + all_feature_names
...@@ -66,6 +67,7 @@ CO_GENERATOR_ALLOWED = 0 # generators (obsolete, was 0x1000) ...@@ -66,6 +67,7 @@ CO_GENERATOR_ALLOWED = 0 # generators (obsolete, was 0x1000)
CO_FUTURE_DIVISION = 0x2000 # division CO_FUTURE_DIVISION = 0x2000 # division
CO_FUTURE_ABSOLUTE_IMPORT = 0x4000 # perform absolute imports by default CO_FUTURE_ABSOLUTE_IMPORT = 0x4000 # perform absolute imports by default
CO_FUTURE_WITH_STATEMENT = 0x8000 # with statement CO_FUTURE_WITH_STATEMENT = 0x8000 # with statement
CO_FUTURE_PRINT_FUNCTION = 0x10000 # print function
class _Feature: class _Feature:
def __init__(self, optionalRelease, mandatoryRelease, compiler_flag): def __init__(self, optionalRelease, mandatoryRelease, compiler_flag):
...@@ -114,3 +116,7 @@ absolute_import = _Feature((2, 5, 0, "alpha", 1), ...@@ -114,3 +116,7 @@ absolute_import = _Feature((2, 5, 0, "alpha", 1),
with_statement = _Feature((2, 5, 0, "alpha", 1), with_statement = _Feature((2, 5, 0, "alpha", 1),
(2, 6, 0, "alpha", 0), (2, 6, 0, "alpha", 0),
CO_FUTURE_WITH_STATEMENT) CO_FUTURE_WITH_STATEMENT)
print_function = _Feature((2, 6, 0, "alpha", 2),
(3, 0, 0, "alpha", 0),
CO_FUTURE_PRINT_FUNCTION)
"""Test correct operation of the print function.
"""
from __future__ import print_function
import unittest
from test import test_support
import sys
try:
# 3.x
from io import StringIO
except ImportError:
# 2.x
from StringIO import StringIO
from contextlib import contextmanager
NotDefined = object()
# A dispatch table all 8 combinations of providing
# sep, end, and file
# I use this machinery so that I'm not just passing default
# values to print, I'm eiher passing or not passing in the
# arguments
dispatch = {
(False, False, False):
lambda args, sep, end, file: print(*args),
(False, False, True):
lambda args, sep, end, file: print(file=file, *args),
(False, True, False):
lambda args, sep, end, file: print(end=end, *args),
(False, True, True):
lambda args, sep, end, file: print(end=end, file=file, *args),
(True, False, False):
lambda args, sep, end, file: print(sep=sep, *args),
(True, False, True):
lambda args, sep, end, file: print(sep=sep, file=file, *args),
(True, True, False):
lambda args, sep, end, file: print(sep=sep, end=end, *args),
(True, True, True):
lambda args, sep, end, file: print(sep=sep, end=end, file=file, *args),
}
@contextmanager
def stdout_redirected(new_stdout):
save_stdout = sys.stdout
sys.stdout = new_stdout
try:
yield None
finally:
sys.stdout = save_stdout
# Class used to test __str__ and print
class ClassWith__str__:
def __init__(self, x):
self.x = x
def __str__(self):
return self.x
class TestPrint(unittest.TestCase):
def check(self, expected, args,
sep=NotDefined, end=NotDefined, file=NotDefined):
# Capture sys.stdout in a StringIO. Call print with args,
# and with sep, end, and file, if they're defined. Result
# must match expected.
# Look up the actual function to call, based on if sep, end, and file
# are defined
fn = dispatch[(sep is not NotDefined,
end is not NotDefined,
file is not NotDefined)]
t = StringIO()
with stdout_redirected(t):
fn(args, sep, end, file)
self.assertEqual(t.getvalue(), expected)
def test_print(self):
def x(expected, args, sep=NotDefined, end=NotDefined):
# Run the test 2 ways: not using file, and using
# file directed to a StringIO
self.check(expected, args, sep=sep, end=end)
# When writing to a file, stdout is expected to be empty
o = StringIO()
self.check('', args, sep=sep, end=end, file=o)
# And o will contain the expected output
self.assertEqual(o.getvalue(), expected)
x('\n', ())
x('a\n', ('a',))
x('None\n', (None,))
x('1 2\n', (1, 2))
x('1 2\n', (1, ' ', 2))
x('1*2\n', (1, 2), sep='*')
x('1 s', (1, 's'), end='')
x('a\nb\n', ('a', 'b'), sep='\n')
x('1.01', (1.0, 1), sep='', end='')
x('1*a*1.3+', (1, 'a', 1.3), sep='*', end='+')
x('a\n\nb\n', ('a\n', 'b'), sep='\n')
x('\0+ +\0\n', ('\0', ' ', '\0'), sep='+')
x('a\n b\n', ('a\n', 'b'))
x('a\n b\n', ('a\n', 'b'), sep=None)
x('a\n b\n', ('a\n', 'b'), end=None)
x('a\n b\n', ('a\n', 'b'), sep=None, end=None)
x('*\n', (ClassWith__str__('*'),))
x('abc 1\n', (ClassWith__str__('abc'), 1))
# 2.x unicode tests
x(u'1 2\n', ('1', u'2'))
x(u'u\1234\n', (u'u\1234',))
x(u' abc 1\n', (' ', ClassWith__str__(u'abc'), 1))
# errors
self.assertRaises(TypeError, print, '', sep=3)
self.assertRaises(TypeError, print, '', end=3)
self.assertRaises(AttributeError, print, '', file='')
def test_main():
test_support.run_unittest(TestPrint)
if __name__ == "__main__":
test_main()
...@@ -622,6 +622,7 @@ George Sipe ...@@ -622,6 +622,7 @@ George Sipe
J. Sipprell J. Sipprell
Kragen Sitaker Kragen Sitaker
Christopher Smith Christopher Smith
Eric V. Smith
Gregory P. Smith Gregory P. Smith
Rafal Smotrzyk Rafal Smotrzyk
Dirk Soede Dirk Soede
......
...@@ -12,6 +12,9 @@ What's New in Python 2.6 alpha 2? ...@@ -12,6 +12,9 @@ What's New in Python 2.6 alpha 2?
Core and builtins Core and builtins
----------------- -----------------
- Issue 1745. Backport print function with:
from __future__ import print_function
- Issue 2332: add new attribute names for instance method objects. - Issue 2332: add new attribute names for instance method objects.
The two changes are: im_self -> __self__ and im_func -> __func__ The two changes are: im_self -> __self__ and im_func -> __func__
......
...@@ -149,12 +149,10 @@ classify(parser_state *ps, int type, char *str) ...@@ -149,12 +149,10 @@ classify(parser_state *ps, int type, char *str)
strcmp(l->lb_str, s) != 0) strcmp(l->lb_str, s) != 0)
continue; continue;
#ifdef PY_PARSER_REQUIRES_FUTURE_KEYWORD #ifdef PY_PARSER_REQUIRES_FUTURE_KEYWORD
if (!(ps->p_flags & CO_FUTURE_WITH_STATEMENT)) { if (ps->p_flags & CO_FUTURE_PRINT_FUNCTION &&
if (s[0] == 'w' && strcmp(s, "with") == 0) s[0] == 'p' && strcmp(s, "print") == 0) {
break; /* not a keyword yet */ break; /* no longer a keyword */
else if (s[0] == 'a' && strcmp(s, "as") == 0) }
break; /* not a keyword yet */
}
#endif #endif
D(printf("It's a keyword\n")); D(printf("It's a keyword\n"));
return n - i; return n - i;
...@@ -208,6 +206,10 @@ future_hack(parser_state *ps) ...@@ -208,6 +206,10 @@ future_hack(parser_state *ps)
strcmp(STR(CHILD(cch, 0)), "with_statement") == 0) { strcmp(STR(CHILD(cch, 0)), "with_statement") == 0) {
ps->p_flags |= CO_FUTURE_WITH_STATEMENT; ps->p_flags |= CO_FUTURE_WITH_STATEMENT;
break; break;
} else if (NCH(cch) >= 1 && TYPE(CHILD(cch, 0)) == NAME &&
strcmp(STR(CHILD(cch, 0)), "print_function") == 0) {
ps->p_flags |= CO_FUTURE_PRINT_FUNCTION;
break;
} }
} }
} }
......
...@@ -123,8 +123,8 @@ parsetok(struct tok_state *tok, grammar *g, int start, perrdetail *err_ret, ...@@ -123,8 +123,8 @@ parsetok(struct tok_state *tok, grammar *g, int start, perrdetail *err_ret,
return NULL; return NULL;
} }
#ifdef PY_PARSER_REQUIRES_FUTURE_KEYWORD #ifdef PY_PARSER_REQUIRES_FUTURE_KEYWORD
if (flags & PyPARSE_WITH_IS_KEYWORD) if (flags & PyPARSE_PRINT_IS_FUNCTION)
ps->p_flags |= CO_FUTURE_WITH_STATEMENT; ps->p_flags |= CO_FUTURE_PRINT_FUNCTION;
#endif #endif
for (;;) { for (;;) {
...@@ -167,26 +167,6 @@ parsetok(struct tok_state *tok, grammar *g, int start, perrdetail *err_ret, ...@@ -167,26 +167,6 @@ parsetok(struct tok_state *tok, grammar *g, int start, perrdetail *err_ret,
str[len] = '\0'; str[len] = '\0';
#ifdef PY_PARSER_REQUIRES_FUTURE_KEYWORD #ifdef PY_PARSER_REQUIRES_FUTURE_KEYWORD
/* This is only necessary to support the "as" warning, but
we don't want to warn about "as" in import statements. */
if (type == NAME &&
len == 6 && str[0] == 'i' && strcmp(str, "import") == 0)
handling_import = 1;
/* Warn about with as NAME */
if (type == NAME &&
!(ps->p_flags & CO_FUTURE_WITH_STATEMENT)) {
if (len == 4 && str[0] == 'w' && strcmp(str, "with") == 0)
warn(with_msg, err_ret->filename, tok->lineno);
else if (!(handling_import || handling_with) &&
len == 2 && str[0] == 'a' &&
strcmp(str, "as") == 0)
warn(as_msg, err_ret->filename, tok->lineno);
}
else if (type == NAME &&
(ps->p_flags & CO_FUTURE_WITH_STATEMENT) &&
len == 4 && str[0] == 'w' && strcmp(str, "with") == 0)
handling_with = 1;
#endif #endif
if (a >= tok->line_start) if (a >= tok->line_start)
col_offset = a - tok->line_start; col_offset = a - tok->line_start;
......
...@@ -1486,6 +1486,78 @@ With two arguments, equivalent to x**y. With three arguments,\n\ ...@@ -1486,6 +1486,78 @@ With two arguments, equivalent to x**y. With three arguments,\n\
equivalent to (x**y) % z, but may be more efficient (e.g. for longs)."); equivalent to (x**y) % z, but may be more efficient (e.g. for longs).");
static PyObject *
builtin_print(PyObject *self, PyObject *args, PyObject *kwds)
{
static char *kwlist[] = {"sep", "end", "file", 0};
static PyObject *dummy_args;
PyObject *sep = NULL, *end = NULL, *file = NULL;
int i, err;
if (dummy_args == NULL) {
if (!(dummy_args = PyTuple_New(0)))
return NULL;
}
if (!PyArg_ParseTupleAndKeywords(dummy_args, kwds, "|OOO:print",
kwlist, &sep, &end, &file))
return NULL;
if (file == NULL || file == Py_None) {
file = PySys_GetObject("stdout");
/* sys.stdout may be None when FILE* stdout isn't connected */
if (file == Py_None)
Py_RETURN_NONE;
}
if (sep && sep != Py_None && !PyString_Check(sep) &&
!PyUnicode_Check(sep)) {
PyErr_Format(PyExc_TypeError,
"sep must be None, str or unicode, not %.200s",
sep->ob_type->tp_name);
return NULL;
}
if (end && end != Py_None && !PyString_Check(end) &&
!PyUnicode_Check(end)) {
PyErr_Format(PyExc_TypeError,
"end must be None, str or unicode, not %.200s",
end->ob_type->tp_name);
return NULL;
}
for (i = 0; i < PyTuple_Size(args); i++) {
if (i > 0) {
if (sep == NULL || sep == Py_None)
err = PyFile_WriteString(" ", file);
else
err = PyFile_WriteObject(sep, file,
Py_PRINT_RAW);
if (err)
return NULL;
}
err = PyFile_WriteObject(PyTuple_GetItem(args, i), file,
Py_PRINT_RAW);
if (err)
return NULL;
}
if (end == NULL || end == Py_None)
err = PyFile_WriteString("\n", file);
else
err = PyFile_WriteObject(end, file, Py_PRINT_RAW);
if (err)
return NULL;
Py_RETURN_NONE;
}
PyDoc_STRVAR(print_doc,
"print(value, ..., sep=' ', end='\\n', file=sys.stdout)\n\
\n\
Prints the values to a stream, or to sys.stdout by default.\n\
Optional keyword arguments:\n\
file: a file-like object (stream); defaults to the current sys.stdout.\n\
sep: string inserted between values, default a space.\n\
end: string appended after the last value, default a newline.");
/* Return number of items in range (lo, hi, step), when arguments are /* Return number of items in range (lo, hi, step), when arguments are
* PyInt or PyLong objects. step > 0 required. Return a value < 0 if * PyInt or PyLong objects. step > 0 required. Return a value < 0 if
...@@ -2424,6 +2496,7 @@ static PyMethodDef builtin_methods[] = { ...@@ -2424,6 +2496,7 @@ static PyMethodDef builtin_methods[] = {
{"open", (PyCFunction)builtin_open, METH_VARARGS | METH_KEYWORDS, open_doc}, {"open", (PyCFunction)builtin_open, METH_VARARGS | METH_KEYWORDS, open_doc},
{"ord", builtin_ord, METH_O, ord_doc}, {"ord", builtin_ord, METH_O, ord_doc},
{"pow", builtin_pow, METH_VARARGS, pow_doc}, {"pow", builtin_pow, METH_VARARGS, pow_doc},
{"print", (PyCFunction)builtin_print, METH_VARARGS | METH_KEYWORDS, print_doc},
{"range", builtin_range, METH_VARARGS, range_doc}, {"range", builtin_range, METH_VARARGS, range_doc},
{"raw_input", builtin_raw_input, METH_VARARGS, raw_input_doc}, {"raw_input", builtin_raw_input, METH_VARARGS, raw_input_doc},
{"reduce", builtin_reduce, METH_VARARGS, reduce_doc}, {"reduce", builtin_reduce, METH_VARARGS, reduce_doc},
......
...@@ -33,6 +33,8 @@ future_check_features(PyFutureFeatures *ff, stmt_ty s, const char *filename) ...@@ -33,6 +33,8 @@ future_check_features(PyFutureFeatures *ff, stmt_ty s, const char *filename)
ff->ff_features |= CO_FUTURE_ABSOLUTE_IMPORT; ff->ff_features |= CO_FUTURE_ABSOLUTE_IMPORT;
} else if (strcmp(feature, FUTURE_WITH_STATEMENT) == 0) { } else if (strcmp(feature, FUTURE_WITH_STATEMENT) == 0) {
ff->ff_features |= CO_FUTURE_WITH_STATEMENT; ff->ff_features |= CO_FUTURE_WITH_STATEMENT;
} else if (strcmp(feature, FUTURE_PRINT_FUNCTION) == 0) {
ff->ff_features |= CO_FUTURE_PRINT_FUNCTION;
} else if (strcmp(feature, "braces") == 0) { } else if (strcmp(feature, "braces") == 0) {
PyErr_SetString(PyExc_SyntaxError, PyErr_SetString(PyExc_SyntaxError,
"not a chance"); "not a chance");
......
...@@ -738,18 +738,19 @@ PyRun_InteractiveLoopFlags(FILE *fp, const char *filename, PyCompilerFlags *flag ...@@ -738,18 +738,19 @@ PyRun_InteractiveLoopFlags(FILE *fp, const char *filename, PyCompilerFlags *flag
} }
} }
#if 0
/* compute parser flags based on compiler flags */ /* compute parser flags based on compiler flags */
#define PARSER_FLAGS(flags) \ #define PARSER_FLAGS(flags) \
((flags) ? ((((flags)->cf_flags & PyCF_DONT_IMPLY_DEDENT) ? \ ((flags) ? ((((flags)->cf_flags & PyCF_DONT_IMPLY_DEDENT) ? \
PyPARSE_DONT_IMPLY_DEDENT : 0)) : 0) PyPARSE_DONT_IMPLY_DEDENT : 0)) : 0)
#endif
#if 0 #if 1
/* Keep an example of flags with future keyword support. */ /* Keep an example of flags with future keyword support. */
#define PARSER_FLAGS(flags) \ #define PARSER_FLAGS(flags) \
((flags) ? ((((flags)->cf_flags & PyCF_DONT_IMPLY_DEDENT) ? \ ((flags) ? ((((flags)->cf_flags & PyCF_DONT_IMPLY_DEDENT) ? \
PyPARSE_DONT_IMPLY_DEDENT : 0) \ PyPARSE_DONT_IMPLY_DEDENT : 0) \
| ((flags)->cf_flags & CO_FUTURE_WITH_STATEMENT ? \ | ((flags)->cf_flags & CO_FUTURE_PRINT_FUNCTION ? \
PyPARSE_WITH_IS_KEYWORD : 0)) : 0) PyPARSE_PRINT_IS_FUNCTION : 0)) : 0)
#endif #endif
int int
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment