Compare commits
4 Commits
f7f0ed59bc
...
e40e2a2cc9
Author | SHA1 | Date |
---|---|---|
Cheryl Sabella | e40e2a2cc9 | |
Serhiy Storchaka | 59f9b4e450 | |
Brandt Bucher | 27f9dafc2b | |
Brandt Bucher | cde988e893 |
|
@ -3,6 +3,9 @@ Released on 2021-10-04?
|
||||||
======================================
|
======================================
|
||||||
|
|
||||||
|
|
||||||
|
bpo-32631: Finish zzdummy example extension module: make menu entries
|
||||||
|
work; add docstrings and tests with 100% coverage.
|
||||||
|
|
||||||
bpo-42508: Keep IDLE running on macOS. Remove obsolete workaround
|
bpo-42508: Keep IDLE running on macOS. Remove obsolete workaround
|
||||||
that prevented running files with shortcuts when using new universal2
|
that prevented running files with shortcuts when using new universal2
|
||||||
installers built on macOS 11.
|
installers built on macOS 11.
|
||||||
|
|
|
@ -2316,7 +2316,15 @@ display when Code Context is turned on for an editor window.
|
||||||
|
|
||||||
Shell Preferences: Auto-Squeeze Min. Lines is the minimum number of lines
|
Shell Preferences: Auto-Squeeze Min. Lines is the minimum number of lines
|
||||||
of output to automatically "squeeze".
|
of output to automatically "squeeze".
|
||||||
'''
|
''',
|
||||||
|
'Extensions': '''
|
||||||
|
ZzDummy: This extension is provided as an example for how to create and
|
||||||
|
use an extension. Enable indicates whether the extension is active or
|
||||||
|
not; likewise enable_editor and enable_shell indicate which windows it
|
||||||
|
will be active on. For this extension, z-text is the text that will be
|
||||||
|
inserted at or removed from the beginning of the lines of selected text,
|
||||||
|
or the current line if no selection.
|
||||||
|
''',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -28,8 +28,8 @@ variables:
|
||||||
(There are a few more, but they are rarely useful.)
|
(There are a few more, but they are rarely useful.)
|
||||||
|
|
||||||
The extension class must not directly bind Window Manager (e.g. X) events.
|
The extension class must not directly bind Window Manager (e.g. X) events.
|
||||||
Rather, it must define one or more virtual events, e.g. <<zoom-height>>, and
|
Rather, it must define one or more virtual events, e.g. <<z-in>>, and
|
||||||
corresponding methods, e.g. zoom_height_event(). The virtual events will be
|
corresponding methods, e.g. z_in_event(). The virtual events will be
|
||||||
bound to the corresponding methods, and Window Manager events can then be bound
|
bound to the corresponding methods, and Window Manager events can then be bound
|
||||||
to the virtual events. (This indirection is done so that the key bindings can
|
to the virtual events. (This indirection is done so that the key bindings can
|
||||||
easily be changed, and so that other sources of virtual events can exist, such
|
easily be changed, and so that other sources of virtual events can exist, such
|
||||||
|
@ -54,21 +54,21 @@ Extensions are not required to define menu entries for all the events they
|
||||||
implement. (They are also not required to create keybindings, but in that
|
implement. (They are also not required to create keybindings, but in that
|
||||||
case there must be empty bindings in cofig-extensions.def)
|
case there must be empty bindings in cofig-extensions.def)
|
||||||
|
|
||||||
Here is a complete example:
|
Here is a partial example from zzdummy.py:
|
||||||
|
|
||||||
class ZoomHeight:
|
class ZzDummy:
|
||||||
|
|
||||||
menudefs = [
|
menudefs = [
|
||||||
('edit', [
|
('format', [
|
||||||
None, # Separator
|
('Z in', '<<z-in>>'),
|
||||||
('_Zoom Height', '<<zoom-height>>'),
|
('Z out', '<<z-out>>'),
|
||||||
])
|
] )
|
||||||
]
|
]
|
||||||
|
|
||||||
def __init__(self, editwin):
|
def __init__(self, editwin):
|
||||||
self.editwin = editwin
|
self.editwin = editwin
|
||||||
|
|
||||||
def zoom_height_event(self, event):
|
def z_in_event(self, event=None):
|
||||||
"...Do what you want here..."
|
"...Do what you want here..."
|
||||||
|
|
||||||
The final piece of the puzzle is the file "config-extensions.def", which is
|
The final piece of the puzzle is the file "config-extensions.def", which is
|
||||||
|
|
|
@ -0,0 +1,152 @@
|
||||||
|
"Test zzdummy, coverage 100%."
|
||||||
|
|
||||||
|
from idlelib import zzdummy
|
||||||
|
import unittest
|
||||||
|
from test.support import requires
|
||||||
|
from tkinter import Tk, Text
|
||||||
|
from unittest import mock
|
||||||
|
from idlelib import config
|
||||||
|
from idlelib import editor
|
||||||
|
from idlelib import format
|
||||||
|
|
||||||
|
|
||||||
|
usercfg = zzdummy.idleConf.userCfg
|
||||||
|
testcfg = {
|
||||||
|
'main': config.IdleUserConfParser(''),
|
||||||
|
'highlight': config.IdleUserConfParser(''),
|
||||||
|
'keys': config.IdleUserConfParser(''),
|
||||||
|
'extensions': config.IdleUserConfParser(''),
|
||||||
|
}
|
||||||
|
code_sample = """\
|
||||||
|
|
||||||
|
class C1():
|
||||||
|
# Class comment.
|
||||||
|
def __init__(self, a, b):
|
||||||
|
self.a = a
|
||||||
|
self.b = b
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class DummyEditwin:
|
||||||
|
get_selection_indices = editor.EditorWindow.get_selection_indices
|
||||||
|
def __init__(self, root, text):
|
||||||
|
self.root = root
|
||||||
|
self.top = root
|
||||||
|
self.text = text
|
||||||
|
self.fregion = format.FormatRegion(self)
|
||||||
|
self.text.undo_block_start = mock.Mock()
|
||||||
|
self.text.undo_block_stop = mock.Mock()
|
||||||
|
|
||||||
|
|
||||||
|
class ZZDummyTest(unittest.TestCase):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
requires('gui')
|
||||||
|
root = cls.root = Tk()
|
||||||
|
root.withdraw()
|
||||||
|
text = cls.text = Text(cls.root)
|
||||||
|
cls.editor = DummyEditwin(root, text)
|
||||||
|
zzdummy.idleConf.userCfg = testcfg
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def tearDownClass(cls):
|
||||||
|
zzdummy.idleConf.userCfg = usercfg
|
||||||
|
del cls.editor, cls.text
|
||||||
|
cls.root.update_idletasks()
|
||||||
|
for id in cls.root.tk.call('after', 'info'):
|
||||||
|
cls.root.after_cancel(id) # Need for EditorWindow.
|
||||||
|
cls.root.destroy()
|
||||||
|
del cls.root
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
text = self.text
|
||||||
|
text.insert('1.0', code_sample)
|
||||||
|
text.undo_block_start.reset_mock()
|
||||||
|
text.undo_block_stop.reset_mock()
|
||||||
|
zz = self.zz = zzdummy.ZzDummy(self.editor)
|
||||||
|
zzdummy.ZzDummy.ztext = '# ignore #'
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
self.text.delete('1.0', 'end')
|
||||||
|
del self.zz
|
||||||
|
|
||||||
|
def checklines(self, text, value):
|
||||||
|
# Verify that there are lines being checked.
|
||||||
|
end_line = int(float(text.index('end')))
|
||||||
|
|
||||||
|
# Check each line for the starting text.
|
||||||
|
actual = []
|
||||||
|
for line in range(1, end_line):
|
||||||
|
txt = text.get(f'{line}.0', f'{line}.end')
|
||||||
|
actual.append(txt.startswith(value))
|
||||||
|
return actual
|
||||||
|
|
||||||
|
def test_init(self):
|
||||||
|
zz = self.zz
|
||||||
|
self.assertEqual(zz.editwin, self.editor)
|
||||||
|
self.assertEqual(zz.text, self.editor.text)
|
||||||
|
|
||||||
|
def test_reload(self):
|
||||||
|
self.assertEqual(self.zz.ztext, '# ignore #')
|
||||||
|
testcfg['extensions'].SetOption('ZzDummy', 'z-text', 'spam')
|
||||||
|
zzdummy.ZzDummy.reload()
|
||||||
|
self.assertEqual(self.zz.ztext, 'spam')
|
||||||
|
|
||||||
|
def test_z_in_event(self):
|
||||||
|
eq = self.assertEqual
|
||||||
|
zz = self.zz
|
||||||
|
text = zz.text
|
||||||
|
eq(self.zz.ztext, '# ignore #')
|
||||||
|
|
||||||
|
# No lines have the leading text.
|
||||||
|
expected = [False, False, False, False, False, False, False]
|
||||||
|
actual = self.checklines(text, zz.ztext)
|
||||||
|
eq(expected, actual)
|
||||||
|
|
||||||
|
text.tag_add('sel', '2.0', '4.end')
|
||||||
|
eq(zz.z_in_event(), 'break')
|
||||||
|
expected = [False, True, True, True, False, False, False]
|
||||||
|
actual = self.checklines(text, zz.ztext)
|
||||||
|
eq(expected, actual)
|
||||||
|
|
||||||
|
text.undo_block_start.assert_called_once()
|
||||||
|
text.undo_block_stop.assert_called_once()
|
||||||
|
|
||||||
|
def test_z_out_event(self):
|
||||||
|
eq = self.assertEqual
|
||||||
|
zz = self.zz
|
||||||
|
text = zz.text
|
||||||
|
eq(self.zz.ztext, '# ignore #')
|
||||||
|
|
||||||
|
# Prepend text.
|
||||||
|
text.tag_add('sel', '2.0', '5.end')
|
||||||
|
zz.z_in_event()
|
||||||
|
text.undo_block_start.reset_mock()
|
||||||
|
text.undo_block_stop.reset_mock()
|
||||||
|
|
||||||
|
# Select a few lines to remove text.
|
||||||
|
text.tag_remove('sel', '1.0', 'end')
|
||||||
|
text.tag_add('sel', '3.0', '4.end')
|
||||||
|
eq(zz.z_out_event(), 'break')
|
||||||
|
expected = [False, True, False, False, True, False, False]
|
||||||
|
actual = self.checklines(text, zz.ztext)
|
||||||
|
eq(expected, actual)
|
||||||
|
|
||||||
|
text.undo_block_start.assert_called_once()
|
||||||
|
text.undo_block_stop.assert_called_once()
|
||||||
|
|
||||||
|
def test_roundtrip(self):
|
||||||
|
# Insert and remove to all code should give back original text.
|
||||||
|
zz = self.zz
|
||||||
|
text = zz.text
|
||||||
|
|
||||||
|
text.tag_add('sel', '1.0', 'end-1c')
|
||||||
|
zz.z_in_event()
|
||||||
|
zz.z_out_event()
|
||||||
|
|
||||||
|
self.assertEqual(text.get('1.0', 'end-1c'), code_sample)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main(verbosity=2)
|
|
@ -1,42 +1,73 @@
|
||||||
"Example extension, also used for testing."
|
"""Example extension, also used for testing.
|
||||||
|
|
||||||
|
See extend.txt for more details on creating an extension.
|
||||||
|
See config-extension.def for configuring an extension.
|
||||||
|
"""
|
||||||
|
|
||||||
from idlelib.config import idleConf
|
from idlelib.config import idleConf
|
||||||
|
from functools import wraps
|
||||||
|
|
||||||
ztext = idleConf.GetOption('extensions', 'ZzDummy', 'z-text')
|
|
||||||
|
def format_selection(format_line):
|
||||||
|
"Apply a formatting function to all of the selected lines."
|
||||||
|
|
||||||
|
@wraps(format_line)
|
||||||
|
def apply(self, event=None):
|
||||||
|
head, tail, chars, lines = self.formatter.get_region()
|
||||||
|
for pos in range(len(lines) - 1):
|
||||||
|
line = lines[pos]
|
||||||
|
lines[pos] = format_line(self, line)
|
||||||
|
self.formatter.set_region(head, tail, chars, lines)
|
||||||
|
return 'break'
|
||||||
|
|
||||||
|
return apply
|
||||||
|
|
||||||
|
|
||||||
class ZzDummy:
|
class ZzDummy:
|
||||||
|
"""Prepend or remove initial text from selected lines."""
|
||||||
|
|
||||||
## menudefs = [
|
# Extend the format menu.
|
||||||
## ('format', [
|
menudefs = [
|
||||||
## ('Z in', '<<z-in>>'),
|
('format', [
|
||||||
## ('Z out', '<<z-out>>'),
|
('Z in', '<<z-in>>'),
|
||||||
## ] )
|
('Z out', '<<z-out>>'),
|
||||||
## ]
|
] )
|
||||||
|
]
|
||||||
|
|
||||||
def __init__(self, editwin):
|
def __init__(self, editwin):
|
||||||
|
"Initialize the settings for this extension."
|
||||||
|
self.editwin = editwin
|
||||||
self.text = editwin.text
|
self.text = editwin.text
|
||||||
z_in = False
|
self.formatter = editwin.fregion
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def reload(cls):
|
def reload(cls):
|
||||||
|
"Load class variables from config."
|
||||||
cls.ztext = idleConf.GetOption('extensions', 'ZzDummy', 'z-text')
|
cls.ztext = idleConf.GetOption('extensions', 'ZzDummy', 'z-text')
|
||||||
|
|
||||||
def z_in_event(self, event):
|
@format_selection
|
||||||
"""
|
def z_in_event(self, line):
|
||||||
"""
|
"""Insert text at the beginning of each selected line.
|
||||||
text = self.text
|
|
||||||
text.undo_block_start()
|
This is bound to the <<z-in>> virtual event when the extensions
|
||||||
for line in range(1, text.index('end')):
|
are loaded.
|
||||||
text.insert('%d.0', ztext)
|
"""
|
||||||
text.undo_block_stop()
|
return f'{self.ztext}{line}'
|
||||||
return "break"
|
|
||||||
|
@format_selection
|
||||||
|
def z_out_event(self, line):
|
||||||
|
"""Remove specific text from the beginning of each selected line.
|
||||||
|
|
||||||
|
This is bound to the <<z-out>> virtual event when the extensions
|
||||||
|
are loaded.
|
||||||
|
"""
|
||||||
|
zlength = 0 if not line.startswith(self.ztext) else len(self.ztext)
|
||||||
|
return line[zlength:]
|
||||||
|
|
||||||
def z_out_event(self, event): pass
|
|
||||||
|
|
||||||
ZzDummy.reload()
|
ZzDummy.reload()
|
||||||
|
|
||||||
##if __name__ == "__main__":
|
|
||||||
## import unittest
|
if __name__ == "__main__":
|
||||||
## unittest.main('idlelib.idle_test.test_zzdummy',
|
import unittest
|
||||||
## verbosity=2, exit=False)
|
unittest.main('idlelib.idle_test.test_zzdummy', verbosity=2, exit=False)
|
||||||
|
|
|
@ -1621,48 +1621,6 @@ class BuiltinTest(unittest.TestCase):
|
||||||
self.assertEqual(self.iter_error(z1, ValueError), t)
|
self.assertEqual(self.iter_error(z1, ValueError), t)
|
||||||
self.assertEqual(self.iter_error(z2, ValueError), t)
|
self.assertEqual(self.iter_error(z2, ValueError), t)
|
||||||
|
|
||||||
def test_zip_pickle_stability(self):
|
|
||||||
# Pickles of zip((1, 2, 3), (4, 5, 6)) dumped from 3.9:
|
|
||||||
pickles = [
|
|
||||||
b'citertools\nizip\np0\n(c__builtin__\niter\np1\n((I1\nI2\nI3\ntp2\ntp3\nRp4\nI0\nbg1\n((I4\nI5\nI6\ntp5\ntp6\nRp7\nI0\nbtp8\nRp9\n.',
|
|
||||||
b'citertools\nizip\nq\x00(c__builtin__\niter\nq\x01((K\x01K\x02K\x03tq\x02tq\x03Rq\x04K\x00bh\x01((K\x04K\x05K\x06tq\x05tq\x06Rq\x07K\x00btq\x08Rq\t.',
|
|
||||||
b'\x80\x02citertools\nizip\nq\x00c__builtin__\niter\nq\x01K\x01K\x02K\x03\x87q\x02\x85q\x03Rq\x04K\x00bh\x01K\x04K\x05K\x06\x87q\x05\x85q\x06Rq\x07K\x00b\x86q\x08Rq\t.',
|
|
||||||
b'\x80\x03cbuiltins\nzip\nq\x00cbuiltins\niter\nq\x01K\x01K\x02K\x03\x87q\x02\x85q\x03Rq\x04K\x00bh\x01K\x04K\x05K\x06\x87q\x05\x85q\x06Rq\x07K\x00b\x86q\x08Rq\t.',
|
|
||||||
b'\x80\x04\x95L\x00\x00\x00\x00\x00\x00\x00\x8c\x08builtins\x94\x8c\x03zip\x94\x93\x94\x8c\x08builtins\x94\x8c\x04iter\x94\x93\x94K\x01K\x02K\x03\x87\x94\x85\x94R\x94K\x00bh\x05K\x04K\x05K\x06\x87\x94\x85\x94R\x94K\x00b\x86\x94R\x94.',
|
|
||||||
b'\x80\x05\x95L\x00\x00\x00\x00\x00\x00\x00\x8c\x08builtins\x94\x8c\x03zip\x94\x93\x94\x8c\x08builtins\x94\x8c\x04iter\x94\x93\x94K\x01K\x02K\x03\x87\x94\x85\x94R\x94K\x00bh\x05K\x04K\x05K\x06\x87\x94\x85\x94R\x94K\x00b\x86\x94R\x94.',
|
|
||||||
]
|
|
||||||
for protocol, dump in enumerate(pickles):
|
|
||||||
z1 = zip((1, 2, 3), (4, 5, 6))
|
|
||||||
z2 = zip((1, 2, 3), (4, 5, 6), strict=False)
|
|
||||||
z3 = pickle.loads(dump)
|
|
||||||
l3 = list(z3)
|
|
||||||
self.assertEqual(type(z3), zip)
|
|
||||||
self.assertEqual(pickle.dumps(z1, protocol), dump)
|
|
||||||
self.assertEqual(pickle.dumps(z2, protocol), dump)
|
|
||||||
self.assertEqual(list(z1), l3)
|
|
||||||
self.assertEqual(list(z2), l3)
|
|
||||||
|
|
||||||
def test_zip_pickle_strict_stability(self):
|
|
||||||
# Pickles of zip((1, 2, 3), (4, 5), strict=True) dumped from 3.10:
|
|
||||||
pickles = [
|
|
||||||
b'citertools\nizip\np0\n(c__builtin__\niter\np1\n((I1\nI2\nI3\ntp2\ntp3\nRp4\nI0\nbg1\n((I4\nI5\ntp5\ntp6\nRp7\nI0\nbtp8\nRp9\nI01\nb.',
|
|
||||||
b'citertools\nizip\nq\x00(c__builtin__\niter\nq\x01((K\x01K\x02K\x03tq\x02tq\x03Rq\x04K\x00bh\x01((K\x04K\x05tq\x05tq\x06Rq\x07K\x00btq\x08Rq\tI01\nb.',
|
|
||||||
b'\x80\x02citertools\nizip\nq\x00c__builtin__\niter\nq\x01K\x01K\x02K\x03\x87q\x02\x85q\x03Rq\x04K\x00bh\x01K\x04K\x05\x86q\x05\x85q\x06Rq\x07K\x00b\x86q\x08Rq\t\x88b.',
|
|
||||||
b'\x80\x03cbuiltins\nzip\nq\x00cbuiltins\niter\nq\x01K\x01K\x02K\x03\x87q\x02\x85q\x03Rq\x04K\x00bh\x01K\x04K\x05\x86q\x05\x85q\x06Rq\x07K\x00b\x86q\x08Rq\t\x88b.',
|
|
||||||
b'\x80\x04\x95L\x00\x00\x00\x00\x00\x00\x00\x8c\x08builtins\x94\x8c\x03zip\x94\x93\x94\x8c\x08builtins\x94\x8c\x04iter\x94\x93\x94K\x01K\x02K\x03\x87\x94\x85\x94R\x94K\x00bh\x05K\x04K\x05\x86\x94\x85\x94R\x94K\x00b\x86\x94R\x94\x88b.',
|
|
||||||
b'\x80\x05\x95L\x00\x00\x00\x00\x00\x00\x00\x8c\x08builtins\x94\x8c\x03zip\x94\x93\x94\x8c\x08builtins\x94\x8c\x04iter\x94\x93\x94K\x01K\x02K\x03\x87\x94\x85\x94R\x94K\x00bh\x05K\x04K\x05\x86\x94\x85\x94R\x94K\x00b\x86\x94R\x94\x88b.',
|
|
||||||
]
|
|
||||||
a = (1, 2, 3)
|
|
||||||
b = (4, 5)
|
|
||||||
t = [(1, 4), (2, 5)]
|
|
||||||
for protocol, dump in enumerate(pickles):
|
|
||||||
z1 = zip(a, b, strict=True)
|
|
||||||
z2 = pickle.loads(dump)
|
|
||||||
self.assertEqual(pickle.dumps(z1, protocol), dump)
|
|
||||||
self.assertEqual(type(z2), zip)
|
|
||||||
self.assertEqual(self.iter_error(z1, ValueError), t)
|
|
||||||
self.assertEqual(self.iter_error(z2, ValueError), t)
|
|
||||||
|
|
||||||
def test_zip_bad_iterable(self):
|
def test_zip_bad_iterable(self):
|
||||||
exception = TypeError()
|
exception = TypeError()
|
||||||
|
|
||||||
|
|
|
@ -47,6 +47,7 @@ def requires_colors(test):
|
||||||
return wrapped
|
return wrapped
|
||||||
|
|
||||||
term = os.environ.get('TERM')
|
term = os.environ.get('TERM')
|
||||||
|
SHORT_MAX = 0x7fff
|
||||||
|
|
||||||
# If newterm was supported we could use it instead of initscr and not exit
|
# If newterm was supported we could use it instead of initscr and not exit
|
||||||
@unittest.skipIf(not term or term == 'unknown',
|
@unittest.skipIf(not term or term == 'unknown',
|
||||||
|
@ -327,11 +328,20 @@ class TestCurses(unittest.TestCase):
|
||||||
def bad_pairs(self):
|
def bad_pairs(self):
|
||||||
return (-1, -2**31 - 1, 2**31, -2**63 - 1, 2**63, 2**64)
|
return (-1, -2**31 - 1, 2**31, -2**63 - 1, 2**63, 2**64)
|
||||||
|
|
||||||
|
def test_start_color(self):
|
||||||
|
if not curses.has_colors():
|
||||||
|
self.skipTest('requires colors support')
|
||||||
|
curses.start_color()
|
||||||
|
if verbose:
|
||||||
|
print(f'COLORS = {curses.COLORS}', file=sys.stderr)
|
||||||
|
print(f'COLOR_PAIRS = {curses.COLOR_PAIRS}', file=sys.stderr)
|
||||||
|
|
||||||
@requires_colors
|
@requires_colors
|
||||||
def test_color_content(self):
|
def test_color_content(self):
|
||||||
self.assertEqual(curses.color_content(curses.COLOR_BLACK), (0, 0, 0))
|
self.assertEqual(curses.color_content(curses.COLOR_BLACK), (0, 0, 0))
|
||||||
curses.color_content(0)
|
curses.color_content(0)
|
||||||
curses.color_content(curses.COLORS - 1)
|
maxcolor = curses.COLORS - 1
|
||||||
|
curses.color_content(maxcolor)
|
||||||
|
|
||||||
for color in self.bad_colors():
|
for color in self.bad_colors():
|
||||||
self.assertRaises(ValueError, curses.color_content, color)
|
self.assertRaises(ValueError, curses.color_content, color)
|
||||||
|
@ -352,11 +362,12 @@ class TestCurses(unittest.TestCase):
|
||||||
curses.init_color(0, 1000, 1000, 1000)
|
curses.init_color(0, 1000, 1000, 1000)
|
||||||
self.assertEqual(curses.color_content(0), (1000, 1000, 1000))
|
self.assertEqual(curses.color_content(0), (1000, 1000, 1000))
|
||||||
|
|
||||||
old = curses.color_content(curses.COLORS - 1)
|
maxcolor = curses.COLORS - 1
|
||||||
curses.init_color(curses.COLORS - 1, *old)
|
old = curses.color_content(maxcolor)
|
||||||
self.addCleanup(curses.init_color, curses.COLORS - 1, *old)
|
curses.init_color(maxcolor, *old)
|
||||||
curses.init_color(curses.COLORS - 1, 0, 500, 1000)
|
self.addCleanup(curses.init_color, maxcolor, *old)
|
||||||
self.assertEqual(curses.color_content(curses.COLORS - 1), (0, 500, 1000))
|
curses.init_color(maxcolor, 0, 500, 1000)
|
||||||
|
self.assertEqual(curses.color_content(maxcolor), (0, 500, 1000))
|
||||||
|
|
||||||
for color in self.bad_colors():
|
for color in self.bad_colors():
|
||||||
self.assertRaises(ValueError, curses.init_color, color, 0, 0, 0)
|
self.assertRaises(ValueError, curses.init_color, color, 0, 0, 0)
|
||||||
|
@ -365,13 +376,25 @@ class TestCurses(unittest.TestCase):
|
||||||
self.assertRaises(ValueError, curses.init_color, 0, 0, comp, 0)
|
self.assertRaises(ValueError, curses.init_color, 0, 0, comp, 0)
|
||||||
self.assertRaises(ValueError, curses.init_color, 0, 0, 0, comp)
|
self.assertRaises(ValueError, curses.init_color, 0, 0, 0, comp)
|
||||||
|
|
||||||
|
def get_pair_limit(self):
|
||||||
|
pair_limit = curses.COLOR_PAIRS
|
||||||
|
if hasattr(curses, 'ncurses_version'):
|
||||||
|
if curses.has_extended_color_support():
|
||||||
|
pair_limit += 2*curses.COLORS + 1
|
||||||
|
if (not curses.has_extended_color_support()
|
||||||
|
or (6, 1) <= curses.ncurses_version < (6, 2)):
|
||||||
|
pair_limit = min(pair_limit, SHORT_MAX)
|
||||||
|
return pair_limit
|
||||||
|
|
||||||
@requires_colors
|
@requires_colors
|
||||||
def test_pair_content(self):
|
def test_pair_content(self):
|
||||||
if not hasattr(curses, 'use_default_colors'):
|
if not hasattr(curses, 'use_default_colors'):
|
||||||
self.assertEqual(curses.pair_content(0),
|
self.assertEqual(curses.pair_content(0),
|
||||||
(curses.COLOR_WHITE, curses.COLOR_BLACK))
|
(curses.COLOR_WHITE, curses.COLOR_BLACK))
|
||||||
curses.pair_content(0)
|
curses.pair_content(0)
|
||||||
curses.pair_content(curses.COLOR_PAIRS - 1)
|
maxpair = self.get_pair_limit() - 1
|
||||||
|
if maxpair > 0:
|
||||||
|
curses.pair_content(maxpair)
|
||||||
|
|
||||||
for pair in self.bad_pairs():
|
for pair in self.bad_pairs():
|
||||||
self.assertRaises(ValueError, curses.pair_content, pair)
|
self.assertRaises(ValueError, curses.pair_content, pair)
|
||||||
|
@ -384,11 +407,15 @@ class TestCurses(unittest.TestCase):
|
||||||
|
|
||||||
curses.init_pair(1, 0, 0)
|
curses.init_pair(1, 0, 0)
|
||||||
self.assertEqual(curses.pair_content(1), (0, 0))
|
self.assertEqual(curses.pair_content(1), (0, 0))
|
||||||
curses.init_pair(1, curses.COLORS - 1, curses.COLORS - 1)
|
maxcolor = curses.COLORS - 1
|
||||||
self.assertEqual(curses.pair_content(1),
|
curses.init_pair(1, maxcolor, 0)
|
||||||
(curses.COLORS - 1, curses.COLORS - 1))
|
self.assertEqual(curses.pair_content(1), (maxcolor, 0))
|
||||||
curses.init_pair(curses.COLOR_PAIRS - 1, 2, 3)
|
curses.init_pair(1, 0, maxcolor)
|
||||||
self.assertEqual(curses.pair_content(curses.COLOR_PAIRS - 1), (2, 3))
|
self.assertEqual(curses.pair_content(1), (0, maxcolor))
|
||||||
|
maxpair = self.get_pair_limit() - 1
|
||||||
|
if maxpair > 1:
|
||||||
|
curses.init_pair(maxpair, 0, 0)
|
||||||
|
self.assertEqual(curses.pair_content(maxpair), (0, 0))
|
||||||
|
|
||||||
for pair in self.bad_pairs():
|
for pair in self.bad_pairs():
|
||||||
self.assertRaises(ValueError, curses.init_pair, pair, 0, 0)
|
self.assertRaises(ValueError, curses.init_pair, pair, 0, 0)
|
||||||
|
@ -582,6 +609,8 @@ class MiscTests(unittest.TestCase):
|
||||||
@requires_curses_func('ncurses_version')
|
@requires_curses_func('ncurses_version')
|
||||||
def test_ncurses_version(self):
|
def test_ncurses_version(self):
|
||||||
v = curses.ncurses_version
|
v = curses.ncurses_version
|
||||||
|
if verbose:
|
||||||
|
print(f'ncurses_version = {curses.ncurses_version}', flush=True)
|
||||||
self.assertIsInstance(v[:], tuple)
|
self.assertIsInstance(v[:], tuple)
|
||||||
self.assertEqual(len(v), 3)
|
self.assertEqual(len(v), 3)
|
||||||
self.assertIsInstance(v[0], int)
|
self.assertIsInstance(v[0], int)
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
Finish zzdummy example extension module: make menu entries work;
|
||||||
|
add docstrings and tests with 100% coverage.
|
|
@ -1 +1 @@
|
||||||
In :mod:`sqlite3`, fix `CheckTraceCallbackContent` for SQLite pre 3.7.15.
|
In :mod:`sqlite3`, fix ``CheckTraceCallbackContent`` for SQLite pre 3.7.15.
|
||||||
|
|
|
@ -135,29 +135,28 @@ typedef chtype attr_t; /* No attr_t type is available */
|
||||||
#define STRICT_SYSV_CURSES
|
#define STRICT_SYSV_CURSES
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(NCURSES_EXT_COLORS) && defined(NCURSES_EXT_FUNCS)
|
#if NCURSES_EXT_COLORS+0 && NCURSES_EXT_FUNCS+0
|
||||||
#define _NCURSES_EXTENDED_COLOR_FUNCS 1
|
#define _NCURSES_EXTENDED_COLOR_FUNCS 1
|
||||||
#else
|
#else
|
||||||
#define _NCURSES_EXTENDED_COLOR_FUNCS 0
|
#define _NCURSES_EXTENDED_COLOR_FUNCS 0
|
||||||
#endif /* defined(NCURSES_EXT_COLORS) && defined(NCURSES_EXT_FUNCS) */
|
#endif /* defined(NCURSES_EXT_COLORS) && defined(NCURSES_EXT_FUNCS) */
|
||||||
|
|
||||||
#if _NCURSES_EXTENDED_COLOR_FUNCS
|
#if _NCURSES_EXTENDED_COLOR_FUNCS
|
||||||
#define _NCURSES_COLOR_VAL_TYPE int
|
#define _CURSES_COLOR_VAL_TYPE int
|
||||||
|
#define _CURSES_COLOR_NUM_TYPE int
|
||||||
#define _CURSES_INIT_COLOR_FUNC init_extended_color
|
#define _CURSES_INIT_COLOR_FUNC init_extended_color
|
||||||
#define _CURSES_INIT_PAIR_FUNC init_extended_pair
|
#define _CURSES_INIT_PAIR_FUNC init_extended_pair
|
||||||
#define _COLOR_CONTENT_FUNC extended_color_content
|
#define _COLOR_CONTENT_FUNC extended_color_content
|
||||||
#define _CURSES_PAIR_NUMBER_FUNC extended_pair_content
|
#define _CURSES_PAIR_CONTENT_FUNC extended_pair_content
|
||||||
#else
|
#else
|
||||||
#define _NCURSES_COLOR_VAL_TYPE short
|
#define _CURSES_COLOR_VAL_TYPE short
|
||||||
|
#define _CURSES_COLOR_NUM_TYPE short
|
||||||
#define _CURSES_INIT_COLOR_FUNC init_color
|
#define _CURSES_INIT_COLOR_FUNC init_color
|
||||||
#define _CURSES_INIT_PAIR_FUNC init_pair
|
#define _CURSES_INIT_PAIR_FUNC init_pair
|
||||||
#define _COLOR_CONTENT_FUNC color_content
|
#define _COLOR_CONTENT_FUNC color_content
|
||||||
#define _CURSES_PAIR_NUMBER_FUNC pair_content
|
#define _CURSES_PAIR_CONTENT_FUNC pair_content
|
||||||
#endif /* _NCURSES_EXTENDED_COLOR_FUNCS */
|
#endif /* _NCURSES_EXTENDED_COLOR_FUNCS */
|
||||||
|
|
||||||
#define _CURSES_INIT_COLOR_FUNC_NAME Py_STRINGIFY(_CURSES_INIT_COLOR_FUNC)
|
|
||||||
#define _CURSES_INIT_PAIR_FUNC_NAME Py_STRINGIFY(_CURSES_INIT_PAIR_FUNC)
|
|
||||||
|
|
||||||
/*[clinic input]
|
/*[clinic input]
|
||||||
module _curses
|
module _curses
|
||||||
class _curses.window "PyCursesWindowObject *" "&PyCursesWindow_Type"
|
class _curses.window "PyCursesWindowObject *" "&PyCursesWindow_Type"
|
||||||
|
@ -2737,18 +2736,18 @@ static PyObject *
|
||||||
_curses_color_content_impl(PyObject *module, int color_number)
|
_curses_color_content_impl(PyObject *module, int color_number)
|
||||||
/*[clinic end generated code: output=17b466df7054e0de input=03b5ed0472662aea]*/
|
/*[clinic end generated code: output=17b466df7054e0de input=03b5ed0472662aea]*/
|
||||||
{
|
{
|
||||||
_NCURSES_COLOR_VAL_TYPE r,g,b;
|
_CURSES_COLOR_VAL_TYPE r,g,b;
|
||||||
|
|
||||||
PyCursesInitialised;
|
PyCursesInitialised;
|
||||||
PyCursesInitialisedColor;
|
PyCursesInitialisedColor;
|
||||||
|
|
||||||
if (_COLOR_CONTENT_FUNC(color_number, &r, &g, &b) != ERR)
|
if (_COLOR_CONTENT_FUNC(color_number, &r, &g, &b) == ERR) {
|
||||||
return Py_BuildValue("(iii)", r, g, b);
|
PyErr_Format(PyCursesError, "%s() returned ERR",
|
||||||
else {
|
Py_STRINGIFY(_COLOR_CONTENT_FUNC));
|
||||||
PyErr_SetString(PyCursesError,
|
|
||||||
"Argument 1 was out of range. Check value of COLORS.");
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return Py_BuildValue("(iii)", r, g, b);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*[clinic input]
|
/*[clinic input]
|
||||||
|
@ -3190,7 +3189,8 @@ _curses_init_color_impl(PyObject *module, int color_number, short r, short g,
|
||||||
PyCursesInitialised;
|
PyCursesInitialised;
|
||||||
PyCursesInitialisedColor;
|
PyCursesInitialisedColor;
|
||||||
|
|
||||||
return PyCursesCheckERR(_CURSES_INIT_COLOR_FUNC(color_number, r, g, b), _CURSES_INIT_COLOR_FUNC_NAME);
|
return PyCursesCheckERR(_CURSES_INIT_COLOR_FUNC(color_number, r, g, b),
|
||||||
|
Py_STRINGIFY(_CURSES_INIT_COLOR_FUNC));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*[clinic input]
|
/*[clinic input]
|
||||||
|
@ -3217,7 +3217,20 @@ _curses_init_pair_impl(PyObject *module, int pair_number, int fg, int bg)
|
||||||
PyCursesInitialised;
|
PyCursesInitialised;
|
||||||
PyCursesInitialisedColor;
|
PyCursesInitialisedColor;
|
||||||
|
|
||||||
return PyCursesCheckERR(_CURSES_INIT_PAIR_FUNC(pair_number, fg, bg), _CURSES_INIT_PAIR_FUNC_NAME);
|
if (_CURSES_INIT_PAIR_FUNC(pair_number, fg, bg) == ERR) {
|
||||||
|
if (pair_number >= COLOR_PAIRS) {
|
||||||
|
PyErr_Format(PyExc_ValueError,
|
||||||
|
"Color pair is greater than COLOR_PAIRS-1 (%d).",
|
||||||
|
COLOR_PAIRS - 1);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
PyErr_Format(PyCursesError, "%s() returned ERR",
|
||||||
|
Py_STRINGIFY(_CURSES_INIT_PAIR_FUNC));
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *ModDict;
|
static PyObject *ModDict;
|
||||||
|
@ -3845,14 +3858,21 @@ static PyObject *
|
||||||
_curses_pair_content_impl(PyObject *module, int pair_number)
|
_curses_pair_content_impl(PyObject *module, int pair_number)
|
||||||
/*[clinic end generated code: output=4a726dd0e6885f3f input=03970f840fc7b739]*/
|
/*[clinic end generated code: output=4a726dd0e6885f3f input=03970f840fc7b739]*/
|
||||||
{
|
{
|
||||||
_NCURSES_COLOR_VAL_TYPE f, b;
|
_CURSES_COLOR_NUM_TYPE f, b;
|
||||||
|
|
||||||
PyCursesInitialised;
|
PyCursesInitialised;
|
||||||
PyCursesInitialisedColor;
|
PyCursesInitialisedColor;
|
||||||
|
|
||||||
if (_CURSES_PAIR_NUMBER_FUNC(pair_number, &f, &b)==ERR) {
|
if (_CURSES_PAIR_CONTENT_FUNC(pair_number, &f, &b) == ERR) {
|
||||||
PyErr_SetString(PyCursesError,
|
if (pair_number >= COLOR_PAIRS) {
|
||||||
"Argument 1 was out of range. (0..COLOR_PAIRS-1)");
|
PyErr_Format(PyExc_ValueError,
|
||||||
|
"Color pair is greater than COLOR_PAIRS-1 (%d).",
|
||||||
|
COLOR_PAIRS - 1);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
PyErr_Format(PyCursesError, "%s() returned ERR",
|
||||||
|
Py_STRINGIFY(_CURSES_PAIR_CONTENT_FUNC));
|
||||||
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue