Python-checkins
Threads by month
- ----- 2024 -----
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2009 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2008 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2007 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2006 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2005 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2004 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2003 -----
- December
- November
- October
- September
- August
August 2017
- 3 participants
- 281 discussions
https://github.com/python/cpython/commit/05b7d9c6675b71d17f5fcf379b3888fba4…
commit: 05b7d9c6675b71d17f5fcf379b3888fba431f14e
branch: 2.7
author: Christian Heimes <christian(a)python.org>
committer: GitHub <noreply(a)github.com>
date: 2017-08-15T10:55:03+02:00
summary:
[2.7] bpo-30714: ALPN changes for OpenSSL 1.1.0f (#3094)
OpenSSL 1.1.0 to 1.1.0e aborted the handshake when server and client
could not agree on a protocol using ALPN. OpenSSL 1.1.0f changed that.
The most recent version now behaves like OpenSSL 1.0.2 again. The ALPN
callback can pretend to not been set.
See https://github.com/openssl/openssl/pull/3158 for more details
Signed-off-by: Christian Heimes <christian(a)python.org>
(cherry picked from commit a5c1bab352671e043645163ca50c5211aa657acd)
files:
A Misc/NEWS.d/next/Tests/2017-07-25-15-27-44.bpo-30715.Sp7bTF.rst
M Doc/library/ssl.rst
M Lib/test/test_ssl.py
diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst
index 8b41317eb20..183c17bb560 100644
--- a/Doc/library/ssl.rst
+++ b/Doc/library/ssl.rst
@@ -1162,8 +1162,9 @@ to speed up repeated connections from the same clients.
This method will raise :exc:`NotImplementedError` if :data:`HAS_ALPN` is
False.
- OpenSSL 1.1.0+ will abort the handshake and raise :exc:`SSLError` when
- both sides support ALPN but cannot agree on a protocol.
+ OpenSSL 1.1.0 to 1.1.0e will abort the handshake and raise :exc:`SSLError`
+ when both sides support ALPN but cannot agree on a protocol. 1.1.0f+
+ behaves like 1.0.2, :meth:`SSLSocket.selected_alpn_protocol` returns None.
.. versionadded:: 2.7.10
diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py
index 2f98cf14550..07526c2cf15 100644
--- a/Lib/test/test_ssl.py
+++ b/Lib/test/test_ssl.py
@@ -2966,8 +2966,9 @@ def test_alpn_protocols(self):
except ssl.SSLError as e:
stats = e
- if expected is None and IS_OPENSSL_1_1:
- # OpenSSL 1.1.0 raises handshake error
+ if (expected is None and IS_OPENSSL_1_1
+ and ssl.OPENSSL_VERSION_INFO < (1, 1, 0, 6)):
+ # OpenSSL 1.1.0 to 1.1.0e raises handshake error
self.assertIsInstance(stats, ssl.SSLError)
else:
msg = "failed trying %s (s) and %s (c).\n" \
diff --git a/Misc/NEWS.d/next/Tests/2017-07-25-15-27-44.bpo-30715.Sp7bTF.rst b/Misc/NEWS.d/next/Tests/2017-07-25-15-27-44.bpo-30715.Sp7bTF.rst
new file mode 100644
index 00000000000..88394e585c5
--- /dev/null
+++ b/Misc/NEWS.d/next/Tests/2017-07-25-15-27-44.bpo-30715.Sp7bTF.rst
@@ -0,0 +1,2 @@
+Address ALPN callback changes for OpenSSL 1.1.0f. The latest version behaves
+like OpenSSL 1.0.2 and no longer aborts handshake.
1
0
https://github.com/python/cpython/commit/7f6a13bd562ff6a265fc63a991327feaec…
commit: 7f6a13bd562ff6a265fc63a991327feaecb07a77
branch: 3.6
author: Christian Heimes <christian(a)python.org>
committer: GitHub <noreply(a)github.com>
date: 2017-08-15T10:45:40+02:00
summary:
[3.6] bpo-30714: ALPN changes for OpenSSL 1.1.0f (#3093)
OpenSSL 1.1.0 to 1.1.0e aborted the handshake when server and client
could not agree on a protocol using ALPN. OpenSSL 1.1.0f changed that.
The most recent version now behaves like OpenSSL 1.0.2 again. The ALPN
callback can pretend to not been set.
See https://github.com/openssl/openssl/pull/3158 for more details
Signed-off-by: Christian Heimes <christian(a)python.org>
(cherry picked from commit a5c1bab352671e043645163ca50c5211aa657acd)
files:
A Misc/NEWS.d/next/Tests/2017-07-25-15-27-44.bpo-30715.Sp7bTF.rst
M Doc/library/ssl.rst
M Lib/test/test_ssl.py
diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst
index 6c30a74a099..357f65a3faf 100644
--- a/Doc/library/ssl.rst
+++ b/Doc/library/ssl.rst
@@ -1442,8 +1442,9 @@ to speed up repeated connections from the same clients.
This method will raise :exc:`NotImplementedError` if :data:`HAS_ALPN` is
False.
- OpenSSL 1.1.0+ will abort the handshake and raise :exc:`SSLError` when
- both sides support ALPN but cannot agree on a protocol.
+ OpenSSL 1.1.0 to 1.1.0e will abort the handshake and raise :exc:`SSLError`
+ when both sides support ALPN but cannot agree on a protocol. 1.1.0f+
+ behaves like 1.0.2, :meth:`SSLSocket.selected_alpn_protocol` returns None.
.. versionadded:: 3.5
diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py
index 85c59a618ce..3fdbba13705 100644
--- a/Lib/test/test_ssl.py
+++ b/Lib/test/test_ssl.py
@@ -3258,8 +3258,9 @@ def test_alpn_protocols(self):
except ssl.SSLError as e:
stats = e
- if expected is None and IS_OPENSSL_1_1:
- # OpenSSL 1.1.0 raises handshake error
+ if (expected is None and IS_OPENSSL_1_1
+ and ssl.OPENSSL_VERSION_INFO < (1, 1, 0, 6)):
+ # OpenSSL 1.1.0 to 1.1.0e raises handshake error
self.assertIsInstance(stats, ssl.SSLError)
else:
msg = "failed trying %s (s) and %s (c).\n" \
diff --git a/Misc/NEWS.d/next/Tests/2017-07-25-15-27-44.bpo-30715.Sp7bTF.rst b/Misc/NEWS.d/next/Tests/2017-07-25-15-27-44.bpo-30715.Sp7bTF.rst
new file mode 100644
index 00000000000..88394e585c5
--- /dev/null
+++ b/Misc/NEWS.d/next/Tests/2017-07-25-15-27-44.bpo-30715.Sp7bTF.rst
@@ -0,0 +1,2 @@
+Address ALPN callback changes for OpenSSL 1.1.0f. The latest version behaves
+like OpenSSL 1.0.2 and no longer aborts handshake.
1
0
https://github.com/python/cpython/commit/7b40cb7293cb14e5c7c8ed123efaf9acb3…
commit: 7b40cb7293cb14e5c7c8ed123efaf9acb33edae2
branch: master
author: Christian Heimes <christian(a)python.org>
committer: GitHub <noreply(a)github.com>
date: 2017-08-15T10:33:43+02:00
summary:
bpo-30714: ALPN changes for OpenSSL 1.1.0f (#2305)
OpenSSL 1.1.0 to 1.1.0e aborted the handshake when server and client
could not agree on a protocol using ALPN. OpenSSL 1.1.0f changed that.
The most recent version now behaves like OpenSSL 1.0.2 again. The ALPN
callback can pretend to not been set.
See https://github.com/openssl/openssl/pull/3158 for more details
Signed-off-by: Christian Heimes <christian(a)python.org>
files:
A Misc/NEWS.d/next/Tests/2017-07-25-15-27-44.bpo-30715.Sp7bTF.rst
M Doc/library/ssl.rst
M Lib/test/test_ssl.py
diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst
index 729a239a1ba..0a09e7e9d4c 100644
--- a/Doc/library/ssl.rst
+++ b/Doc/library/ssl.rst
@@ -1447,8 +1447,9 @@ to speed up repeated connections from the same clients.
This method will raise :exc:`NotImplementedError` if :data:`HAS_ALPN` is
False.
- OpenSSL 1.1.0+ will abort the handshake and raise :exc:`SSLError` when
- both sides support ALPN but cannot agree on a protocol.
+ OpenSSL 1.1.0 to 1.1.0e will abort the handshake and raise :exc:`SSLError`
+ when both sides support ALPN but cannot agree on a protocol. 1.1.0f+
+ behaves like 1.0.2, :meth:`SSLSocket.selected_alpn_protocol` returns None.
.. versionadded:: 3.5
diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py
index d960d820650..104b7f377a5 100644
--- a/Lib/test/test_ssl.py
+++ b/Lib/test/test_ssl.py
@@ -3268,8 +3268,9 @@ def test_alpn_protocols(self):
except ssl.SSLError as e:
stats = e
- if expected is None and IS_OPENSSL_1_1:
- # OpenSSL 1.1.0 raises handshake error
+ if (expected is None and IS_OPENSSL_1_1
+ and ssl.OPENSSL_VERSION_INFO < (1, 1, 0, 6)):
+ # OpenSSL 1.1.0 to 1.1.0e raises handshake error
self.assertIsInstance(stats, ssl.SSLError)
else:
msg = "failed trying %s (s) and %s (c).\n" \
diff --git a/Misc/NEWS.d/next/Tests/2017-07-25-15-27-44.bpo-30715.Sp7bTF.rst b/Misc/NEWS.d/next/Tests/2017-07-25-15-27-44.bpo-30715.Sp7bTF.rst
new file mode 100644
index 00000000000..88394e585c5
--- /dev/null
+++ b/Misc/NEWS.d/next/Tests/2017-07-25-15-27-44.bpo-30715.Sp7bTF.rst
@@ -0,0 +1,2 @@
+Address ALPN callback changes for OpenSSL 1.1.0f. The latest version behaves
+like OpenSSL 1.0.2 and no longer aborts handshake.
1
0
![](https://secure.gravatar.com/avatar/cc7737cd64a84f1b5c61a160798e97ee.jpg?s=120&d=mm&r=g)
[3.6] bpo-31002: IDLE: Add tests for configdialog keys tab (GH-2996) (#3092)
by Terry Jan Reedy 14 Aug '17
by Terry Jan Reedy 14 Aug '17
14 Aug '17
https://github.com/python/cpython/commit/a31459008c5b3230363d155a2e8616664d…
commit: a31459008c5b3230363d155a2e8616664dc4f0c6
branch: 3.6
author: Terry Jan Reedy <tjreedy(a)udel.edu>
committer: GitHub <noreply(a)github.com>
date: 2017-08-14T21:45:02-04:00
summary:
[3.6] bpo-31002: IDLE: Add tests for configdialog keys tab (GH-2996) (#3092)
Patch by Cheryl Sabella.
(cherry picked from commit 2f89646)
files:
A Misc/NEWS.d/next/IDLE/2017-08-03-17-54-02.bpo-31002.kUSgTE.rst
M Lib/idlelib/configdialog.py
M Lib/idlelib/idle_test/test_configdialog.py
diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py
index e634d5f3599..7c5f3c8ca4e 100644
--- a/Lib/idlelib/configdialog.py
+++ b/Lib/idlelib/configdialog.py
@@ -251,25 +251,24 @@ def create_page_highlight(self):
delete_custom_theme: Ativate default [button_delete_custom_theme].
save_new_theme: Save to userCfg['theme'] (is function).
- Widget Structure: (*) widgets bound to self
- frame
- frame_custom: LabelFrame
- (*)highlight_sample: Text
- (*)frame_color_set: Frame
- button_set_color: Button
- (*)opt_menu_highlight_target: DynOptionMenu - highlight_target
- frame_fg_bg_toggle: Frame
- (*)radio_fg: Radiobutton - fg_bg_toggle
- (*)radio_bg: Radiobutton - fg_bg_toggle
- button_save_custom_theme: Button
- frame_theme: LabelFrame
- theme_type_title: Label
- (*)radio_theme_builtin: Radiobutton - is_builtin_theme
- (*)radio_theme_custom: Radiobutton - is_builtin_theme
- (*)opt_menu_theme_builtin: DynOptionMenu - builtin_theme
- (*)opt_menu_theme_custom: DynOptionMenu - custom_theme
- (*)button_delete_custom_theme: Button
- (*)new_custom_theme: Label
+ Widgets of highlights page frame: (*) widgets bound to self
+ frame_custom: LabelFrame
+ (*)highlight_sample: Text
+ (*)frame_color_set: Frame
+ button_set_color: Button
+ (*)opt_menu_highlight_target: DynOptionMenu - highlight_target
+ frame_fg_bg_toggle: Frame
+ (*)radio_fg: Radiobutton - fg_bg_toggle
+ (*)radio_bg: Radiobutton - fg_bg_toggle
+ button_save_custom_theme: Button
+ frame_theme: LabelFrame
+ theme_type_title: Label
+ (*)radio_theme_builtin: Radiobutton - is_builtin_theme
+ (*)radio_theme_custom: Radiobutton - is_builtin_theme
+ (*)opt_menu_theme_builtin: DynOptionMenu - builtin_theme
+ (*)opt_menu_theme_custom: DynOptionMenu - custom_theme
+ (*)button_delete_custom_theme: Button
+ (*)new_custom_theme: Label
"""
self.theme_elements={
'Normal Text': ('normal', '00'),
@@ -796,52 +795,92 @@ def delete_custom_theme(self):
def create_page_keys(self):
"""Return frame of widgets for Keys tab.
+ Enable users to provisionally change both individual and sets of
+ keybindings (shortcut keys). Except for features implemented as
+ extensions, keybindings are stored in complete sets called
+ keysets. Built-in keysets in idlelib/config-keys.def are fixed
+ as far as the dialog is concerned. Any keyset can be used as the
+ base for a new custom keyset, stored in .idlerc/config-keys.cfg.
+
+ Function load_key_cfg() initializes tk variables and keyset
+ lists and calls load_keys_list for the current keyset.
+ Radiobuttons builtin_keyset_on and custom_keyset_on toggle var
+ keyset_source, which controls if the current set of keybindings
+ are from a builtin or custom keyset. DynOptionMenus builtinlist
+ and customlist contain lists of the builtin and custom keysets,
+ respectively, and the current item from each list is stored in
+ vars builtin_name and custom_name.
+
+ Button delete_custom_keys invokes delete_custom_keys() to delete
+ a custom keyset from idleConf.userCfg['keys'] and changes. Button
+ save_custom_keys invokes save_as_new_key_set() which calls
+ get_new_keys_name() and create_new_key_set() to save a custom keyset
+ and its keybindings to idleConf.userCfg['keys'].
+
+ Listbox bindingslist contains all of the keybindings for the
+ selected keyset. The keybindings are loaded in load_keys_list()
+ and are pairs of (event, [keys]) where keys can be a list
+ of one or more key combinations to bind to the same event.
+ Mouse button 1 click invokes on_bindingslist_select(), which
+ allows button_new_keys to be clicked.
+
+ So, an item is selected in listbindings, which activates
+ button_new_keys, and clicking button_new_keys calls function
+ get_new_keys(). Function get_new_keys() gets the key mappings from the
+ current keyset for the binding event item that was selected. The
+ function then displays another dialog, GetKeysDialog, with the
+ selected binding event and current keys and always new key sequences
+ to be entered for that binding event. If the keys aren't
+ changed, nothing happens. If the keys are changed and the keyset
+ is a builtin, function get_new_keys_name() will be called
+ for input of a custom keyset name. If no name is given, then the
+ change to the keybinding will abort and no updates will be made. If
+ a custom name is entered in the prompt or if the current keyset was
+ already custom (and thus didn't require a prompt), then
+ idleConf.userCfg['keys'] is updated in function create_new_key_set()
+ with the change to the event binding. The item listing in bindingslist
+ is updated with the new keys. Var keybinding is also set which invokes
+ the callback function, var_changed_keybinding, to add the change to
+ the 'keys' or 'extensions' changes tracker based on the binding type.
+
Tk Variables:
- builtin_keys: Menu variable for built-in keybindings.
- custom_keys: Menu variable for custom keybindings.
- are_keys_builtin: Selector for built-in or custom keybindings.
keybinding: Action/key bindings.
Methods:
- load_key_config: Set table.
load_keys_list: Reload active set.
- keybinding_selected: Bound to list_bindings button release.
- get_new_keys: Command for button_new_keys.
- get_new_keys_name: Call popup.
create_new_key_set: Combine active keyset and changes.
- set_keys_type: Command for are_keys_builtin.
- delete_custom_keys: Command for button_delete_custom_keys.
- save_as_new_key_set: Command for button_save_custom_keys.
+ set_keys_type: Command for keyset_source.
save_new_key_set: Save to idleConf.userCfg['keys'] (is function).
deactivate_current_config: Remove keys bindings in editors.
- Widget Structure: (*) widgets bound to self
- frame
- frame_custom: LabelFrame
- frame_target: Frame
- target_title: Label
- scroll_target_y: Scrollbar
- scroll_target_x: Scrollbar
- (*)list_bindings: ListBox
- (*)button_new_keys: Button
- frame_key_sets: LabelFrame
- frames[0]: Frame
- (*)radio_keys_builtin: Radiobutton - are_keys_builtin
- (*)radio_keys_custom: Radiobutton - are_keys_builtin
- (*)opt_menu_keys_builtin: DynOptionMenu - builtin_keys
- (*)opt_menu_keys_custom: DynOptionMenu - custom_keys
- (*)new_custom_keys: Label
- frames[1]: Frame
- (*)button_delete_custom_keys: Button
- button_save_custom_keys: Button
+ Widgets for keys page frame: (*) widgets bound to self
+ frame_key_sets: LabelFrame
+ frames[0]: Frame
+ (*)builtin_keyset_on: Radiobutton - var keyset_source
+ (*)custom_keyset_on: Radiobutton - var keyset_source
+ (*)builtinlist: DynOptionMenu - var builtin_name,
+ func keybinding_selected
+ (*)customlist: DynOptionMenu - var custom_name,
+ func keybinding_selected
+ (*)keys_message: Label
+ frames[1]: Frame
+ (*)button_delete_custom_keys: Button - delete_custom_keys
+ (*)button_save_custom_keys: Button - save_as_new_key_set
+ frame_custom: LabelFrame
+ frame_target: Frame
+ target_title: Label
+ scroll_target_y: Scrollbar
+ scroll_target_x: Scrollbar
+ (*)bindingslist: ListBox - on_bindingslist_select
+ (*)button_new_keys: Button - get_new_keys & ..._name
"""
parent = self.parent
- self.builtin_keys = tracers.add(
- StringVar(parent), self.var_changed_builtin_keys)
- self.custom_keys = tracers.add(
- StringVar(parent), self.var_changed_custom_keys)
- self.are_keys_builtin = tracers.add(
- BooleanVar(parent), self.var_changed_are_keys_builtin)
+ self.builtin_name = tracers.add(
+ StringVar(parent), self.var_changed_builtin_name)
+ self.custom_name = tracers.add(
+ StringVar(parent), self.var_changed_custom_name)
+ self.keyset_source = tracers.add(
+ BooleanVar(parent), self.var_changed_keyset_source)
self.keybinding = tracers.add(
StringVar(parent), self.var_changed_keybinding)
@@ -858,36 +897,37 @@ def create_page_keys(self):
target_title = Label(frame_target, text='Action - Key(s)')
scroll_target_y = Scrollbar(frame_target)
scroll_target_x = Scrollbar(frame_target, orient=HORIZONTAL)
- self.list_bindings = Listbox(
+ self.bindingslist = Listbox(
frame_target, takefocus=FALSE, exportselection=FALSE)
- self.list_bindings.bind('<ButtonRelease-1>', self.keybinding_selected)
- scroll_target_y.config(command=self.list_bindings.yview)
- scroll_target_x.config(command=self.list_bindings.xview)
- self.list_bindings.config(yscrollcommand=scroll_target_y.set)
- self.list_bindings.config(xscrollcommand=scroll_target_x.set)
+ self.bindingslist.bind('<ButtonRelease-1>',
+ self.on_bindingslist_select)
+ scroll_target_y['command'] = self.bindingslist.yview
+ scroll_target_x['command'] = self.bindingslist.xview
+ self.bindingslist['yscrollcommand'] = scroll_target_y.set
+ self.bindingslist['xscrollcommand'] = scroll_target_x.set
self.button_new_keys = Button(
frame_custom, text='Get New Keys for Selection',
command=self.get_new_keys, state=DISABLED)
#frame_key_sets
frames = [Frame(frame_key_sets, padx=2, pady=2, borderwidth=0)
for i in range(2)]
- self.radio_keys_builtin = Radiobutton(
- frames[0], variable=self.are_keys_builtin, value=1,
+ self.builtin_keyset_on = Radiobutton(
+ frames[0], variable=self.keyset_source, value=1,
command=self.set_keys_type, text='Use a Built-in Key Set')
- self.radio_keys_custom = Radiobutton(
- frames[0], variable=self.are_keys_builtin, value=0,
+ self.custom_keyset_on = Radiobutton(
+ frames[0], variable=self.keyset_source, value=0,
command=self.set_keys_type, text='Use a Custom Key Set')
- self.opt_menu_keys_builtin = DynOptionMenu(
- frames[0], self.builtin_keys, None, command=None)
- self.opt_menu_keys_custom = DynOptionMenu(
- frames[0], self.custom_keys, None, command=None)
+ self.builtinlist = DynOptionMenu(
+ frames[0], self.builtin_name, None, command=None)
+ self.customlist = DynOptionMenu(
+ frames[0], self.custom_name, None, command=None)
self.button_delete_custom_keys = Button(
frames[1], text='Delete Custom Key Set',
command=self.delete_custom_keys)
- button_save_custom_keys = Button(
+ self.button_save_custom_keys = Button(
frames[1], text='Save as New Custom Key Set',
command=self.save_as_new_key_set)
- self.new_custom_keys = Label(frames[0], bd=2)
+ self.keys_message = Label(frames[0], bd=2)
##widget packing
#body
@@ -900,17 +940,17 @@ def create_page_keys(self):
frame_target.columnconfigure(0, weight=1)
frame_target.rowconfigure(1, weight=1)
target_title.grid(row=0, column=0, columnspan=2, sticky=W)
- self.list_bindings.grid(row=1, column=0, sticky=NSEW)
+ self.bindingslist.grid(row=1, column=0, sticky=NSEW)
scroll_target_y.grid(row=1, column=1, sticky=NS)
scroll_target_x.grid(row=2, column=0, sticky=EW)
#frame_key_sets
- self.radio_keys_builtin.grid(row=0, column=0, sticky=W+NS)
- self.radio_keys_custom.grid(row=1, column=0, sticky=W+NS)
- self.opt_menu_keys_builtin.grid(row=0, column=1, sticky=NSEW)
- self.opt_menu_keys_custom.grid(row=1, column=1, sticky=NSEW)
- self.new_custom_keys.grid(row=0, column=2, sticky=NSEW, padx=5, pady=5)
+ self.builtin_keyset_on.grid(row=0, column=0, sticky=W+NS)
+ self.custom_keyset_on.grid(row=1, column=0, sticky=W+NS)
+ self.builtinlist.grid(row=0, column=1, sticky=NSEW)
+ self.customlist.grid(row=1, column=1, sticky=NSEW)
+ self.keys_message.grid(row=0, column=2, sticky=NSEW, padx=5, pady=5)
self.button_delete_custom_keys.pack(side=LEFT, fill=X, expand=True, padx=2)
- button_save_custom_keys.pack(side=LEFT, fill=X, expand=True, padx=2)
+ self.button_save_custom_keys.pack(side=LEFT, fill=X, expand=True, padx=2)
frames[0].pack(side=TOP, fill=BOTH, expand=True)
frames[1].pack(side=TOP, fill=X, expand=True, pady=2)
return frame
@@ -918,35 +958,35 @@ def create_page_keys(self):
def load_key_cfg(self):
"Load current configuration settings for the keybinding options."
# Set current keys type radiobutton.
- self.are_keys_builtin.set(idleConf.GetOption(
+ self.keyset_source.set(idleConf.GetOption(
'main', 'Keys', 'default', type='bool', default=1))
# Set current keys.
current_option = idleConf.CurrentKeys()
# Load available keyset option menus.
- if self.are_keys_builtin.get(): # Default theme selected.
+ if self.keyset_source.get(): # Default theme selected.
item_list = idleConf.GetSectionList('default', 'keys')
item_list.sort()
- self.opt_menu_keys_builtin.SetMenu(item_list, current_option)
+ self.builtinlist.SetMenu(item_list, current_option)
item_list = idleConf.GetSectionList('user', 'keys')
item_list.sort()
if not item_list:
- self.radio_keys_custom['state'] = DISABLED
- self.custom_keys.set('- no custom keys -')
+ self.custom_keyset_on['state'] = DISABLED
+ self.custom_name.set('- no custom keys -')
else:
- self.opt_menu_keys_custom.SetMenu(item_list, item_list[0])
+ self.customlist.SetMenu(item_list, item_list[0])
else: # User key set selected.
item_list = idleConf.GetSectionList('user', 'keys')
item_list.sort()
- self.opt_menu_keys_custom.SetMenu(item_list, current_option)
+ self.customlist.SetMenu(item_list, current_option)
item_list = idleConf.GetSectionList('default', 'keys')
item_list.sort()
- self.opt_menu_keys_builtin.SetMenu(item_list, idleConf.default_keys())
+ self.builtinlist.SetMenu(item_list, idleConf.default_keys())
self.set_keys_type()
# Load keyset element list.
keyset_name = idleConf.CurrentKeys()
self.load_keys_list(keyset_name)
- def var_changed_builtin_keys(self, *params):
+ def var_changed_builtin_name(self, *params):
"Process selection of builtin key set."
old_keys = (
'IDLE Classic Windows',
@@ -954,40 +994,41 @@ def var_changed_builtin_keys(self, *params):
'IDLE Classic Mac',
'IDLE Classic OSX',
)
- value = self.builtin_keys.get()
+ value = self.builtin_name.get()
if value not in old_keys:
if idleConf.GetOption('main', 'Keys', 'name') not in old_keys:
changes.add_option('main', 'Keys', 'name', old_keys[0])
changes.add_option('main', 'Keys', 'name2', value)
- self.new_custom_keys.config(text='New key set, see Help',
- fg='#500000')
+ self.keys_message['text'] = 'New key set, see Help'
+ self.keys_message['fg'] = '#500000'
else:
changes.add_option('main', 'Keys', 'name', value)
changes.add_option('main', 'Keys', 'name2', '')
- self.new_custom_keys.config(text='', fg='black')
+ self.keys_message['text'] = ''
+ self.keys_message['fg'] = 'black'
self.load_keys_list(value)
- def var_changed_custom_keys(self, *params):
+ def var_changed_custom_name(self, *params):
"Process selection of custom key set."
- value = self.custom_keys.get()
+ value = self.custom_name.get()
if value != '- no custom keys -':
changes.add_option('main', 'Keys', 'name', value)
self.load_keys_list(value)
- def var_changed_are_keys_builtin(self, *params):
+ def var_changed_keyset_source(self, *params):
"Process toggle between builtin key set and custom key set."
- value = self.are_keys_builtin.get()
+ value = self.keyset_source.get()
changes.add_option('main', 'Keys', 'default', value)
if value:
- self.var_changed_builtin_keys()
+ self.var_changed_builtin_name()
else:
- self.var_changed_custom_keys()
+ self.var_changed_custom_name()
def var_changed_keybinding(self, *params):
"Store change to a keybinding."
value = self.keybinding.get()
- key_set = self.custom_keys.get()
- event = self.list_bindings.get(ANCHOR).split()[0]
+ key_set = self.custom_name.get()
+ event = self.bindingslist.get(ANCHOR).split()[0]
if idleConf.IsCoreBinding(event):
changes.add_option('keys', key_set, event, value)
else: # Event is an extension binding.
@@ -997,14 +1038,14 @@ def var_changed_keybinding(self, *params):
def set_keys_type(self):
"Set available screen options based on builtin or custom key set."
- if self.are_keys_builtin.get():
- self.opt_menu_keys_builtin['state'] = NORMAL
- self.opt_menu_keys_custom['state'] = DISABLED
+ if self.keyset_source.get():
+ self.builtinlist['state'] = NORMAL
+ self.customlist['state'] = DISABLED
self.button_delete_custom_keys['state'] = DISABLED
else:
- self.opt_menu_keys_builtin['state'] = DISABLED
- self.radio_keys_custom['state'] = NORMAL
- self.opt_menu_keys_custom['state'] = NORMAL
+ self.builtinlist['state'] = DISABLED
+ self.custom_keyset_on['state'] = NORMAL
+ self.customlist['state'] = NORMAL
self.button_delete_custom_keys['state'] = NORMAL
def get_new_keys(self):
@@ -1016,13 +1057,13 @@ def get_new_keys(self):
changed, then a name for a custom key set needs to be
entered for the change to be applied.
"""
- list_index = self.list_bindings.index(ANCHOR)
- binding = self.list_bindings.get(list_index)
+ list_index = self.bindingslist.index(ANCHOR)
+ binding = self.bindingslist.get(list_index)
bind_name = binding.split()[0]
- if self.are_keys_builtin.get():
- current_key_set_name = self.builtin_keys.get()
+ if self.keyset_source.get():
+ current_key_set_name = self.builtin_name.get()
else:
- current_key_set_name = self.custom_keys.get()
+ current_key_set_name = self.custom_name.get()
current_bindings = idleConf.GetCurrentKeySet()
if current_key_set_name in changes['keys']: # unsaved changes
key_set_changes = changes['keys'][current_key_set_name]
@@ -1032,24 +1073,24 @@ def get_new_keys(self):
new_keys = GetKeysDialog(self, 'Get New Keys', bind_name,
current_key_sequences).result
if new_keys:
- if self.are_keys_builtin.get(): # Current key set is a built-in.
+ if self.keyset_source.get(): # Current key set is a built-in.
message = ('Your changes will be saved as a new Custom Key Set.'
' Enter a name for your new Custom Key Set below.')
new_keyset = self.get_new_keys_name(message)
if not new_keyset: # User cancelled custom key set creation.
- self.list_bindings.select_set(list_index)
- self.list_bindings.select_anchor(list_index)
+ self.bindingslist.select_set(list_index)
+ self.bindingslist.select_anchor(list_index)
return
else: # Create new custom key set based on previously active key set.
self.create_new_key_set(new_keyset)
- self.list_bindings.delete(list_index)
- self.list_bindings.insert(list_index, bind_name+' - '+new_keys)
- self.list_bindings.select_set(list_index)
- self.list_bindings.select_anchor(list_index)
+ self.bindingslist.delete(list_index)
+ self.bindingslist.insert(list_index, bind_name+' - '+new_keys)
+ self.bindingslist.select_set(list_index)
+ self.bindingslist.select_anchor(list_index)
self.keybinding.set(new_keys)
else:
- self.list_bindings.select_set(list_index)
- self.list_bindings.select_anchor(list_index)
+ self.bindingslist.select_set(list_index)
+ self.bindingslist.select_anchor(list_index)
def get_new_keys_name(self, message):
"Return new key set name from query popup."
@@ -1065,21 +1106,20 @@ def save_as_new_key_set(self):
if new_keys_name:
self.create_new_key_set(new_keys_name)
- def keybinding_selected(self, event):
+ def on_bindingslist_select(self, event):
"Activate button to assign new keys to selected action."
self.button_new_keys['state'] = NORMAL
def create_new_key_set(self, new_key_set_name):
"""Create a new custom key set with the given name.
- Create the new key set based on the previously active set
- with the current changes applied. Once it is saved, then
- activate the new key set.
+ Copy the bindings/keys from the previously active keyset
+ to the new keyset and activate the new custom keyset.
"""
- if self.are_keys_builtin.get():
- prev_key_set_name = self.builtin_keys.get()
+ if self.keyset_source.get():
+ prev_key_set_name = self.builtin_name.get()
else:
- prev_key_set_name = self.custom_keys.get()
+ prev_key_set_name = self.custom_name.get()
prev_keys = idleConf.GetCoreKeys(prev_key_set_name)
new_keys = {}
for event in prev_keys: # Add key set to changed items.
@@ -1096,8 +1136,8 @@ def create_new_key_set(self, new_key_set_name):
# Change GUI over to the new key set.
custom_key_list = idleConf.GetSectionList('user', 'keys')
custom_key_list.sort()
- self.opt_menu_keys_custom.SetMenu(custom_key_list, new_key_set_name)
- self.are_keys_builtin.set(0)
+ self.customlist.SetMenu(custom_key_list, new_key_set_name)
+ self.keyset_source.set(0)
self.set_keys_type()
def load_keys_list(self, keyset_name):
@@ -1105,14 +1145,14 @@ def load_keys_list(self, keyset_name):
An action/key binding can be selected to change the key binding.
"""
- reselect = 0
- if self.list_bindings.curselection():
- reselect = 1
- list_index = self.list_bindings.index(ANCHOR)
+ reselect = False
+ if self.bindingslist.curselection():
+ reselect = True
+ list_index = self.bindingslist.index(ANCHOR)
keyset = idleConf.GetKeySet(keyset_name)
bind_names = list(keyset.keys())
bind_names.sort()
- self.list_bindings.delete(0, END)
+ self.bindingslist.delete(0, END)
for bind_name in bind_names:
key = ' '.join(keyset[bind_name])
bind_name = bind_name[2:-2] # Trim off the angle brackets.
@@ -1120,17 +1160,21 @@ def load_keys_list(self, keyset_name):
# Handle any unsaved changes to this key set.
if bind_name in changes['keys'][keyset_name]:
key = changes['keys'][keyset_name][bind_name]
- self.list_bindings.insert(END, bind_name+' - '+key)
+ self.bindingslist.insert(END, bind_name+' - '+key)
if reselect:
- self.list_bindings.see(list_index)
- self.list_bindings.select_set(list_index)
- self.list_bindings.select_anchor(list_index)
+ self.bindingslist.see(list_index)
+ self.bindingslist.select_set(list_index)
+ self.bindingslist.select_anchor(list_index)
def save_new_key_set(self, keyset_name, keyset):
"""Save a newly created core key set.
+ Add keyset to idleConf.userCfg['keys'], not to disk.
+ If the keyset doesn't exist, it is created. The
+ binding/keys are taken from the keyset argument.
+
keyset_name - string, the name of the new key set
- keyset - dictionary containing the new key set
+ keyset - dictionary containing the new keybindings
"""
if not idleConf.userCfg['keys'].has_section(keyset_name):
idleConf.userCfg['keys'].add_section(keyset_name)
@@ -1145,7 +1189,7 @@ def delete_custom_keys(self):
reverts to the default. The custom key set is permanently
deleted from the config file.
"""
- keyset_name=self.custom_keys.get()
+ keyset_name=self.custom_name.get()
delmsg = 'Are you sure you wish to delete the key set %r ?'
if not tkMessageBox.askyesno(
'Delete Key Set', delmsg % keyset_name, parent=self):
@@ -1157,14 +1201,14 @@ def delete_custom_keys(self):
item_list = idleConf.GetSectionList('user', 'keys')
item_list.sort()
if not item_list:
- self.radio_keys_custom['state'] = DISABLED
- self.opt_menu_keys_custom.SetMenu(item_list, '- no custom keys -')
+ self.custom_keyset_on['state'] = DISABLED
+ self.customlist.SetMenu(item_list, '- no custom keys -')
else:
- self.opt_menu_keys_custom.SetMenu(item_list, item_list[0])
+ self.customlist.SetMenu(item_list, item_list[0])
# Revert to default key set.
- self.are_keys_builtin.set(idleConf.defaultCfg['main']
+ self.keyset_source.set(idleConf.defaultCfg['main']
.Get('Keys', 'default'))
- self.builtin_keys.set(idleConf.defaultCfg['main'].Get('Keys', 'name')
+ self.builtin_name.set(idleConf.defaultCfg['main'].Get('Keys', 'name')
or idleConf.default_keys())
# User can't back out of these changes, they must be applied now.
changes.save_all()
@@ -1438,22 +1482,21 @@ def create_page_font_tab(self):
which invokes the default callback to add an entry to
changes. Load_tab_cfg initializes space_num to default.
- Widget Structure: (*) widgets bound to self
- frame (of tab_pages)
- frame_font: LabelFrame
- frame_font_name: Frame
- font_name_title: Label
- (*)fontlist: ListBox - font_name
- scroll_font: Scrollbar
- frame_font_param: Frame
- font_size_title: Label
- (*)sizelist: DynOptionMenu - font_size
- (*)bold_toggle: Checkbutton - font_bold
- frame_font_sample: Frame
- (*)font_sample: Label
- frame_indent: LabelFrame
- indent_title: Label
- (*)indent_scale: Scale - space_num
+ Widgets for FontPage(Frame): (*) widgets bound to self
+ frame_font: LabelFrame
+ frame_font_name: Frame
+ font_name_title: Label
+ (*)fontlist: ListBox - font_name
+ scroll_font: Scrollbar
+ frame_font_param: Frame
+ font_size_title: Label
+ (*)sizelist: DynOptionMenu - font_size
+ (*)bold_toggle: Checkbutton - font_bold
+ frame_font_sample: Frame
+ (*)font_sample: Label
+ frame_indent: LabelFrame
+ indent_title: Label
+ (*)indent_scale: Scale - space_num
"""
self.font_name = tracers.add(StringVar(self), self.var_changed_font)
self.font_size = tracers.add(StringVar(self), self.var_changed_font)
@@ -1633,30 +1676,29 @@ def create_page_general(self):
set_add_delete_state. All but load call update_help_changes to
rewrite changes['main']['HelpFiles'].
- Widget Structure: (*) widgets bound to self
- frame
- frame_run: LabelFrame
- startup_title: Label
- (*)startup_editor_on: Radiobutton - startup_edit
- (*)startup_shell_on: Radiobutton - startup_edit
- frame_save: LabelFrame
- run_save_title: Label
- (*)save_ask_on: Radiobutton - autosave
- (*)save_auto_on: Radiobutton - autosave
- frame_win_size: LabelFrame
- win_size_title: Label
- win_width_title: Label
- (*)win_width_int: Entry - win_width
- win_height_title: Label
- (*)win_height_int: Entry - win_height
- frame_help: LabelFrame
- frame_helplist: Frame
- frame_helplist_buttons: Frame
- (*)button_helplist_edit
- (*)button_helplist_add
- (*)button_helplist_remove
- (*)helplist: ListBox
- scroll_helplist: Scrollbar
+ Widgets for GenPage(Frame): (*) widgets bound to self
+ frame_run: LabelFrame
+ startup_title: Label
+ (*)startup_editor_on: Radiobutton - startup_edit
+ (*)startup_shell_on: Radiobutton - startup_edit
+ frame_save: LabelFrame
+ run_save_title: Label
+ (*)save_ask_on: Radiobutton - autosave
+ (*)save_auto_on: Radiobutton - autosave
+ frame_win_size: LabelFrame
+ win_size_title: Label
+ win_width_title: Label
+ (*)win_width_int: Entry - win_width
+ win_height_title: Label
+ (*)win_height_int: Entry - win_height
+ frame_help: LabelFrame
+ frame_helplist: Frame
+ frame_helplist_buttons: Frame
+ (*)button_helplist_edit
+ (*)button_helplist_add
+ (*)button_helplist_remove
+ (*)helplist: ListBox
+ scroll_helplist: Scrollbar
"""
self.startup_edit = tracers.add(
IntVar(self), ('main', 'General', 'editor-on-startup'))
diff --git a/Lib/idlelib/idle_test/test_configdialog.py b/Lib/idlelib/idle_test/test_configdialog.py
index cd78482150c..b07a65cf56a 100644
--- a/Lib/idlelib/idle_test/test_configdialog.py
+++ b/Lib/idlelib/idle_test/test_configdialog.py
@@ -1,7 +1,7 @@
"""Test idlelib.configdialog.
Half the class creates dialog, half works with user customizations.
-Coverage: 63%.
+Coverage: 81%.
"""
from idlelib import configdialog
from test.support import requires
@@ -29,6 +29,7 @@
mainpage = changes['main']
highpage = changes['highlight']
keyspage = changes['keys']
+extpage = changes['extensions']
def setUpModule():
global root, dialog
@@ -59,8 +60,6 @@ class FontPageTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
page = cls.page = dialog.fontpage
- #dialog.note.insert(0, page, text='copy')
- #dialog.note.add(page, text='copyfgfg')
dialog.note.select(page)
page.set_samples = Func() # Mask instance method.
@@ -120,7 +119,7 @@ def test_fontlist_mouse(self):
# Click on item should select that item.
d = self.page
if d.fontlist.size() < 2:
- cls.skipTest('need at least 2 fonts')
+ self.skipTest('need at least 2 fonts')
fontlist = d.fontlist
fontlist.activate(0)
@@ -233,11 +232,391 @@ def setUp(self):
changes.clear()
-class KeysTest(unittest.TestCase):
+class KeyTest(unittest.TestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ d = dialog
+ dialog.note.select(d.keyspage)
+ d.set_keys_type = Func()
+ d.load_keys_list = Func()
+
+ @classmethod
+ def tearDownClass(cls):
+ d = dialog
+ del d.set_keys_type, d.load_keys_list
def setUp(self):
+ d = dialog
+ # The following is needed for test_load_key_cfg, _delete_custom_keys.
+ # This may indicate a defect in some test or function.
+ for section in idleConf.GetSectionList('user', 'keys'):
+ idleConf.userCfg['keys'].remove_section(section)
+ changes.clear()
+ d.set_keys_type.called = 0
+ d.load_keys_list.called = 0
+
+ def test_load_key_cfg(self):
+ tracers.detach()
+ d = dialog
+ eq = self.assertEqual
+
+ # Use builtin keyset with no user keysets created.
+ idleConf.CurrentKeys = mock.Mock(return_value='IDLE Classic OSX')
+ d.load_key_cfg()
+ self.assertTrue(d.keyset_source.get())
+ # builtinlist sets variable builtin_name to the CurrentKeys default.
+ eq(d.builtin_name.get(), 'IDLE Classic OSX')
+ eq(d.custom_name.get(), '- no custom keys -')
+ eq(d.custom_keyset_on['state'], DISABLED)
+ eq(d.set_keys_type.called, 1)
+ eq(d.load_keys_list.called, 1)
+ eq(d.load_keys_list.args, ('IDLE Classic OSX', ))
+
+ # Builtin keyset with non-empty user keyset list.
+ idleConf.SetOption('keys', 'test1', 'option', 'value')
+ idleConf.SetOption('keys', 'test2', 'option2', 'value2')
+ d.load_key_cfg()
+ eq(d.builtin_name.get(), 'IDLE Classic OSX')
+ eq(d.custom_name.get(), 'test1')
+ eq(d.set_keys_type.called, 2)
+ eq(d.load_keys_list.called, 2)
+ eq(d.load_keys_list.args, ('IDLE Classic OSX', ))
+
+ # Use custom keyset.
+ idleConf.CurrentKeys = mock.Mock(return_value='test2')
+ idleConf.default_keys = mock.Mock(return_value='IDLE Modern Unix')
+ idleConf.SetOption('main', 'Keys', 'default', '0')
+ d.load_key_cfg()
+ self.assertFalse(d.keyset_source.get())
+ eq(d.builtin_name.get(), 'IDLE Modern Unix')
+ eq(d.custom_name.get(), 'test2')
+ eq(d.set_keys_type.called, 3)
+ eq(d.load_keys_list.called, 3)
+ eq(d.load_keys_list.args, ('test2', ))
+
+ del idleConf.CurrentKeys, idleConf.default_keys
+ tracers.attach()
+
+ def test_keyset_source(self):
+ eq = self.assertEqual
+ d = dialog
+ # Test these separately.
+ d.var_changed_builtin_name = Func()
+ d.var_changed_custom_name = Func()
+ # Builtin selected.
+ d.builtin_keyset_on.invoke()
+ eq(mainpage, {'Keys': {'default': 'True'}})
+ eq(d.var_changed_builtin_name.called, 1)
+ eq(d.var_changed_custom_name.called, 0)
changes.clear()
+ # Custom selected.
+ d.custom_keyset_on['state'] = NORMAL
+ d.custom_keyset_on.invoke()
+ self.assertEqual(mainpage, {'Keys': {'default': 'False'}})
+ eq(d.var_changed_builtin_name.called, 1)
+ eq(d.var_changed_custom_name.called, 1)
+ del d.var_changed_builtin_name, d.var_changed_custom_name
+
+ def test_builtin_name(self):
+ eq = self.assertEqual
+ d = dialog
+ idleConf.userCfg['main'].remove_section('Keys')
+ item_list = ['IDLE Classic Windows', 'IDLE Classic OSX',
+ 'IDLE Modern UNIX']
+
+ # Not in old_keys, defaults name to first item.
+ d.builtinlist.SetMenu(item_list, 'IDLE Modern UNIX')
+ eq(mainpage, {'Keys': {'name': 'IDLE Classic Windows',
+ 'name2': 'IDLE Modern UNIX'}})
+ eq(d.keys_message['text'], 'New key set, see Help')
+ eq(d.load_keys_list.called, 1)
+ eq(d.load_keys_list.args, ('IDLE Modern UNIX', ))
+
+ # Not in old keys - uses name2.
+ changes.clear()
+ idleConf.SetOption('main', 'Keys', 'name', 'IDLE Classic Unix')
+ d.builtinlist.SetMenu(item_list, 'IDLE Modern UNIX')
+ eq(mainpage, {'Keys': {'name2': 'IDLE Modern UNIX'}})
+ eq(d.keys_message['text'], 'New key set, see Help')
+ eq(d.load_keys_list.called, 2)
+ eq(d.load_keys_list.args, ('IDLE Modern UNIX', ))
+
+ # Builtin name in old_keys.
+ changes.clear()
+ d.builtinlist.SetMenu(item_list, 'IDLE Classic OSX')
+ eq(mainpage, {'Keys': {'name': 'IDLE Classic OSX', 'name2': ''}})
+ eq(d.keys_message['text'], '')
+ eq(d.load_keys_list.called, 3)
+ eq(d.load_keys_list.args, ('IDLE Classic OSX', ))
+
+ def test_custom_name(self):
+ d = dialog
+
+ # If no selections, doesn't get added.
+ d.customlist.SetMenu([], '- no custom keys -')
+ self.assertNotIn('Keys', mainpage)
+ self.assertEqual(d.load_keys_list.called, 0)
+
+ # Custom name selected.
+ changes.clear()
+ d.customlist.SetMenu(['a', 'b', 'c'], 'c')
+ self.assertEqual(mainpage, {'Keys': {'name': 'c'}})
+ self.assertEqual(d.load_keys_list.called, 1)
+
+ def test_keybinding(self):
+ d = dialog
+ d.custom_name.set('my custom keys')
+ d.bindingslist.delete(0, 'end')
+ d.bindingslist.insert(0, 'copy')
+ d.bindingslist.insert(1, 'expand-word')
+ d.bindingslist.selection_set(0)
+ d.bindingslist.selection_anchor(0)
+ # Core binding - adds to keys.
+ d.keybinding.set('<Key-F11>')
+ self.assertEqual(keyspage,
+ {'my custom keys': {'copy': '<Key-F11>'}})
+ # Not a core binding - adds to extensions.
+ d.bindingslist.selection_set(1)
+ d.bindingslist.selection_anchor(1)
+ d.keybinding.set('<Key-F11>')
+ self.assertEqual(extpage,
+ {'AutoExpand_cfgBindings': {'expand-word': '<Key-F11>'}})
+
+ def test_set_keys_type(self):
+ eq = self.assertEqual
+ d = dialog
+ del d.set_keys_type
+
+ # Builtin keyset selected.
+ d.keyset_source.set(True)
+ d.set_keys_type()
+ eq(d.builtinlist['state'], NORMAL)
+ eq(d.customlist['state'], DISABLED)
+ eq(d.button_delete_custom_keys['state'], DISABLED)
+
+ # Custom keyset selected.
+ d.keyset_source.set(False)
+ d.set_keys_type()
+ eq(d.builtinlist['state'], DISABLED)
+ eq(d.custom_keyset_on['state'], NORMAL)
+ eq(d.customlist['state'], NORMAL)
+ eq(d.button_delete_custom_keys['state'], NORMAL)
+ d.set_keys_type = Func()
+
+ def test_get_new_keys(self):
+ eq = self.assertEqual
+ d = dialog
+ orig_getkeysdialog = configdialog.GetKeysDialog
+ gkd = configdialog.GetKeysDialog = Func(return_self=True)
+ gnkn = d.get_new_keys_name = Func()
+
+ d.button_new_keys['state'] = NORMAL
+ d.bindingslist.delete(0, 'end')
+ d.bindingslist.insert(0, 'copy - <Control-Shift-Key-C>')
+ d.bindingslist.selection_set(0)
+ d.bindingslist.selection_anchor(0)
+ d.keybinding.set('Key-a')
+ d.keyset_source.set(True) # Default keyset.
+
+ # Default keyset; no change to binding.
+ gkd.result = ''
+ d.button_new_keys.invoke()
+ eq(d.bindingslist.get('anchor'), 'copy - <Control-Shift-Key-C>')
+ # Keybinding isn't changed when there isn't a change entered.
+ eq(d.keybinding.get(), 'Key-a')
+
+ # Default keyset; binding changed.
+ gkd.result = '<Key-F11>'
+ # No keyset name selected therefore binding not saved.
+ gnkn.result = ''
+ d.button_new_keys.invoke()
+ eq(gnkn.called, 1)
+ eq(d.bindingslist.get('anchor'), 'copy - <Control-Shift-Key-C>')
+ # Keyset name selected.
+ gnkn.result = 'My New Key Set'
+ d.button_new_keys.invoke()
+ eq(d.custom_name.get(), gnkn.result)
+ eq(d.bindingslist.get('anchor'), 'copy - <Key-F11>')
+ eq(d.keybinding.get(), '<Key-F11>')
+
+ # User keyset; binding changed.
+ d.keyset_source.set(False) # Custom keyset.
+ gnkn.called = 0
+ gkd.result = '<Key-p>'
+ d.button_new_keys.invoke()
+ eq(gnkn.called, 0)
+ eq(d.bindingslist.get('anchor'), 'copy - <Key-p>')
+ eq(d.keybinding.get(), '<Key-p>')
+
+ del d.get_new_keys_name
+ configdialog.GetKeysDialog = orig_getkeysdialog
+
+ def test_get_new_keys_name(self):
+ orig_sectionname = configdialog.SectionName
+ sn = configdialog.SectionName = Func(return_self=True)
+ d = dialog
+
+ sn.result = 'New Keys'
+ self.assertEqual(d.get_new_keys_name(''), 'New Keys')
+
+ configdialog.SectionName = orig_sectionname
+
+ def test_save_as_new_key_set(self):
+ d = dialog
+ gnkn = d.get_new_keys_name = Func()
+ d.keyset_source.set(True)
+
+ # No name entered.
+ gnkn.result = ''
+ d.button_save_custom_keys.invoke()
+
+ # Name entered.
+ gnkn.result = 'my new key set'
+ gnkn.called = 0
+ self.assertNotIn(gnkn.result, idleConf.userCfg['keys'])
+ d.button_save_custom_keys.invoke()
+ self.assertIn(gnkn.result, idleConf.userCfg['keys'])
+
+ del d.get_new_keys_name
+
+ def test_on_bindingslist_select(self):
+ d = dialog
+ b = d.bindingslist
+ b.delete(0, 'end')
+ b.insert(0, 'copy')
+ b.insert(1, 'find')
+ b.activate(0)
+
+ b.focus_force()
+ b.see(1)
+ b.update()
+ x, y, dx, dy = b.bbox(1)
+ x += dx // 2
+ y += dy // 2
+ b.event_generate('<Enter>', x=0, y=0)
+ b.event_generate('<Motion>', x=x, y=y)
+ b.event_generate('<Button-1>', x=x, y=y)
+ b.event_generate('<ButtonRelease-1>', x=x, y=y)
+ self.assertEqual(b.get('anchor'), 'find')
+ self.assertEqual(d.button_new_keys['state'], NORMAL)
+
+ def test_create_new_key_set_and_save_new_key_set(self):
+ eq = self.assertEqual
+ d = dialog
+
+ # Use default as previously active keyset.
+ d.keyset_source.set(True)
+ d.builtin_name.set('IDLE Classic Windows')
+ first_new = 'my new custom key set'
+ second_new = 'my second custom keyset'
+
+ # No changes, so keysets are an exact copy.
+ self.assertNotIn(first_new, idleConf.userCfg)
+ d.create_new_key_set(first_new)
+ eq(idleConf.GetSectionList('user', 'keys'), [first_new])
+ eq(idleConf.GetKeySet('IDLE Classic Windows'),
+ idleConf.GetKeySet(first_new))
+ eq(d.custom_name.get(), first_new)
+ self.assertFalse(d.keyset_source.get()) # Use custom set.
+ eq(d.set_keys_type.called, 1)
+
+ # Test that changed keybindings are in new keyset.
+ changes.add_option('keys', first_new, 'copy', '<Key-F11>')
+ self.assertNotIn(second_new, idleConf.userCfg)
+ d.create_new_key_set(second_new)
+ eq(idleConf.GetSectionList('user', 'keys'), [first_new, second_new])
+ self.assertNotEqual(idleConf.GetKeySet(first_new),
+ idleConf.GetKeySet(second_new))
+ # Check that difference in keysets was in option `copy` from `changes`.
+ idleConf.SetOption('keys', first_new, 'copy', '<Key-F11>')
+ eq(idleConf.GetKeySet(first_new), idleConf.GetKeySet(second_new))
+
+ def test_load_keys_list(self):
+ eq = self.assertEqual
+ d = dialog
+ gks = idleConf.GetKeySet = Func()
+ del d.load_keys_list
+ b = d.bindingslist
+
+ b.delete(0, 'end')
+ b.insert(0, '<<find>>')
+ b.insert(1, '<<help>>')
+ gks.result = {'<<copy>>': ['<Control-Key-c>', '<Control-Key-C>'],
+ '<<force-open-completions>>': ['<Control-Key-space>'],
+ '<<spam>>': ['<Key-F11>']}
+ changes.add_option('keys', 'my keys', 'spam', '<Shift-Key-a>')
+ expected = ('copy - <Control-Key-c> <Control-Key-C>',
+ 'force-open-completions - <Control-Key-space>',
+ 'spam - <Shift-Key-a>')
+
+ # No current selection.
+ d.load_keys_list('my keys')
+ eq(b.get(0, 'end'), expected)
+ eq(b.get('anchor'), '')
+ eq(b.curselection(), ())
+
+ # Check selection.
+ b.selection_set(1)
+ b.selection_anchor(1)
+ d.load_keys_list('my keys')
+ eq(b.get(0, 'end'), expected)
+ eq(b.get('anchor'), 'force-open-completions - <Control-Key-space>')
+ eq(b.curselection(), (1, ))
+
+ # Change selection.
+ b.selection_set(2)
+ b.selection_anchor(2)
+ d.load_keys_list('my keys')
+ eq(b.get(0, 'end'), expected)
+ eq(b.get('anchor'), 'spam - <Shift-Key-a>')
+ eq(b.curselection(), (2, ))
+ d.load_keys_list = Func()
+
+ del idleConf.GetKeySet
+
+ def test_delete_custom_keys(self):
+ eq = self.assertEqual
+ d = dialog
+ d.button_delete_custom_keys['state'] = NORMAL
+ yesno = configdialog.tkMessageBox.askyesno = Func()
+ d.deactivate_current_config = Func()
+ d.activate_config_changes = Func()
+
+ keyset_name = 'spam key set'
+ idleConf.userCfg['keys'].SetOption(keyset_name, 'name', 'value')
+ keyspage[keyset_name] = {'option': 'True'}
+
+ # Force custom keyset.
+ d.keyset_source.set(False)
+ d.custom_name.set(keyset_name)
+
+ # Cancel deletion.
+ yesno.result = False
+ d.button_delete_custom_keys.invoke()
+ eq(yesno.called, 1)
+ eq(keyspage[keyset_name], {'option': 'True'})
+ eq(idleConf.GetSectionList('user', 'keys'), ['spam key set'])
+ eq(d.deactivate_current_config.called, 0)
+ eq(d.activate_config_changes.called, 0)
+ eq(d.set_keys_type.called, 0)
+
+ # Confirm deletion.
+ yesno.result = True
+ d.button_delete_custom_keys.invoke()
+ eq(yesno.called, 2)
+ self.assertNotIn(keyset_name, keyspage)
+ eq(idleConf.GetSectionList('user', 'keys'), [])
+ eq(d.custom_keyset_on['state'], DISABLED)
+ eq(d.custom_name.get(), '- no custom keys -')
+ eq(d.deactivate_current_config.called, 1)
+ eq(d.activate_config_changes.called, 1)
+ eq(d.set_keys_type.called, 1)
+
+ del d.activate_config_changes, d.deactivate_current_config
+ del configdialog.tkMessageBox.askyesno
+
class GenPageTest(unittest.TestCase):
"""Test that general tab widgets enable users to make changes.
diff --git a/Misc/NEWS.d/next/IDLE/2017-08-03-17-54-02.bpo-31002.kUSgTE.rst b/Misc/NEWS.d/next/IDLE/2017-08-03-17-54-02.bpo-31002.kUSgTE.rst
new file mode 100644
index 00000000000..1708be72b8b
--- /dev/null
+++ b/Misc/NEWS.d/next/IDLE/2017-08-03-17-54-02.bpo-31002.kUSgTE.rst
@@ -0,0 +1 @@
+Add tests for configdialog keys tab. Patch by Cheryl Sabella.
1
0
![](https://secure.gravatar.com/avatar/cc7737cd64a84f1b5c61a160798e97ee.jpg?s=120&d=mm&r=g)
14 Aug '17
https://github.com/python/cpython/commit/2f8964634918bdf09107c49a2d5ca62460…
commit: 2f8964634918bdf09107c49a2d5ca62460091e54
branch: master
author: Cheryl Sabella <cheryl.sabella(a)gmail.com>
committer: Terry Jan Reedy <tjreedy(a)udel.edu>
date: 2017-08-14T21:21:43-04:00
summary:
bpo-31002: IDLE: Add tests for configdialog keys tab (#2996)
Patch by Cheryl Sabella.
files:
A Misc/NEWS.d/next/IDLE/2017-08-03-17-54-02.bpo-31002.kUSgTE.rst
M Lib/idlelib/configdialog.py
M Lib/idlelib/idle_test/test_configdialog.py
diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py
index 63172371a5e..66219f1820e 100644
--- a/Lib/idlelib/configdialog.py
+++ b/Lib/idlelib/configdialog.py
@@ -251,25 +251,24 @@ def create_page_highlight(self):
delete_custom_theme: Ativate default [button_delete_custom_theme].
save_new_theme: Save to userCfg['theme'] (is function).
- Widget Structure: (*) widgets bound to self
- frame
- frame_custom: LabelFrame
- (*)highlight_sample: Text
- (*)frame_color_set: Frame
- button_set_color: Button
- (*)opt_menu_highlight_target: DynOptionMenu - highlight_target
- frame_fg_bg_toggle: Frame
- (*)radio_fg: Radiobutton - fg_bg_toggle
- (*)radio_bg: Radiobutton - fg_bg_toggle
- button_save_custom_theme: Button
- frame_theme: LabelFrame
- theme_type_title: Label
- (*)radio_theme_builtin: Radiobutton - is_builtin_theme
- (*)radio_theme_custom: Radiobutton - is_builtin_theme
- (*)opt_menu_theme_builtin: DynOptionMenu - builtin_theme
- (*)opt_menu_theme_custom: DynOptionMenu - custom_theme
- (*)button_delete_custom_theme: Button
- (*)new_custom_theme: Label
+ Widgets of highlights page frame: (*) widgets bound to self
+ frame_custom: LabelFrame
+ (*)highlight_sample: Text
+ (*)frame_color_set: Frame
+ button_set_color: Button
+ (*)opt_menu_highlight_target: DynOptionMenu - highlight_target
+ frame_fg_bg_toggle: Frame
+ (*)radio_fg: Radiobutton - fg_bg_toggle
+ (*)radio_bg: Radiobutton - fg_bg_toggle
+ button_save_custom_theme: Button
+ frame_theme: LabelFrame
+ theme_type_title: Label
+ (*)radio_theme_builtin: Radiobutton - is_builtin_theme
+ (*)radio_theme_custom: Radiobutton - is_builtin_theme
+ (*)opt_menu_theme_builtin: DynOptionMenu - builtin_theme
+ (*)opt_menu_theme_custom: DynOptionMenu - custom_theme
+ (*)button_delete_custom_theme: Button
+ (*)new_custom_theme: Label
"""
self.theme_elements={
'Normal Text': ('normal', '00'),
@@ -796,52 +795,92 @@ def delete_custom_theme(self):
def create_page_keys(self):
"""Return frame of widgets for Keys tab.
+ Enable users to provisionally change both individual and sets of
+ keybindings (shortcut keys). Except for features implemented as
+ extensions, keybindings are stored in complete sets called
+ keysets. Built-in keysets in idlelib/config-keys.def are fixed
+ as far as the dialog is concerned. Any keyset can be used as the
+ base for a new custom keyset, stored in .idlerc/config-keys.cfg.
+
+ Function load_key_cfg() initializes tk variables and keyset
+ lists and calls load_keys_list for the current keyset.
+ Radiobuttons builtin_keyset_on and custom_keyset_on toggle var
+ keyset_source, which controls if the current set of keybindings
+ are from a builtin or custom keyset. DynOptionMenus builtinlist
+ and customlist contain lists of the builtin and custom keysets,
+ respectively, and the current item from each list is stored in
+ vars builtin_name and custom_name.
+
+ Button delete_custom_keys invokes delete_custom_keys() to delete
+ a custom keyset from idleConf.userCfg['keys'] and changes. Button
+ save_custom_keys invokes save_as_new_key_set() which calls
+ get_new_keys_name() and create_new_key_set() to save a custom keyset
+ and its keybindings to idleConf.userCfg['keys'].
+
+ Listbox bindingslist contains all of the keybindings for the
+ selected keyset. The keybindings are loaded in load_keys_list()
+ and are pairs of (event, [keys]) where keys can be a list
+ of one or more key combinations to bind to the same event.
+ Mouse button 1 click invokes on_bindingslist_select(), which
+ allows button_new_keys to be clicked.
+
+ So, an item is selected in listbindings, which activates
+ button_new_keys, and clicking button_new_keys calls function
+ get_new_keys(). Function get_new_keys() gets the key mappings from the
+ current keyset for the binding event item that was selected. The
+ function then displays another dialog, GetKeysDialog, with the
+ selected binding event and current keys and always new key sequences
+ to be entered for that binding event. If the keys aren't
+ changed, nothing happens. If the keys are changed and the keyset
+ is a builtin, function get_new_keys_name() will be called
+ for input of a custom keyset name. If no name is given, then the
+ change to the keybinding will abort and no updates will be made. If
+ a custom name is entered in the prompt or if the current keyset was
+ already custom (and thus didn't require a prompt), then
+ idleConf.userCfg['keys'] is updated in function create_new_key_set()
+ with the change to the event binding. The item listing in bindingslist
+ is updated with the new keys. Var keybinding is also set which invokes
+ the callback function, var_changed_keybinding, to add the change to
+ the 'keys' or 'extensions' changes tracker based on the binding type.
+
Tk Variables:
- builtin_keys: Menu variable for built-in keybindings.
- custom_keys: Menu variable for custom keybindings.
- are_keys_builtin: Selector for built-in or custom keybindings.
keybinding: Action/key bindings.
Methods:
- load_key_config: Set table.
load_keys_list: Reload active set.
- keybinding_selected: Bound to list_bindings button release.
- get_new_keys: Command for button_new_keys.
- get_new_keys_name: Call popup.
create_new_key_set: Combine active keyset and changes.
- set_keys_type: Command for are_keys_builtin.
- delete_custom_keys: Command for button_delete_custom_keys.
- save_as_new_key_set: Command for button_save_custom_keys.
+ set_keys_type: Command for keyset_source.
save_new_key_set: Save to idleConf.userCfg['keys'] (is function).
deactivate_current_config: Remove keys bindings in editors.
- Widget Structure: (*) widgets bound to self
- frame
- frame_custom: LabelFrame
- frame_target: Frame
- target_title: Label
- scroll_target_y: Scrollbar
- scroll_target_x: Scrollbar
- (*)list_bindings: ListBox
- (*)button_new_keys: Button
- frame_key_sets: LabelFrame
- frames[0]: Frame
- (*)radio_keys_builtin: Radiobutton - are_keys_builtin
- (*)radio_keys_custom: Radiobutton - are_keys_builtin
- (*)opt_menu_keys_builtin: DynOptionMenu - builtin_keys
- (*)opt_menu_keys_custom: DynOptionMenu - custom_keys
- (*)new_custom_keys: Label
- frames[1]: Frame
- (*)button_delete_custom_keys: Button
- button_save_custom_keys: Button
+ Widgets for keys page frame: (*) widgets bound to self
+ frame_key_sets: LabelFrame
+ frames[0]: Frame
+ (*)builtin_keyset_on: Radiobutton - var keyset_source
+ (*)custom_keyset_on: Radiobutton - var keyset_source
+ (*)builtinlist: DynOptionMenu - var builtin_name,
+ func keybinding_selected
+ (*)customlist: DynOptionMenu - var custom_name,
+ func keybinding_selected
+ (*)keys_message: Label
+ frames[1]: Frame
+ (*)button_delete_custom_keys: Button - delete_custom_keys
+ (*)button_save_custom_keys: Button - save_as_new_key_set
+ frame_custom: LabelFrame
+ frame_target: Frame
+ target_title: Label
+ scroll_target_y: Scrollbar
+ scroll_target_x: Scrollbar
+ (*)bindingslist: ListBox - on_bindingslist_select
+ (*)button_new_keys: Button - get_new_keys & ..._name
"""
parent = self.parent
- self.builtin_keys = tracers.add(
- StringVar(parent), self.var_changed_builtin_keys)
- self.custom_keys = tracers.add(
- StringVar(parent), self.var_changed_custom_keys)
- self.are_keys_builtin = tracers.add(
- BooleanVar(parent), self.var_changed_are_keys_builtin)
+ self.builtin_name = tracers.add(
+ StringVar(parent), self.var_changed_builtin_name)
+ self.custom_name = tracers.add(
+ StringVar(parent), self.var_changed_custom_name)
+ self.keyset_source = tracers.add(
+ BooleanVar(parent), self.var_changed_keyset_source)
self.keybinding = tracers.add(
StringVar(parent), self.var_changed_keybinding)
@@ -858,36 +897,37 @@ def create_page_keys(self):
target_title = Label(frame_target, text='Action - Key(s)')
scroll_target_y = Scrollbar(frame_target)
scroll_target_x = Scrollbar(frame_target, orient=HORIZONTAL)
- self.list_bindings = Listbox(
+ self.bindingslist = Listbox(
frame_target, takefocus=FALSE, exportselection=FALSE)
- self.list_bindings.bind('<ButtonRelease-1>', self.keybinding_selected)
- scroll_target_y.config(command=self.list_bindings.yview)
- scroll_target_x.config(command=self.list_bindings.xview)
- self.list_bindings.config(yscrollcommand=scroll_target_y.set)
- self.list_bindings.config(xscrollcommand=scroll_target_x.set)
+ self.bindingslist.bind('<ButtonRelease-1>',
+ self.on_bindingslist_select)
+ scroll_target_y['command'] = self.bindingslist.yview
+ scroll_target_x['command'] = self.bindingslist.xview
+ self.bindingslist['yscrollcommand'] = scroll_target_y.set
+ self.bindingslist['xscrollcommand'] = scroll_target_x.set
self.button_new_keys = Button(
frame_custom, text='Get New Keys for Selection',
command=self.get_new_keys, state=DISABLED)
#frame_key_sets
frames = [Frame(frame_key_sets, padx=2, pady=2, borderwidth=0)
for i in range(2)]
- self.radio_keys_builtin = Radiobutton(
- frames[0], variable=self.are_keys_builtin, value=1,
+ self.builtin_keyset_on = Radiobutton(
+ frames[0], variable=self.keyset_source, value=1,
command=self.set_keys_type, text='Use a Built-in Key Set')
- self.radio_keys_custom = Radiobutton(
- frames[0], variable=self.are_keys_builtin, value=0,
+ self.custom_keyset_on = Radiobutton(
+ frames[0], variable=self.keyset_source, value=0,
command=self.set_keys_type, text='Use a Custom Key Set')
- self.opt_menu_keys_builtin = DynOptionMenu(
- frames[0], self.builtin_keys, None, command=None)
- self.opt_menu_keys_custom = DynOptionMenu(
- frames[0], self.custom_keys, None, command=None)
+ self.builtinlist = DynOptionMenu(
+ frames[0], self.builtin_name, None, command=None)
+ self.customlist = DynOptionMenu(
+ frames[0], self.custom_name, None, command=None)
self.button_delete_custom_keys = Button(
frames[1], text='Delete Custom Key Set',
command=self.delete_custom_keys)
- button_save_custom_keys = Button(
+ self.button_save_custom_keys = Button(
frames[1], text='Save as New Custom Key Set',
command=self.save_as_new_key_set)
- self.new_custom_keys = Label(frames[0], bd=2)
+ self.keys_message = Label(frames[0], bd=2)
##widget packing
#body
@@ -900,17 +940,17 @@ def create_page_keys(self):
frame_target.columnconfigure(0, weight=1)
frame_target.rowconfigure(1, weight=1)
target_title.grid(row=0, column=0, columnspan=2, sticky=W)
- self.list_bindings.grid(row=1, column=0, sticky=NSEW)
+ self.bindingslist.grid(row=1, column=0, sticky=NSEW)
scroll_target_y.grid(row=1, column=1, sticky=NS)
scroll_target_x.grid(row=2, column=0, sticky=EW)
#frame_key_sets
- self.radio_keys_builtin.grid(row=0, column=0, sticky=W+NS)
- self.radio_keys_custom.grid(row=1, column=0, sticky=W+NS)
- self.opt_menu_keys_builtin.grid(row=0, column=1, sticky=NSEW)
- self.opt_menu_keys_custom.grid(row=1, column=1, sticky=NSEW)
- self.new_custom_keys.grid(row=0, column=2, sticky=NSEW, padx=5, pady=5)
+ self.builtin_keyset_on.grid(row=0, column=0, sticky=W+NS)
+ self.custom_keyset_on.grid(row=1, column=0, sticky=W+NS)
+ self.builtinlist.grid(row=0, column=1, sticky=NSEW)
+ self.customlist.grid(row=1, column=1, sticky=NSEW)
+ self.keys_message.grid(row=0, column=2, sticky=NSEW, padx=5, pady=5)
self.button_delete_custom_keys.pack(side=LEFT, fill=X, expand=True, padx=2)
- button_save_custom_keys.pack(side=LEFT, fill=X, expand=True, padx=2)
+ self.button_save_custom_keys.pack(side=LEFT, fill=X, expand=True, padx=2)
frames[0].pack(side=TOP, fill=BOTH, expand=True)
frames[1].pack(side=TOP, fill=X, expand=True, pady=2)
return frame
@@ -918,35 +958,35 @@ def create_page_keys(self):
def load_key_cfg(self):
"Load current configuration settings for the keybinding options."
# Set current keys type radiobutton.
- self.are_keys_builtin.set(idleConf.GetOption(
+ self.keyset_source.set(idleConf.GetOption(
'main', 'Keys', 'default', type='bool', default=1))
# Set current keys.
current_option = idleConf.CurrentKeys()
# Load available keyset option menus.
- if self.are_keys_builtin.get(): # Default theme selected.
+ if self.keyset_source.get(): # Default theme selected.
item_list = idleConf.GetSectionList('default', 'keys')
item_list.sort()
- self.opt_menu_keys_builtin.SetMenu(item_list, current_option)
+ self.builtinlist.SetMenu(item_list, current_option)
item_list = idleConf.GetSectionList('user', 'keys')
item_list.sort()
if not item_list:
- self.radio_keys_custom['state'] = DISABLED
- self.custom_keys.set('- no custom keys -')
+ self.custom_keyset_on['state'] = DISABLED
+ self.custom_name.set('- no custom keys -')
else:
- self.opt_menu_keys_custom.SetMenu(item_list, item_list[0])
+ self.customlist.SetMenu(item_list, item_list[0])
else: # User key set selected.
item_list = idleConf.GetSectionList('user', 'keys')
item_list.sort()
- self.opt_menu_keys_custom.SetMenu(item_list, current_option)
+ self.customlist.SetMenu(item_list, current_option)
item_list = idleConf.GetSectionList('default', 'keys')
item_list.sort()
- self.opt_menu_keys_builtin.SetMenu(item_list, idleConf.default_keys())
+ self.builtinlist.SetMenu(item_list, idleConf.default_keys())
self.set_keys_type()
# Load keyset element list.
keyset_name = idleConf.CurrentKeys()
self.load_keys_list(keyset_name)
- def var_changed_builtin_keys(self, *params):
+ def var_changed_builtin_name(self, *params):
"Process selection of builtin key set."
old_keys = (
'IDLE Classic Windows',
@@ -954,40 +994,41 @@ def var_changed_builtin_keys(self, *params):
'IDLE Classic Mac',
'IDLE Classic OSX',
)
- value = self.builtin_keys.get()
+ value = self.builtin_name.get()
if value not in old_keys:
if idleConf.GetOption('main', 'Keys', 'name') not in old_keys:
changes.add_option('main', 'Keys', 'name', old_keys[0])
changes.add_option('main', 'Keys', 'name2', value)
- self.new_custom_keys.config(text='New key set, see Help',
- fg='#500000')
+ self.keys_message['text'] = 'New key set, see Help'
+ self.keys_message['fg'] = '#500000'
else:
changes.add_option('main', 'Keys', 'name', value)
changes.add_option('main', 'Keys', 'name2', '')
- self.new_custom_keys.config(text='', fg='black')
+ self.keys_message['text'] = ''
+ self.keys_message['fg'] = 'black'
self.load_keys_list(value)
- def var_changed_custom_keys(self, *params):
+ def var_changed_custom_name(self, *params):
"Process selection of custom key set."
- value = self.custom_keys.get()
+ value = self.custom_name.get()
if value != '- no custom keys -':
changes.add_option('main', 'Keys', 'name', value)
self.load_keys_list(value)
- def var_changed_are_keys_builtin(self, *params):
+ def var_changed_keyset_source(self, *params):
"Process toggle between builtin key set and custom key set."
- value = self.are_keys_builtin.get()
+ value = self.keyset_source.get()
changes.add_option('main', 'Keys', 'default', value)
if value:
- self.var_changed_builtin_keys()
+ self.var_changed_builtin_name()
else:
- self.var_changed_custom_keys()
+ self.var_changed_custom_name()
def var_changed_keybinding(self, *params):
"Store change to a keybinding."
value = self.keybinding.get()
- key_set = self.custom_keys.get()
- event = self.list_bindings.get(ANCHOR).split()[0]
+ key_set = self.custom_name.get()
+ event = self.bindingslist.get(ANCHOR).split()[0]
if idleConf.IsCoreBinding(event):
changes.add_option('keys', key_set, event, value)
else: # Event is an extension binding.
@@ -997,14 +1038,14 @@ def var_changed_keybinding(self, *params):
def set_keys_type(self):
"Set available screen options based on builtin or custom key set."
- if self.are_keys_builtin.get():
- self.opt_menu_keys_builtin['state'] = NORMAL
- self.opt_menu_keys_custom['state'] = DISABLED
+ if self.keyset_source.get():
+ self.builtinlist['state'] = NORMAL
+ self.customlist['state'] = DISABLED
self.button_delete_custom_keys['state'] = DISABLED
else:
- self.opt_menu_keys_builtin['state'] = DISABLED
- self.radio_keys_custom['state'] = NORMAL
- self.opt_menu_keys_custom['state'] = NORMAL
+ self.builtinlist['state'] = DISABLED
+ self.custom_keyset_on['state'] = NORMAL
+ self.customlist['state'] = NORMAL
self.button_delete_custom_keys['state'] = NORMAL
def get_new_keys(self):
@@ -1016,13 +1057,13 @@ def get_new_keys(self):
changed, then a name for a custom key set needs to be
entered for the change to be applied.
"""
- list_index = self.list_bindings.index(ANCHOR)
- binding = self.list_bindings.get(list_index)
+ list_index = self.bindingslist.index(ANCHOR)
+ binding = self.bindingslist.get(list_index)
bind_name = binding.split()[0]
- if self.are_keys_builtin.get():
- current_key_set_name = self.builtin_keys.get()
+ if self.keyset_source.get():
+ current_key_set_name = self.builtin_name.get()
else:
- current_key_set_name = self.custom_keys.get()
+ current_key_set_name = self.custom_name.get()
current_bindings = idleConf.GetCurrentKeySet()
if current_key_set_name in changes['keys']: # unsaved changes
key_set_changes = changes['keys'][current_key_set_name]
@@ -1032,24 +1073,24 @@ def get_new_keys(self):
new_keys = GetKeysDialog(self, 'Get New Keys', bind_name,
current_key_sequences).result
if new_keys:
- if self.are_keys_builtin.get(): # Current key set is a built-in.
+ if self.keyset_source.get(): # Current key set is a built-in.
message = ('Your changes will be saved as a new Custom Key Set.'
' Enter a name for your new Custom Key Set below.')
new_keyset = self.get_new_keys_name(message)
if not new_keyset: # User cancelled custom key set creation.
- self.list_bindings.select_set(list_index)
- self.list_bindings.select_anchor(list_index)
+ self.bindingslist.select_set(list_index)
+ self.bindingslist.select_anchor(list_index)
return
else: # Create new custom key set based on previously active key set.
self.create_new_key_set(new_keyset)
- self.list_bindings.delete(list_index)
- self.list_bindings.insert(list_index, bind_name+' - '+new_keys)
- self.list_bindings.select_set(list_index)
- self.list_bindings.select_anchor(list_index)
+ self.bindingslist.delete(list_index)
+ self.bindingslist.insert(list_index, bind_name+' - '+new_keys)
+ self.bindingslist.select_set(list_index)
+ self.bindingslist.select_anchor(list_index)
self.keybinding.set(new_keys)
else:
- self.list_bindings.select_set(list_index)
- self.list_bindings.select_anchor(list_index)
+ self.bindingslist.select_set(list_index)
+ self.bindingslist.select_anchor(list_index)
def get_new_keys_name(self, message):
"Return new key set name from query popup."
@@ -1065,21 +1106,20 @@ def save_as_new_key_set(self):
if new_keys_name:
self.create_new_key_set(new_keys_name)
- def keybinding_selected(self, event):
+ def on_bindingslist_select(self, event):
"Activate button to assign new keys to selected action."
self.button_new_keys['state'] = NORMAL
def create_new_key_set(self, new_key_set_name):
"""Create a new custom key set with the given name.
- Create the new key set based on the previously active set
- with the current changes applied. Once it is saved, then
- activate the new key set.
+ Copy the bindings/keys from the previously active keyset
+ to the new keyset and activate the new custom keyset.
"""
- if self.are_keys_builtin.get():
- prev_key_set_name = self.builtin_keys.get()
+ if self.keyset_source.get():
+ prev_key_set_name = self.builtin_name.get()
else:
- prev_key_set_name = self.custom_keys.get()
+ prev_key_set_name = self.custom_name.get()
prev_keys = idleConf.GetCoreKeys(prev_key_set_name)
new_keys = {}
for event in prev_keys: # Add key set to changed items.
@@ -1096,8 +1136,8 @@ def create_new_key_set(self, new_key_set_name):
# Change GUI over to the new key set.
custom_key_list = idleConf.GetSectionList('user', 'keys')
custom_key_list.sort()
- self.opt_menu_keys_custom.SetMenu(custom_key_list, new_key_set_name)
- self.are_keys_builtin.set(0)
+ self.customlist.SetMenu(custom_key_list, new_key_set_name)
+ self.keyset_source.set(0)
self.set_keys_type()
def load_keys_list(self, keyset_name):
@@ -1105,14 +1145,14 @@ def load_keys_list(self, keyset_name):
An action/key binding can be selected to change the key binding.
"""
- reselect = 0
- if self.list_bindings.curselection():
- reselect = 1
- list_index = self.list_bindings.index(ANCHOR)
+ reselect = False
+ if self.bindingslist.curselection():
+ reselect = True
+ list_index = self.bindingslist.index(ANCHOR)
keyset = idleConf.GetKeySet(keyset_name)
bind_names = list(keyset.keys())
bind_names.sort()
- self.list_bindings.delete(0, END)
+ self.bindingslist.delete(0, END)
for bind_name in bind_names:
key = ' '.join(keyset[bind_name])
bind_name = bind_name[2:-2] # Trim off the angle brackets.
@@ -1120,17 +1160,21 @@ def load_keys_list(self, keyset_name):
# Handle any unsaved changes to this key set.
if bind_name in changes['keys'][keyset_name]:
key = changes['keys'][keyset_name][bind_name]
- self.list_bindings.insert(END, bind_name+' - '+key)
+ self.bindingslist.insert(END, bind_name+' - '+key)
if reselect:
- self.list_bindings.see(list_index)
- self.list_bindings.select_set(list_index)
- self.list_bindings.select_anchor(list_index)
+ self.bindingslist.see(list_index)
+ self.bindingslist.select_set(list_index)
+ self.bindingslist.select_anchor(list_index)
def save_new_key_set(self, keyset_name, keyset):
"""Save a newly created core key set.
+ Add keyset to idleConf.userCfg['keys'], not to disk.
+ If the keyset doesn't exist, it is created. The
+ binding/keys are taken from the keyset argument.
+
keyset_name - string, the name of the new key set
- keyset - dictionary containing the new key set
+ keyset - dictionary containing the new keybindings
"""
if not idleConf.userCfg['keys'].has_section(keyset_name):
idleConf.userCfg['keys'].add_section(keyset_name)
@@ -1145,7 +1189,7 @@ def delete_custom_keys(self):
reverts to the default. The custom key set is permanently
deleted from the config file.
"""
- keyset_name=self.custom_keys.get()
+ keyset_name=self.custom_name.get()
delmsg = 'Are you sure you wish to delete the key set %r ?'
if not tkMessageBox.askyesno(
'Delete Key Set', delmsg % keyset_name, parent=self):
@@ -1157,14 +1201,14 @@ def delete_custom_keys(self):
item_list = idleConf.GetSectionList('user', 'keys')
item_list.sort()
if not item_list:
- self.radio_keys_custom['state'] = DISABLED
- self.opt_menu_keys_custom.SetMenu(item_list, '- no custom keys -')
+ self.custom_keyset_on['state'] = DISABLED
+ self.customlist.SetMenu(item_list, '- no custom keys -')
else:
- self.opt_menu_keys_custom.SetMenu(item_list, item_list[0])
+ self.customlist.SetMenu(item_list, item_list[0])
# Revert to default key set.
- self.are_keys_builtin.set(idleConf.defaultCfg['main']
+ self.keyset_source.set(idleConf.defaultCfg['main']
.Get('Keys', 'default'))
- self.builtin_keys.set(idleConf.defaultCfg['main'].Get('Keys', 'name')
+ self.builtin_name.set(idleConf.defaultCfg['main'].Get('Keys', 'name')
or idleConf.default_keys())
# User can't back out of these changes, they must be applied now.
changes.save_all()
@@ -1438,22 +1482,21 @@ def create_page_font_tab(self):
which invokes the default callback to add an entry to
changes. Load_tab_cfg initializes space_num to default.
- Widget Structure: (*) widgets bound to self
- frame (of tab_pages)
- frame_font: LabelFrame
- frame_font_name: Frame
- font_name_title: Label
- (*)fontlist: ListBox - font_name
- scroll_font: Scrollbar
- frame_font_param: Frame
- font_size_title: Label
- (*)sizelist: DynOptionMenu - font_size
- (*)bold_toggle: Checkbutton - font_bold
- frame_font_sample: Frame
- (*)font_sample: Label
- frame_indent: LabelFrame
- indent_title: Label
- (*)indent_scale: Scale - space_num
+ Widgets for FontPage(Frame): (*) widgets bound to self
+ frame_font: LabelFrame
+ frame_font_name: Frame
+ font_name_title: Label
+ (*)fontlist: ListBox - font_name
+ scroll_font: Scrollbar
+ frame_font_param: Frame
+ font_size_title: Label
+ (*)sizelist: DynOptionMenu - font_size
+ (*)bold_toggle: Checkbutton - font_bold
+ frame_font_sample: Frame
+ (*)font_sample: Label
+ frame_indent: LabelFrame
+ indent_title: Label
+ (*)indent_scale: Scale - space_num
"""
self.font_name = tracers.add(StringVar(self), self.var_changed_font)
self.font_size = tracers.add(StringVar(self), self.var_changed_font)
@@ -1633,30 +1676,29 @@ def create_page_general(self):
set_add_delete_state. All but load call update_help_changes to
rewrite changes['main']['HelpFiles'].
- Widget Structure: (*) widgets bound to self
- frame
- frame_run: LabelFrame
- startup_title: Label
- (*)startup_editor_on: Radiobutton - startup_edit
- (*)startup_shell_on: Radiobutton - startup_edit
- frame_save: LabelFrame
- run_save_title: Label
- (*)save_ask_on: Radiobutton - autosave
- (*)save_auto_on: Radiobutton - autosave
- frame_win_size: LabelFrame
- win_size_title: Label
- win_width_title: Label
- (*)win_width_int: Entry - win_width
- win_height_title: Label
- (*)win_height_int: Entry - win_height
- frame_help: LabelFrame
- frame_helplist: Frame
- frame_helplist_buttons: Frame
- (*)button_helplist_edit
- (*)button_helplist_add
- (*)button_helplist_remove
- (*)helplist: ListBox
- scroll_helplist: Scrollbar
+ Widgets for GenPage(Frame): (*) widgets bound to self
+ frame_run: LabelFrame
+ startup_title: Label
+ (*)startup_editor_on: Radiobutton - startup_edit
+ (*)startup_shell_on: Radiobutton - startup_edit
+ frame_save: LabelFrame
+ run_save_title: Label
+ (*)save_ask_on: Radiobutton - autosave
+ (*)save_auto_on: Radiobutton - autosave
+ frame_win_size: LabelFrame
+ win_size_title: Label
+ win_width_title: Label
+ (*)win_width_int: Entry - win_width
+ win_height_title: Label
+ (*)win_height_int: Entry - win_height
+ frame_help: LabelFrame
+ frame_helplist: Frame
+ frame_helplist_buttons: Frame
+ (*)button_helplist_edit
+ (*)button_helplist_add
+ (*)button_helplist_remove
+ (*)helplist: ListBox
+ scroll_helplist: Scrollbar
"""
self.startup_edit = tracers.add(
IntVar(self), ('main', 'General', 'editor-on-startup'))
diff --git a/Lib/idlelib/idle_test/test_configdialog.py b/Lib/idlelib/idle_test/test_configdialog.py
index cd78482150c..b07a65cf56a 100644
--- a/Lib/idlelib/idle_test/test_configdialog.py
+++ b/Lib/idlelib/idle_test/test_configdialog.py
@@ -1,7 +1,7 @@
"""Test idlelib.configdialog.
Half the class creates dialog, half works with user customizations.
-Coverage: 63%.
+Coverage: 81%.
"""
from idlelib import configdialog
from test.support import requires
@@ -29,6 +29,7 @@
mainpage = changes['main']
highpage = changes['highlight']
keyspage = changes['keys']
+extpage = changes['extensions']
def setUpModule():
global root, dialog
@@ -59,8 +60,6 @@ class FontPageTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
page = cls.page = dialog.fontpage
- #dialog.note.insert(0, page, text='copy')
- #dialog.note.add(page, text='copyfgfg')
dialog.note.select(page)
page.set_samples = Func() # Mask instance method.
@@ -120,7 +119,7 @@ def test_fontlist_mouse(self):
# Click on item should select that item.
d = self.page
if d.fontlist.size() < 2:
- cls.skipTest('need at least 2 fonts')
+ self.skipTest('need at least 2 fonts')
fontlist = d.fontlist
fontlist.activate(0)
@@ -233,11 +232,391 @@ def setUp(self):
changes.clear()
-class KeysTest(unittest.TestCase):
+class KeyTest(unittest.TestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ d = dialog
+ dialog.note.select(d.keyspage)
+ d.set_keys_type = Func()
+ d.load_keys_list = Func()
+
+ @classmethod
+ def tearDownClass(cls):
+ d = dialog
+ del d.set_keys_type, d.load_keys_list
def setUp(self):
+ d = dialog
+ # The following is needed for test_load_key_cfg, _delete_custom_keys.
+ # This may indicate a defect in some test or function.
+ for section in idleConf.GetSectionList('user', 'keys'):
+ idleConf.userCfg['keys'].remove_section(section)
+ changes.clear()
+ d.set_keys_type.called = 0
+ d.load_keys_list.called = 0
+
+ def test_load_key_cfg(self):
+ tracers.detach()
+ d = dialog
+ eq = self.assertEqual
+
+ # Use builtin keyset with no user keysets created.
+ idleConf.CurrentKeys = mock.Mock(return_value='IDLE Classic OSX')
+ d.load_key_cfg()
+ self.assertTrue(d.keyset_source.get())
+ # builtinlist sets variable builtin_name to the CurrentKeys default.
+ eq(d.builtin_name.get(), 'IDLE Classic OSX')
+ eq(d.custom_name.get(), '- no custom keys -')
+ eq(d.custom_keyset_on['state'], DISABLED)
+ eq(d.set_keys_type.called, 1)
+ eq(d.load_keys_list.called, 1)
+ eq(d.load_keys_list.args, ('IDLE Classic OSX', ))
+
+ # Builtin keyset with non-empty user keyset list.
+ idleConf.SetOption('keys', 'test1', 'option', 'value')
+ idleConf.SetOption('keys', 'test2', 'option2', 'value2')
+ d.load_key_cfg()
+ eq(d.builtin_name.get(), 'IDLE Classic OSX')
+ eq(d.custom_name.get(), 'test1')
+ eq(d.set_keys_type.called, 2)
+ eq(d.load_keys_list.called, 2)
+ eq(d.load_keys_list.args, ('IDLE Classic OSX', ))
+
+ # Use custom keyset.
+ idleConf.CurrentKeys = mock.Mock(return_value='test2')
+ idleConf.default_keys = mock.Mock(return_value='IDLE Modern Unix')
+ idleConf.SetOption('main', 'Keys', 'default', '0')
+ d.load_key_cfg()
+ self.assertFalse(d.keyset_source.get())
+ eq(d.builtin_name.get(), 'IDLE Modern Unix')
+ eq(d.custom_name.get(), 'test2')
+ eq(d.set_keys_type.called, 3)
+ eq(d.load_keys_list.called, 3)
+ eq(d.load_keys_list.args, ('test2', ))
+
+ del idleConf.CurrentKeys, idleConf.default_keys
+ tracers.attach()
+
+ def test_keyset_source(self):
+ eq = self.assertEqual
+ d = dialog
+ # Test these separately.
+ d.var_changed_builtin_name = Func()
+ d.var_changed_custom_name = Func()
+ # Builtin selected.
+ d.builtin_keyset_on.invoke()
+ eq(mainpage, {'Keys': {'default': 'True'}})
+ eq(d.var_changed_builtin_name.called, 1)
+ eq(d.var_changed_custom_name.called, 0)
changes.clear()
+ # Custom selected.
+ d.custom_keyset_on['state'] = NORMAL
+ d.custom_keyset_on.invoke()
+ self.assertEqual(mainpage, {'Keys': {'default': 'False'}})
+ eq(d.var_changed_builtin_name.called, 1)
+ eq(d.var_changed_custom_name.called, 1)
+ del d.var_changed_builtin_name, d.var_changed_custom_name
+
+ def test_builtin_name(self):
+ eq = self.assertEqual
+ d = dialog
+ idleConf.userCfg['main'].remove_section('Keys')
+ item_list = ['IDLE Classic Windows', 'IDLE Classic OSX',
+ 'IDLE Modern UNIX']
+
+ # Not in old_keys, defaults name to first item.
+ d.builtinlist.SetMenu(item_list, 'IDLE Modern UNIX')
+ eq(mainpage, {'Keys': {'name': 'IDLE Classic Windows',
+ 'name2': 'IDLE Modern UNIX'}})
+ eq(d.keys_message['text'], 'New key set, see Help')
+ eq(d.load_keys_list.called, 1)
+ eq(d.load_keys_list.args, ('IDLE Modern UNIX', ))
+
+ # Not in old keys - uses name2.
+ changes.clear()
+ idleConf.SetOption('main', 'Keys', 'name', 'IDLE Classic Unix')
+ d.builtinlist.SetMenu(item_list, 'IDLE Modern UNIX')
+ eq(mainpage, {'Keys': {'name2': 'IDLE Modern UNIX'}})
+ eq(d.keys_message['text'], 'New key set, see Help')
+ eq(d.load_keys_list.called, 2)
+ eq(d.load_keys_list.args, ('IDLE Modern UNIX', ))
+
+ # Builtin name in old_keys.
+ changes.clear()
+ d.builtinlist.SetMenu(item_list, 'IDLE Classic OSX')
+ eq(mainpage, {'Keys': {'name': 'IDLE Classic OSX', 'name2': ''}})
+ eq(d.keys_message['text'], '')
+ eq(d.load_keys_list.called, 3)
+ eq(d.load_keys_list.args, ('IDLE Classic OSX', ))
+
+ def test_custom_name(self):
+ d = dialog
+
+ # If no selections, doesn't get added.
+ d.customlist.SetMenu([], '- no custom keys -')
+ self.assertNotIn('Keys', mainpage)
+ self.assertEqual(d.load_keys_list.called, 0)
+
+ # Custom name selected.
+ changes.clear()
+ d.customlist.SetMenu(['a', 'b', 'c'], 'c')
+ self.assertEqual(mainpage, {'Keys': {'name': 'c'}})
+ self.assertEqual(d.load_keys_list.called, 1)
+
+ def test_keybinding(self):
+ d = dialog
+ d.custom_name.set('my custom keys')
+ d.bindingslist.delete(0, 'end')
+ d.bindingslist.insert(0, 'copy')
+ d.bindingslist.insert(1, 'expand-word')
+ d.bindingslist.selection_set(0)
+ d.bindingslist.selection_anchor(0)
+ # Core binding - adds to keys.
+ d.keybinding.set('<Key-F11>')
+ self.assertEqual(keyspage,
+ {'my custom keys': {'copy': '<Key-F11>'}})
+ # Not a core binding - adds to extensions.
+ d.bindingslist.selection_set(1)
+ d.bindingslist.selection_anchor(1)
+ d.keybinding.set('<Key-F11>')
+ self.assertEqual(extpage,
+ {'AutoExpand_cfgBindings': {'expand-word': '<Key-F11>'}})
+
+ def test_set_keys_type(self):
+ eq = self.assertEqual
+ d = dialog
+ del d.set_keys_type
+
+ # Builtin keyset selected.
+ d.keyset_source.set(True)
+ d.set_keys_type()
+ eq(d.builtinlist['state'], NORMAL)
+ eq(d.customlist['state'], DISABLED)
+ eq(d.button_delete_custom_keys['state'], DISABLED)
+
+ # Custom keyset selected.
+ d.keyset_source.set(False)
+ d.set_keys_type()
+ eq(d.builtinlist['state'], DISABLED)
+ eq(d.custom_keyset_on['state'], NORMAL)
+ eq(d.customlist['state'], NORMAL)
+ eq(d.button_delete_custom_keys['state'], NORMAL)
+ d.set_keys_type = Func()
+
+ def test_get_new_keys(self):
+ eq = self.assertEqual
+ d = dialog
+ orig_getkeysdialog = configdialog.GetKeysDialog
+ gkd = configdialog.GetKeysDialog = Func(return_self=True)
+ gnkn = d.get_new_keys_name = Func()
+
+ d.button_new_keys['state'] = NORMAL
+ d.bindingslist.delete(0, 'end')
+ d.bindingslist.insert(0, 'copy - <Control-Shift-Key-C>')
+ d.bindingslist.selection_set(0)
+ d.bindingslist.selection_anchor(0)
+ d.keybinding.set('Key-a')
+ d.keyset_source.set(True) # Default keyset.
+
+ # Default keyset; no change to binding.
+ gkd.result = ''
+ d.button_new_keys.invoke()
+ eq(d.bindingslist.get('anchor'), 'copy - <Control-Shift-Key-C>')
+ # Keybinding isn't changed when there isn't a change entered.
+ eq(d.keybinding.get(), 'Key-a')
+
+ # Default keyset; binding changed.
+ gkd.result = '<Key-F11>'
+ # No keyset name selected therefore binding not saved.
+ gnkn.result = ''
+ d.button_new_keys.invoke()
+ eq(gnkn.called, 1)
+ eq(d.bindingslist.get('anchor'), 'copy - <Control-Shift-Key-C>')
+ # Keyset name selected.
+ gnkn.result = 'My New Key Set'
+ d.button_new_keys.invoke()
+ eq(d.custom_name.get(), gnkn.result)
+ eq(d.bindingslist.get('anchor'), 'copy - <Key-F11>')
+ eq(d.keybinding.get(), '<Key-F11>')
+
+ # User keyset; binding changed.
+ d.keyset_source.set(False) # Custom keyset.
+ gnkn.called = 0
+ gkd.result = '<Key-p>'
+ d.button_new_keys.invoke()
+ eq(gnkn.called, 0)
+ eq(d.bindingslist.get('anchor'), 'copy - <Key-p>')
+ eq(d.keybinding.get(), '<Key-p>')
+
+ del d.get_new_keys_name
+ configdialog.GetKeysDialog = orig_getkeysdialog
+
+ def test_get_new_keys_name(self):
+ orig_sectionname = configdialog.SectionName
+ sn = configdialog.SectionName = Func(return_self=True)
+ d = dialog
+
+ sn.result = 'New Keys'
+ self.assertEqual(d.get_new_keys_name(''), 'New Keys')
+
+ configdialog.SectionName = orig_sectionname
+
+ def test_save_as_new_key_set(self):
+ d = dialog
+ gnkn = d.get_new_keys_name = Func()
+ d.keyset_source.set(True)
+
+ # No name entered.
+ gnkn.result = ''
+ d.button_save_custom_keys.invoke()
+
+ # Name entered.
+ gnkn.result = 'my new key set'
+ gnkn.called = 0
+ self.assertNotIn(gnkn.result, idleConf.userCfg['keys'])
+ d.button_save_custom_keys.invoke()
+ self.assertIn(gnkn.result, idleConf.userCfg['keys'])
+
+ del d.get_new_keys_name
+
+ def test_on_bindingslist_select(self):
+ d = dialog
+ b = d.bindingslist
+ b.delete(0, 'end')
+ b.insert(0, 'copy')
+ b.insert(1, 'find')
+ b.activate(0)
+
+ b.focus_force()
+ b.see(1)
+ b.update()
+ x, y, dx, dy = b.bbox(1)
+ x += dx // 2
+ y += dy // 2
+ b.event_generate('<Enter>', x=0, y=0)
+ b.event_generate('<Motion>', x=x, y=y)
+ b.event_generate('<Button-1>', x=x, y=y)
+ b.event_generate('<ButtonRelease-1>', x=x, y=y)
+ self.assertEqual(b.get('anchor'), 'find')
+ self.assertEqual(d.button_new_keys['state'], NORMAL)
+
+ def test_create_new_key_set_and_save_new_key_set(self):
+ eq = self.assertEqual
+ d = dialog
+
+ # Use default as previously active keyset.
+ d.keyset_source.set(True)
+ d.builtin_name.set('IDLE Classic Windows')
+ first_new = 'my new custom key set'
+ second_new = 'my second custom keyset'
+
+ # No changes, so keysets are an exact copy.
+ self.assertNotIn(first_new, idleConf.userCfg)
+ d.create_new_key_set(first_new)
+ eq(idleConf.GetSectionList('user', 'keys'), [first_new])
+ eq(idleConf.GetKeySet('IDLE Classic Windows'),
+ idleConf.GetKeySet(first_new))
+ eq(d.custom_name.get(), first_new)
+ self.assertFalse(d.keyset_source.get()) # Use custom set.
+ eq(d.set_keys_type.called, 1)
+
+ # Test that changed keybindings are in new keyset.
+ changes.add_option('keys', first_new, 'copy', '<Key-F11>')
+ self.assertNotIn(second_new, idleConf.userCfg)
+ d.create_new_key_set(second_new)
+ eq(idleConf.GetSectionList('user', 'keys'), [first_new, second_new])
+ self.assertNotEqual(idleConf.GetKeySet(first_new),
+ idleConf.GetKeySet(second_new))
+ # Check that difference in keysets was in option `copy` from `changes`.
+ idleConf.SetOption('keys', first_new, 'copy', '<Key-F11>')
+ eq(idleConf.GetKeySet(first_new), idleConf.GetKeySet(second_new))
+
+ def test_load_keys_list(self):
+ eq = self.assertEqual
+ d = dialog
+ gks = idleConf.GetKeySet = Func()
+ del d.load_keys_list
+ b = d.bindingslist
+
+ b.delete(0, 'end')
+ b.insert(0, '<<find>>')
+ b.insert(1, '<<help>>')
+ gks.result = {'<<copy>>': ['<Control-Key-c>', '<Control-Key-C>'],
+ '<<force-open-completions>>': ['<Control-Key-space>'],
+ '<<spam>>': ['<Key-F11>']}
+ changes.add_option('keys', 'my keys', 'spam', '<Shift-Key-a>')
+ expected = ('copy - <Control-Key-c> <Control-Key-C>',
+ 'force-open-completions - <Control-Key-space>',
+ 'spam - <Shift-Key-a>')
+
+ # No current selection.
+ d.load_keys_list('my keys')
+ eq(b.get(0, 'end'), expected)
+ eq(b.get('anchor'), '')
+ eq(b.curselection(), ())
+
+ # Check selection.
+ b.selection_set(1)
+ b.selection_anchor(1)
+ d.load_keys_list('my keys')
+ eq(b.get(0, 'end'), expected)
+ eq(b.get('anchor'), 'force-open-completions - <Control-Key-space>')
+ eq(b.curselection(), (1, ))
+
+ # Change selection.
+ b.selection_set(2)
+ b.selection_anchor(2)
+ d.load_keys_list('my keys')
+ eq(b.get(0, 'end'), expected)
+ eq(b.get('anchor'), 'spam - <Shift-Key-a>')
+ eq(b.curselection(), (2, ))
+ d.load_keys_list = Func()
+
+ del idleConf.GetKeySet
+
+ def test_delete_custom_keys(self):
+ eq = self.assertEqual
+ d = dialog
+ d.button_delete_custom_keys['state'] = NORMAL
+ yesno = configdialog.tkMessageBox.askyesno = Func()
+ d.deactivate_current_config = Func()
+ d.activate_config_changes = Func()
+
+ keyset_name = 'spam key set'
+ idleConf.userCfg['keys'].SetOption(keyset_name, 'name', 'value')
+ keyspage[keyset_name] = {'option': 'True'}
+
+ # Force custom keyset.
+ d.keyset_source.set(False)
+ d.custom_name.set(keyset_name)
+
+ # Cancel deletion.
+ yesno.result = False
+ d.button_delete_custom_keys.invoke()
+ eq(yesno.called, 1)
+ eq(keyspage[keyset_name], {'option': 'True'})
+ eq(idleConf.GetSectionList('user', 'keys'), ['spam key set'])
+ eq(d.deactivate_current_config.called, 0)
+ eq(d.activate_config_changes.called, 0)
+ eq(d.set_keys_type.called, 0)
+
+ # Confirm deletion.
+ yesno.result = True
+ d.button_delete_custom_keys.invoke()
+ eq(yesno.called, 2)
+ self.assertNotIn(keyset_name, keyspage)
+ eq(idleConf.GetSectionList('user', 'keys'), [])
+ eq(d.custom_keyset_on['state'], DISABLED)
+ eq(d.custom_name.get(), '- no custom keys -')
+ eq(d.deactivate_current_config.called, 1)
+ eq(d.activate_config_changes.called, 1)
+ eq(d.set_keys_type.called, 1)
+
+ del d.activate_config_changes, d.deactivate_current_config
+ del configdialog.tkMessageBox.askyesno
+
class GenPageTest(unittest.TestCase):
"""Test that general tab widgets enable users to make changes.
diff --git a/Misc/NEWS.d/next/IDLE/2017-08-03-17-54-02.bpo-31002.kUSgTE.rst b/Misc/NEWS.d/next/IDLE/2017-08-03-17-54-02.bpo-31002.kUSgTE.rst
new file mode 100644
index 00000000000..1708be72b8b
--- /dev/null
+++ b/Misc/NEWS.d/next/IDLE/2017-08-03-17-54-02.bpo-31002.kUSgTE.rst
@@ -0,0 +1 @@
+Add tests for configdialog keys tab. Patch by Cheryl Sabella.
1
0
![](https://secure.gravatar.com/avatar/628f74ad69d6dcc809f7cfa1417733a9.jpg?s=120&d=mm&r=g)
14 Aug '17
Results for project python/master, build date: 2017-08-14 03:03:53-07:00.
- commit: 48d9823
- previous commit: dadca48
- revision date: 2017-08-12 17:37:09+02:00
- environment: Broadwell-EP
- cpu: Intel(R) Xeon(R) CPU E5-2699 v4 @ 2.20GHz 2x22 cores,
stepping 1, LLC 55 MB
- mem: 128 GB
- os: Ubuntu 16.04.2 LTS
- kernel: 4.4.0-62-generic x86_64 GNU/Linux
Baseline results were generated using release v3.6.0, with hash 5c4568a from
2016-12-22 23:38:47+00:00.
+-----+------------------------+--------+------------+------------+------------+
| | |relative|change since|change since|current rev |
| | benchmark|std_dev*| last run | baseline |run with PGO|
+-----+------------------------+--------+------------+------------+------------+
| :-| | 2to3| 1.171% | -0.154% | +4.210% | +8.254% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | call_method| 2.117% | -0.318% | +21.455% | +10.257% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | call_method_slots| 1.847% | -0.598% | +23.709% | +9.927% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | call_method_unknown| 0.885% | +0.100% | +21.124% | +8.076% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | call_simple| 1.436% | +0.845% | +2.928% | +13.847% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | chameleon| 2.455% | -0.270% | +10.982% | +9.757% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | chaos| 0.886% | +0.175% | +6.095% | +11.640% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | crypto_pyaes| 0.785% | -0.325% | +4.197% | +5.437% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | deltablue| 4.538% | -1.122% | +5.659% | +20.197% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | django_template| 4.849% | -0.639% | +9.342% | +13.914% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | dulwich_log| 2.034% | -0.415% | +4.177% | +6.989% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | fannkuch| 0.497% | -0.559% | +4.543% | +5.528% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | float| 0.703% | -0.038% | +2.992% | +6.065% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | genshi_text| 1.222% | +0.220% | +9.090% | +10.725% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | genshi_xml| 2.749% | -0.171% | +7.329% | +9.693% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | go| 0.842% | +0.288% | +6.219% | +10.837% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | hexiom| 0.511% | -0.175% | +9.036% | +11.862% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | html5lib| 3.455% | +0.492% | +8.449% | +10.749% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | json_dumps| 1.528% | -0.077% | +4.516% | +9.472% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | json_loads| 3.144% | -0.205% | +2.878% | +9.798% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | logging_format| 1.667% | +0.274% | +8.933% | +12.458% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | logging_silent| 2.766% | +0.356% | +47.284% | +10.056% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | logging_simple| 1.468% | +0.167% | +10.241% | +12.760% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | mako| 1.258% | -0.241% | +18.632% | +12.121% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | mdp| 6.578% | -1.165% | +5.368% | +14.562% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | meteor_contest| 3.524% | +0.959% | +4.648% | +4.878% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | nbody| 0.384% | +0.020% | -1.899% | -0.451% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | nqueens| 2.728% | -0.759% | +1.813% | +9.265% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | pathlib| 1.466% | -0.355% | +4.664% | +9.816% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | pickle| 2.296% | +0.032% | +0.848% | +22.215% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | pickle_dict| 0.503% | +0.033% | +2.494% | +19.753% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | pickle_list| 0.956% | -0.030% | +5.217% | +18.827% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | pickle_pure_python| 3.616% | +1.049% | +12.169% | +9.986% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | pidigits| 0.162% | +0.011% | +0.301% | +9.529% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | python_startup| 0.125% | -0.081% | +9.002% | +4.741% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | python_startup_no_site| 0.090% | -0.083% | +1.052% | +4.559% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | raytrace| 1.182% | -0.185% | +9.334% | +13.413% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | regex_compile| 4.936% | +0.278% | -10.042% | +13.606% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | regex_dna| 1.089% | +0.080% | +1.436% | +9.348% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | regex_effbot| 2.104% | +0.332% | +0.824% | +3.156% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | regex_v8| 0.857% | -0.116% | +11.336% | +3.485% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | richards| 1.473% | +0.582% | +8.064% | +13.323% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | scimark_fft| 0.354% | +0.615% | +0.896% | +1.460% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | scimark_lu| 1.289% | +0.288% | +27.084% | +8.934% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | scimark_monte_carlo| 2.329% | +0.325% | +3.213% | +7.817% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | scimark_sor| 0.727% | +0.036% | +13.189% | +9.197% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | scimark_sparse_mat_mult| 2.874% | -0.029% | -0.771% | -0.617% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | spectral_norm| 0.393% | +0.135% | +5.721% | +0.153% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | sqlalchemy_declarative| 1.349% | +0.010% | +5.413% | +8.464% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | sqlalchemy_imperative| 2.664% | +0.437% | +6.014% | +4.499% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | sqlite_synth| 4.342% | -0.874% | +3.074% | +7.336% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | sympy_expand| 2.147% | -0.510% | +12.405% | +8.356% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | sympy_integrate| 1.493% | -0.128% | +10.377% | +6.471% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | sympy_str| 3.900% | -0.711% | +12.006% | +9.458% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | sympy_sum| 4.549% | -0.284% | +13.631% | +9.223% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | telco| 4.230% | -0.102% | +23.480% | +9.544% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | tornado_http| 1.117% | +0.060% | +5.965% | +5.409% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | unpack_sequence| 1.371% | -0.008% | +0.627% | +0.268% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | unpickle| 9.273% | +1.941% | +7.814% | +20.261% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | unpickle_list| 1.605% | +0.190% | -3.436% | +17.826% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | unpickle_pure_python| 0.946% | -0.129% | +7.205% | +6.335% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | xml_etree_generate| 1.227% | +0.119% | +5.770% | +8.755% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | xml_etree_iterparse| 2.208% | +0.666% | +2.453% | +5.665% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | xml_etree_parse| 1.454% | +0.979% | -5.294% | +9.941% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | xml_etree_process| 1.369% | -0.022% | +6.520% | +8.903% |
+-----+------------------------+--------+------------+------------+------------+
* Relative Standard Deviation (Standard Deviation/Average)
If this is not displayed properly please visit our results page here:
http://languagesperformance.intel.com/65-flat-results-for-python-master-bra…
Our lab does a nightly source pull and build of the Python project and measures
performance changes against the previous stable version and the previous nightly
measurement. This is provided as a service to the community so that quality
issues with current hardware can be identified quickly.
Intel technologies' features and benefits depend on system configuration and may
require enabled hardware, software or service activation. Performance varies
depending on system configuration.
1
0
![](https://secure.gravatar.com/avatar/cc7737cd64a84f1b5c61a160798e97ee.jpg?s=120&d=mm&r=g)
[3.6] bpo-30983: eval frame rename in pep 0523 broke gdb's python extension (GH-2803) (#3090)
by Łukasz Langa 14 Aug '17
by Łukasz Langa 14 Aug '17
14 Aug '17
https://github.com/python/cpython/commit/09b77165e3fffa7b7ff160ad06042cdcfa…
commit: 09b77165e3fffa7b7ff160ad06042cdcfa004bf5
branch: 3.6
author: Łukasz Langa <lukasz(a)langa.pl>
committer: GitHub <noreply(a)github.com>
date: 2017-08-14T16:06:28-07:00
summary:
[3.6] bpo-30983: eval frame rename in pep 0523 broke gdb's python extension (GH-2803) (#3090)
pep 0523 renames PyEval_EvalFrameEx to _PyEval_EvalFrameDefault while the gdb python extension only looks for PyEval_EvalFrameEx to understand if it is dealing with a frame.
Final effect is that attaching gdb to a python3.6 process doesnt resolve python objects. Eg. py-list and py-bt dont work properly.
This patch fixes that. Tested locally on python3.6
(cherry picked from commit 2e0f4db114)
files:
A Misc/NEWS.d/next/Tools-Demos/2017-08-14-15-37-38.bpo-30983.A7UzX8.rst
M Tools/gdb/libpython.py
diff --git a/Misc/NEWS.d/next/Tools-Demos/2017-08-14-15-37-38.bpo-30983.A7UzX8.rst b/Misc/NEWS.d/next/Tools-Demos/2017-08-14-15-37-38.bpo-30983.A7UzX8.rst
new file mode 100644
index 00000000000..44c5e1c6967
--- /dev/null
+++ b/Misc/NEWS.d/next/Tools-Demos/2017-08-14-15-37-38.bpo-30983.A7UzX8.rst
@@ -0,0 +1,4 @@
+With PEP 523, gdb's Python integration stopped working properly for frames
+using the ``_PyEval_EvalFrameDefault`` function. Affected functionality
+included `py-list` and `py-bt`. This is now fixed. Patch by Bruno "Polaco"
+Penteado.
diff --git a/Tools/gdb/libpython.py b/Tools/gdb/libpython.py
index 31ae8117c78..40e0a677cd0 100755
--- a/Tools/gdb/libpython.py
+++ b/Tools/gdb/libpython.py
@@ -1502,8 +1502,10 @@ def is_python_frame(self):
return False
def is_evalframeex(self):
- '''Is this a PyEval_EvalFrameEx frame?'''
- if self._gdbframe.name() == 'PyEval_EvalFrameEx':
+ '''Is this a PyEval_EvalFrameEx or _PyEval_EvalFrameDefault (PEP 0523)
+ frame?'''
+ if self._gdbframe.name() in ('PyEval_EvalFrameEx',
+ '_PyEval_EvalFrameDefault'):
'''
I believe we also need to filter on the inline
struct frame_id.inline_depth, only regarding frames with
1
0
https://github.com/python/cpython/commit/f978405b3f092e4005b92ba1dbaab15f60…
commit: f978405b3f092e4005b92ba1dbaab15f609b3bb0
branch: master
author: Łukasz Langa <lukasz(a)langa.pl>
committer: GitHub <noreply(a)github.com>
date: 2017-08-14T15:43:42-07:00
summary:
Add Bruno Penteado to ACKS (#3091)
files:
M Misc/ACKS
diff --git a/Misc/ACKS b/Misc/ACKS
index 6fc41b36b59..c55d09ac67c 100644
--- a/Misc/ACKS
+++ b/Misc/ACKS
@@ -1184,6 +1184,7 @@ Berker Peksag
Andreas Pelme
Steven Pemberton
Bo Peng
+Bruno "Polaco" Penteado
Santiago Peresón
George Peristerakis
Thomas Perl
1
0
![](https://secure.gravatar.com/avatar/cc7737cd64a84f1b5c61a160798e97ee.jpg?s=120&d=mm&r=g)
bpo-30983: eval frame rename in pep 0523 broke gdb's python extension (#2803)
by Łukasz Langa 14 Aug '17
by Łukasz Langa 14 Aug '17
14 Aug '17
https://github.com/python/cpython/commit/2e0f4db114424a00354eab889ba8f7334a…
commit: 2e0f4db114424a00354eab889ba8f7334a2ab8f0
branch: master
author: Bruno "Polaco" Penteado <polaco(a)gmail.com>
committer: Łukasz Langa <lukasz(a)langa.pl>
date: 2017-08-14T15:14:17-07:00
summary:
bpo-30983: eval frame rename in pep 0523 broke gdb's python extension (#2803)
pep 0523 renames PyEval_EvalFrameEx to _PyEval_EvalFrameDefault while the gdb python extension only looks for PyEval_EvalFrameEx to understand if it is dealing with a frame.
Final effect is that attaching gdb to a python3.6 process doesnt resolve python objects. Eg. py-list and py-bt dont work properly.
This patch fixes that. Tested locally on python3.6
files:
M Tools/gdb/libpython.py
diff --git a/Tools/gdb/libpython.py b/Tools/gdb/libpython.py
index cc23b8402df..7c8874a7ace 100755
--- a/Tools/gdb/libpython.py
+++ b/Tools/gdb/libpython.py
@@ -1502,8 +1502,10 @@ def is_python_frame(self):
return False
def is_evalframeex(self):
- '''Is this a PyEval_EvalFrameEx frame?'''
- if self._gdbframe.name() == 'PyEval_EvalFrameEx':
+ '''Is this a PyEval_EvalFrameEx or _PyEval_EvalFrameDefault (PEP 0523)
+ frame?'''
+ if self._gdbframe.name() in ('PyEval_EvalFrameEx',
+ '_PyEval_EvalFrameDefault'):
'''
I believe we also need to filter on the inline
struct frame_id.inline_depth, only regarding frames with
1
0
https://github.com/python/cpython/commit/143be366295038b36fc32c44b8e1b48a37…
commit: 143be366295038b36fc32c44b8e1b48a375eab56
branch: master
author: Saurabh Chaturvedi <saurabh.chaturvedi63(a)gmail.com>
committer: Mariatta <Mariatta(a)users.noreply.github.com>
date: 2017-08-14T11:54:53-07:00
summary:
bpo-31191: Improve grammar in threading.Barrier docs (GH-3080)
files:
M Doc/library/threading.rst
diff --git a/Doc/library/threading.rst b/Doc/library/threading.rst
index cda859fe4cb..021e29e7124 100644
--- a/Doc/library/threading.rst
+++ b/Doc/library/threading.rst
@@ -875,8 +875,8 @@ Barrier Objects
This class provides a simple synchronization primitive for use by a fixed number
of threads that need to wait for each other. Each of the threads tries to pass
the barrier by calling the :meth:`~Barrier.wait` method and will block until
-all of the threads have made the call. At this points, the threads are released
-simultaneously.
+all of the threads have made their :meth:`~Barrier.wait` calls. At this point,
+the threads are released simultaneously.
The barrier can be reused any number of times for the same number of threads.
1
0