Skip to content

gh-101714: Isolate _curses extension module #104732

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 15 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 23 additions & 4 deletions Include/py_curses.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,15 @@
extern "C" {
#endif

#define PyCurses_API_pointers 4
#define PyCurses_API_pointers 5

/* Type declarations */

typedef struct {
PyObject_HEAD
WINDOW *win;
char *encoding;
struct _curses_state *state;
} PyCursesWindowObject;

#define PyCursesWindow_Check(v) Py_IS_TYPE((v), &PyCursesWindow_Type)
Expand All @@ -78,9 +79,27 @@ typedef struct {
static void **PyCurses_API;

#define PyCursesWindow_Type (*_PyType_CAST(PyCurses_API[0]))
#define PyCursesSetupTermCalled {if (! ((int (*)(void))PyCurses_API[1]) () ) return NULL;}
#define PyCursesInitialised {if (! ((int (*)(void))PyCurses_API[2]) () ) return NULL;}
#define PyCursesInitialisedColor {if (! ((int (*)(void))PyCurses_API[3]) () ) return NULL;}

#define PyCursesSetupTermCalled \
if (!(*(int *)PyCurses_API[1])) { \
PyErr_SetString(PyCurses_API[4], \
"must call (at least) setupterm() first"); \
return NULL; \
}

#define PyCursesInitialised \
if (!(*(int *)PyCurses_API[2])) { \
PyErr_SetString(PyCurses_API[4], \
"must call initscr() first"); \
return NULL; \
}

#define PyCursesInitialisedColor \
if (!(*(int *)PyCurses_API[3])) { \
PyErr_SetString(PyCurses_API[4], \
"must call start_color() first"); \
return NULL; \
}

#define import_curses() \
PyCurses_API = (void **)PyCapsule_Import(PyCurses_CAPSULE_NAME, 1);
Expand Down
14 changes: 14 additions & 0 deletions Lib/test/test_curses.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import functools
import inspect
import os
import pickle
import string
import sys
import tempfile
Expand Down Expand Up @@ -1179,6 +1180,19 @@ def test_issue13051(self):
# this may cause infinite recursion, leading to a RuntimeError
box._insert_printable_char('a')

def test_window_non_instantiable(self):
# Ensure that '_curses.window' type in not directly instantiable
check_disallow_instantiation(self, curses.window)

def test_window_immutable(self):
with self.assertRaisesRegex(TypeError, "immutable"):
curses.window.foo = 123

def test_window_non_picklable(self):
win = curses.newwin(10, 10)
for proto in range(pickle.HIGHEST_PROTOCOL + 1):
self.assertRaises(TypeError, pickle.dumps, win, proto)


class MiscTests(unittest.TestCase):

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Isolate :mod:`!_curses` (apply :pep:`687`). Patch by Radislav Chugunov.
Loading