Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 393661d

Browse files
committed
Add warning mode for classic division, almost exactly as specified in
PEP 238. Changes: - add a new flag variable Py_DivisionWarningFlag, declared in pydebug.h, defined in object.c, set in main.c, and used in {int,long,float,complex}object.c. When this flag is set, the classic division operator issues a DeprecationWarning message. - add a new API PyRun_SimpleStringFlags() to match PyRun_SimpleString(). The main() function calls this so that commands run with -c can also benefit from -Dnew. - While I was at it, I changed the usage message in main() somewhat: alphabetized the options, split it in *four* parts to fit in under 512 bytes (not that I still believe this is necessary -- doc strings elsewhere are much longer), and perhaps most visibly, don't display the full list of options on each command line error. Instead, the full list is only displayed when -h is used, and otherwise a brief reminder of -h is displayed. When -h is used, write to stdout so that you can do `python -h | more'. Notes: - I don't want to use the -W option to control whether the classic division warning is issued or not, because the machinery to decide whether to display the warning or not is very expensive (it involves calling into the warnings.py module). You can use -Werror to turn the warnings into exceptions though. - The -Dnew option doesn't select future division for all of the program -- only for the __main__ module. I don't know if I'll ever change this -- it would require changes to the .pyc file magic number to do it right, and a more global notion of compiler flags. - You can usefully combine -Dwarn and -Dnew: this gives the __main__ module new division, and warns about classic division everywhere else.
1 parent 29d55a3 commit 393661d

9 files changed

Lines changed: 146 additions & 25 deletions

File tree

Include/pydebug.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ extern DL_IMPORT(int) Py_FrozenFlag;
1515
extern DL_IMPORT(int) Py_TabcheckFlag;
1616
extern DL_IMPORT(int) Py_UnicodeFlag;
1717
extern DL_IMPORT(int) Py_IgnoreEnvironmentFlag;
18+
extern DL_IMPORT(int) Py_DivisionWarningFlag;
1819

1920
/* this is a wrapper around getenv() that pays attention to
2021
Py_IgnoreEnvironmentFlag. It should be used for getting variables like

Include/pythonrun.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ DL_IMPORT(int) PyRun_AnyFileFlags(FILE *, char *, PyCompilerFlags *);
3333
DL_IMPORT(int) PyRun_AnyFileExFlags(FILE *, char *, int, PyCompilerFlags *);
3434

3535
DL_IMPORT(int) PyRun_SimpleString(char *);
36+
DL_IMPORT(int) PyRun_SimpleStringFlags(char *, PyCompilerFlags *);
3637
DL_IMPORT(int) PyRun_SimpleFile(FILE *, char *);
3738
DL_IMPORT(int) PyRun_SimpleFileEx(FILE *, char *, int);
3839
DL_IMPORT(int) PyRun_SimpleFileExFlags(FILE *, char *, int, PyCompilerFlags *);

Modules/main.c

Lines changed: 49 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#include "Python.h"
44
#include "osdefs.h"
5+
#include "compile.h" /* For CO_FUTURE_DIVISION */
56

67
#ifdef HAVE_UNISTD_H
78
#include <unistd.h>
@@ -28,7 +29,7 @@ static char **orig_argv;
2829
static int orig_argc;
2930

3031
/* command line options */
31-
#define BASE_OPTS "c:diOSEtuUvxXhVW:"
32+
#define BASE_OPTS "c:dD:EhiOStuUvVWxX:"
3233

3334
#ifndef RISCOS
3435
#define PROGRAM_OPTS BASE_OPTS
@@ -45,30 +46,33 @@ static char *usage_line =
4546
"usage: %s [option] ... [-c cmd | file | -] [arg] ...\n";
4647

4748
/* Long usage message, split into parts < 512 bytes */
48-
static char *usage_top = "\
49+
static char *usage_1 = "\
4950
Options and arguments (and corresponding environment variables):\n\
51+
-c cmd : program passed in as string (terminates option list)\n\
5052
-d : debug output from parser (also PYTHONDEBUG=x)\n\
53+
-D arg : division options: -Dold (default), -Dwarn, -Dnew\n\
54+
-E : ignore environment variables (such as PYTHONPATH)\n\
55+
-h : print this help message and exit\n\
56+
";
57+
static char *usage_2 = "\
5158
-i : inspect interactively after running script, (also PYTHONINSPECT=x)\n\
5259
and force prompts, even if stdin does not appear to be a terminal\n\
5360
-O : optimize generated bytecode (a tad; also PYTHONOPTIMIZE=x)\n\
5461
-OO : remove doc-strings in addition to the -O optimizations\n\
5562
-S : don't imply 'import site' on initialization\n\
56-
-E : ignore environment variables (such as PYTHONPATH)\n\
5763
-t : issue warnings about inconsistent tab usage (-tt: issue errors)\n\
58-
";
59-
static char *usage_mid = "\
6064
-u : unbuffered binary stdout and stderr (also PYTHONUNBUFFERED=x)\n\
65+
";
66+
static char *usage_3 = "\
6167
-U : Unicode literals: treats '...' literals like u'...'\n\
6268
-v : verbose (trace import statements) (also PYTHONVERBOSE=x)\n\
63-
-x : skip first line of source, allowing use of non-Unix forms of #!cmd\n\
64-
-h : print this help message and exit\n\
6569
-V : print the Python version number and exit\n\
6670
-W arg : warning control (arg is action:message:category:module:lineno)\n\
67-
-c cmd : program passed in as string (terminates option list)\n\
71+
-x : skip first line of source, allowing use of non-Unix forms of #!cmd\n\
6872
file : program read from script file\n\
6973
- : program read from stdin (default; interactive mode if a tty)\n\
7074
";
71-
static char *usage_bot = "\
75+
static char *usage_4 = "\
7276
arg ...: arguments passed to program in sys.argv[1:]\n\
7377
Other environment variables:\n\
7478
PYTHONSTARTUP: file executed on interactive startup (no default)\n\
@@ -83,10 +87,17 @@ PYTHONCASEOK : ignore case in 'import' statements (Windows).\n\
8387
static void
8488
usage(int exitcode, char* program)
8589
{
86-
fprintf(stderr, usage_line, program);
87-
fprintf(stderr, usage_top);
88-
fprintf(stderr, usage_mid);
89-
fprintf(stderr, usage_bot, DELIM, DELIM, PYTHONHOMEHELP);
90+
FILE *f = exitcode ? stderr : stdout;
91+
92+
fprintf(f, usage_line, program);
93+
if (exitcode)
94+
fprintf(f, "Try `python -h' for more information.\n");
95+
else {
96+
fprintf(f, usage_1);
97+
fprintf(f, usage_2);
98+
fprintf(f, usage_3);
99+
fprintf(f, usage_4, DELIM, DELIM, PYTHONHOMEHELP);
100+
}
90101
exit(exitcode);
91102
/*NOTREACHED*/
92103
}
@@ -113,6 +124,8 @@ Py_Main(int argc, char **argv)
113124
int saw_unbuffered_flag = 0;
114125
PyCompilerFlags cf;
115126

127+
cf.cf_flags = 0;
128+
116129
orig_argc = argc; /* For Py_GetArgcArgv() */
117130
orig_argv = argv;
118131

@@ -135,13 +148,33 @@ Py_Main(int argc, char **argv)
135148
strcat(command, "\n");
136149
break;
137150
}
138-
151+
139152
switch (c) {
140153

141154
case 'd':
142155
Py_DebugFlag++;
143156
break;
144157

158+
case 'D':
159+
if (strcmp(_PyOS_optarg, "old") == 0) {
160+
Py_DivisionWarningFlag = 0;
161+
break;
162+
}
163+
if (strcmp(_PyOS_optarg, "warn") == 0) {
164+
Py_DivisionWarningFlag++;
165+
break;
166+
}
167+
if (strcmp(_PyOS_optarg, "new") == 0) {
168+
/* XXX This only affects __main__ */
169+
cf.cf_flags |= CO_FUTURE_DIVISION;
170+
break;
171+
}
172+
fprintf(stderr,
173+
"-D option should be "
174+
"`-Dold', `-Dwarn' or `-Dnew' only\n");
175+
usage(2, argv[0]);
176+
/* NOTREACHED */
177+
145178
case 'i':
146179
inspect++;
147180
saw_inspect_flag = 1;
@@ -290,8 +323,7 @@ Py_Main(int argc, char **argv)
290323
(command == NULL && filename == NULL && stdin_is_interactive))
291324
fprintf(stderr, "Python %s on %s\n%s\n",
292325
Py_GetVersion(), Py_GetPlatform(), COPYRIGHT);
293-
294-
326+
295327
if (command != NULL) {
296328
/* Backup _PyOS_optind and force sys.argv[0] = '-c' */
297329
_PyOS_optind--;
@@ -310,10 +342,8 @@ Py_Main(int argc, char **argv)
310342
Py_DECREF(v);
311343
}
312344

313-
cf.cf_flags = 0;
314-
315345
if (command) {
316-
sts = PyRun_SimpleString(command) != 0;
346+
sts = PyRun_SimpleStringFlags(command, &cf) != 0;
317347
free(command);
318348
}
319349
else {

Objects/complexobject.c

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,27 @@ complex_div(PyComplexObject *v, PyComplexObject *w)
372372
return PyComplex_FromCComplex(quot);
373373
}
374374

375+
static PyObject *
376+
complex_classic_div(PyComplexObject *v, PyComplexObject *w)
377+
{
378+
Py_complex quot;
379+
380+
if (Py_DivisionWarningFlag &&
381+
PyErr_Warn(PyExc_DeprecationWarning,
382+
"classic complex division") < 0)
383+
return NULL;
384+
385+
PyFPE_START_PROTECT("complex_classic_div", return 0)
386+
errno = 0;
387+
quot = c_quot(v->cval,w->cval);
388+
PyFPE_END_PROTECT(quot)
389+
if (errno == EDOM) {
390+
PyErr_SetString(PyExc_ZeroDivisionError, "complex division");
391+
return NULL;
392+
}
393+
return PyComplex_FromCComplex(quot);
394+
}
395+
375396
static PyObject *
376397
complex_remainder(PyComplexObject *v, PyComplexObject *w)
377398
{
@@ -854,7 +875,7 @@ static PyNumberMethods complex_as_number = {
854875
(binaryfunc)complex_add, /* nb_add */
855876
(binaryfunc)complex_sub, /* nb_subtract */
856877
(binaryfunc)complex_mul, /* nb_multiply */
857-
(binaryfunc)complex_div, /* nb_divide */
878+
(binaryfunc)complex_classic_div, /* nb_divide */
858879
(binaryfunc)complex_remainder, /* nb_remainder */
859880
(binaryfunc)complex_divmod, /* nb_divmod */
860881
(ternaryfunc)complex_pow, /* nb_power */

Objects/floatobject.c

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,25 @@ float_div(PyObject *v, PyObject *w)
413413
return PyFloat_FromDouble(a);
414414
}
415415

416+
static PyObject *
417+
float_classic_div(PyObject *v, PyObject *w)
418+
{
419+
double a,b;
420+
CONVERT_TO_DOUBLE(v, a);
421+
CONVERT_TO_DOUBLE(w, b);
422+
if (Py_DivisionWarningFlag &&
423+
PyErr_Warn(PyExc_DeprecationWarning, "classic float division") < 0)
424+
return NULL;
425+
if (b == 0.0) {
426+
PyErr_SetString(PyExc_ZeroDivisionError, "float division");
427+
return NULL;
428+
}
429+
PyFPE_START_PROTECT("divide", return 0)
430+
a = a / b;
431+
PyFPE_END_PROTECT(a)
432+
return PyFloat_FromDouble(a);
433+
}
434+
416435
static PyObject *
417436
float_rem(PyObject *v, PyObject *w)
418437
{
@@ -677,7 +696,7 @@ static PyNumberMethods float_as_number = {
677696
(binaryfunc)float_add, /*nb_add*/
678697
(binaryfunc)float_sub, /*nb_subtract*/
679698
(binaryfunc)float_mul, /*nb_multiply*/
680-
(binaryfunc)float_div, /*nb_divide*/
699+
(binaryfunc)float_classic_div, /*nb_divide*/
681700
(binaryfunc)float_rem, /*nb_remainder*/
682701
(binaryfunc)float_divmod, /*nb_divmod*/
683702
(ternaryfunc)float_pow, /*nb_power*/

Objects/intobject.c

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,27 @@ int_div(PyIntObject *x, PyIntObject *y)
511511
}
512512
}
513513

514+
static PyObject *
515+
int_classic_div(PyIntObject *x, PyIntObject *y)
516+
{
517+
long xi, yi;
518+
long d, m;
519+
CONVERT_TO_LONG(x, xi);
520+
CONVERT_TO_LONG(y, yi);
521+
if (Py_DivisionWarningFlag &&
522+
PyErr_Warn(PyExc_DeprecationWarning, "classic int division") < 0)
523+
return NULL;
524+
switch (i_divmod(xi, yi, &d, &m)) {
525+
case DIVMOD_OK:
526+
return PyInt_FromLong(d);
527+
case DIVMOD_OVERFLOW:
528+
return PyLong_Type.tp_as_number->nb_divide((PyObject *)x,
529+
(PyObject *)y);
530+
default:
531+
return NULL;
532+
}
533+
}
534+
514535
static PyObject *
515536
int_mod(PyIntObject *x, PyIntObject *y)
516537
{
@@ -744,7 +765,7 @@ int_or(PyIntObject *v, PyIntObject *w)
744765
static PyObject *
745766
int_true_divide(PyObject *v, PyObject *w)
746767
{
747-
return PyFloat_Type.tp_as_number->nb_divide(v, w);
768+
return PyFloat_Type.tp_as_number->nb_true_divide(v, w);
748769
}
749770

750771
static PyObject *
@@ -855,7 +876,7 @@ static PyNumberMethods int_as_number = {
855876
(binaryfunc)int_add, /*nb_add*/
856877
(binaryfunc)int_sub, /*nb_subtract*/
857878
(binaryfunc)int_mul, /*nb_multiply*/
858-
(binaryfunc)int_div, /*nb_divide*/
879+
(binaryfunc)int_classic_div, /*nb_divide*/
859880
(binaryfunc)int_mod, /*nb_remainder*/
860881
(binaryfunc)int_divmod, /*nb_divmod*/
861882
(ternaryfunc)int_pow, /*nb_power*/

Objects/longobject.c

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1508,6 +1508,26 @@ long_div(PyObject *v, PyObject *w)
15081508
return (PyObject *)div;
15091509
}
15101510

1511+
static PyObject *
1512+
long_classic_div(PyObject *v, PyObject *w)
1513+
{
1514+
PyLongObject *a, *b, *div, *mod;
1515+
1516+
CONVERT_BINOP(v, w, &a, &b);
1517+
1518+
if (Py_DivisionWarningFlag &&
1519+
PyErr_Warn(PyExc_DeprecationWarning, "classic long division") < 0)
1520+
div = NULL;
1521+
else if (l_divmod(a, b, &div, &mod) < 0)
1522+
div = NULL;
1523+
else
1524+
Py_DECREF(mod);
1525+
1526+
Py_DECREF(a);
1527+
Py_DECREF(b);
1528+
return (PyObject *)div;
1529+
}
1530+
15111531
static PyObject *
15121532
long_mod(PyObject *v, PyObject *w)
15131533
{
@@ -2115,7 +2135,7 @@ static PyNumberMethods long_as_number = {
21152135
(binaryfunc) long_add, /*nb_add*/
21162136
(binaryfunc) long_sub, /*nb_subtract*/
21172137
(binaryfunc) long_mul, /*nb_multiply*/
2118-
(binaryfunc) long_div, /*nb_divide*/
2138+
(binaryfunc) long_classic_div, /*nb_divide*/
21192139
(binaryfunc) long_mod, /*nb_remainder*/
21202140
(binaryfunc) long_divmod, /*nb_divmod*/
21212141
(ternaryfunc) long_pow, /*nb_power*/

Objects/object.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
DL_IMPORT(long) _Py_RefTotal;
1717
#endif
1818

19+
DL_IMPORT(int) Py_DivisionWarningFlag;
20+
1921
/* Object allocation routines used by NEWOBJ and NEWVAROBJ macros.
2022
These are used by the individual routines for object creation.
2123
Do not call them otherwise, they do not initialize the object! */

Python/pythonrun.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -686,13 +686,19 @@ PyRun_SimpleFileExFlags(FILE *fp, char *filename, int closeit,
686686

687687
int
688688
PyRun_SimpleString(char *command)
689+
{
690+
return PyRun_SimpleStringFlags(command, NULL);
691+
}
692+
693+
int
694+
PyRun_SimpleStringFlags(char *command, PyCompilerFlags *flags)
689695
{
690696
PyObject *m, *d, *v;
691697
m = PyImport_AddModule("__main__");
692698
if (m == NULL)
693699
return -1;
694700
d = PyModule_GetDict(m);
695-
v = PyRun_String(command, Py_file_input, d, d);
701+
v = PyRun_StringFlags(command, Py_file_input, d, d, flags);
696702
if (v == NULL) {
697703
PyErr_Print();
698704
return -1;

0 commit comments

Comments
 (0)