Skip to content
Merged
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
11 changes: 11 additions & 0 deletions Doc/library/curses.rst
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,17 @@ The module :mod:`!curses` defines the following functions:
character-at-a-time line editing without touching the rest of the screen.


.. function:: nofilter()

Undo the effect of a previous :func:`.filter` call.
Like :func:`.filter`, it must be called before :func:`initscr` so that the
next initialization uses the full screen again.

Availability: if the underlying curses library provides ``nofilter()``.

.. versionadded:: next


.. function:: flash()

Flash the screen. That is, change it to reverse-video and then change it back
Expand Down
6 changes: 6 additions & 0 deletions Doc/whatsnew/3.16.rst
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,12 @@ New modules
Improved modules
================

curses
------

* Add :func:`curses.nofilter`, which undoes the effect of :func:`curses.filter`.
(Contributed by Serhiy Storchaka in :gh:`151744`.)

gzip
----

Expand Down
3 changes: 2 additions & 1 deletion Lib/test/test_curses.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,9 @@ def setUp(self):
@requires_curses_func('filter')
def test_filter(self):
# TODO: Should be called before initscr() or newterm() are called.
# TODO: nofilter()
curses.filter()
if hasattr(curses, 'nofilter'):
curses.nofilter()

@requires_curses_func('use_env')
def test_use_env(self):
Expand Down
33 changes: 33 additions & 0 deletions Lib/test/test_tkinter/test_font.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ def setUpClass(cls):
cls.font = font.Font(root=cls.root, name=fontname, exists=False)

def test_configure(self):
self.assertEqual(self.font.config, self.font.configure)
options = self.font.configure()
self.assertGreaterEqual(set(options),
{'family', 'size', 'weight', 'slant', 'underline', 'overstrike'})
Expand All @@ -36,6 +37,26 @@ def test_configure(self):
self.assertIsInstance(options[key], sizetype)
self.assertIsInstance(self.font.cget(key), sizetype)
self.assertIsInstance(self.font[key], sizetype)
self.assertRaisesRegex(tkinter.TclError, 'bad option "-spam"',
self.font.cget, 'spam')
self.assertRaisesRegex(tkinter.TclError, 'bad option "-spam"',
self.font.configure, spam='x')
self.assertRaises(TypeError, self.font.cget)
self.assertRaises(TypeError, self.font.cget, 'size', 'weight')

def test_copy(self):
f = font.Font(root=self.root, family='Times', size=10, weight='bold')
copied = f.copy()
self.assertIsInstance(copied, font.Font)
self.assertIsNot(copied, f)
self.assertNotEqual(copied.name, f.name)
self.assertEqual(copied.actual(), f.actual())
# The copy is independent of the original.
sizetype = int if self.wantobjects else str
copied.configure(size=20)
self.assertEqual(f.cget('size'), sizetype(10))
self.assertEqual(copied.cget('size'), sizetype(20))
self.assertRaises(TypeError, f.copy, 'x')

def test_unicode_family(self):
family = 'MS \u30b4\u30b7\u30c3\u30af'
Expand All @@ -60,6 +81,9 @@ def test_actual(self):
for key in 'size', 'underline', 'overstrike':
self.assertIsInstance(options[key], sizetype)
self.assertIsInstance(self.font.actual(key), sizetype)
self.assertRaisesRegex(tkinter.TclError, 'bad option "-spam"',
self.font.actual, 'spam')
self.assertRaises(TypeError, self.font.actual, 'size', 'weight', 'slant')

def test_name(self):
self.assertEqual(self.font.name, fontname)
Expand All @@ -83,15 +107,24 @@ def test_equality(self):

def test_measure(self):
self.assertIsInstance(self.font.measure('abc'), int)
self.assertEqual(self.font.measure(''), 0)
self.assertIsInstance(
self.font.measure('abc', displayof=self.root), int)
self.assertRaises(TypeError, self.font.measure)
self.assertRaises(TypeError, self.font.measure, 'a', 'b', 'c')

def test_metrics(self):
metrics = self.font.metrics()
self.assertGreaterEqual(set(metrics),
{'ascent', 'descent', 'linespace', 'fixed'})
for key in metrics:
self.assertEqual(self.font.metrics(key), metrics[key])
self.assertEqual(self.font.metrics(key, displayof=self.root),
metrics[key])
self.assertIsInstance(metrics[key], int)
self.assertIsInstance(self.font.metrics(key), int)
self.assertRaisesRegex(tkinter.TclError, 'bad metric "-spam"',
self.font.metrics, 'spam')

def test_families(self):
families = font.families(self.root)
Expand Down
13 changes: 13 additions & 0 deletions Lib/test/test_tkinter/test_images.py
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,11 @@ def test_configure_width_height(self):
image.configure(height=10)
self.assertEqual(image['width'], '20')
self.assertEqual(image['height'], '10')
self.assertEqual(image.cget('width'), image['width'])
self.assertEqual(image.cget('height'), image['height'])
self.assertRaises(TypeError, image.cget)
self.assertRaises(TypeError, image.cget, 'width', 'height')
self.assertEqual(image.config, image.configure)
self.assertEqual(image.width(), 20)
self.assertEqual(image.height(), 10)

Expand Down Expand Up @@ -656,6 +661,14 @@ def test_transparency(self):
self.assertEqual(image.transparency_get(4, 6), True)
image.transparency_set(4, 6, False)
self.assertEqual(image.transparency_get(4, 6), False)
self.assertRaises(tkinter.TclError, image.transparency_get, -1, 0)
self.assertRaises(tkinter.TclError, image.transparency_get, 16, 0)
self.assertRaises(tkinter.TclError, image.transparency_set, -1, 0, True)
self.assertRaises(tkinter.TclError, image.transparency_set, 16, 0, True)
self.assertRaises(TypeError, image.transparency_get, 0)
self.assertRaises(TypeError, image.transparency_get, 0, 0, 0)
self.assertRaises(TypeError, image.transparency_set, 0, 0)
self.assertRaises(TypeError, image.transparency_set, 0, 0, True, 0)


if __name__ == "__main__":
Expand Down
53 changes: 53 additions & 0 deletions Lib/test/test_tkinter/test_misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from tkinter import TclError
import enum
from test import support
from test.support import os_helper
from test.test_tkinter.support import setUpModule # noqa: F401
from test.test_tkinter.support import (AbstractTkTest, AbstractDefaultRootTest,
requires_tk, get_tk_patchlevel)
Expand Down Expand Up @@ -357,6 +358,19 @@ def test_option(self):
self.root.option_clear()
self.assertEqual(b.option_get('background', 'Background'), '')

def test_option_readfile(self):
self.addCleanup(self.root.option_clear)
self.addCleanup(os_helper.unlink, os_helper.TESTFN)
with open(os_helper.TESTFN, 'w') as f:
f.write('*Button.background: red\n')
self.root.option_readfile(os_helper.TESTFN)
b = tkinter.Button(self.root)
self.assertEqual(b.option_get('background', 'Background'), 'red')
self.assertRaises(TclError, self.root.option_readfile,
os_helper.TESTFN + '.nonexistent')
self.assertRaises(TypeError, self.root.option_readfile)
self.assertRaises(TypeError, self.root.option_readfile, 'a', 'b', 'c')

def test_nametowidget(self):
b = tkinter.Button(self.root, name='btn')
self.assertIs(self.root.nametowidget('btn'), b)
Expand Down Expand Up @@ -417,6 +431,38 @@ def test_bell(self):
self.root.bell() # No exception.
self.root.bell(displayof=self.root)

def test_tk_focusNext_focusPrev(self):
f = tkinter.Frame(self.root)
f.pack()
entries = [tkinter.Entry(f) for _ in range(3)]
for entry in entries:
entry.pack()
# tk_focusNext skips widgets that are not viewable.
entries[-1].wait_visibility()
self.assertIs(entries[0].tk_focusNext(), entries[1])
self.assertIs(entries[1].tk_focusNext(), entries[2])
self.assertIs(entries[2].tk_focusPrev(), entries[1])
self.assertIs(entries[1].tk_focusPrev(), entries[0])
self.assertRaises(TypeError, entries[0].tk_focusNext, 'x')
self.assertRaises(TypeError, entries[0].tk_focusPrev, 'x')

def test_tk_strictMotif(self):
self.addCleanup(self.root.tk_strictMotif, False)
self.assertIs(self.root.tk_strictMotif(), False)
self.assertIs(self.root.tk_strictMotif(True), True)
self.assertIs(self.root.tk_strictMotif(), True)
self.assertIs(self.root.tk_strictMotif(False), False)
self.assertRaises(TypeError, self.root.tk_strictMotif, 1, 2)

def test_tk_bisque(self):
# tk_bisque resets the color palette; use a separate root so that
# the shared one is not affected.
root = tkinter.Tk()
self.addCleanup(root.destroy)
root.tk_bisque()
self.assertEqual(root['background'], '#ffe4c4')
self.assertRaises(TypeError, root.tk_bisque, 'x')

def test_event_repr_defaults(self):
e = tkinter.Event()
e.serial = 12345
Expand Down Expand Up @@ -819,6 +865,13 @@ def test_wm_iconbitmap(self):

t.destroy()

def test_wm_iconphoto(self):
t = tkinter.Toplevel(self.root)
img = tkinter.PhotoImage(master=t, width=16, height=16)
t.wm_iconphoto(False, img) # No exception.
t.wm_iconphoto(True, img)
self.assertRaises(tkinter.TclError, t.wm_iconphoto, False, 'spam')

def test_wm_title(self):
t = tkinter.Toplevel(self.root)
t.title('Hello')
Expand Down
3 changes: 3 additions & 0 deletions Lib/test/test_tkinter/test_variables.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,13 +111,15 @@ def test_initialize(self):
self.assertFalse(v.side_effect)
v.set("value")
self.assertTrue(v.side_effect)
self.assertEqual(Variable.initialize, Variable.set)

def test_trace_old(self):
if tcl_version >= (9, 0):
self.skipTest('requires Tcl version < 9.0')
# Old interface
v = Variable(self.root)
vname = str(v)
self.assertEqual(v.trace, v.trace_variable)
trace = []
def read_tracer(*args):
trace.append(('read',) + args)
Expand Down Expand Up @@ -328,6 +330,7 @@ def test_set(self):
self.assertEqual(self.root.globalgetvar("name"), false)
v.set("on")
self.assertEqual(self.root.globalgetvar("name"), true)
self.assertEqual(BooleanVar.initialize, BooleanVar.set)

def test_invalid_value_domain(self):
false = 0 if self.root.wantobjects() else "0"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add :func:`curses.nofilter`, which undoes the effect of :func:`curses.filter`.
23 changes: 23 additions & 0 deletions Modules/_cursesmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -3220,6 +3220,28 @@ _curses_filter_impl(PyObject *module)
}
#endif

#ifdef HAVE_CURSES_NOFILTER
/*[clinic input]
_curses.nofilter

Undo the effect of a preceding filter() call.

Must be called before initscr(). It restores the normal behaviour
disabled by filter(), so that the next initscr() uses the full screen
rather than a single line.
[clinic start generated code]*/

static PyObject *
_curses_nofilter_impl(PyObject *module)
/*[clinic end generated code: output=d95ca4d48a6bdbdf input=58aea83b1a5c969f]*/
{
/* not checking for PyCursesInitialised here since nofilter() must
be called before initscr() */
nofilter();
Py_RETURN_NONE;
}
#endif

/*[clinic input]
_curses.baudrate

Expand Down Expand Up @@ -5321,6 +5343,7 @@ static PyMethodDef cursesmodule_methods[] = {
_CURSES_ENDWIN_METHODDEF
_CURSES_ERASECHAR_METHODDEF
_CURSES_FILTER_METHODDEF
_CURSES_NOFILTER_METHODDEF
_CURSES_FLASH_METHODDEF
_CURSES_FLUSHINP_METHODDEF
_CURSES_GETMOUSE_METHODDEF
Expand Down
32 changes: 31 additions & 1 deletion Modules/clinic/_cursesmodule.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

60 changes: 60 additions & 0 deletions configure

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading