Issue #16612: Add "Argument Clinic", a compile-time preprocessor

for C files to generate argument parsing code.  (See PEP 436.)
This commit is contained in:
Larry Hastings 2013-10-19 00:09:25 -07:00
parent 5ceae41083
commit 3182680210
12 changed files with 4016 additions and 276 deletions

View File

@ -10,6 +10,9 @@ Projected release date: 2013-10-20
Core and Builtins Core and Builtins
----------------- -----------------
- Issue #16612: Add "Argument Clinic", a compile-time preprocessor for
C files to generate argument parsing code. (See PEP 436.)
- Issue #18810: Shift stat calls in importlib.machinery.FileFinder such that - Issue #18810: Shift stat calls in importlib.machinery.FileFinder such that
the code is optimistic that if something exists in a directory named exactly the code is optimistic that if something exists in a directory named exactly
like the possible package being searched for that it's in actuality a like the possible package being searched for that it's in actuality a

View File

@ -549,68 +549,141 @@ PyCursesWindow_Dealloc(PyCursesWindowObject *wo)
/* Addch, Addstr, Addnstr */ /* Addch, Addstr, Addnstr */
/*[clinic]
module curses
class curses.window
curses.window.addch
[
x: int
X-coordinate.
y: int
Y-coordinate.
]
ch: object
Character to add.
[
attr: long
Attributes for the character.
]
/
Paint character ch at (y, x) with attributes attr.
Paint character ch at (y, x) with attributes attr,
overwriting any character previously painted at that location.
By default, the character position and attributes are the
current settings for the window object.
[clinic]*/
PyDoc_STRVAR(curses_window_addch__doc__,
"Paint character ch at (y, x) with attributes attr.\n"
"\n"
"curses.window.addch([x, y,] ch, [attr])\n"
" x\n"
" X-coordinate.\n"
" y\n"
" Y-coordinate.\n"
" ch\n"
" Character to add.\n"
" attr\n"
" Attributes for the character.\n"
"\n"
"Paint character ch at (y, x) with attributes attr,\n"
"overwriting any character previously painted at that location.\n"
"By default, the character position and attributes are the\n"
"current settings for the window object.");
#define CURSES_WINDOW_ADDCH_METHODDEF \
{"addch", (PyCFunction)curses_window_addch, METH_VARARGS, curses_window_addch__doc__},
static PyObject * static PyObject *
PyCursesWindow_AddCh(PyCursesWindowObject *self, PyObject *args) curses_window_addch_impl(PyObject *self, int group_left_1, int x, int y, PyObject *ch, int group_right_1, long attr);
static PyObject *
curses_window_addch(PyObject *self, PyObject *args)
{ {
int rtn, x, y, use_xy = FALSE; PyObject *return_value = NULL;
PyObject *chobj; int group_left_1 = 0;
int x;
int y;
PyObject *ch;
int group_right_1 = 0;
long attr;
switch (PyTuple_Size(args)) {
case 1:
if (!PyArg_ParseTuple(args, "O:addch", &ch))
return NULL;
break;
case 2:
if (!PyArg_ParseTuple(args, "Ol:addch", &ch, &attr))
return NULL;
group_right_1 = 1;
break;
case 3:
if (!PyArg_ParseTuple(args, "iiO:addch", &x, &y, &ch))
return NULL;
group_left_1 = 1;
break;
case 4:
if (!PyArg_ParseTuple(args, "iiOl:addch", &x, &y, &ch, &attr))
return NULL;
group_right_1 = 1;
group_left_1 = 1;
break;
default:
PyErr_SetString(PyExc_TypeError, "curses.window.addch requires 1 to 4 arguments");
return NULL;
}
return_value = curses_window_addch_impl(self, group_left_1, x, y, ch, group_right_1, attr);
return return_value;
}
static PyObject *
curses_window_addch_impl(PyObject *self, int group_left_1, int x, int y, PyObject *ch, int group_right_1, long attr)
/*[clinic checksum: 98ade780397a48d0be48439763424b3b00c92089]*/
{
PyCursesWindowObject *cwself = (PyCursesWindowObject *)self;
int coordinates_group = group_left_1;
int attr_group = group_right_1;
int rtn;
int type; int type;
chtype ch; chtype cch;
#ifdef HAVE_NCURSESW #ifdef HAVE_NCURSESW
cchar_t wch; cchar_t wch;
#endif #endif
attr_t attr = A_NORMAL;
long lattr;
const char *funcname; const char *funcname;
switch (PyTuple_Size(args)) { if (!attr_group)
case 1: attr = A_NORMAL;
if (!PyArg_ParseTuple(args, "O;ch or int", &chobj))
return NULL;
break;
case 2:
if (!PyArg_ParseTuple(args, "Ol;ch or int,attr", &chobj, &lattr))
return NULL;
attr = lattr;
break;
case 3:
if (!PyArg_ParseTuple(args,"iiO;y,x,ch or int", &y, &x, &chobj))
return NULL;
use_xy = TRUE;
break;
case 4:
if (!PyArg_ParseTuple(args,"iiOl;y,x,ch or int, attr",
&y, &x, &chobj, &lattr))
return NULL;
attr = lattr;
use_xy = TRUE;
break;
default:
PyErr_SetString(PyExc_TypeError, "addch requires 1 to 4 arguments");
return NULL;
}
#ifdef HAVE_NCURSESW #ifdef HAVE_NCURSESW
type = PyCurses_ConvertToCchar_t(self, chobj, &ch, &wch); type = PyCurses_ConvertToCchar_t(cwself, ch, &cch, &wch);
if (type == 2) { if (type == 2) {
funcname = "add_wch"; funcname = "add_wch";
wch.attr = attr; wch.attr = attr;
if (use_xy == TRUE) if (coordinates_group)
rtn = mvwadd_wch(self->win,y,x, &wch); rtn = mvwadd_wch(cwself->win,y,x, &wch);
else { else {
rtn = wadd_wch(self->win, &wch); rtn = wadd_wch(cwself->win, &wch);
} }
} }
else else
#else #else
type = PyCurses_ConvertToCchar_t(self, chobj, &ch); type = PyCurses_ConvertToCchar_t(cwself, chobj, &cch);
#endif #endif
if (type == 1) { if (type == 1) {
funcname = "addch"; funcname = "addch";
if (use_xy == TRUE) if (coordinates_group)
rtn = mvwaddch(self->win,y,x, ch | attr); rtn = mvwaddch(cwself->win,y,x, cch | attr);
else { else {
rtn = waddch(self->win, ch | attr); rtn = waddch(cwself->win, cch | attr);
} }
} }
else { else {
@ -1954,7 +2027,7 @@ PyCursesWindow_set_encoding(PyCursesWindowObject *self, PyObject *value)
static PyMethodDef PyCursesWindow_Methods[] = { static PyMethodDef PyCursesWindow_Methods[] = {
{"addch", (PyCFunction)PyCursesWindow_AddCh, METH_VARARGS}, CURSES_WINDOW_ADDCH_METHODDEF
{"addnstr", (PyCFunction)PyCursesWindow_AddNStr, METH_VARARGS}, {"addnstr", (PyCFunction)PyCursesWindow_AddNStr, METH_VARARGS},
{"addstr", (PyCFunction)PyCursesWindow_AddStr, METH_VARARGS}, {"addstr", (PyCFunction)PyCursesWindow_AddStr, METH_VARARGS},
{"attroff", (PyCFunction)PyCursesWindow_AttrOff, METH_VARARGS}, {"attroff", (PyCFunction)PyCursesWindow_AttrOff, METH_VARARGS},

View File

@ -4143,31 +4143,73 @@ datetime_best_possible(PyObject *cls, TM_FUNC f, PyObject *tzinfo)
tzinfo); tzinfo);
} }
/* Return best possible local time -- this isn't constrained by the /*[clinic]
* precision of a timestamp. module datetime
*/
@classmethod
datetime.now
tz: object = None
Timezone object.
Returns new datetime object representing current time local to tz.
If no tz is specified, uses local timezone.
[clinic]*/
PyDoc_STRVAR(datetime_now__doc__,
"Returns new datetime object representing current time local to tz.\n"
"\n"
"datetime.now(tz=None)\n"
" tz\n"
" Timezone object.\n"
"\n"
"If no tz is specified, uses local timezone.");
#define DATETIME_NOW_METHODDEF \
{"now", (PyCFunction)datetime_now, METH_VARARGS|METH_KEYWORDS|METH_CLASS, datetime_now__doc__},
static PyObject * static PyObject *
datetime_now(PyObject *cls, PyObject *args, PyObject *kw) datetime_now_impl(PyObject *cls, PyObject *tz);
static PyObject *
datetime_now(PyObject *cls, PyObject *args, PyObject *kwargs)
{
PyObject *return_value = NULL;
static char *_keywords[] = {"tz", NULL};
PyObject *tz = Py_None;
if (!PyArg_ParseTupleAndKeywords(args, kwargs,
"|O:now", _keywords,
&tz))
goto exit;
return_value = datetime_now_impl(cls, tz);
exit:
return return_value;
}
static PyObject *
datetime_now_impl(PyObject *cls, PyObject *tz)
/*[clinic checksum: 328b54387f4c2f8cb534997e1bd55f8cb38c4992]*/
{ {
PyObject *self; PyObject *self;
PyObject *tzinfo = Py_None;
static char *keywords[] = {"tz", NULL};
if (! PyArg_ParseTupleAndKeywords(args, kw, "|O:now", keywords, /* Return best possible local time -- this isn't constrained by the
&tzinfo)) * precision of a timestamp.
return NULL; */
if (check_tzinfo_subclass(tzinfo) < 0) if (check_tzinfo_subclass(tz) < 0)
return NULL; return NULL;
self = datetime_best_possible(cls, self = datetime_best_possible(cls,
tzinfo == Py_None ? localtime : gmtime, tz == Py_None ? localtime : gmtime,
tzinfo); tz);
if (self != NULL && tzinfo != Py_None) { if (self != NULL && tz != Py_None) {
/* Convert UTC to tzinfo's zone. */ /* Convert UTC to tzinfo's zone. */
PyObject *temp = self; PyObject *temp = self;
_Py_IDENTIFIER(fromutc); _Py_IDENTIFIER(fromutc);
self = _PyObject_CallMethodId(tzinfo, &PyId_fromutc, "O", self); self = _PyObject_CallMethodId(tz, &PyId_fromutc, "O", self);
Py_DECREF(temp); Py_DECREF(temp);
} }
return self; return self;
@ -5001,9 +5043,7 @@ static PyMethodDef datetime_methods[] = {
/* Class methods: */ /* Class methods: */
{"now", (PyCFunction)datetime_now, DATETIME_NOW_METHODDEF
METH_VARARGS | METH_KEYWORDS | METH_CLASS,
PyDoc_STR("[tz] -> new datetime with tz's local day and time.")},
{"utcnow", (PyCFunction)datetime_utcnow, {"utcnow", (PyCFunction)datetime_utcnow,
METH_NOARGS | METH_CLASS, METH_NOARGS | METH_CLASS,

View File

@ -44,7 +44,7 @@ static PyTypeObject Dbmtype;
static PyObject *DbmError; static PyObject *DbmError;
static PyObject * static PyObject *
newdbmobject(char *file, int flags, int mode) newdbmobject(const char *file, int flags, int mode)
{ {
dbmobject *dp; dbmobject *dp;
@ -361,16 +361,69 @@ static PyTypeObject Dbmtype = {
/* ----------------------------------------------------------------- */ /* ----------------------------------------------------------------- */
/*[clinic]
module dbm
dbm.open as dbmopen
filename: str
The filename to open.
flags: str="r"
How to open the file. "r" for reading, "w" for writing, etc.
mode: int(doc_default="0o666") = 0o666
If creating a new file, the mode bits for the new file
(e.g. os.O_RDWR).
/
Return a database object.
[clinic]*/
PyDoc_STRVAR(dbmopen__doc__,
"Return a database object.\n"
"\n"
"dbm.open(filename, flags=\'r\', mode=0o666)\n"
" filename\n"
" The filename to open.\n"
" flags\n"
" How to open the file. \"r\" for reading, \"w\" for writing, etc.\n"
" mode\n"
" If creating a new file, the mode bits for the new file\n"
" (e.g. os.O_RDWR).");
#define DBMOPEN_METHODDEF \
{"open", (PyCFunction)dbmopen, METH_VARARGS, dbmopen__doc__},
static PyObject *
dbmopen_impl(PyObject *self, const char *filename, const char *flags, int mode);
static PyObject * static PyObject *
dbmopen(PyObject *self, PyObject *args) dbmopen(PyObject *self, PyObject *args)
{ {
char *name; PyObject *return_value = NULL;
char *flags = "r"; const char *filename;
int iflags; const char *flags = "r";
int mode = 0666; int mode = 438;
if (!PyArg_ParseTuple(args,
"s|si:open",
&filename, &flags, &mode))
goto exit;
return_value = dbmopen_impl(self, filename, flags, mode);
exit:
return return_value;
}
static PyObject *
dbmopen_impl(PyObject *self, const char *filename, const char *flags, int mode)
/*[clinic checksum: 61007c796d38af85c8035afa769fb4bb453429ee]*/
{
int iflags;
if ( !PyArg_ParseTuple(args, "s|si:open", &name, &flags, &mode) )
return NULL;
if ( strcmp(flags, "r") == 0 ) if ( strcmp(flags, "r") == 0 )
iflags = O_RDONLY; iflags = O_RDONLY;
else if ( strcmp(flags, "w") == 0 ) else if ( strcmp(flags, "w") == 0 )
@ -386,13 +439,11 @@ dbmopen(PyObject *self, PyObject *args)
"arg 2 to open should be 'r', 'w', 'c', or 'n'"); "arg 2 to open should be 'r', 'w', 'c', or 'n'");
return NULL; return NULL;
} }
return newdbmobject(name, iflags, mode); return newdbmobject(filename, iflags, mode);
} }
static PyMethodDef dbmmodule_methods[] = { static PyMethodDef dbmmodule_methods[] = {
{ "open", (PyCFunction)dbmopen, METH_VARARGS, DBMOPEN_METHODDEF
"open(path[, flag[, mode]]) -> mapping\n"
"Return a database object."},
{ 0, 0 }, { 0, 0 },
}; };

View File

@ -4,25 +4,54 @@
#define GET_WEAKREFS_LISTPTR(o) \ #define GET_WEAKREFS_LISTPTR(o) \
((PyWeakReference **) PyObject_GET_WEAKREFS_LISTPTR(o)) ((PyWeakReference **) PyObject_GET_WEAKREFS_LISTPTR(o))
/*[clinic]
PyDoc_STRVAR(weakref_getweakrefcount__doc__, module _weakref
"getweakrefcount(object) -- return the number of weak references\n"
"to 'object'."); _weakref.getweakrefcount -> Py_ssize_t
object: object
/
Return the number of weak references to 'object'.
[clinic]*/
PyDoc_STRVAR(_weakref_getweakrefcount__doc__,
"Return the number of weak references to \'object\'.\n"
"\n"
"_weakref.getweakrefcount(object)");
#define _WEAKREF_GETWEAKREFCOUNT_METHODDEF \
{"getweakrefcount", (PyCFunction)_weakref_getweakrefcount, METH_O, _weakref_getweakrefcount__doc__},
static Py_ssize_t
_weakref_getweakrefcount_impl(PyObject *self, PyObject *object);
static PyObject * static PyObject *
weakref_getweakrefcount(PyObject *self, PyObject *object) _weakref_getweakrefcount(PyObject *self, PyObject *object)
{ {
PyObject *result = NULL; PyObject *return_value = NULL;
Py_ssize_t _return_value;
_return_value = _weakref_getweakrefcount_impl(self, object);
if ((_return_value == -1) && PyErr_Occurred())
goto exit;
return_value = PyLong_FromSsize_t(_return_value);
if (PyType_SUPPORTS_WEAKREFS(Py_TYPE(object))) { exit:
PyWeakReference **list = GET_WEAKREFS_LISTPTR(object); return return_value;
}
result = PyLong_FromSsize_t(_PyWeakref_GetWeakrefCount(*list)); static Py_ssize_t
} _weakref_getweakrefcount_impl(PyObject *self, PyObject *object)
else /*[clinic checksum: 0b7e7ddd87d483719ebac0fba364fff0ed0182d9]*/
result = PyLong_FromLong(0); {
PyWeakReference **list;
return result; if (!PyType_SUPPORTS_WEAKREFS(Py_TYPE(object)))
return 0;
list = GET_WEAKREFS_LISTPTR(object);
return _PyWeakref_GetWeakrefCount(*list);
} }
@ -78,8 +107,7 @@ weakref_proxy(PyObject *self, PyObject *args)
static PyMethodDef static PyMethodDef
weakref_functions[] = { weakref_functions[] = {
{"getweakrefcount", weakref_getweakrefcount, METH_O, _WEAKREF_GETWEAKREFCOUNT_METHODDEF
weakref_getweakrefcount__doc__},
{"getweakrefs", weakref_getweakrefs, METH_O, {"getweakrefs", weakref_getweakrefs, METH_O,
weakref_getweakrefs__doc__}, weakref_getweakrefs__doc__},
{"proxy", weakref_proxy, METH_VARARGS, {"proxy", weakref_proxy, METH_VARARGS,
@ -106,7 +134,7 @@ PyInit__weakref(void)
PyObject *m; PyObject *m;
m = PyModule_Create(&weakrefmodule); m = PyModule_Create(&weakrefmodule);
if (m != NULL) { if (m != NULL) {
Py_INCREF(&_PyWeakref_RefType); Py_INCREF(&_PyWeakref_RefType);
PyModule_AddObject(m, "ref", PyModule_AddObject(m, "ref",

View File

@ -8,6 +8,8 @@
of the compiler used. Different compilers define their own feature of the compiler used. Different compilers define their own feature
test macro, e.g. '__BORLANDC__' or '_MSC_VER'. */ test macro, e.g. '__BORLANDC__' or '_MSC_VER'. */
#ifdef __APPLE__ #ifdef __APPLE__
/* /*
* Step 1 of support for weak-linking a number of symbols existing on * Step 1 of support for weak-linking a number of symbols existing on
@ -712,7 +714,7 @@ dir_fd_converter(PyObject *o, void *p)
* path.function_name * path.function_name
* If non-NULL, path_converter will use that as the name * If non-NULL, path_converter will use that as the name
* of the function in error messages. * of the function in error messages.
* (If path.argument_name is NULL it omits the function name.) * (If path.function_name is NULL it omits the function name.)
* path.argument_name * path.argument_name
* If non-NULL, path_converter will use that as the name * If non-NULL, path_converter will use that as the name
* of the parameter in error messages. * of the parameter in error messages.
@ -776,6 +778,9 @@ typedef struct {
PyObject *cleanup; PyObject *cleanup;
} path_t; } path_t;
#define PATH_T_INITIALIZE(function_name, nullable, allow_fd) \
{function_name, NULL, nullable, allow_fd, NULL, NULL, 0, 0, NULL, NULL}
static void static void
path_cleanup(path_t *path) { path_cleanup(path_t *path) {
if (path->cleanup) { if (path->cleanup) {
@ -1313,6 +1318,7 @@ path_error(path_t *path)
#endif #endif
} }
/* POSIX generic methods */ /* POSIX generic methods */
static PyObject * static PyObject *
@ -2347,48 +2353,145 @@ posix_do_stat(char *function_name, path_t *path,
return _pystat_fromstructstat(&st); return _pystat_fromstructstat(&st);
} }
PyDoc_STRVAR(posix_stat__doc__, #ifdef HAVE_FSTATAT
"stat(path, *, dir_fd=None, follow_symlinks=True) -> stat result\n\n\ #define OS_STAT_DIR_FD_CONVERTER dir_fd_converter
Perform a stat system call on the given path.\n\ #else
\n\ #define OS_STAT_DIR_FD_CONVERTER dir_fd_unavailable
path may be specified as either a string or as an open file descriptor.\n\ #endif
\n\
If dir_fd is not None, it should be a file descriptor open to a directory,\n\
and path should be relative; path will then be relative to that directory.\n\ /*[python]
dir_fd may not be supported on your platform; if it is unavailable, using\n\
it will raise a NotImplementedError.\n\ class path_t_converter(CConverter):
If follow_symlinks is False, and the last element of the path is a symbolic\n\
link, stat will examine the symbolic link itself instead of the file the\n\ type = "path_t"
link points to.\n\ impl_by_reference = True
It is an error to use dir_fd or follow_symlinks when specifying path as\n\ parse_by_reference = True
an open file descriptor.");
converter = 'path_converter'
def converter_init(self, *, allow_fd=False, nullable=False):
def strify(value):
return str(int(bool(value)))
# right now path_t doesn't support default values.
# to support a default value, you'll need to override initialize().
assert self.default is unspecified
self.nullable = nullable
self.allow_fd = allow_fd
self.c_default = 'PATH_T_INITIALIZE("{}", {}, {})'.format(
self.function.name,
strify(nullable),
strify(allow_fd),
)
def cleanup(self):
return "path_cleanup(&" + self.name + ");\n"
class dir_fd_converter(CConverter):
type = 'int'
converter = 'OS_STAT_DIR_FD_CONVERTER'
def converter_init(self):
if self.default in (unspecified, None):
self.c_default = 'DEFAULT_DIR_FD'
[python]*/
/*[python checksum: da39a3ee5e6b4b0d3255bfef95601890afd80709]*/
/*[clinic]
module os
os.stat -> object(doc_default='stat_result')
path : path_t(allow_fd=True)
Path to be examined; can be string, bytes, or open-file-descriptor int.
*
dir_fd : dir_fd = None
If not None, it should be a file descriptor open to a directory,
and path should be a relative string; path will then be relative to
that directory.
follow_symlinks: bool = True
If False, and the last element of the path is a symbolic link,
stat will examine the symbolic link itself instead of the file
the link points to.
Perform a stat system call on the given path.
dir_fd and follow_symlinks may not be implemented
on your platform. If they are unavailable, using them will raise a
NotImplementedError.
It's an error to use dir_fd or follow_symlinks when specifying path as
an open file descriptor.
[clinic]*/
PyDoc_STRVAR(os_stat__doc__,
"Perform a stat system call on the given path.\n"
"\n"
"os.stat(path, *, dir_fd=None, follow_symlinks=True) -> stat_result\n"
" path\n"
" Path to be examined; can be string, bytes, or open-file-descriptor int.\n"
" dir_fd\n"
" If not None, it should be a file descriptor open to a directory,\n"
" and path should be a relative string; path will then be relative to\n"
" that directory.\n"
" follow_symlinks\n"
" If False, and the last element of the path is a symbolic link,\n"
" stat will examine the symbolic link itself instead of the file\n"
" the link points to.\n"
"\n"
"dir_fd and follow_symlinks may not be implemented\n"
" on your platform. If they are unavailable, using them will raise a\n"
" NotImplementedError.\n"
"\n"
"It\'s an error to use dir_fd or follow_symlinks when specifying path as\n"
" an open file descriptor.");
#define OS_STAT_METHODDEF \
{"stat", (PyCFunction)os_stat, METH_VARARGS|METH_KEYWORDS, os_stat__doc__},
static PyObject * static PyObject *
posix_stat(PyObject *self, PyObject *args, PyObject *kwargs) os_stat_impl(PyObject *self, path_t *path, int dir_fd, int follow_symlinks);
static PyObject *
os_stat(PyObject *self, PyObject *args, PyObject *kwargs)
{ {
static char *keywords[] = {"path", "dir_fd", "follow_symlinks", NULL}; PyObject *return_value = NULL;
path_t path; static char *_keywords[] = {"path", "dir_fd", "follow_symlinks", NULL};
path_t path = PATH_T_INITIALIZE("stat", 0, 1);
int dir_fd = DEFAULT_DIR_FD; int dir_fd = DEFAULT_DIR_FD;
int follow_symlinks = 1; int follow_symlinks = 1;
PyObject *return_value;
memset(&path, 0, sizeof(path)); if (!PyArg_ParseTupleAndKeywords(args, kwargs,
path.function_name = "stat"; "O&|$O&p:stat", _keywords,
path.allow_fd = 1; path_converter, &path, OS_STAT_DIR_FD_CONVERTER, &dir_fd, &follow_symlinks))
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&|$O&p:stat", keywords, goto exit;
path_converter, &path, return_value = os_stat_impl(self, &path, dir_fd, follow_symlinks);
#ifdef HAVE_FSTATAT
dir_fd_converter, &dir_fd, exit:
#else /* Cleanup for path */
dir_fd_unavailable, &dir_fd,
#endif
&follow_symlinks))
return NULL;
return_value = posix_do_stat("stat", &path, dir_fd, follow_symlinks);
path_cleanup(&path); path_cleanup(&path);
return return_value; return return_value;
} }
static PyObject *
os_stat_impl(PyObject *self, path_t *path, int dir_fd, int follow_symlinks)
/*[clinic checksum: 9d9af08e8cfafd12f94e73ea3065eb3056f99515]*/
{
return posix_do_stat("stat", path, dir_fd, follow_symlinks);
}
PyDoc_STRVAR(posix_lstat__doc__, PyDoc_STRVAR(posix_lstat__doc__,
"lstat(path, *, dir_fd=None) -> stat result\n\n\ "lstat(path, *, dir_fd=None) -> stat result\n\n\
Like stat(), but do not follow symbolic links.\n\ Like stat(), but do not follow symbolic links.\n\
@ -2414,44 +2517,120 @@ posix_lstat(PyObject *self, PyObject *args, PyObject *kwargs)
#endif #endif
)) ))
return NULL; return NULL;
return_value = posix_do_stat("stat", &path, dir_fd, follow_symlinks); return_value = posix_do_stat("lstat", &path, dir_fd, follow_symlinks);
path_cleanup(&path); path_cleanup(&path);
return return_value; return return_value;
} }
PyDoc_STRVAR(posix_access__doc__,
"access(path, mode, *, dir_fd=None, effective_ids=False,\ #ifdef HAVE_FACCESSAT
follow_symlinks=True)\n\n\ #define OS_ACCESS_DIR_FD_CONVERTER dir_fd_converter
Use the real uid/gid to test for access to a path. Returns True if granted,\n\ #else
False otherwise.\n\ #define OS_ACCESS_DIR_FD_CONVERTER dir_fd_unavailable
\n\ #endif
If dir_fd is not None, it should be a file descriptor open to a directory,\n\ /*[clinic]
and path should be relative; path will then be relative to that directory.\n\ os.access -> object(doc_default='True if granted, False otherwise')
If effective_ids is True, access will use the effective uid/gid instead of\n\
the real uid/gid.\n\ path: path_t(allow_fd=True)
If follow_symlinks is False, and the last element of the path is a symbolic\n\ Path to be tested; can be string, bytes, or open-file-descriptor int.
link, access will examine the symbolic link itself instead of the file the\n\
link points to.\n\ mode: int
dir_fd, effective_ids, and follow_symlinks may not be implemented\n\ Operating-system mode bitfield. Can be F_OK to test existence,
on your platform. If they are unavailable, using them will raise a\n\ or the inclusive-OR of R_OK, W_OK, and X_OK.
NotImplementedError.\n\
\n\ *
Note that most operations will use the effective uid/gid, therefore this\n\
routine can be used in a suid/sgid environment to test if the invoking user\n\ dir_fd : dir_fd = None
has the specified access to the path.\n\ If not None, it should be a file descriptor open to a directory,
The mode argument can be F_OK to test existence, or the inclusive-OR\n\ and path should be relative; path will then be relative to that
of R_OK, W_OK, and X_OK."); directory.
effective_ids: bool = False
If True, access will use the effective uid/gid instead of
the real uid/gid.
follow_symlinks: bool = True
If False, and the last element of the path is a symbolic link,
access will examine the symbolic link itself instead of the file
the link points to.
Use the real uid/gid to test for access to a path.
{parameters}
dir_fd, effective_ids, and follow_symlinks may not be implemented
on your platform. If they are unavailable, using them will raise a
NotImplementedError.
Note that most operations will use the effective uid/gid, therefore this
routine can be used in a suid/sgid environment to test if the invoking user
has the specified access to the path.
[clinic]*/
PyDoc_STRVAR(os_access__doc__,
"Use the real uid/gid to test for access to a path.\n"
"\n"
"os.access(path, mode, *, dir_fd=None, effective_ids=False, follow_symlinks=True) -> True if granted, False otherwise\n"
" path\n"
" Path to be tested; can be string, bytes, or open-file-descriptor int.\n"
" mode\n"
" Operating-system mode bitfield. Can be F_OK to test existence,\n"
" or the inclusive-OR of R_OK, W_OK, and X_OK.\n"
" dir_fd\n"
" If not None, it should be a file descriptor open to a directory,\n"
" and path should be relative; path will then be relative to that\n"
" directory.\n"
" effective_ids\n"
" If True, access will use the effective uid/gid instead of\n"
" the real uid/gid.\n"
" follow_symlinks\n"
" If False, and the last element of the path is a symbolic link,\n"
" access will examine the symbolic link itself instead of the file\n"
" the link points to.\n"
"\n"
"{parameters}\n"
"dir_fd, effective_ids, and follow_symlinks may not be implemented\n"
" on your platform. If they are unavailable, using them will raise a\n"
" NotImplementedError.\n"
"\n"
"Note that most operations will use the effective uid/gid, therefore this\n"
" routine can be used in a suid/sgid environment to test if the invoking user\n"
" has the specified access to the path.");
#define OS_ACCESS_METHODDEF \
{"access", (PyCFunction)os_access, METH_VARARGS|METH_KEYWORDS, os_access__doc__},
static PyObject * static PyObject *
posix_access(PyObject *self, PyObject *args, PyObject *kwargs) os_access_impl(PyObject *self, path_t *path, int mode, int dir_fd, int effective_ids, int follow_symlinks);
static PyObject *
os_access(PyObject *self, PyObject *args, PyObject *kwargs)
{ {
static char *keywords[] = {"path", "mode", "dir_fd", "effective_ids", PyObject *return_value = NULL;
"follow_symlinks", NULL}; static char *_keywords[] = {"path", "mode", "dir_fd", "effective_ids", "follow_symlinks", NULL};
path_t path; path_t path = PATH_T_INITIALIZE("access", 0, 1);
int mode; int mode;
int dir_fd = DEFAULT_DIR_FD; int dir_fd = DEFAULT_DIR_FD;
int effective_ids = 0; int effective_ids = 0;
int follow_symlinks = 1; int follow_symlinks = 1;
if (!PyArg_ParseTupleAndKeywords(args, kwargs,
"O&i|$O&pp:access", _keywords,
path_converter, &path, &mode, OS_STAT_DIR_FD_CONVERTER, &dir_fd, &effective_ids, &follow_symlinks))
goto exit;
return_value = os_access_impl(self, &path, mode, dir_fd, effective_ids, follow_symlinks);
exit:
/* Cleanup for path */
path_cleanup(&path);
return return_value;
}
static PyObject *
os_access_impl(PyObject *self, path_t *path, int mode, int dir_fd, int effective_ids, int follow_symlinks)
/*[clinic checksum: 0147557eb43243df57ba616cc7c35f232c69bc6a]*/
{
PyObject *return_value = NULL; PyObject *return_value = NULL;
#ifdef MS_WINDOWS #ifdef MS_WINDOWS
@ -2460,17 +2639,6 @@ posix_access(PyObject *self, PyObject *args, PyObject *kwargs)
int result; int result;
#endif #endif
memset(&path, 0, sizeof(path));
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&i|$O&pp:access", keywords,
path_converter, &path, &mode,
#ifdef HAVE_FACCESSAT
dir_fd_converter, &dir_fd,
#else
dir_fd_unavailable, &dir_fd,
#endif
&effective_ids, &follow_symlinks))
return NULL;
#ifndef HAVE_FACCESSAT #ifndef HAVE_FACCESSAT
if (follow_symlinks_specified("access", follow_symlinks)) if (follow_symlinks_specified("access", follow_symlinks))
goto exit; goto exit;
@ -2514,11 +2682,11 @@ posix_access(PyObject *self, PyObject *args, PyObject *kwargs)
flags |= AT_SYMLINK_NOFOLLOW; flags |= AT_SYMLINK_NOFOLLOW;
if (effective_ids) if (effective_ids)
flags |= AT_EACCESS; flags |= AT_EACCESS;
result = faccessat(dir_fd, path.narrow, mode, flags); result = faccessat(dir_fd, path->narrow, mode, flags);
} }
else else
#endif #endif
result = access(path.narrow, mode); result = access(path->narrow, mode);
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
return_value = PyBool_FromLong(!result); return_value = PyBool_FromLong(!result);
#endif #endif
@ -2526,7 +2694,6 @@ posix_access(PyObject *self, PyObject *args, PyObject *kwargs)
#ifndef HAVE_FACCESSAT #ifndef HAVE_FACCESSAT
exit: exit:
#endif #endif
path_cleanup(&path);
return return_value; return return_value;
} }
@ -2543,35 +2710,76 @@ exit:
#define X_OK 1 #define X_OK 1
#endif #endif
#ifdef HAVE_TTYNAME #ifdef HAVE_TTYNAME
PyDoc_STRVAR(posix_ttyname__doc__,
"ttyname(fd) -> string\n\n\ /*[clinic]
Return the name of the terminal device connected to 'fd'."); os.ttyname -> DecodeFSDefault
fd: int
Integer file descriptor handle.
/
Return the name of the terminal device connected to 'fd'.
[clinic]*/
PyDoc_STRVAR(os_ttyname__doc__,
"Return the name of the terminal device connected to \'fd\'.\n"
"\n"
"os.ttyname(fd)\n"
" fd\n"
" Integer file descriptor handle.");
#define OS_TTYNAME_METHODDEF \
{"ttyname", (PyCFunction)os_ttyname, METH_VARARGS, os_ttyname__doc__},
static char *
os_ttyname_impl(PyObject *self, int fd);
static PyObject * static PyObject *
posix_ttyname(PyObject *self, PyObject *args) os_ttyname(PyObject *self, PyObject *args)
{ {
int id; PyObject *return_value = NULL;
char *ret; int fd;
char *_return_value;
if (!PyArg_ParseTuple(args, "i:ttyname", &id)) if (!PyArg_ParseTuple(args,
return NULL; "i:ttyname",
&fd))
goto exit;
_return_value = os_ttyname_impl(self, fd);
if (_return_value == NULL)
goto exit;
return_value = PyUnicode_DecodeFSDefault(_return_value);
exit:
return return_value;
}
static char *
os_ttyname_impl(PyObject *self, int fd)
/*[clinic checksum: ea680155d87bb733f542d67653eca732dd0981a8]*/
{
char *ret;
#if defined(__VMS) #if defined(__VMS)
/* file descriptor 0 only, the default input device (stdin) */ /* file descriptor 0 only, the default input device (stdin) */
if (id == 0) { if (fd == 0) {
ret = ttyname(); ret = ttyname();
} }
else { else {
ret = NULL; ret = NULL;
} }
#else #else
ret = ttyname(id); ret = ttyname(fd);
#endif #endif
if (ret == NULL) if (ret == NULL)
return posix_error(); posix_error();
return PyUnicode_DecodeFSDefault(ret); return ret;
} }
#else
#define OS_TTYNAME_METHODDEF
#endif #endif
#ifdef HAVE_CTERMID #ifdef HAVE_CTERMID
@ -10912,13 +11120,13 @@ posix_set_handle_inheritable(PyObject *self, PyObject *args)
#endif /* MS_WINDOWS */ #endif /* MS_WINDOWS */
static PyMethodDef posix_methods[] = { static PyMethodDef posix_methods[] = {
{"access", (PyCFunction)posix_access,
METH_VARARGS | METH_KEYWORDS, OS_STAT_METHODDEF
posix_access__doc__}, OS_ACCESS_METHODDEF
#ifdef HAVE_TTYNAME OS_TTYNAME_METHODDEF
{"ttyname", posix_ttyname, METH_VARARGS, posix_ttyname__doc__},
#endif
{"chdir", (PyCFunction)posix_chdir, {"chdir", (PyCFunction)posix_chdir,
METH_VARARGS | METH_KEYWORDS, METH_VARARGS | METH_KEYWORDS,
posix_chdir__doc__}, posix_chdir__doc__},
@ -11002,9 +11210,6 @@ static PyMethodDef posix_methods[] = {
{"rmdir", (PyCFunction)posix_rmdir, {"rmdir", (PyCFunction)posix_rmdir,
METH_VARARGS | METH_KEYWORDS, METH_VARARGS | METH_KEYWORDS,
posix_rmdir__doc__}, posix_rmdir__doc__},
{"stat", (PyCFunction)posix_stat,
METH_VARARGS | METH_KEYWORDS,
posix_stat__doc__},
{"stat_float_times", stat_float_times, METH_VARARGS, stat_float_times__doc__}, {"stat_float_times", stat_float_times, METH_VARARGS, stat_float_times__doc__},
#if defined(HAVE_SYMLINK) #if defined(HAVE_SYMLINK)
{"symlink", (PyCFunction)posix_symlink, {"symlink", (PyCFunction)posix_symlink,

View File

@ -107,24 +107,62 @@ static Py_UCS4 getuchar(PyUnicodeObject *obj)
/* --- Module API --------------------------------------------------------- */ /* --- Module API --------------------------------------------------------- */
/*[clinic]
module unicodedata
unicodedata.decimal
unichr: object(type='str')
default: object=NULL
/
Converts a Unicode character into its equivalent decimal value.
Returns the decimal value assigned to the Unicode character unichr
as integer. If no such value is defined, default is returned, or, if
not given, ValueError is raised.
[clinic]*/
PyDoc_STRVAR(unicodedata_decimal__doc__, PyDoc_STRVAR(unicodedata_decimal__doc__,
"decimal(unichr[, default])\n\ "Converts a Unicode character into its equivalent decimal value.\n"
\n\ "\n"
Returns the decimal value assigned to the Unicode character unichr\n\ "unicodedata.decimal(unichr, default=None)\n"
as integer. If no such value is defined, default is returned, or, if\n\ "\n"
not given, ValueError is raised."); "Returns the decimal value assigned to the Unicode character unichr\n"
"as integer. If no such value is defined, default is returned, or, if\n"
"not given, ValueError is raised.");
#define UNICODEDATA_DECIMAL_METHODDEF \
{"decimal", (PyCFunction)unicodedata_decimal, METH_VARARGS, unicodedata_decimal__doc__},
static PyObject *
unicodedata_decimal_impl(PyObject *self, PyObject *unichr, PyObject *default_value);
static PyObject * static PyObject *
unicodedata_decimal(PyObject *self, PyObject *args) unicodedata_decimal(PyObject *self, PyObject *args)
{ {
PyUnicodeObject *v; PyObject *return_value = NULL;
PyObject *defobj = NULL; PyObject *unichr;
PyObject *default_value = NULL;
if (!PyArg_ParseTuple(args,
"O!|O:decimal",
&PyUnicode_Type, &unichr, &default_value))
goto exit;
return_value = unicodedata_decimal_impl(self, unichr, default_value);
exit:
return return_value;
}
static PyObject *
unicodedata_decimal_impl(PyObject *self, PyObject *unichr, PyObject *default_value)
/*[clinic checksum: 76c8d1c3dbee495d4cfd86ca6829543a3129344a]*/
{
PyUnicodeObject *v = (PyUnicodeObject *)unichr;
int have_old = 0; int have_old = 0;
long rc; long rc;
Py_UCS4 c; Py_UCS4 c;
if (!PyArg_ParseTuple(args, "O!|O:decimal", &PyUnicode_Type, &v, &defobj))
return NULL;
c = getuchar(v); c = getuchar(v);
if (c == (Py_UCS4)-1) if (c == (Py_UCS4)-1)
return NULL; return NULL;
@ -145,14 +183,14 @@ unicodedata_decimal(PyObject *self, PyObject *args)
if (!have_old) if (!have_old)
rc = Py_UNICODE_TODECIMAL(c); rc = Py_UNICODE_TODECIMAL(c);
if (rc < 0) { if (rc < 0) {
if (defobj == NULL) { if (default_value == NULL) {
PyErr_SetString(PyExc_ValueError, PyErr_SetString(PyExc_ValueError,
"not a decimal"); "not a decimal");
return NULL; return NULL;
} }
else { else {
Py_INCREF(defobj); Py_INCREF(default_value);
return defobj; return default_value;
} }
} }
return PyLong_FromLong(rc); return PyLong_FromLong(rc);
@ -1250,7 +1288,7 @@ unicodedata_lookup(PyObject* self, PyObject* args)
/* XXX Add doc strings. */ /* XXX Add doc strings. */
static PyMethodDef unicodedata_functions[] = { static PyMethodDef unicodedata_functions[] = {
{"decimal", unicodedata_decimal, METH_VARARGS, unicodedata_decimal__doc__}, UNICODEDATA_DECIMAL_METHODDEF
{"digit", unicodedata_digit, METH_VARARGS, unicodedata_digit__doc__}, {"digit", unicodedata_digit, METH_VARARGS, unicodedata_digit__doc__},
{"numeric", unicodedata_numeric, METH_VARARGS, unicodedata_numeric__doc__}, {"numeric", unicodedata_numeric, METH_VARARGS, unicodedata_numeric__doc__},
{"category", unicodedata_category, METH_VARARGS, {"category", unicodedata_category, METH_VARARGS,

View File

@ -8,6 +8,7 @@
#include "structmember.h" #include "structmember.h"
#include "zlib.h" #include "zlib.h"
#ifdef WITH_THREAD #ifdef WITH_THREAD
#include "pythread.h" #include "pythread.h"
#define ENTER_ZLIB(obj) \ #define ENTER_ZLIB(obj) \
@ -626,87 +627,136 @@ save_unconsumed_input(compobject *self, int err)
return 0; return 0;
} }
PyDoc_STRVAR(decomp_decompress__doc__, /*[clinic]
"decompress(data, max_length) -- Return a string containing the decompressed\n"
"version of the data.\n" module zlib
zlib.decompress
data: Py_buffer
The binary data to decompress.
max_length: int = 0
The maximum allowable length of the decompressed data.
Unconsumed input data will be stored in
the unconsumed_tail attribute.
/
Return a string containing the decompressed version of the data.
After calling this function, some of the input data may still be stored in
internal buffers for later processing.
Call the flush() method to clear these buffers.
[clinic]*/
PyDoc_STRVAR(zlib_decompress__doc__,
"Return a string containing the decompressed version of the data.\n"
"\n"
"zlib.decompress(data, max_length=0)\n"
" data\n"
" The binary data to decompress.\n"
" max_length\n"
" The maximum allowable length of the decompressed data.\n"
" Unconsumed input data will be stored in\n"
" the unconsumed_tail attribute.\n"
"\n" "\n"
"After calling this function, some of the input data may still be stored in\n" "After calling this function, some of the input data may still be stored in\n"
"internal buffers for later processing.\n" "internal buffers for later processing.\n"
"Call the flush() method to clear these buffers.\n" "Call the flush() method to clear these buffers.");
"If the max_length parameter is specified then the return value will be\n"
"no longer than max_length. Unconsumed input data will be stored in\n" #define ZLIB_DECOMPRESS_METHODDEF \
"the unconsumed_tail attribute."); {"decompress", (PyCFunction)zlib_decompress, METH_VARARGS, zlib_decompress__doc__},
static PyObject * static PyObject *
PyZlib_objdecompress(compobject *self, PyObject *args) zlib_decompress_impl(PyObject *self, Py_buffer *data, int max_length);
static PyObject *
zlib_decompress(PyObject *self, PyObject *args)
{ {
int err, max_length = 0; PyObject *return_value = NULL;
Py_buffer data;
int max_length = 0;
if (!PyArg_ParseTuple(args,
"y*|i:decompress",
&data, &max_length))
goto exit;
return_value = zlib_decompress_impl(self, &data, max_length);
exit:
/* Cleanup for data */
PyBuffer_Release(&data);
return return_value;
}
static PyObject *
zlib_decompress_impl(PyObject *self, Py_buffer *data, int max_length)
/*[clinic checksum: 168d093d400739dde947cca1f4fb0f9d51cdc2c9]*/
{
compobject *zself = (compobject *)self;
int err;
unsigned int inplen; unsigned int inplen;
Py_ssize_t old_length, length = DEFAULTALLOC; Py_ssize_t old_length, length = DEFAULTALLOC;
PyObject *RetVal = NULL; PyObject *RetVal = NULL;
Py_buffer pinput;
Byte *input; Byte *input;
unsigned long start_total_out; unsigned long start_total_out;
if (!PyArg_ParseTuple(args, "y*|i:decompress", &pinput, if (data->len > UINT_MAX) {
&max_length))
return NULL;
if (pinput.len > UINT_MAX) {
PyErr_SetString(PyExc_OverflowError, PyErr_SetString(PyExc_OverflowError,
"Size does not fit in an unsigned int"); "Size does not fit in an unsigned int");
goto error_outer; return NULL;
} }
input = pinput.buf; input = data->buf;
inplen = pinput.len; inplen = data->len;
if (max_length < 0) { if (max_length < 0) {
PyErr_SetString(PyExc_ValueError, PyErr_SetString(PyExc_ValueError,
"max_length must be greater than zero"); "max_length must be greater than zero");
goto error_outer; return NULL;
} }
/* limit amount of data allocated to max_length */ /* limit amount of data allocated to max_length */
if (max_length && length > max_length) if (max_length && length > max_length)
length = max_length; length = max_length;
if (!(RetVal = PyBytes_FromStringAndSize(NULL, length))) if (!(RetVal = PyBytes_FromStringAndSize(NULL, length)))
goto error_outer; return NULL;
ENTER_ZLIB(self); ENTER_ZLIB(zself);
start_total_out = self->zst.total_out; start_total_out = zself->zst.total_out;
self->zst.avail_in = inplen; zself->zst.avail_in = inplen;
self->zst.next_in = input; zself->zst.next_in = input;
self->zst.avail_out = length; zself->zst.avail_out = length;
self->zst.next_out = (unsigned char *)PyBytes_AS_STRING(RetVal); zself->zst.next_out = (unsigned char *)PyBytes_AS_STRING(RetVal);
Py_BEGIN_ALLOW_THREADS Py_BEGIN_ALLOW_THREADS
err = inflate(&(self->zst), Z_SYNC_FLUSH); err = inflate(&(zself->zst), Z_SYNC_FLUSH);
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
if (err == Z_NEED_DICT && self->zdict != NULL) { if (err == Z_NEED_DICT && zself->zdict != NULL) {
Py_buffer zdict_buf; Py_buffer zdict_buf;
if (PyObject_GetBuffer(self->zdict, &zdict_buf, PyBUF_SIMPLE) == -1) { if (PyObject_GetBuffer(zself->zdict, &zdict_buf, PyBUF_SIMPLE) == -1) {
Py_DECREF(RetVal); Py_DECREF(RetVal);
RetVal = NULL; RetVal = NULL;
goto error; goto error;
} }
err = inflateSetDictionary(&(self->zst), zdict_buf.buf, zdict_buf.len); err = inflateSetDictionary(&(zself->zst), zdict_buf.buf, zdict_buf.len);
PyBuffer_Release(&zdict_buf); PyBuffer_Release(&zdict_buf);
if (err != Z_OK) { if (err != Z_OK) {
zlib_error(self->zst, err, "while decompressing data"); zlib_error(zself->zst, err, "while decompressing data");
Py_DECREF(RetVal); Py_DECREF(RetVal);
RetVal = NULL; RetVal = NULL;
goto error; goto error;
} }
/* Repeat the call to inflate. */ /* Repeat the call to inflate. */
Py_BEGIN_ALLOW_THREADS Py_BEGIN_ALLOW_THREADS
err = inflate(&(self->zst), Z_SYNC_FLUSH); err = inflate(&(zself->zst), Z_SYNC_FLUSH);
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
} }
/* While Z_OK and the output buffer is full, there might be more output. /* While Z_OK and the output buffer is full, there might be more output.
So extend the output buffer and try again. So extend the output buffer and try again.
*/ */
while (err == Z_OK && self->zst.avail_out == 0) { while (err == Z_OK && zself->zst.avail_out == 0) {
/* If max_length set, don't continue decompressing if we've already /* If max_length set, don't continue decompressing if we've already
reached the limit. reached the limit.
*/ */
@ -723,16 +773,16 @@ PyZlib_objdecompress(compobject *self, PyObject *args)
Py_CLEAR(RetVal); Py_CLEAR(RetVal);
goto error; goto error;
} }
self->zst.next_out = zself->zst.next_out =
(unsigned char *)PyBytes_AS_STRING(RetVal) + old_length; (unsigned char *)PyBytes_AS_STRING(RetVal) + old_length;
self->zst.avail_out = length - old_length; zself->zst.avail_out = length - old_length;
Py_BEGIN_ALLOW_THREADS Py_BEGIN_ALLOW_THREADS
err = inflate(&(self->zst), Z_SYNC_FLUSH); err = inflate(&(zself->zst), Z_SYNC_FLUSH);
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
} }
if (save_unconsumed_input(self, err) < 0) { if (save_unconsumed_input(zself, err) < 0) {
Py_DECREF(RetVal); Py_DECREF(RetVal);
RetVal = NULL; RetVal = NULL;
goto error; goto error;
@ -741,26 +791,24 @@ PyZlib_objdecompress(compobject *self, PyObject *args)
if (err == Z_STREAM_END) { if (err == Z_STREAM_END) {
/* This is the logical place to call inflateEnd, but the old behaviour /* This is the logical place to call inflateEnd, but the old behaviour
of only calling it on flush() is preserved. */ of only calling it on flush() is preserved. */
self->eof = 1; zself->eof = 1;
} else if (err != Z_OK && err != Z_BUF_ERROR) { } else if (err != Z_OK && err != Z_BUF_ERROR) {
/* We will only get Z_BUF_ERROR if the output buffer was full /* We will only get Z_BUF_ERROR if the output buffer was full
but there wasn't more output when we tried again, so it is but there wasn't more output when we tried again, so it is
not an error condition. not an error condition.
*/ */
zlib_error(self->zst, err, "while decompressing data"); zlib_error(zself->zst, err, "while decompressing data");
Py_DECREF(RetVal); Py_DECREF(RetVal);
RetVal = NULL; RetVal = NULL;
goto error; goto error;
} }
if (_PyBytes_Resize(&RetVal, self->zst.total_out - start_total_out) < 0) { if (_PyBytes_Resize(&RetVal, zself->zst.total_out - start_total_out) < 0) {
Py_CLEAR(RetVal); Py_CLEAR(RetVal);
} }
error: error:
LEAVE_ZLIB(self); LEAVE_ZLIB(zself);
error_outer:
PyBuffer_Release(&pinput);
return RetVal; return RetVal;
} }
@ -856,12 +904,27 @@ PyZlib_flush(compobject *self, PyObject *args)
} }
#ifdef HAVE_ZLIB_COPY #ifdef HAVE_ZLIB_COPY
PyDoc_STRVAR(comp_copy__doc__,
"copy() -- Return a copy of the compression object."); /*[clinic]
zlib.copy
Return a copy of the compression object.
[clinic]*/
PyDoc_STRVAR(zlib_copy__doc__,
"Return a copy of the compression object.\n"
"\n"
"zlib.copy()");
#define ZLIB_COPY_METHODDEF \
{"copy", (PyCFunction)zlib_copy, METH_NOARGS, zlib_copy__doc__},
static PyObject * static PyObject *
PyZlib_copy(compobject *self) zlib_copy(PyObject *self)
/*[clinic checksum: 7b648de2c1f933ba2b9fa17331ff1a44d9a4a740]*/
{ {
compobject *zself = (compobject *)self;
compobject *retval = NULL; compobject *retval = NULL;
int err; int err;
@ -871,8 +934,8 @@ PyZlib_copy(compobject *self)
/* Copy the zstream state /* Copy the zstream state
* We use ENTER_ZLIB / LEAVE_ZLIB to make this thread-safe * We use ENTER_ZLIB / LEAVE_ZLIB to make this thread-safe
*/ */
ENTER_ZLIB(self); ENTER_ZLIB(zself);
err = deflateCopy(&retval->zst, &self->zst); err = deflateCopy(&retval->zst, &zself->zst);
switch(err) { switch(err) {
case(Z_OK): case(Z_OK):
break; break;
@ -884,28 +947,28 @@ PyZlib_copy(compobject *self)
"Can't allocate memory for compression object"); "Can't allocate memory for compression object");
goto error; goto error;
default: default:
zlib_error(self->zst, err, "while copying compression object"); zlib_error(zself->zst, err, "while copying compression object");
goto error; goto error;
} }
Py_INCREF(self->unused_data); Py_INCREF(zself->unused_data);
Py_INCREF(self->unconsumed_tail); Py_INCREF(zself->unconsumed_tail);
Py_XINCREF(self->zdict); Py_XINCREF(zself->zdict);
Py_XDECREF(retval->unused_data); Py_XDECREF(retval->unused_data);
Py_XDECREF(retval->unconsumed_tail); Py_XDECREF(retval->unconsumed_tail);
Py_XDECREF(retval->zdict); Py_XDECREF(retval->zdict);
retval->unused_data = self->unused_data; retval->unused_data = zself->unused_data;
retval->unconsumed_tail = self->unconsumed_tail; retval->unconsumed_tail = zself->unconsumed_tail;
retval->zdict = self->zdict; retval->zdict = zself->zdict;
retval->eof = self->eof; retval->eof = zself->eof;
/* Mark it as being initialized */ /* Mark it as being initialized */
retval->is_initialised = 1; retval->is_initialised = 1;
LEAVE_ZLIB(self); LEAVE_ZLIB(zself);
return (PyObject *)retval; return (PyObject *)retval;
error: error:
LEAVE_ZLIB(self); LEAVE_ZLIB(zself);
Py_XDECREF(retval); Py_XDECREF(retval);
return NULL; return NULL;
} }
@ -1055,16 +1118,14 @@ static PyMethodDef comp_methods[] =
{"flush", (binaryfunc)PyZlib_flush, METH_VARARGS, {"flush", (binaryfunc)PyZlib_flush, METH_VARARGS,
comp_flush__doc__}, comp_flush__doc__},
#ifdef HAVE_ZLIB_COPY #ifdef HAVE_ZLIB_COPY
{"copy", (PyCFunction)PyZlib_copy, METH_NOARGS, ZLIB_COPY_METHODDEF
comp_copy__doc__},
#endif #endif
{NULL, NULL} {NULL, NULL}
}; };
static PyMethodDef Decomp_methods[] = static PyMethodDef Decomp_methods[] =
{ {
{"decompress", (binaryfunc)PyZlib_objdecompress, METH_VARARGS, ZLIB_DECOMPRESS_METHODDEF
decomp_decompress__doc__},
{"flush", (binaryfunc)PyZlib_unflush, METH_VARARGS, {"flush", (binaryfunc)PyZlib_unflush, METH_VARARGS,
decomp_flush__doc__}, decomp_flush__doc__},
#ifdef HAVE_ZLIB_COPY #ifdef HAVE_ZLIB_COPY

View File

@ -2160,9 +2160,31 @@ dict_richcompare(PyObject *v, PyObject *w, int op)
return res; return res;
} }
/*[clinic]
module dict
@coexist
dict.__contains__
key: object
/
True if D has a key k, else False"
[clinic]*/
PyDoc_STRVAR(dict___contains____doc__,
"True if D has a key k, else False\"\n"
"\n"
"dict.__contains__(key)");
#define DICT___CONTAINS___METHODDEF \
{"__contains__", (PyCFunction)dict___contains__, METH_O|METH_COEXIST, dict___contains____doc__},
static PyObject * static PyObject *
dict_contains(PyDictObject *mp, PyObject *key) dict___contains__(PyObject *self, PyObject *key)
/*[clinic checksum: 61c5c802ea1d35699a1a754f1f3538ea9b259cf4]*/
{ {
register PyDictObject *mp = (PyDictObject *)self;
Py_hash_t hash; Py_hash_t hash;
PyDictKeyEntry *ep; PyDictKeyEntry *ep;
PyObject **value_addr; PyObject **value_addr;
@ -2447,9 +2469,6 @@ _PyDict_KeysSize(PyDictKeysObject *keys)
return sizeof(PyDictKeysObject) + (DK_SIZE(keys)-1) * sizeof(PyDictKeyEntry); return sizeof(PyDictKeysObject) + (DK_SIZE(keys)-1) * sizeof(PyDictKeyEntry);
} }
PyDoc_STRVAR(contains__doc__,
"D.__contains__(k) -> True if D has a key k, else False");
PyDoc_STRVAR(getitem__doc__, "x.__getitem__(y) <==> x[y]"); PyDoc_STRVAR(getitem__doc__, "x.__getitem__(y) <==> x[y]");
PyDoc_STRVAR(sizeof__doc__, PyDoc_STRVAR(sizeof__doc__,
@ -2498,8 +2517,7 @@ PyDoc_STRVAR(values__doc__,
"D.values() -> an object providing a view on D's values"); "D.values() -> an object providing a view on D's values");
static PyMethodDef mapp_methods[] = { static PyMethodDef mapp_methods[] = {
{"__contains__",(PyCFunction)dict_contains, METH_O | METH_COEXIST, DICT___CONTAINS___METHODDEF
contains__doc__},
{"__getitem__", (PyCFunction)dict_subscript, METH_O | METH_COEXIST, {"__getitem__", (PyCFunction)dict_subscript, METH_O | METH_COEXIST,
getitem__doc__}, getitem__doc__},
{"__sizeof__", (PyCFunction)dict_sizeof, METH_NOARGS, {"__sizeof__", (PyCFunction)dict_sizeof, METH_NOARGS,

View File

@ -12656,28 +12656,76 @@ unicode_swapcase(PyObject *self)
return case_operation(self, do_swapcase); return case_operation(self, do_swapcase);
} }
PyDoc_STRVAR(maketrans__doc__, /*[clinic]
"str.maketrans(x[, y[, z]]) -> dict (static method)\n\ module str
\n\
Return a translation table usable for str.translate().\n\
If there is only one argument, it must be a dictionary mapping Unicode\n\
ordinals (integers) or characters to Unicode ordinals, strings or None.\n\
Character keys will be then converted to ordinals.\n\
If there are two arguments, they must be strings of equal length, and\n\
in the resulting dictionary, each character in x will be mapped to the\n\
character at the same position in y. If there is a third argument, it\n\
must be a string, whose characters will be mapped to None in the result.");
static PyObject* @staticmethod
str.maketrans as unicode_maketrans
x: object
y: unicode=NULL
z: unicode=NULL
/
Return a translation table usable for str.translate().
If there is only one argument, it must be a dictionary mapping Unicode
ordinals (integers) or characters to Unicode ordinals, strings or None.
Character keys will be then converted to ordinals.
If there are two arguments, they must be strings of equal length, and
in the resulting dictionary, each character in x will be mapped to the
character at the same position in y. If there is a third argument, it
must be a string, whose characters will be mapped to None in the result.
[clinic]*/
PyDoc_STRVAR(unicode_maketrans__doc__,
"Return a translation table usable for str.translate().\n"
"\n"
"str.maketrans(x, y=None, z=None)\n"
"\n"
"If there is only one argument, it must be a dictionary mapping Unicode\n"
"ordinals (integers) or characters to Unicode ordinals, strings or None.\n"
"Character keys will be then converted to ordinals.\n"
"If there are two arguments, they must be strings of equal length, and\n"
"in the resulting dictionary, each character in x will be mapped to the\n"
"character at the same position in y. If there is a third argument, it\n"
"must be a string, whose characters will be mapped to None in the result.");
#define UNICODE_MAKETRANS_METHODDEF \
{"maketrans", (PyCFunction)unicode_maketrans, METH_VARARGS|METH_STATIC, unicode_maketrans__doc__},
static PyObject *
unicode_maketrans_impl(PyObject *x, PyObject *y, PyObject *z);
static PyObject *
unicode_maketrans(PyObject *null, PyObject *args) unicode_maketrans(PyObject *null, PyObject *args)
{ {
PyObject *x, *y = NULL, *z = NULL; PyObject *return_value = NULL;
PyObject *x;
PyObject *y = NULL;
PyObject *z = NULL;
if (!PyArg_ParseTuple(args,
"O|UU:maketrans",
&x, &y, &z))
goto exit;
return_value = unicode_maketrans_impl(x, y, z);
exit:
return return_value;
}
static PyObject *
unicode_maketrans_impl(PyObject *x, PyObject *y, PyObject *z)
/*[clinic checksum: 137db9c3199e7906b7967009f511c24fa3235b5f]*/
{
PyObject *new = NULL, *key, *value; PyObject *new = NULL, *key, *value;
Py_ssize_t i = 0; Py_ssize_t i = 0;
int res; int res;
if (!PyArg_ParseTuple(args, "O|UU:maketrans", &x, &y, &z))
return NULL;
new = PyDict_New(); new = PyDict_New();
if (!new) if (!new)
return NULL; return NULL;
@ -13317,8 +13365,7 @@ static PyMethodDef unicode_methods[] = {
{"format", (PyCFunction) do_string_format, METH_VARARGS | METH_KEYWORDS, format__doc__}, {"format", (PyCFunction) do_string_format, METH_VARARGS | METH_KEYWORDS, format__doc__},
{"format_map", (PyCFunction) do_string_format_map, METH_O, format_map__doc__}, {"format_map", (PyCFunction) do_string_format_map, METH_O, format_map__doc__},
{"__format__", (PyCFunction) unicode__format__, METH_VARARGS, p_format__doc__}, {"__format__", (PyCFunction) unicode__format__, METH_VARARGS, p_format__doc__},
{"maketrans", (PyCFunction) unicode_maketrans, UNICODE_MAKETRANS_METHODDEF
METH_VARARGS | METH_STATIC, maketrans__doc__},
{"__sizeof__", (PyCFunction) unicode__sizeof__, METH_NOARGS, sizeof__doc__}, {"__sizeof__", (PyCFunction) unicode__sizeof__, METH_NOARGS, sizeof__doc__},
#if 0 #if 0
/* These methods are just used for debugging the implementation. */ /* These methods are just used for debugging the implementation. */

2477
Tools/clinic/clinic.py Executable file

File diff suppressed because it is too large Load Diff

699
Tools/clinic/clinic_test.py Normal file
View File

@ -0,0 +1,699 @@
#!/usr/bin/env python3
#
# Argument Clinic
# Copyright 2012-2013 by Larry Hastings.
# Licensed to the PSF under a contributor agreement.
#
import builtins
import clinic
from clinic import DSLParser
import collections
import inspect
from test import support
import unittest
from unittest import TestCase
class FakeConverter:
def __init__(self, name, args):
self.name = name
self.args = args
class FakeConverterFactory:
def __init__(self, name):
self.name = name
def __call__(self, name, default, **kwargs):
return FakeConverter(self.name, kwargs)
class FakeConvertersDict:
def __init__(self):
self.used_converters = {}
def get(self, name, default):
return self.used_converters.setdefault(name, FakeConverterFactory(name))
class FakeClinic:
def __init__(self):
self.converters = FakeConvertersDict()
self.legacy_converters = FakeConvertersDict()
self.language = clinic.CLanguage()
self.filename = None
self.block_parser = clinic.BlockParser('', self.language)
self.modules = collections.OrderedDict()
clinic.clinic = self
self.name = "FakeClinic"
def is_directive(self, name):
return name == "module"
def directive(self, name, args):
self.called_directives[name] = args
_module_and_class = clinic.Clinic._module_and_class
class ClinicGroupPermuterTest(TestCase):
def _test(self, l, m, r, output):
computed = clinic.permute_optional_groups(l, m, r)
self.assertEqual(output, computed)
def test_range(self):
self._test([['start']], ['stop'], [['step']],
(
('stop',),
('start', 'stop',),
('start', 'stop', 'step',),
))
def test_add_window(self):
self._test([['x', 'y']], ['ch'], [['attr']],
(
('ch',),
('ch', 'attr'),
('x', 'y', 'ch',),
('x', 'y', 'ch', 'attr'),
))
def test_ludicrous(self):
self._test([['a1', 'a2', 'a3'], ['b1', 'b2']], ['c1'], [['d1', 'd2'], ['e1', 'e2', 'e3']],
(
('c1',),
('b1', 'b2', 'c1'),
('b1', 'b2', 'c1', 'd1', 'd2'),
('a1', 'a2', 'a3', 'b1', 'b2', 'c1'),
('a1', 'a2', 'a3', 'b1', 'b2', 'c1', 'd1', 'd2'),
('a1', 'a2', 'a3', 'b1', 'b2', 'c1', 'd1', 'd2', 'e1', 'e2', 'e3'),
))
def test_right_only(self):
self._test([], [], [['a'],['b'],['c']],
(
(),
('a',),
('a', 'b'),
('a', 'b', 'c')
))
def test_have_left_options_but_required_is_empty(self):
def fn():
clinic.permute_optional_groups(['a'], [], [])
self.assertRaises(AssertionError, fn)
class ClinicLinearFormatTest(TestCase):
def _test(self, input, output, **kwargs):
computed = clinic.linear_format(input, **kwargs)
self.assertEqual(output, computed)
def test_empty_strings(self):
self._test('', '')
def test_solo_newline(self):
self._test('\n', '\n')
def test_no_substitution(self):
self._test("""
abc
""", """
abc
""")
def test_empty_substitution(self):
self._test("""
abc
{name}
def
""", """
abc
def
""", name='')
def test_single_line_substitution(self):
self._test("""
abc
{name}
def
""", """
abc
GARGLE
def
""", name='GARGLE')
def test_multiline_substitution(self):
self._test("""
abc
{name}
def
""", """
abc
bingle
bungle
def
""", name='bingle\nbungle\n')
class InertParser:
def __init__(self, clinic):
pass
def parse(self, block):
pass
class CopyParser:
def __init__(self, clinic):
pass
def parse(self, block):
block.output = block.input
class ClinicBlockParserTest(TestCase):
def _test(self, input, output):
language = clinic.CLanguage()
blocks = list(clinic.BlockParser(input, language))
writer = clinic.BlockPrinter(language)
for block in blocks:
writer.print_block(block)
output = writer.f.getvalue()
assert output == input, "output != input!\n\noutput " + repr(output) + "\n\n input " + repr(input)
def round_trip(self, input):
return self._test(input, input)
def test_round_trip_1(self):
self.round_trip("""
verbatim text here
lah dee dah
""")
def test_round_trip_2(self):
self.round_trip("""
verbatim text here
lah dee dah
/*[inert]
abc
[inert]*/
def
/*[inert checksum: 7b18d017f89f61cf17d47f92749ea6930a3f1deb]*/
xyz
""")
def _test_clinic(self, input, output):
language = clinic.CLanguage()
c = clinic.Clinic(language)
c.parsers['inert'] = InertParser(c)
c.parsers['copy'] = CopyParser(c)
computed = c.parse(input)
self.assertEqual(output, computed)
def test_clinic_1(self):
self._test_clinic("""
verbatim text here
lah dee dah
/*[copy]
def
[copy]*/
abc
/*[copy checksum: 03cfd743661f07975fa2f1220c5194cbaff48451]*/
xyz
""", """
verbatim text here
lah dee dah
/*[copy]
def
[copy]*/
def
/*[copy checksum: 7b18d017f89f61cf17d47f92749ea6930a3f1deb]*/
xyz
""")
class ClinicParserTest(TestCase):
def test_trivial(self):
parser = DSLParser(FakeClinic())
block = clinic.Block("module os\nos.access")
parser.parse(block)
module, function = block.signatures
self.assertEqual("access", function.name)
self.assertEqual("os", module.name)
def test_ignore_line(self):
block = self.parse("#\nmodule os\nos.access")
module, function = block.signatures
self.assertEqual("access", function.name)
self.assertEqual("os", module.name)
def test_param(self):
function = self.parse_function("module os\nos.access\n path: int")
self.assertEqual("access", function.name)
self.assertEqual(1, len(function.parameters))
p = function.parameters['path']
self.assertEqual('path', p.name)
self.assertIsInstance(p.converter, clinic.int_converter)
def test_param_default(self):
function = self.parse_function("module os\nos.access\n follow_symlinks: bool = True")
p = function.parameters['follow_symlinks']
self.assertEqual(True, p.default)
def test_param_no_docstring(self):
function = self.parse_function("""
module os
os.access
follow_symlinks: bool = True
something_else: str""")
p = function.parameters['follow_symlinks']
self.assertEqual(2, len(function.parameters))
self.assertIsInstance(function.parameters['something_else'].converter, clinic.str_converter)
def disabled_test_converter_arguments(self):
function = self.parse_function("module os\nos.access\n path: path_t(allow_fd=1)")
p = function.parameters['path']
self.assertEqual(1, p.converter.args['allow_fd'])
def test_param_docstring(self):
function = self.parse_function("""
module os
os.stat as os_stat_fn -> object(doc_default='stat_result')
path: str
Path to be examined""")
p = function.parameters['path']
self.assertEqual("Path to be examined", p.docstring)
self.assertEqual(function.return_converter.doc_default, 'stat_result')
def test_function_docstring(self):
function = self.parse_function("""
module os
os.stat as os_stat_fn
path: str
Path to be examined
Perform a stat system call on the given path.""")
self.assertEqual("""
Perform a stat system call on the given path.
os.stat(path)
path
Path to be examined
""".strip(), function.docstring)
def test_explicit_parameters_in_docstring(self):
function = self.parse_function("""
module foo
foo.bar
x: int
Documentation for x.
y: int
This is the documentation for foo.
Okay, we're done here.
""")
self.assertEqual("""
This is the documentation for foo.
foo.bar(x, y)
x
Documentation for x.
Okay, we're done here.
""".strip(), function.docstring)
def test_parser_regression_special_character_in_parameter_column_of_docstring_first_line(self):
function = self.parse_function("""
module os
os.stat
path: str
This/used to break Clinic!
""")
self.assertEqual("os.stat(path)\n\nThis/used to break Clinic!", function.docstring)
def test_c_name(self):
function = self.parse_function("module os\nos.stat as os_stat_fn")
self.assertEqual("os_stat_fn", function.c_basename)
def test_return_converter(self):
function = self.parse_function("module os\nos.stat -> int")
self.assertIsInstance(function.return_converter, clinic.int_return_converter)
def test_star(self):
function = self.parse_function("module os\nos.access\n *\n follow_symlinks: bool = True")
p = function.parameters['follow_symlinks']
self.assertEqual(inspect.Parameter.KEYWORD_ONLY, p.kind)
self.assertEqual(0, p.group)
def test_group(self):
function = self.parse_function("module window\nwindow.border\n [\n ls : int\n ]\n /\n")
p = function.parameters['ls']
self.assertEqual(1, p.group)
def test_left_group(self):
function = self.parse_function("""
module curses
curses.window.addch
[
y: int
Y-coordinate.
x: int
X-coordinate.
]
ch: char
Character to add.
[
attr: long
Attributes for the character.
]
/
""")
for name, group in (
('y', -1), ('x', -1),
('ch', 0),
('attr', 1),
):
p = function.parameters[name]
self.assertEqual(p.group, group)
self.assertEqual(p.kind, inspect.Parameter.POSITIONAL_ONLY)
self.assertEqual(function.docstring.strip(), """
curses.window.addch([y, x,] ch, [attr])
y
Y-coordinate.
x
X-coordinate.
ch
Character to add.
attr
Attributes for the character.
""".strip())
def test_nested_groups(self):
function = self.parse_function("""
module curses
curses.window.imaginary
[
[
y1: int
Y-coordinate.
y2: int
Y-coordinate.
]
x1: int
X-coordinate.
x2: int
X-coordinate.
]
ch: char
Character to add.
[
attr1: long
Attributes for the character.
attr2: long
Attributes for the character.
attr3: long
Attributes for the character.
[
attr4: long
Attributes for the character.
attr5: long
Attributes for the character.
attr6: long
Attributes for the character.
]
]
/
""")
for name, group in (
('y1', -2), ('y2', -2),
('x1', -1), ('x2', -1),
('ch', 0),
('attr1', 1), ('attr2', 1), ('attr3', 1),
('attr4', 2), ('attr5', 2), ('attr6', 2),
):
p = function.parameters[name]
self.assertEqual(p.group, group)
self.assertEqual(p.kind, inspect.Parameter.POSITIONAL_ONLY)
self.assertEqual(function.docstring.strip(), """
curses.window.imaginary([[y1, y2,] x1, x2,] ch, [attr1, attr2, attr3, [attr4, attr5, attr6]])
y1
Y-coordinate.
y2
Y-coordinate.
x1
X-coordinate.
x2
X-coordinate.
ch
Character to add.
attr1
Attributes for the character.
attr2
Attributes for the character.
attr3
Attributes for the character.
attr4
Attributes for the character.
attr5
Attributes for the character.
attr6
Attributes for the character.
""".strip())
def parse_function_should_fail(self, s):
with support.captured_stdout() as stdout:
with self.assertRaises(SystemExit):
self.parse_function(s)
return stdout.getvalue()
def test_disallowed_grouping__two_top_groups_on_left(self):
s = self.parse_function_should_fail("""
module foo
foo.two_top_groups_on_left
[
group1 : int
]
[
group2 : int
]
param: int
""")
self.assertEqual(s,
('Error on line 0:\n'
'Function two_top_groups_on_left has an unsupported group configuration. (Unexpected state 2)\n'))
def test_disallowed_grouping__two_top_groups_on_right(self):
self.parse_function_should_fail("""
module foo
foo.two_top_groups_on_right
param: int
[
group1 : int
]
[
group2 : int
]
""")
def test_disallowed_grouping__parameter_after_group_on_right(self):
self.parse_function_should_fail("""
module foo
foo.parameter_after_group_on_right
param: int
[
[
group1 : int
]
group2 : int
]
""")
def test_disallowed_grouping__group_after_parameter_on_left(self):
self.parse_function_should_fail("""
module foo
foo.group_after_parameter_on_left
[
group2 : int
[
group1 : int
]
]
param: int
""")
def test_disallowed_grouping__empty_group_on_left(self):
self.parse_function_should_fail("""
module foo
foo.empty_group
[
[
]
group2 : int
]
param: int
""")
def test_disallowed_grouping__empty_group_on_right(self):
self.parse_function_should_fail("""
module foo
foo.empty_group
param: int
[
[
]
group2 : int
]
""")
def test_no_parameters(self):
function = self.parse_function("""
module foo
foo.bar
Docstring
""")
self.assertEqual("Docstring\n\nfoo.bar()", function.docstring)
self.assertEqual(0, len(function.parameters))
def test_single_star(self):
self.parse_function_should_fail("""
module foo
foo.bar
*
*
""")
def test_parameters_required_after_star_without_initial_parameters_or_docstring(self):
self.parse_function_should_fail("""
module foo
foo.bar
*
""")
def test_parameters_required_after_star_without_initial_parameters_with_docstring(self):
self.parse_function_should_fail("""
module foo
foo.bar
*
Docstring here.
""")
def test_parameters_required_after_star_with_initial_parameters_without_docstring(self):
self.parse_function_should_fail("""
module foo
foo.bar
this: int
*
""")
def test_parameters_required_after_star_with_initial_parameters_and_docstring(self):
self.parse_function_should_fail("""
module foo
foo.bar
this: int
*
Docstring.
""")
def test_single_slash(self):
self.parse_function_should_fail("""
module foo
foo.bar
/
/
""")
def test_mix_star_and_slash(self):
self.parse_function_should_fail("""
module foo
foo.bar
x: int
y: int
*
z: int
/
""")
def test_parameters_not_permitted_after_slash_for_now(self):
self.parse_function_should_fail("""
module foo
foo.bar
/
x: int
""")
def test_function_not_at_column_0(self):
function = self.parse_function("""
module foo
foo.bar
x: int
Nested docstring here, goeth.
*
y: str
Not at column 0!
""")
self.assertEqual("""
Not at column 0!
foo.bar(x, *, y)
x
Nested docstring here, goeth.
""".strip(), function.docstring)
def test_parser_regression_special_character_in_parameter_column_of_docstring_first_line(self):
function = self.parse_function("""
module os
os.stat
path: str
This/used to break Clinic!
""")
self.assertEqual("This/used to break Clinic!\n\nos.stat(path)", function.docstring)
def test_directive(self):
c = FakeClinic()
parser = DSLParser(c)
parser.flag = False
parser.directives['setflag'] = lambda : setattr(parser, 'flag', True)
block = clinic.Block("setflag")
parser.parse(block)
self.assertTrue(parser.flag)
def test_legacy_converters(self):
block = self.parse('module os\nos.access\n path: "s"')
module, function = block.signatures
self.assertIsInstance((function.parameters['path']).converter, clinic.str_converter)
def parse(self, text):
c = FakeClinic()
parser = DSLParser(c)
block = clinic.Block(text)
parser.parse(block)
return block
def parse_function(self, text):
block = self.parse(text)
s = block.signatures
assert len(s) == 2
assert isinstance(s[0], clinic.Module)
assert isinstance(s[1], clinic.Function)
return s[1]
def test_scaffolding(self):
# test repr on special values
self.assertEqual(repr(clinic.unspecified), '<Unspecified>')
self.assertEqual(repr(clinic.NULL), '<Null>')
# test that fail fails
with support.captured_stdout() as stdout:
with self.assertRaises(SystemExit):
clinic.fail('The igloos are melting!', filename='clown.txt', line_number=69)
self.assertEqual(stdout.getvalue(), 'Error in file "clown.txt" on line 69:\nThe igloos are melting!\n')
if __name__ == "__main__":
unittest.main()