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://secure.gravatar.com/avatar/cc7737cd64a84f1b5c61a160798e97ee.jpg?s=120&d=mm&r=g)
17 Aug '17
https://github.com/python/cpython/commit/82aff62462e65077a6614b466c986f93a6…
commit: 82aff62462e65077a6614b466c986f93a601c33d
branch: master
author: Cheryl Sabella <cheryl.sabella(a)gmail.com>
committer: Terry Jan Reedy <tjreedy(a)udel.edu>
date: 2017-08-17T20:39:00-04:00
summary:
bpo-31001: IDLE: Add tests for configdialog highlight tab (#3123)
files:
A Misc/NEWS.d/next/IDLE/2017-08-17-15-00-20.bpo-31001.KLxYHC.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 e1ac82b7df3..a36bf83ad0f 100644
--- a/Lib/idlelib/configdialog.py
+++ b/Lib/idlelib/configdialog.py
@@ -222,13 +222,79 @@ def help(self):
def create_page_highlight(self):
"""Return frame of widgets for Highlighting tab.
+ Enable users to provisionally change foreground and background
+ colors applied to textual tags. Color mappings are stored in
+ complete listings called themes. Built-in themes in
+ idlelib/config-highlight.def are fixed as far as the dialog is
+ concerned. Any theme can be used as the base for a new custom
+ theme, stored in .idlerc/config-highlight.cfg.
+
+ Function load_theme_cfg() initializes tk variables and theme
+ lists and calls paint_theme_sample() and set_highlight_target()
+ for the current theme. Radiobuttons builtin_theme_on and
+ custom_theme_on toggle var theme_source, which controls if the
+ current set of colors are from a builtin or custom theme.
+ DynOptionMenus builtinlist and customlist contain lists of the
+ builtin and custom themes, respectively, and the current item
+ from each list is stored in vars builtin_name and custom_name.
+
+ Function paint_theme_sample() applies the colors from the theme
+ to the tags in text widget highlight_sample and then invokes
+ set_color_sample(). Function set_highlight_target() sets the state
+ of the radiobuttons fg_on and bg_on based on the tag and it also
+ invokes set_color_sample().
+
+ Function set_color_sample() sets the background color for the frame
+ holding the color selector. This provides a larger visual of the
+ color for the current tag and plane (foreground/background).
+
+ Note: set_color_sample() is called from many places and is often
+ called more than once when a change is made. It is invoked when
+ foreground or background is selected (radiobuttons), from
+ paint_theme_sample() (theme is changed or load_cfg is called), and
+ from set_highlight_target() (target tag is changed or load_cfg called).
+
+ Button delete_custom invokes delete_custom() to delete
+ a custom theme from idleConf.userCfg['highlight'] and changes.
+ Button save_custom invokes save_as_new_theme() which calls
+ get_new_theme_name() and create_new() to save a custom theme
+ and its colors to idleConf.userCfg['highlight'].
+
+ Radiobuttons fg_on and bg_on toggle var fg_bg_toggle to control
+ if the current selected color for a tag is for the foreground or
+ background.
+
+ DynOptionMenu targetlist contains a readable description of the
+ tags applied to Python source within IDLE. Selecting one of the
+ tags from this list populates highlight_target, which has a callback
+ function set_highlight_target().
+
+ Text widget highlight_sample displays a block of text (which is
+ mock Python code) in which is embedded the defined tags and reflects
+ the color attributes of the current theme and changes for those tags.
+ Mouse button 1 allows for selection of a tag and updates
+ highlight_target with that tag value.
+
+ Note: The font in highlight_sample is set through the config in
+ the fonts tab.
+
+ In other words, a tag can be selected either from targetlist or
+ by clicking on the sample text within highlight_sample. The
+ plane (foreground/background) is selected via the radiobutton.
+ Together, these two (tag and plane) control what color is
+ shown in set_color_sample() for the current theme. Button set_color
+ invokes get_color() which displays a ColorChooser to change the
+ color for the selected tag/plane. If a new color is picked,
+ it will be saved to changes and the highlight_sample and
+ frame background will be updated.
+
Tk Variables:
color: Color of selected target.
- builtin_theme: Menu variable for built-in theme.
- custom_theme: Menu variable for custom theme.
+ builtin_name: Menu variable for built-in theme.
+ custom_name: Menu variable for custom theme.
fg_bg_toggle: Toggle for foreground/background color.
Note: this has no callback.
- is_builtin_theme: Selector for built-in or custom theme.
+ theme_source: Selector for built-in or custom theme.
highlight_target: Menu variable for the highlight tag target.
Instance Data Attributes:
@@ -245,30 +311,30 @@ def create_page_highlight(self):
on_new_color_set: Set new color and add option.
paint_theme_sample: Recolor sample.
get_new_theme_name: Get from popup.
- create_new_theme: Combine theme with changes and save.
- save_as_new_theme: Save [button_save_custom_theme].
- set_theme_type: Command for [is_builtin_theme].
- delete_custom_theme: Ativate default [button_delete_custom_theme].
- save_new_theme: Save to userCfg['theme'] (is function).
+ create_new: Combine theme with changes and save.
+ save_as_new_theme: Save [button_save_custom].
+ set_theme_type: Command for [theme_source].
+ delete_custom: Activate default [button_delete_custom].
+ save_new: Save to userCfg['theme'] (is function).
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
+ (*)button_set_color: Button
+ (*)targetlist: 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
+ (*)fg_on: Radiobutton - fg_bg_toggle
+ (*)bg_on: Radiobutton - fg_bg_toggle
+ (*)button_save_custom: 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
+ (*)builtin_theme_on: Radiobutton - theme_source
+ (*)custom_theme_on: Radiobutton - theme_source
+ (*)builtinlist: DynOptionMenu - builtin_name
+ (*)customlist: DynOptionMenu - custom_name
+ (*)button_delete_custom: Button
+ (*)theme_message: Label
"""
self.theme_elements={
'Normal Text': ('normal', '00'),
@@ -287,31 +353,30 @@ def create_page_highlight(self):
'Shell Stderr Text': ('stderr', '13'),
}
parent = self.parent
- self.builtin_theme = tracers.add(
- StringVar(parent), self.var_changed_builtin_theme)
- self.custom_theme = tracers.add(
- StringVar(parent), self.var_changed_custom_theme)
+ 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.fg_bg_toggle = BooleanVar(parent)
self.color = tracers.add(
StringVar(parent), self.var_changed_color)
- self.is_builtin_theme = tracers.add(
- BooleanVar(parent), self.var_changed_is_builtin_theme)
+ self.theme_source = tracers.add(
+ BooleanVar(parent), self.var_changed_theme_source)
self.highlight_target = tracers.add(
StringVar(parent), self.var_changed_highlight_target)
- # Widget creation:
- # body frame and section frames
+ # Create widgets:
+ # body frame and section frames.
frame = Frame(self.note)
frame_custom = LabelFrame(frame, borderwidth=2, relief=GROOVE,
text=' Custom Highlighting ')
frame_theme = LabelFrame(frame, borderwidth=2, relief=GROOVE,
text=' Highlighting Theme ')
- #frame_custom
+ # frame_custom.
text = self.highlight_sample = frame.highlight_sample = Text(
frame_custom, relief=SOLID, borderwidth=1,
font=('courier', 12, ''), cursor='hand2', width=21, height=13,
takefocus=FALSE, highlightthickness=0, wrap=NONE)
- text=self.highlight_sample
text.bind('<Double-Button-1>', lambda e: 'break')
text.bind('<B1-Motion>', lambda e: 'break')
text_and_tags=(('\n', 'normal'),
@@ -341,78 +406,77 @@ def tem(event, elem=element):
text['state'] = DISABLED
self.frame_color_set = Frame(frame_custom, relief=SOLID, borderwidth=1)
frame_fg_bg_toggle = Frame(frame_custom)
- button_set_color = Button(
+ self.button_set_color = Button(
self.frame_color_set, text='Choose Color for :',
command=self.get_color, highlightthickness=0)
- self.opt_menu_highlight_target = DynOptionMenu(
+ self.targetlist = DynOptionMenu(
self.frame_color_set, self.highlight_target, None,
highlightthickness=0) #, command=self.set_highlight_targetBinding
- self.radio_fg = Radiobutton(
+ self.fg_on = Radiobutton(
frame_fg_bg_toggle, variable=self.fg_bg_toggle, value=1,
text='Foreground', command=self.set_color_sample_binding)
- self.radio_bg=Radiobutton(
+ self.bg_on = Radiobutton(
frame_fg_bg_toggle, variable=self.fg_bg_toggle, value=0,
text='Background', command=self.set_color_sample_binding)
self.fg_bg_toggle.set(1)
- button_save_custom_theme = Button(
+ self.button_save_custom = Button(
frame_custom, text='Save as New Custom Theme',
command=self.save_as_new_theme)
- #frame_theme
+ # frame_theme.
theme_type_title = Label(frame_theme, text='Select : ')
- self.radio_theme_builtin = Radiobutton(
- frame_theme, variable=self.is_builtin_theme, value=1,
+ self.builtin_theme_on = Radiobutton(
+ frame_theme, variable=self.theme_source, value=1,
command=self.set_theme_type, text='a Built-in Theme')
- self.radio_theme_custom = Radiobutton(
- frame_theme, variable=self.is_builtin_theme, value=0,
+ self.custom_theme_on = Radiobutton(
+ frame_theme, variable=self.theme_source, value=0,
command=self.set_theme_type, text='a Custom Theme')
- self.opt_menu_theme_builtin = DynOptionMenu(
- frame_theme, self.builtin_theme, None, command=None)
- self.opt_menu_theme_custom=DynOptionMenu(
- frame_theme, self.custom_theme, None, command=None)
- self.button_delete_custom_theme=Button(
+ self.builtinlist = DynOptionMenu(
+ frame_theme, self.builtin_name, None, command=None)
+ self.customlist = DynOptionMenu(
+ frame_theme, self.custom_name, None, command=None)
+ self.button_delete_custom = Button(
frame_theme, text='Delete Custom Theme',
- command=self.delete_custom_theme)
- self.new_custom_theme = Label(frame_theme, bd=2)
+ command=self.delete_custom)
+ self.theme_message = Label(frame_theme, bd=2)
- ##widget packing
- #body
+ # Pack widgets:
+ # body.
frame_custom.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH)
frame_theme.pack(side=LEFT, padx=5, pady=5, fill=Y)
- #frame_custom
+ # frame_custom.
self.frame_color_set.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=X)
frame_fg_bg_toggle.pack(side=TOP, padx=5, pady=0)
self.highlight_sample.pack(
side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH)
- button_set_color.pack(side=TOP, expand=TRUE, fill=X, padx=8, pady=4)
- self.opt_menu_highlight_target.pack(
- side=TOP, expand=TRUE, fill=X, padx=8, pady=3)
- self.radio_fg.pack(side=LEFT, anchor=E)
- self.radio_bg.pack(side=RIGHT, anchor=W)
- button_save_custom_theme.pack(side=BOTTOM, fill=X, padx=5, pady=5)
- #frame_theme
+ self.button_set_color.pack(side=TOP, expand=TRUE, fill=X, padx=8, pady=4)
+ self.targetlist.pack(side=TOP, expand=TRUE, fill=X, padx=8, pady=3)
+ self.fg_on.pack(side=LEFT, anchor=E)
+ self.bg_on.pack(side=RIGHT, anchor=W)
+ self.button_save_custom.pack(side=BOTTOM, fill=X, padx=5, pady=5)
+ # frame_theme.
theme_type_title.pack(side=TOP, anchor=W, padx=5, pady=5)
- self.radio_theme_builtin.pack(side=TOP, anchor=W, padx=5)
- self.radio_theme_custom.pack(side=TOP, anchor=W, padx=5, pady=2)
- self.opt_menu_theme_builtin.pack(side=TOP, fill=X, padx=5, pady=5)
- self.opt_menu_theme_custom.pack(side=TOP, fill=X, anchor=W, padx=5, pady=5)
- self.button_delete_custom_theme.pack(side=TOP, fill=X, padx=5, pady=5)
- self.new_custom_theme.pack(side=TOP, fill=X, pady=5)
+ self.builtin_theme_on.pack(side=TOP, anchor=W, padx=5)
+ self.custom_theme_on.pack(side=TOP, anchor=W, padx=5, pady=2)
+ self.builtinlist.pack(side=TOP, fill=X, padx=5, pady=5)
+ self.customlist.pack(side=TOP, fill=X, anchor=W, padx=5, pady=5)
+ self.button_delete_custom.pack(side=TOP, fill=X, padx=5, pady=5)
+ self.theme_message.pack(side=TOP, fill=X, pady=5)
return frame
def load_theme_cfg(self):
"""Load current configuration settings for the theme options.
- Based on the is_builtin_theme toggle, the theme is set as
+ Based on the theme_source toggle, the theme is set as
either builtin or custom and the initial widget values
reflect the current settings from idleConf.
Attributes updated:
- is_builtin_theme: Set from idleConf.
- opt_menu_theme_builtin: List of default themes from idleConf.
- opt_menu_theme_custom: List of custom themes from idleConf.
- radio_theme_custom: Disabled if there are no custom themes.
+ theme_source: Set from idleConf.
+ builtinlist: List of default themes from idleConf.
+ customlist: List of custom themes from idleConf.
+ custom_theme_on: Disabled if there are no custom themes.
custom_theme: Message with additional information.
- opt_menu_highlight_target: Create menu from self.theme_elements.
+ targetlist: Create menu from self.theme_elements.
Methods:
set_theme_type
@@ -420,80 +484,81 @@ def load_theme_cfg(self):
set_highlight_target
"""
# Set current theme type radiobutton.
- self.is_builtin_theme.set(idleConf.GetOption(
+ self.theme_source.set(idleConf.GetOption(
'main', 'Theme', 'default', type='bool', default=1))
# Set current theme.
current_option = idleConf.CurrentTheme()
# Load available theme option menus.
- if self.is_builtin_theme.get(): # Default theme selected.
+ if self.theme_source.get(): # Default theme selected.
item_list = idleConf.GetSectionList('default', 'highlight')
item_list.sort()
- self.opt_menu_theme_builtin.SetMenu(item_list, current_option)
+ self.builtinlist.SetMenu(item_list, current_option)
item_list = idleConf.GetSectionList('user', 'highlight')
item_list.sort()
if not item_list:
- self.radio_theme_custom['state'] = DISABLED
- self.custom_theme.set('- no custom themes -')
+ self.custom_theme_on['state'] = DISABLED
+ self.custom_name.set('- no custom themes -')
else:
- self.opt_menu_theme_custom.SetMenu(item_list, item_list[0])
+ self.customlist.SetMenu(item_list, item_list[0])
else: # User theme selected.
item_list = idleConf.GetSectionList('user', 'highlight')
item_list.sort()
- self.opt_menu_theme_custom.SetMenu(item_list, current_option)
+ self.customlist.SetMenu(item_list, current_option)
item_list = idleConf.GetSectionList('default', 'highlight')
item_list.sort()
- self.opt_menu_theme_builtin.SetMenu(item_list, item_list[0])
+ self.builtinlist.SetMenu(item_list, item_list[0])
self.set_theme_type()
# Load theme element option menu.
theme_names = list(self.theme_elements.keys())
theme_names.sort(key=lambda x: self.theme_elements[x][1])
- self.opt_menu_highlight_target.SetMenu(theme_names, theme_names[0])
+ self.targetlist.SetMenu(theme_names, theme_names[0])
self.paint_theme_sample()
self.set_highlight_target()
- def var_changed_builtin_theme(self, *params):
+ def var_changed_builtin_name(self, *params):
"""Process new builtin theme selection.
Add the changed theme's name to the changed_items and recreate
the sample with the values from the selected theme.
"""
old_themes = ('IDLE Classic', 'IDLE New')
- value = self.builtin_theme.get()
+ value = self.builtin_name.get()
if value not in old_themes:
if idleConf.GetOption('main', 'Theme', 'name') not in old_themes:
changes.add_option('main', 'Theme', 'name', old_themes[0])
changes.add_option('main', 'Theme', 'name2', value)
- self.new_custom_theme.config(text='New theme, see Help',
- fg='#500000')
+ self.theme_message['text'] = 'New theme, see Help'
+ self.theme_message['fg'] = '#500000'
else:
changes.add_option('main', 'Theme', 'name', value)
changes.add_option('main', 'Theme', 'name2', '')
- self.new_custom_theme.config(text='', fg='black')
+ self.theme_message['text'] = ''
+ self.theme_message['fg'] = 'black'
self.paint_theme_sample()
- def var_changed_custom_theme(self, *params):
+ def var_changed_custom_name(self, *params):
"""Process new custom theme selection.
If a new custom theme is selected, add the name to the
changed_items and apply the theme to the sample.
"""
- value = self.custom_theme.get()
+ value = self.custom_name.get()
if value != '- no custom themes -':
changes.add_option('main', 'Theme', 'name', value)
self.paint_theme_sample()
- def var_changed_is_builtin_theme(self, *params):
+ def var_changed_theme_source(self, *params):
"""Process toggle between builtin and custom theme.
Update the default toggle value and apply the newly
selected theme type.
"""
- value = self.is_builtin_theme.get()
+ value = self.theme_source.get()
changes.add_option('main', 'Theme', 'default', value)
if value:
- self.var_changed_builtin_theme()
+ self.var_changed_builtin_name()
else:
- self.var_changed_custom_theme()
+ self.var_changed_custom_name()
def var_changed_color(self, *params):
"Process change to color choice."
@@ -507,29 +572,29 @@ def set_theme_type(self):
"""Set available screen options based on builtin or custom theme.
Attributes accessed:
- is_builtin_theme
+ theme_source
Attributes updated:
- opt_menu_theme_builtin
- opt_menu_theme_custom
- button_delete_custom_theme
- radio_theme_custom
+ builtinlist
+ customlist
+ button_delete_custom
+ custom_theme_on
Called from:
- handler for radio_theme_builtin and radio_theme_custom
- delete_custom_theme
- create_new_theme
+ handler for builtin_theme_on and custom_theme_on
+ delete_custom
+ create_new
load_theme_cfg
"""
- if self.is_builtin_theme.get():
- self.opt_menu_theme_builtin['state'] = NORMAL
- self.opt_menu_theme_custom['state'] = DISABLED
- self.button_delete_custom_theme['state'] = DISABLED
+ if self.theme_source.get():
+ self.builtinlist['state'] = NORMAL
+ self.customlist['state'] = DISABLED
+ self.button_delete_custom['state'] = DISABLED
else:
- self.opt_menu_theme_builtin['state'] = DISABLED
- self.radio_theme_custom['state'] = NORMAL
- self.opt_menu_theme_custom['state'] = NORMAL
- self.button_delete_custom_theme['state'] = NORMAL
+ self.builtinlist['state'] = DISABLED
+ self.custom_theme_on['state'] = NORMAL
+ self.customlist['state'] = NORMAL
+ self.button_delete_custom['state'] = NORMAL
def get_color(self):
"""Handle button to select a new color for the target tag.
@@ -540,14 +605,14 @@ def get_color(self):
Attributes accessed:
highlight_target
frame_color_set
- is_builtin_theme
+ theme_source
Attributes updated:
color
Methods:
get_new_theme_name
- create_new_theme
+ create_new
"""
target = self.highlight_target.get()
prev_color = self.frame_color_set.cget('bg')
@@ -556,26 +621,26 @@ def get_color(self):
initialcolor=prev_color)
if color_string and (color_string != prev_color):
# User didn't cancel and they chose a new color.
- if self.is_builtin_theme.get(): # Current theme is a built-in.
+ if self.theme_source.get(): # Current theme is a built-in.
message = ('Your changes will be saved as a new Custom Theme. '
'Enter a name for your new Custom Theme below.')
new_theme = self.get_new_theme_name(message)
if not new_theme: # User cancelled custom theme creation.
return
else: # Create new custom theme based on previously active theme.
- self.create_new_theme(new_theme)
+ self.create_new(new_theme)
self.color.set(color_string)
else: # Current theme is user defined.
self.color.set(color_string)
def on_new_color_set(self):
"Display sample of new color selection on the dialog."
- new_color=self.color.get()
- self.frame_color_set.config(bg=new_color) # Set sample.
- plane ='foreground' if self.fg_bg_toggle.get() else 'background'
+ new_color = self.color.get()
+ self.frame_color_set['bg'] = new_color # Set sample.
+ plane = 'foreground' if self.fg_bg_toggle.get() else 'background'
sample_element = self.theme_elements[self.highlight_target.get()][0]
- self.highlight_sample.tag_config(sample_element, **{plane:new_color})
- theme = self.custom_theme.get()
+ self.highlight_sample.tag_config(sample_element, **{plane: new_color})
+ theme = self.custom_name.get()
theme_element = sample_element + '-' + plane
changes.add_option('highlight', theme, theme_element, new_color)
@@ -592,13 +657,13 @@ def save_as_new_theme(self):
Methods:
get_new_theme_name
- create_new_theme
+ create_new
"""
new_theme_name = self.get_new_theme_name('New Theme Name:')
if new_theme_name:
- self.create_new_theme(new_theme_name)
+ self.create_new(new_theme_name)
- def create_new_theme(self, new_theme_name):
+ def create_new(self, new_theme_name):
"""Create a new custom theme with the given name.
Create the new theme based on the previously active theme
@@ -606,23 +671,23 @@ def create_new_theme(self, new_theme_name):
activate the new theme.
Attributes accessed:
- builtin_theme
- custom_theme
+ builtin_name
+ custom_name
Attributes updated:
- opt_menu_theme_custom
- is_builtin_theme
+ customlist
+ theme_source
Method:
- save_new_theme
+ save_new
set_theme_type
"""
- if self.is_builtin_theme.get():
+ if self.theme_source.get():
theme_type = 'default'
- theme_name = self.builtin_theme.get()
+ theme_name = self.builtin_name.get()
else:
theme_type = 'user'
- theme_name = self.custom_theme.get()
+ theme_name = self.custom_name.get()
new_theme = idleConf.GetThemeDict(theme_type, theme_name)
# Apply any of the old theme's unsaved changes to the new theme.
if theme_name in changes['highlight']:
@@ -630,12 +695,12 @@ def create_new_theme(self, new_theme_name):
for element in theme_changes:
new_theme[element] = theme_changes[element]
# Save the new theme.
- self.save_new_theme(new_theme_name, new_theme)
+ self.save_new(new_theme_name, new_theme)
# Change GUI over to the new theme.
custom_theme_list = idleConf.GetSectionList('user', 'highlight')
custom_theme_list.sort()
- self.opt_menu_theme_custom.SetMenu(custom_theme_list, new_theme_name)
- self.is_builtin_theme.set(0)
+ self.customlist.SetMenu(custom_theme_list, new_theme_name)
+ self.theme_source.set(0)
self.set_theme_type()
def set_highlight_target(self):
@@ -645,8 +710,8 @@ def set_highlight_target(self):
highlight_target
Attributes updated:
- radio_fg
- radio_bg
+ fg_on
+ bg_on
fg_bg_toggle
Methods:
@@ -657,12 +722,12 @@ def set_highlight_target(self):
load_theme_cfg
"""
if self.highlight_target.get() == 'Cursor': # bg not possible
- self.radio_fg['state'] = DISABLED
- self.radio_bg['state'] = DISABLED
+ self.fg_on['state'] = DISABLED
+ self.bg_on['state'] = DISABLED
self.fg_bg_toggle.set(1)
else: # Both fg and bg can be set.
- self.radio_fg['state'] = NORMAL
- self.radio_bg['state'] = NORMAL
+ self.fg_on['state'] = NORMAL
+ self.bg_on['state'] = NORMAL
self.fg_bg_toggle.set(1)
self.set_color_sample()
@@ -690,16 +755,16 @@ def set_color_sample(self):
tag = self.theme_elements[self.highlight_target.get()][0]
plane = 'foreground' if self.fg_bg_toggle.get() else 'background'
color = self.highlight_sample.tag_cget(tag, plane)
- self.frame_color_set.config(bg=color)
+ self.frame_color_set['bg'] = color
def paint_theme_sample(self):
"""Apply the theme colors to each element tag in the sample text.
Instance attributes accessed:
theme_elements
- is_builtin_theme
- builtin_theme
- custom_theme
+ theme_source
+ builtin_name
+ custom_name
Attributes updated:
highlight_sample: Set the tag elements to the theme.
@@ -708,14 +773,14 @@ def paint_theme_sample(self):
set_color_sample
Called from:
- var_changed_builtin_theme
- var_changed_custom_theme
+ var_changed_builtin_name
+ var_changed_custom_name
load_theme_cfg
"""
- if self.is_builtin_theme.get(): # Default theme
- theme = self.builtin_theme.get()
+ if self.theme_source.get(): # Default theme
+ theme = self.builtin_name.get()
else: # User theme
- theme = self.custom_theme.get()
+ theme = self.custom_name.get()
for element_title in self.theme_elements:
element = self.theme_elements[element_title][0]
colors = idleConf.GetHighlight(theme, element)
@@ -732,7 +797,7 @@ def paint_theme_sample(self):
self.highlight_sample.tag_config(element, **colors)
self.set_color_sample()
- def save_new_theme(self, theme_name, theme):
+ def save_new(self, theme_name, theme):
"""Save a newly created theme to idleConf.
theme_name - string, the name of the new theme
@@ -744,7 +809,7 @@ def save_new_theme(self, theme_name, theme):
value = theme[element]
idleConf.userCfg['highlight'].SetOption(theme_name, element, value)
- def delete_custom_theme(self):
+ def delete_custom(self):
"""Handle event to delete custom theme.
The current theme is deactivated and the default theme is
@@ -752,13 +817,13 @@ def delete_custom_theme(self):
the config file.
Attributes accessed:
- custom_theme
+ custom_name
Attributes updated:
- radio_theme_custom
- opt_menu_theme_custom
- is_builtin_theme
- builtin_theme
+ custom_theme_on
+ customlist
+ theme_source
+ builtin_name
Methods:
deactivate_current_config
@@ -766,7 +831,7 @@ def delete_custom_theme(self):
activate_config_changes
set_theme_type
"""
- theme_name = self.custom_theme.get()
+ theme_name = self.custom_name.get()
delmsg = 'Are you sure you wish to delete the theme %r ?'
if not tkMessageBox.askyesno(
'Delete Theme', delmsg % theme_name, parent=self):
@@ -778,13 +843,13 @@ def delete_custom_theme(self):
item_list = idleConf.GetSectionList('user', 'highlight')
item_list.sort()
if not item_list:
- self.radio_theme_custom['state'] = DISABLED
- self.opt_menu_theme_custom.SetMenu(item_list, '- no custom themes -')
+ self.custom_theme_on['state'] = DISABLED
+ self.customlist.SetMenu(item_list, '- no custom themes -')
else:
- self.opt_menu_theme_custom.SetMenu(item_list, item_list[0])
+ self.customlist.SetMenu(item_list, item_list[0])
# Revert to default theme.
- self.is_builtin_theme.set(idleConf.defaultCfg['main'].Get('Theme', 'default'))
- self.builtin_theme.set(idleConf.defaultCfg['main'].Get('Theme', 'name'))
+ self.theme_source.set(idleConf.defaultCfg['main'].Get('Theme', 'default'))
+ self.builtin_name.set(idleConf.defaultCfg['main'].Get('Theme', 'name'))
# User can't back out of these changes, they must be applied now.
changes.save_all()
self.save_all_changed_extensions()
@@ -1270,7 +1335,7 @@ def create_page_keys(self):
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
+ selected binding event and current keys and allows 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
diff --git a/Lib/idlelib/idle_test/test_configdialog.py b/Lib/idlelib/idle_test/test_configdialog.py
index 964784508f8..966a72b50a9 100644
--- a/Lib/idlelib/idle_test/test_configdialog.py
+++ b/Lib/idlelib/idle_test/test_configdialog.py
@@ -139,7 +139,7 @@ def test_fontlist_mouse(self):
self.assertIn(d.font_name.get(), font1.lower())
def test_sizelist(self):
- # Click on number shouod select that number
+ # Click on number should select that number
d = self.page
d.sizelist.variable.set(40)
self.assertEqual(d.font_size.get(), '40')
@@ -227,9 +227,457 @@ def test_indent_scale(self):
class HighlightTest(unittest.TestCase):
+ """Test that highlight tab widgets enable users to make changes.
+
+ Test that widget actions set vars, that var changes add
+ options to changes and that themes work correctly.
+ """
+
+ @classmethod
+ def setUpClass(cls):
+ d = dialog
+ dialog.note.select(d.highpage)
+ d.set_theme_type = Func()
+ d.paint_theme_sample = Func()
+ d.set_highlight_target = Func()
+ d.set_color_sample = Func()
+
+ @classmethod
+ def tearDownClass(cls):
+ d = dialog
+ del d.set_theme_type, d.paint_theme_sample
+ del d.set_highlight_target, d.set_color_sample
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', 'highlight'):
+ idleConf.userCfg['highlight'].remove_section(section)
changes.clear()
+ d.set_theme_type.called = 0
+ d.paint_theme_sample.called = 0
+ d.set_highlight_target.called = 0
+ d.set_color_sample.called = 0
+
+ def test_load_theme_cfg(self):
+ tracers.detach()
+ d = dialog
+ eq = self.assertEqual
+
+ # Use builtin theme with no user themes created.
+ idleConf.CurrentTheme = mock.Mock(return_value='IDLE Classic')
+ d.load_theme_cfg()
+ self.assertTrue(d.theme_source.get())
+ # builtinlist sets variable builtin_name to the CurrentTheme default.
+ eq(d.builtin_name.get(), 'IDLE Classic')
+ eq(d.custom_name.get(), '- no custom themes -')
+ eq(d.custom_theme_on['state'], DISABLED)
+ eq(d.set_theme_type.called, 1)
+ eq(d.paint_theme_sample.called, 1)
+ eq(d.set_highlight_target.called, 1)
+
+ # Builtin theme with non-empty user theme list.
+ idleConf.SetOption('highlight', 'test1', 'option', 'value')
+ idleConf.SetOption('highlight', 'test2', 'option2', 'value2')
+ d.load_theme_cfg()
+ eq(d.builtin_name.get(), 'IDLE Classic')
+ eq(d.custom_name.get(), 'test1')
+ eq(d.set_theme_type.called, 2)
+ eq(d.paint_theme_sample.called, 2)
+ eq(d.set_highlight_target.called, 2)
+
+ # Use custom theme.
+ idleConf.CurrentTheme = mock.Mock(return_value='test2')
+ idleConf.SetOption('main', 'Theme', 'default', '0')
+ d.load_theme_cfg()
+ self.assertFalse(d.theme_source.get())
+ eq(d.builtin_name.get(), 'IDLE Classic')
+ eq(d.custom_name.get(), 'test2')
+ eq(d.set_theme_type.called, 3)
+ eq(d.paint_theme_sample.called, 3)
+ eq(d.set_highlight_target.called, 3)
+
+ del idleConf.CurrentTheme
+ tracers.attach()
+
+ def test_theme_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_theme_on.invoke()
+ eq(mainpage, {'Theme': {'default': 'True'}})
+ eq(d.var_changed_builtin_name.called, 1)
+ eq(d.var_changed_custom_name.called, 0)
+ changes.clear()
+
+ # Custom selected.
+ d.custom_theme_on['state'] = NORMAL
+ d.custom_theme_on.invoke()
+ self.assertEqual(mainpage, {'Theme': {'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
+ item_list = ['IDLE Classic', 'IDLE Dark', 'IDLE New']
+
+ # Not in old_themes, defaults name to first item.
+ idleConf.SetOption('main', 'Theme', 'name', 'spam')
+ d.builtinlist.SetMenu(item_list, 'IDLE Dark')
+ eq(mainpage, {'Theme': {'name': 'IDLE Classic',
+ 'name2': 'IDLE Dark'}})
+ eq(d.theme_message['text'], 'New theme, see Help')
+ eq(d.paint_theme_sample.called, 1)
+
+ # Not in old themes - uses name2.
+ changes.clear()
+ idleConf.SetOption('main', 'Theme', 'name', 'IDLE New')
+ d.builtinlist.SetMenu(item_list, 'IDLE Dark')
+ eq(mainpage, {'Theme': {'name2': 'IDLE Dark'}})
+ eq(d.theme_message['text'], 'New theme, see Help')
+ eq(d.paint_theme_sample.called, 2)
+
+ # Builtin name in old_themes.
+ changes.clear()
+ d.builtinlist.SetMenu(item_list, 'IDLE Classic')
+ eq(mainpage, {'Theme': {'name': 'IDLE Classic', 'name2': ''}})
+ eq(d.theme_message['text'], '')
+ eq(d.paint_theme_sample.called, 3)
+
+ def test_custom_name(self):
+ d = dialog
+
+ # If no selections, doesn't get added.
+ d.customlist.SetMenu([], '- no custom themes -')
+ self.assertNotIn('Theme', mainpage)
+ self.assertEqual(d.paint_theme_sample.called, 0)
+
+ # Custom name selected.
+ changes.clear()
+ d.customlist.SetMenu(['a', 'b', 'c'], 'c')
+ self.assertEqual(mainpage, {'Theme': {'name': 'c'}})
+ self.assertEqual(d.paint_theme_sample.called, 1)
+
+ def test_color(self):
+ d = dialog
+ d.on_new_color_set = Func()
+ # self.color is only set in get_color through ColorChooser.
+ d.color.set('green')
+ self.assertEqual(d.on_new_color_set.called, 1)
+ del d.on_new_color_set
+
+ def test_highlight_target_list_mouse(self):
+ # Set highlight_target through targetlist.
+ eq = self.assertEqual
+ d = dialog
+
+ d.targetlist.SetMenu(['a', 'b', 'c'], 'c')
+ eq(d.highlight_target.get(), 'c')
+ eq(d.set_highlight_target.called, 1)
+
+ def test_highlight_target_text_mouse(self):
+ # Set highlight_target through clicking highlight_sample.
+ eq = self.assertEqual
+ d = dialog
+
+ elem = {}
+ count = 0
+ hs = d.highlight_sample
+ hs.focus_force()
+ hs.see(1.0)
+ hs.update_idletasks()
+
+ def tag_to_element(elem):
+ for element, tag in d.theme_elements.items():
+ elem[tag[0]] = element
+
+ def click_it(start):
+ x, y, dx, dy = hs.bbox(start)
+ x += dx // 2
+ y += dy // 2
+ hs.event_generate('<Enter>', x=0, y=0)
+ hs.event_generate('<Motion>', x=x, y=y)
+ hs.event_generate('<ButtonPress-1>', x=x, y=y)
+ hs.event_generate('<ButtonRelease-1>', x=x, y=y)
+
+ # Flip theme_elements to make the tag the key.
+ tag_to_element(elem)
+
+ # If highlight_sample has a tag that isn't in theme_elements, there
+ # will be a KeyError in the test run.
+ for tag in hs.tag_names():
+ for start_index in hs.tag_ranges(tag)[0::2]:
+ count += 1
+ click_it(start_index)
+ eq(d.highlight_target.get(), elem[tag])
+ eq(d.set_highlight_target.called, count)
+
+ def test_set_theme_type(self):
+ eq = self.assertEqual
+ d = dialog
+ del d.set_theme_type
+
+ # Builtin theme selected.
+ d.theme_source.set(True)
+ d.set_theme_type()
+ eq(d.builtinlist['state'], NORMAL)
+ eq(d.customlist['state'], DISABLED)
+ eq(d.button_delete_custom['state'], DISABLED)
+
+ # Custom theme selected.
+ d.theme_source.set(False)
+ d.set_theme_type()
+ eq(d.builtinlist['state'], DISABLED)
+ eq(d.custom_theme_on['state'], NORMAL)
+ eq(d.customlist['state'], NORMAL)
+ eq(d.button_delete_custom['state'], NORMAL)
+ d.set_theme_type = Func()
+
+ def test_get_color(self):
+ eq = self.assertEqual
+ d = dialog
+ orig_chooser = configdialog.tkColorChooser.askcolor
+ chooser = configdialog.tkColorChooser.askcolor = Func()
+ gntn = d.get_new_theme_name = Func()
+
+ d.highlight_target.set('Editor Breakpoint')
+ d.color.set('#ffffff')
+
+ # Nothing selected.
+ chooser.result = (None, None)
+ d.button_set_color.invoke()
+ eq(d.color.get(), '#ffffff')
+
+ # Selection same as previous color.
+ chooser.result = ('', d.frame_color_set.cget('bg'))
+ d.button_set_color.invoke()
+ eq(d.color.get(), '#ffffff')
+
+ # Select different color.
+ chooser.result = ((222.8671875, 0.0, 0.0), '#de0000')
+
+ # Default theme.
+ d.color.set('#ffffff')
+ d.theme_source.set(True)
+
+ # No theme name selected therefore color not saved.
+ gntn.result = ''
+ d.button_set_color.invoke()
+ eq(gntn.called, 1)
+ eq(d.color.get(), '#ffffff')
+ # Theme name selected.
+ gntn.result = 'My New Theme'
+ d.button_set_color.invoke()
+ eq(d.custom_name.get(), gntn.result)
+ eq(d.color.get(), '#de0000')
+
+ # Custom theme.
+ d.color.set('#ffffff')
+ d.theme_source.set(False)
+ d.button_set_color.invoke()
+ eq(d.color.get(), '#de0000')
+
+ del d.get_new_theme_name
+ configdialog.tkColorChooser.askcolor = orig_chooser
+
+ def test_on_new_color_set(self):
+ d = dialog
+ color = '#3f7cae'
+ d.custom_name.set('Python')
+ d.highlight_target.set('Selected Text')
+ d.fg_bg_toggle.set(True)
+
+ d.color.set(color)
+ self.assertEqual(d.frame_color_set.cget('bg'), color)
+ self.assertEqual(d.highlight_sample.tag_cget('hilite', 'foreground'), color)
+ self.assertEqual(highpage,
+ {'Python': {'hilite-foreground': color}})
+
+ def test_get_new_theme_name(self):
+ orig_sectionname = configdialog.SectionName
+ sn = configdialog.SectionName = Func(return_self=True)
+ d = dialog
+
+ sn.result = 'New Theme'
+ self.assertEqual(d.get_new_theme_name(''), 'New Theme')
+
+ configdialog.SectionName = orig_sectionname
+
+ def test_save_as_new_theme(self):
+ d = dialog
+ gntn = d.get_new_theme_name = Func()
+ d.theme_source.set(True)
+
+ # No name entered.
+ gntn.result = ''
+ d.button_save_custom.invoke()
+ self.assertNotIn(gntn.result, idleConf.userCfg['highlight'])
+
+ # Name entered.
+ gntn.result = 'my new theme'
+ gntn.called = 0
+ self.assertNotIn(gntn.result, idleConf.userCfg['highlight'])
+ d.button_save_custom.invoke()
+ self.assertIn(gntn.result, idleConf.userCfg['highlight'])
+
+ del d.get_new_theme_name
+
+ def test_create_new_and_save_new(self):
+ eq = self.assertEqual
+ d = dialog
+
+ # Use default as previously active theme.
+ d.theme_source.set(True)
+ d.builtin_name.set('IDLE Classic')
+ first_new = 'my new custom theme'
+ second_new = 'my second custom theme'
+
+ # No changes, so themes are an exact copy.
+ self.assertNotIn(first_new, idleConf.userCfg)
+ d.create_new(first_new)
+ eq(idleConf.GetSectionList('user', 'highlight'), [first_new])
+ eq(idleConf.GetThemeDict('default', 'IDLE Classic'),
+ idleConf.GetThemeDict('user', first_new))
+ eq(d.custom_name.get(), first_new)
+ self.assertFalse(d.theme_source.get()) # Use custom set.
+ eq(d.set_theme_type.called, 1)
+
+ # Test that changed targets are in new theme.
+ changes.add_option('highlight', first_new, 'hit-background', 'yellow')
+ self.assertNotIn(second_new, idleConf.userCfg)
+ d.create_new(second_new)
+ eq(idleConf.GetSectionList('user', 'highlight'), [first_new, second_new])
+ self.assertNotEqual(idleConf.GetThemeDict('user', first_new),
+ idleConf.GetThemeDict('user', second_new))
+ # Check that difference in themes was in `hit-background` from `changes`.
+ idleConf.SetOption('highlight', first_new, 'hit-background', 'yellow')
+ eq(idleConf.GetThemeDict('user', first_new),
+ idleConf.GetThemeDict('user', second_new))
+
+ def test_set_highlight_target(self):
+ eq = self.assertEqual
+ d = dialog
+ del d.set_highlight_target
+
+ # Target is cursor.
+ d.highlight_target.set('Cursor')
+ eq(d.fg_on['state'], DISABLED)
+ eq(d.bg_on['state'], DISABLED)
+ self.assertTrue(d.fg_bg_toggle)
+ eq(d.set_color_sample.called, 1)
+
+ # Target is not cursor.
+ d.highlight_target.set('Comment')
+ eq(d.fg_on['state'], NORMAL)
+ eq(d.bg_on['state'], NORMAL)
+ self.assertTrue(d.fg_bg_toggle)
+ eq(d.set_color_sample.called, 2)
+
+ d.set_highlight_target = Func()
+
+ def test_set_color_sample_binding(self):
+ d = dialog
+ scs = d.set_color_sample
+
+ d.fg_on.invoke()
+ self.assertEqual(scs.called, 1)
+
+ d.bg_on.invoke()
+ self.assertEqual(scs.called, 2)
+
+ def test_set_color_sample(self):
+ d = dialog
+ del d.set_color_sample
+ d.highlight_target.set('Selected Text')
+ d.fg_bg_toggle.set(True)
+ d.set_color_sample()
+ self.assertEqual(d.frame_color_set.cget('bg'),
+ d.highlight_sample.tag_cget('hilite', 'foreground'))
+ d.set_color_sample = Func()
+
+ def test_paint_theme_sample(self):
+ eq = self.assertEqual
+ d = dialog
+ del d.paint_theme_sample
+ hs_tag = d.highlight_sample.tag_cget
+ gh = idleConf.GetHighlight
+ fg = 'foreground'
+ bg = 'background'
+
+ # Create custom theme based on IDLE Dark.
+ d.theme_source.set(True)
+ d.builtin_name.set('IDLE Dark')
+ theme = 'IDLE Test'
+ d.create_new(theme)
+ d.set_color_sample.called = 0
+
+ # Base theme with nothing in `changes`.
+ d.paint_theme_sample()
+ eq(hs_tag('break', fg), gh(theme, 'break', fgBg='fg'))
+ eq(hs_tag('cursor', bg), gh(theme, 'normal', fgBg='bg'))
+ self.assertNotEqual(hs_tag('console', fg), 'blue')
+ self.assertNotEqual(hs_tag('console', bg), 'yellow')
+ eq(d.set_color_sample.called, 1)
+
+ # Apply changes.
+ changes.add_option('highlight', theme, 'console-foreground', 'blue')
+ changes.add_option('highlight', theme, 'console-background', 'yellow')
+ d.paint_theme_sample()
+
+ eq(hs_tag('break', fg), gh(theme, 'break', fgBg='fg'))
+ eq(hs_tag('cursor', bg), gh(theme, 'normal', fgBg='bg'))
+ eq(hs_tag('console', fg), 'blue')
+ eq(hs_tag('console', bg), 'yellow')
+ eq(d.set_color_sample.called, 2)
+
+ d.paint_theme_sample = Func()
+
+ def test_delete_custom(self):
+ eq = self.assertEqual
+ d = dialog
+ d.button_delete_custom['state'] = NORMAL
+ yesno = configdialog.tkMessageBox.askyesno = Func()
+ dialog.deactivate_current_config = Func()
+ dialog.activate_config_changes = Func()
+
+ theme_name = 'spam theme'
+ idleConf.userCfg['highlight'].SetOption(theme_name, 'name', 'value')
+ highpage[theme_name] = {'option': 'True'}
+
+ # Force custom theme.
+ d.theme_source.set(False)
+ d.custom_name.set(theme_name)
+
+ # Cancel deletion.
+ yesno.result = False
+ d.button_delete_custom.invoke()
+ eq(yesno.called, 1)
+ eq(highpage[theme_name], {'option': 'True'})
+ eq(idleConf.GetSectionList('user', 'highlight'), ['spam theme'])
+ eq(dialog.deactivate_current_config.called, 0)
+ eq(dialog.activate_config_changes.called, 0)
+ eq(d.set_theme_type.called, 0)
+
+ # Confirm deletion.
+ yesno.result = True
+ d.button_delete_custom.invoke()
+ eq(yesno.called, 2)
+ self.assertNotIn(theme_name, highpage)
+ eq(idleConf.GetSectionList('user', 'highlight'), [])
+ eq(d.custom_theme_on['state'], DISABLED)
+ eq(d.custom_name.get(), '- no custom themes -')
+ eq(dialog.deactivate_current_config.called, 1)
+ eq(dialog.activate_config_changes.called, 1)
+ eq(d.set_theme_type.called, 1)
+
+ del dialog.activate_config_changes, dialog.deactivate_current_config
+ del configdialog.tkMessageBox.askyesno
class KeysPageTest(unittest.TestCase):
diff --git a/Misc/NEWS.d/next/IDLE/2017-08-17-15-00-20.bpo-31001.KLxYHC.rst b/Misc/NEWS.d/next/IDLE/2017-08-17-15-00-20.bpo-31001.KLxYHC.rst
new file mode 100644
index 00000000000..5e1eeee0419
--- /dev/null
+++ b/Misc/NEWS.d/next/IDLE/2017-08-17-15-00-20.bpo-31001.KLxYHC.rst
@@ -0,0 +1 @@
+Add tests for configdialog highlight tab. Patch by Cheryl Sabella.
1
0
![](https://secure.gravatar.com/avatar/628f74ad69d6dcc809f7cfa1417733a9.jpg?s=120&d=mm&r=g)
17 Aug '17
Results for project python/master, build date: 2017-08-17 03:04:01-07:00.
- commit: ee84a60
- previous commit: 7cc3399
- revision date: 2017-08-16 20:53:28+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.061% | -0.145% | +4.118% | +8.632% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | call_method| 3.056% | -1.580% | +20.334% | +15.601% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | call_method_slots| 2.353% | -0.665% | +23.820% | +12.999% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | call_method_unknown| 0.970% | -0.074% | +20.970% | +13.868% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | call_simple| 2.254% | -0.294% | +2.309% | +15.976% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | chameleon| 1.357% | +0.580% | +11.549% | +10.002% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | chaos| 0.580% | +0.024% | +6.221% | +11.565% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | crypto_pyaes| 0.525% | +0.250% | +4.512% | +4.916% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | deltablue| 4.261% | +0.238% | +6.017% | +20.053% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | django_template| 4.672% | -0.842% | +8.565% | +15.787% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | dulwich_log| 1.067% | +0.646% | +4.708% | +6.384% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | fannkuch| 0.482% | +0.368% | +4.489% | +4.997% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | float| 0.820% | -0.050% | +3.187% | +7.701% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | genshi_text| 1.567% | -0.294% | +8.695% | +12.135% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | genshi_xml| 2.170% | +0.088% | +8.000% | +9.063% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | go| 0.753% | -0.272% | +6.154% | +11.793% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | hexiom| 0.422% | +0.093% | +9.145% | +11.709% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | html5lib| 3.461% | +0.125% | +7.909% | +10.865% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | json_dumps| 1.573% | +0.748% | +4.267% | +9.951% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | json_loads| 0.994% | +0.065% | +2.833% | +9.916% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | logging_format| 1.608% | +0.451% | +8.945% | +10.220% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | logging_silent| 3.015% | -0.789% | +46.803% | +12.647% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | logging_simple| 1.844% | +0.208% | +9.685% | +10.853% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | mako| 0.458% | +0.105% | +18.850% | +11.197% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | mdp| 6.226% | +1.154% | +6.173% | +13.678% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | meteor_contest| 1.576% | +0.139% | +3.518% | +5.935% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | nbody| 0.864% | -0.101% | -2.105% | +3.456% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | nqueens| 1.402% | +0.080% | +2.785% | +4.555% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | pathlib| 1.563% | -0.988% | +4.987% | +9.645% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | pickle| 3.104% | -0.360% | +0.457% | +22.779% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | pickle_dict| 0.492% | +0.113% | +2.541% | +23.735% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | pickle_list| 0.921% | +0.507% | +5.614% | +21.342% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | pickle_pure_python| 6.366% | -2.590% | +10.398% | +12.269% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | pidigits| 0.101% | +0.012% | +0.311% | +10.089% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | python_startup| 0.128% | +0.156% | +9.021% | +4.616% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | python_startup_no_site| 0.090% | +0.131% | +1.032% | +4.363% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | raytrace| 1.003% | +0.144% | +9.329% | +13.942% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | regex_compile| 4.699% | -2.998% | -12.526% | +15.165% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | regex_dna| 0.529% | -0.030% | +1.491% | +4.331% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | regex_effbot| 1.835% | -0.212% | +0.532% | -3.299% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | regex_v8| 0.588% | +0.541% | +11.771% | +4.028% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | richards| 1.912% | +0.084% | +7.804% | +14.127% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | scimark_fft| 1.861% | -0.205% | +0.598% | +2.790% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | scimark_lu| 2.355% | -0.347% | +26.524% | +8.732% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | scimark_monte_carlo| 1.791% | -0.746% | +2.937% | +8.354% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | scimark_sor| 0.890% | -0.227% | +13.127% | +11.853% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | scimark_sparse_mat_mult| 1.013% | +0.037% | -0.352% | +1.565% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | spectral_norm| 0.356% | +0.025% | +5.656% | -1.513% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | sqlalchemy_declarative| 1.266% | +0.309% | +5.490% | +7.527% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | sqlalchemy_imperative| 2.732% | +0.108% | +5.458% | +6.671% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | sqlite_synth| 3.776% | +1.377% | +4.310% | +8.164% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | sympy_expand| 2.319% | +0.087% | +12.328% | +9.157% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | sympy_integrate| 1.580% | -0.140% | +10.308% | +7.254% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | sympy_str| 3.823% | +0.026% | +12.135% | +10.055% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | sympy_sum| 4.877% | -0.325% | +13.399% | +10.733% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | telco| 4.928% | +0.578% | +23.538% | +9.948% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | tornado_http| 1.072% | -0.038% | +6.058% | +7.421% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | unpack_sequence| 1.473% | -0.524% | +0.385% | +1.629% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | unpickle| 9.315% | -0.623% | +7.428% | +21.719% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | unpickle_list| 0.635% | +0.616% | -2.773% | +20.149% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | unpickle_pure_python| 1.616% | +0.153% | +6.997% | +7.610% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | xml_etree_generate| 1.110% | -0.022% | +5.701% | +8.478% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | xml_etree_iterparse| 2.001% | +0.378% | +2.595% | +5.540% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | xml_etree_parse| 1.124% | +0.212% | -5.162% | +10.738% |
+-----+------------------------+--------+------------+------------+------------+
| :-| | xml_etree_process| 1.371% | -0.120% | +6.582% | +8.902% |
+-----+------------------------+--------+------------+------------+------------+
* 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://github.com/python/cpython/commit/f6ebd838f00b4c211c72d85ee49749e910…
commit: f6ebd838f00b4c211c72d85ee49749e910cd3afe
branch: master
author: Victor Stinner <victor.stinner(a)gmail.com>
committer: GitHub <noreply(a)github.com>
date: 2017-08-17T22:13:11+02:00
summary:
bpo-30871: pythoninfo: add expat and _decimal (#3121)
* bpo-30871: pythoninfo: add expat and _decimal
* Remove _decimal.__version__
The string is hardcoded, not really interesting.
files:
M Lib/test/pythoninfo.py
diff --git a/Lib/test/pythoninfo.py b/Lib/test/pythoninfo.py
index 03081b6dddc..e0541135331 100644
--- a/Lib/test/pythoninfo.py
+++ b/Lib/test/pythoninfo.py
@@ -345,6 +345,26 @@ def collect_zlib(info_add):
copy_attributes(info_add, zlib, 'zlib.%s', attributes)
+def collect_expat(info_add):
+ try:
+ from xml.parsers import expat
+ except ImportError:
+ return
+
+ attributes = ('EXPAT_VERSION',)
+ copy_attributes(info_add, expat, 'expat.%s', attributes)
+
+
+def collect_decimal(info_add):
+ try:
+ import _decimal
+ except ImportError:
+ return
+
+ attributes = ('__libmpdec_version__',)
+ copy_attributes(info_add, _decimal, '_decimal.%s', attributes)
+
+
def collect_info(info):
error = False
info_add = info.add
@@ -365,6 +385,8 @@ def collect_info(info):
collect_time,
collect_tkinter,
collect_zlib,
+ collect_expat,
+ collect_decimal,
):
try:
collect_func(info_add)
1
0
https://github.com/python/cpython/commit/f1ff2c4b613ba7acc5d80e67d3c175d844…
commit: f1ff2c4b613ba7acc5d80e67d3c175d8443191b1
branch: master
author: Steve Dower <steve.dower(a)microsoft.com>
committer: GitHub <noreply(a)github.com>
date: 2017-08-17T10:24:18-07:00
summary:
Adds nuget symbols package for daily builds. (#3122)
files:
A Tools/nuget/pythondaily.symbols.nuspec
M Tools/nuget/make_pkg.proj
diff --git a/Tools/nuget/make_pkg.proj b/Tools/nuget/make_pkg.proj
index 3750d8dcc86..f21d21ee693 100644
--- a/Tools/nuget/make_pkg.proj
+++ b/Tools/nuget/make_pkg.proj
@@ -30,8 +30,8 @@
<PipArguments>"$(IntermediateOutputPath)\python.exe" -B -c "import sys; sys.path.append(r'$(PySourcePath)\Lib'); import ensurepip; ensurepip._main()"</PipArguments>
<PackageArguments Condition="$(Packages) != ''">"$(IntermediateOutputPath)\python.exe" -B -m pip install -U $(Packages)</PackageArguments>
- <NugetArguments>"$(Nuget)" pack "$(MSBuildThisFileDirectory)\$(OutputName).nuspec"</NugetArguments>
- <NugetArguments>$(NugetArguments) -BasePath "$(IntermediateOutputPath)"</NugetArguments>
+ <NugetPackCommand>"$(Nuget)" pack "$(MSBuildThisFileDirectory)\$(OutputName).nuspec" -BasePath "$(IntermediateOutputPath)"</NugetPackCommand>
+ <NugetPackSymbolsCommand Condition="Exists('$(MSBuildThisFileDirectory)\$(OutputName).symbols.nuspec')">"$(Nuget)" pack "$(MSBuildThisFileDirectory)\$(OutputName).symbols.nuspec" -BasePath "$(BuildPath.TrimEnd(`\`))"</NugetPackSymbolsCommand>
<NugetArguments>$(NugetArguments) -OutputDirectory "$(OutputPath.Trim(`\`))"</NugetArguments>
<NugetArguments>$(NugetArguments) -Version "$(NuspecVersion)"</NugetArguments>
<NugetArguments>$(NugetArguments) -NoPackageAnalysis -NonInteractive</NugetArguments>
@@ -51,7 +51,8 @@
<Exec Command="setlocal%0D%0A$(Environment)%0D%0A$(PythonArguments)" />
<Exec Command="$(PipArguments)" />
<Exec Command="$(PackageArguments)" Condition="$(PackageArguments) != ''" />
- <Exec Command="$(NugetArguments)" />
+ <Exec Command="$(NugetPackCommand) $(NugetArguments)" />
+ <Exec Command="$(NugetPackSymbolsCommand) $(NugetArguments)" Condition="$(NugetPackSymbolsCommand) != ''" />
</Target>
<Target Name="AfterBuild" />
diff --git a/Tools/nuget/pythondaily.symbols.nuspec b/Tools/nuget/pythondaily.symbols.nuspec
new file mode 100644
index 00000000000..77792b8c24d
--- /dev/null
+++ b/Tools/nuget/pythondaily.symbols.nuspec
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+<package >
+ <metadata>
+ <id>pythondaily.symbols</id>
+ <title>Python (Daily build)</title>
+ <version>0.0.0.0</version>
+ <authors>Python Software Foundation</authors>
+ <licenseUrl>https://docs.python.org/3/license.html</licenseUrl>
+ <projectUrl>https://www.python.org/</projectUrl>
+ <requireLicenseAcceptance>false</requireLicenseAcceptance>
+ <description>Contains symbols for the daily build of Python.</description>
+ <iconUrl>https://www.python.org/static/favicon.ico</iconUrl>
+ <tags>python</tags>
+ </metadata>
+ <files>
+ <file src="python*.dll" target="tools" exclude="*_d*" />
+ <file src="python*.exe" target="tools" exclude="*_d*" />
+ <file src="python*.pdb" target="tools" exclude="*_d*" />
+ <file src="*.pdb" target="tools\DLLs" exclude="python*;*_test*;xx*;_freeze*;*_d*;py.*;pyw.*;pyshellext.*" />
+ <file src="*.pyd" target="tools\DLLs" exclude="python*;*_test*;xx*;_freeze*;*_d*;py.*;pyw.*;pyshellext.*" />
+ <file src="libeay32.dll;ssleay32.dll;sqlite3.dll" target="tools\DLLs" />
+ <file src="libeay32.pdb;ssleay32.pdb;sqlite3.pdb" target="tools\DLLs" />
+ </files>
+</package>
1
0
![](https://secure.gravatar.com/avatar/cc7737cd64a84f1b5c61a160798e97ee.jpg?s=120&d=mm&r=g)
17 Aug '17
https://github.com/python/cpython/commit/d45cb040b9fcd74e8824d417bf789fcef4…
commit: d45cb040b9fcd74e8824d417bf789fcef4c9592c
branch: 2.7
author: Victor Stinner <victor.stinner(a)gmail.com>
committer: GitHub <noreply(a)github.com>
date: 2017-08-17T17:13:01+02:00
summary:
[2.7] bpo-31221: patchcheck ignores external libraries (#3109) (#3118)
* bpo-31221: patchcheck ignores external libraries (#3109)
Tools/scripts/patchcheck.py now ignores changes in directories which
are copies of external libraries:
* Modules/_ctypes/libffi_msvc/
* Modules/_ctypes/libffi_osx/
* Modules/_decimal/libmpdec/
* Modules/expat/
* Modules/zlib/
Drop also support for Mercurial, since CPython migrated to Git.
Exclude also libmpdec
patchcheck: exclude also libffi_osx and libffi_msvc
(cherry picked from commit 4a347ce426fe7381885703d9074d7a6b3aeb2f2b)
* Exclude also Modules/_ctypes/libffi on Python 2.7
* Remove _decimal/libmpdec, not in Python 2.7
files:
M Tools/scripts/patchcheck.py
diff --git a/Tools/scripts/patchcheck.py b/Tools/scripts/patchcheck.py
index be46fe3d083..b69d54029ac 100755
--- a/Tools/scripts/patchcheck.py
+++ b/Tools/scripts/patchcheck.py
@@ -10,8 +10,16 @@
import untabify
+# Excluded directories which are copies of external libraries:
+# don't check their coding style
+EXCLUDE_DIRS = [os.path.join('Modules', '_ctypes', 'libffi'),
+ os.path.join('Modules', '_ctypes', 'libffi_osx'),
+ os.path.join('Modules', '_ctypes', 'libffi_msvc'),
+ os.path.join('Modules', 'expat'),
+ os.path.join('Modules', 'zlib')]
SRCDIR = sysconfig.get_config_var('srcdir')
+
def n_files_str(count):
"""Return 'N file(s)' with the proper plurality on 'file'."""
return "{} file{}".format(count, "s" if count != 1 else "")
@@ -102,7 +110,7 @@ def changed_files(base_branch=None):
cmd += ' --rev qparent'
st = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE)
try:
- return [x.decode().rstrip() for x in st.stdout]
+ filenames = [x.decode().rstrip() for x in st.stdout]
finally:
st.stdout.close()
elif os.path.exists(os.path.join(SRCDIR, '.git')):
@@ -129,9 +137,19 @@ def changed_files(base_branch=None):
filenames.append(filename)
finally:
st.stdout.close()
- return filenames
else:
- sys.exit('need a checkout to get modified files')
+ sys.exit('need a Mercurial or git checkout to get modified files')
+
+ filenames2 = []
+ for filename in filenames:
+ # Normalize the path to be able to match using .startswith()
+ filename = os.path.normpath(filename)
+ if any(filename.startswith(path) for path in EXCLUDE_DIRS):
+ # Exclude the file
+ continue
+ filenames2.append(filename)
+
+ return filenames2
def report_modified_files(file_paths):
1
0
![](https://secure.gravatar.com/avatar/cc7737cd64a84f1b5c61a160798e97ee.jpg?s=120&d=mm&r=g)
17 Aug '17
https://github.com/python/cpython/commit/d23b1c4fd82a326e729027a791220d6011…
commit: d23b1c4fd82a326e729027a791220d6011e097b4
branch: 3.6
author: Victor Stinner <victor.stinner(a)gmail.com>
committer: GitHub <noreply(a)github.com>
date: 2017-08-17T16:53:27+02:00
summary:
[3.6] bpo-31221: patchcheck ignores external libraries (#3109) (#3116)
* bpo-31221: patchcheck ignores external libraries (#3109)
Tools/scripts/patchcheck.py now ignores changes in directories which
are copies of external libraries:
* Modules/_ctypes/libffi_msvc/
* Modules/_ctypes/libffi_osx/
* Modules/_decimal/libmpdec/
* Modules/expat/
* Modules/zlib/
Drop also support for Mercurial, since CPython migrated to Git.
Exclude also libmpdec
patchcheck: exclude also libffi_osx and libffi_msvc
(cherry picked from commit 4a347ce426fe7381885703d9074d7a6b3aeb2f2b)
* Exclude also Modules/_ctypes/libffi on Python 3.6
files:
M Tools/scripts/patchcheck.py
diff --git a/Tools/scripts/patchcheck.py b/Tools/scripts/patchcheck.py
index 436b277b6ae..5a1c94edee8 100755
--- a/Tools/scripts/patchcheck.py
+++ b/Tools/scripts/patchcheck.py
@@ -11,8 +11,17 @@
import untabify
+# Excluded directories which are copies of external libraries:
+# don't check their coding style
+EXCLUDE_DIRS = [os.path.join('Modules', '_ctypes', 'libffi'),
+ os.path.join('Modules', '_ctypes', 'libffi_osx'),
+ os.path.join('Modules', '_ctypes', 'libffi_msvc'),
+ os.path.join('Modules', '_decimal', 'libmpdec'),
+ os.path.join('Modules', 'expat'),
+ os.path.join('Modules', 'zlib')]
SRCDIR = sysconfig.get_config_var('srcdir')
+
def n_files_str(count):
"""Return 'N file(s)' with the proper plurality on 'file'."""
return "{} file{}".format(count, "s" if count != 1 else "")
@@ -98,7 +107,7 @@ def changed_files(base_branch=None):
if mq_patches_applied():
cmd += ' --rev qparent'
with subprocess.Popen(cmd.split(), stdout=subprocess.PIPE) as st:
- return [x.decode().rstrip() for x in st.stdout]
+ filenames = [x.decode().rstrip() for x in st.stdout]
elif os.path.exists(os.path.join(SRCDIR, '.git')):
# We just use an existence check here as:
# directory = normal git checkout/clone
@@ -120,10 +129,20 @@ def changed_files(base_branch=None):
# file is renamed
filename = filename.split(' -> ', 2)[1].strip()
filenames.append(filename)
- return filenames
else:
sys.exit('need a Mercurial or git checkout to get modified files')
+ filenames2 = []
+ for filename in filenames:
+ # Normalize the path to be able to match using .startswith()
+ filename = os.path.normpath(filename)
+ if any(filename.startswith(path) for path in EXCLUDE_DIRS):
+ # Exclude the file
+ continue
+ filenames2.append(filename)
+
+ return filenames2
+
def report_modified_files(file_paths):
count = len(file_paths)
1
0
https://github.com/python/cpython/commit/b907abc88589f7bea52c5afe172ececc6e…
commit: b907abc88589f7bea52c5afe172ececc6edcda70
branch: master
author: Victor Stinner <victor.stinner(a)gmail.com>
committer: GitHub <noreply(a)github.com>
date: 2017-08-17T16:40:51+02:00
summary:
bpo-30871: Add test.pythoninfo (#3075)
* Add Lib/test/pythoninfo.py: script collecting various informations
about Python to help debugging test failures.
* regrtest: remove sys.hash_info and sys.flags from header.
* Travis CI, Appveyor: run pythoninfo before tests
files:
A Lib/test/pythoninfo.py
M .github/appveyor.yml
M .travis.yml
M Lib/test/libregrtest/main.py
diff --git a/.github/appveyor.yml b/.github/appveyor.yml
index deb0e309a9d..cb9821ccc65 100644
--- a/.github/appveyor.yml
+++ b/.github/appveyor.yml
@@ -7,6 +7,7 @@ branches:
- buildbot-custom
build_script:
- cmd: PCbuild\build.bat -e
+- cmd: PCbuild\win32\python.exe -m test.pythoninfo
test_script:
- cmd: PCbuild\rt.bat -q -uall -u-cpu -rwW --slowest --timeout=1200 --fail-env-changed -j0
environment:
diff --git a/.travis.yml b/.travis.yml
index ba1e417c633..4c431241680 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -60,6 +60,7 @@ matrix:
# Need a venv that can parse covered code.
./python -m venv venv
./venv/bin/python -m pip install -U coverage
+ ./venv/bin/python -m test.pythoninfo
script:
# Skip tests that re-run the entire test suite.
- ./venv/bin/python -m coverage run --pylib -m test -uall,-cpu -x test_multiprocessing_fork -x test_multiprocessing_forkserver -x test_multiprocessing_spawn
@@ -87,6 +88,7 @@ before_script:
echo "$changes"
exit 1
fi
+ ./python -m test.pythoninfo
script:
# Using the built Python as patchcheck.py is built around the idea of using
diff --git a/Lib/test/libregrtest/main.py b/Lib/test/libregrtest/main.py
index 0b48bebb0d7..9871a28dbf2 100644
--- a/Lib/test/libregrtest/main.py
+++ b/Lib/test/libregrtest/main.py
@@ -424,8 +424,6 @@ def display_header(self):
print("==", platform.python_implementation(), *sys.version.split())
print("==", platform.platform(aliased=True),
"%s-endian" % sys.byteorder)
- print("== hash algorithm:", sys.hash_info.algorithm,
- "64bit" if sys.maxsize > 2**32 else "32bit")
print("== cwd:", os.getcwd())
cpu_count = os.cpu_count()
if cpu_count:
@@ -433,7 +431,6 @@ def display_header(self):
print("== encodings: locale=%s, FS=%s"
% (locale.getpreferredencoding(False),
sys.getfilesystemencoding()))
- print("Testing with flags:", sys.flags)
def run_tests(self):
# For a partial run, we do not need to clutter the output.
diff --git a/Lib/test/pythoninfo.py b/Lib/test/pythoninfo.py
new file mode 100644
index 00000000000..03081b6dddc
--- /dev/null
+++ b/Lib/test/pythoninfo.py
@@ -0,0 +1,407 @@
+"""
+Collect various informations about Python to help debugging test failures.
+"""
+from __future__ import print_function
+import re
+import sys
+import traceback
+
+
+def normalize_text(text):
+ if text is None:
+ return None
+ text = str(text)
+ text = re.sub(r'\s+', ' ', text)
+ return text.strip()
+
+
+class PythonInfo:
+ def __init__(self):
+ self.info = {}
+
+ def add(self, key, value):
+ if key in self.info:
+ raise ValueError("duplicate key: %r" % key)
+
+ if isinstance(value, str):
+ value = value.strip()
+ if not value:
+ return
+ elif value is None:
+ return
+ elif not isinstance(value, int):
+ raise TypeError("value type must be str, int or None")
+
+ self.info[key] = value
+
+ def get_infos(self):
+ """
+ Get informations as a key:value dictionary where values are strings.
+ """
+ return {key: str(value) for key, value in self.info.items()}
+
+
+def copy_attributes(info_add, obj, name_fmt, attributes, *, formatter=None):
+ for attr in attributes:
+ value = getattr(obj, attr, None)
+ if value is None:
+ continue
+ name = name_fmt % attr
+ if formatter is not None:
+ value = formatter(attr, value)
+ info_add(name, value)
+
+
+def collect_sys(info_add):
+ def format_attr(attr, value):
+ if attr == 'flags':
+ # convert sys.flags tuple to string
+ return str(value)
+ else:
+ return value
+
+ attributes = (
+ '_framework',
+ 'byteorder',
+ 'executable',
+ 'flags',
+ 'maxsize',
+ 'maxunicode',
+ 'version',
+ )
+ copy_attributes(info_add, sys, 'sys.%s', attributes,
+ formatter=format_attr)
+
+ encoding = sys.getfilesystemencoding()
+ if hasattr(sys, 'getfilesystemencodeerrors'):
+ encoding = '%s/%s' % (encoding, sys.getfilesystemencodeerrors())
+ info_add('sys.filesystem_encoding', encoding)
+
+ for name in ('stdin', 'stdout', 'stderr'):
+ stream = getattr(sys, name)
+ if stream is None:
+ continue
+ encoding = getattr(stream, 'encoding', None)
+ if not encoding:
+ continue
+ errors = getattr(stream, 'errors', None)
+ if errors:
+ encoding = '%s/%s' % (encoding, errors)
+ info_add('sys.%s.encoding' % name, encoding)
+
+ if hasattr(sys, 'hash_info'):
+ alg = sys.hash_info.algorithm
+ bits = 64 if sys.maxsize > 2**32 else 32
+ alg = '%s (%s bits)' % (alg, bits)
+ info_add('sys.hash_info', alg)
+
+ if hasattr(sys, 'getandroidapilevel'):
+ info_add('sys.androidapilevel', sys.getandroidapilevel())
+
+
+def collect_platform(info_add):
+ import platform
+
+ arch = platform.architecture()
+ arch = ' '.join(filter(bool, arch))
+ info_add('platform.architecture', arch)
+
+ info_add('platform.python_implementation',
+ platform.python_implementation())
+ info_add('platform.platform',
+ platform.platform(aliased=True))
+
+
+def collect_locale(info_add):
+ import locale
+
+ info_add('locale.encoding', locale.getpreferredencoding(False))
+
+
+def collect_os(info_add):
+ import os
+
+ if hasattr(os, 'getrandom'):
+ # PEP 524: Check is system urandom is initialized
+ try:
+ os.getrandom(1, os.GRND_NONBLOCK)
+ state = 'ready (initialized)'
+ except BlockingIOError as exc:
+ state = 'not seeded yet (%s)' % exc
+ info_add('os.getrandom', state)
+
+ info_add("os.cwd", os.getcwd())
+
+ if hasattr(os, 'getuid'):
+ info_add("os.uid", os.getuid())
+ info_add("os.gid", os.getgid())
+
+ if hasattr(os, 'getgroups'):
+ groups = os.getgroups()
+ groups = map(str, groups)
+ groups = ', '.join(groups)
+ info_add("os.groups", groups)
+
+ if hasattr(os, 'getlogin'):
+ try:
+ login = os.getlogin()
+ except OSError:
+ # getlogin() fails with "OSError: [Errno 25] Inappropriate ioctl
+ # for device" on Travis CI
+ pass
+ else:
+ info_add("os.login", login)
+
+ if hasattr(os, 'cpu_count'):
+ cpu_count = os.cpu_count()
+ if cpu_count:
+ info_add('os.cpu_count', cpu_count)
+
+ if hasattr(os, 'getloadavg'):
+ load = os.getloadavg()
+ info_add('os.loadavg', str(load))
+
+ # Get environment variables: filter to list
+ # to not leak sensitive information
+ ENV_VARS = (
+ "CC",
+ "COMSPEC",
+ "DISPLAY",
+ "DISTUTILS_USE_SDK",
+ "DYLD_LIBRARY_PATH",
+ "HOME",
+ "HOMEDRIVE",
+ "HOMEPATH",
+ "LANG",
+ "LD_LIBRARY_PATH",
+ "MACOSX_DEPLOYMENT_TARGET",
+ "MAKEFLAGS",
+ "MSSDK",
+ "PATH",
+ "SDK_TOOLS_BIN",
+ "SHELL",
+ "TEMP",
+ "TERM",
+ "TMP",
+ "TMPDIR",
+ "USERPROFILE",
+ "WAYLAND_DISPLAY",
+ )
+ for name, value in os.environ.items():
+ uname = name.upper()
+ if (uname in ENV_VARS or uname.startswith(("PYTHON", "LC_"))
+ # Visual Studio: VS140COMNTOOLS
+ or (uname.startswith("VS") and uname.endswith("COMNTOOLS"))):
+ info_add('os.environ[%s]' % name, value)
+
+
+def collect_readline(info_add):
+ try:
+ import readline
+ except ImportError:
+ return
+
+ def format_attr(attr, value):
+ if isinstance(value, int):
+ return "%#x" % value
+ else:
+ return value
+
+ attributes = (
+ "_READLINE_VERSION",
+ "_READLINE_RUNTIME_VERSION",
+ "_READLINE_LIBRARY_VERSION",
+ )
+ copy_attributes(info_add, readline, 'readline.%s', attributes,
+ formatter=format_attr)
+
+
+def collect_gdb(info_add):
+ import subprocess
+
+ try:
+ proc = subprocess.Popen(["gdb", "-nx", "--version"],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ universal_newlines=True)
+ version = proc.communicate()[0]
+ except OSError:
+ return
+
+ # Only keep the first line
+ version = version.splitlines()[0]
+ info_add('gdb_version', version)
+
+
+def collect_tkinter(info_add):
+ try:
+ import _tkinter
+ except ImportError:
+ pass
+ else:
+ attributes = ('TK_VERSION', 'TCL_VERSION')
+ copy_attributes(info_add, _tkinter, 'tkinter.%s', attributes)
+
+ try:
+ import tkinter
+ except ImportError:
+ pass
+ else:
+ tcl = tkinter.Tcl()
+ patchlevel = tcl.call('info', 'patchlevel')
+ info_add('tkinter.info_patchlevel', patchlevel)
+
+
+def collect_time(info_add):
+ import time
+
+ if not hasattr(time, 'get_clock_info'):
+ return
+
+ for clock in ('time', 'perf_counter'):
+ tinfo = time.get_clock_info(clock)
+ info_add('time.%s' % clock, str(tinfo))
+
+
+def collect_sysconfig(info_add):
+ import sysconfig
+
+ for name in (
+ 'ABIFLAGS',
+ 'ANDROID_API_LEVEL',
+ 'CC',
+ 'CCSHARED',
+ 'CFLAGS',
+ 'CFLAGSFORSHARED',
+ 'PY_LDFLAGS',
+ 'CONFIG_ARGS',
+ 'HOST_GNU_TYPE',
+ 'MACHDEP',
+ 'MULTIARCH',
+ 'OPT',
+ 'PY_CFLAGS',
+ 'PY_CFLAGS_NODIST',
+ 'Py_DEBUG',
+ 'Py_ENABLE_SHARED',
+ 'SHELL',
+ 'SOABI',
+ 'prefix',
+ ):
+ value = sysconfig.get_config_var(name)
+ if name == 'ANDROID_API_LEVEL' and not value:
+ # skip ANDROID_API_LEVEL=0
+ continue
+ value = normalize_text(value)
+ info_add('sysconfig[%s]' % name, value)
+
+
+def collect_ssl(info_add):
+ try:
+ import ssl
+ except ImportError:
+ return
+
+ def format_attr(attr, value):
+ if attr.startswith('OP_'):
+ return '%#8x' % value
+ else:
+ # Convert OPENSSL_VERSION_INFO tuple to str
+ return str(value)
+
+ attributes = (
+ 'OPENSSL_VERSION',
+ 'OPENSSL_VERSION_INFO',
+ 'HAS_SNI',
+ 'OP_ALL',
+ 'OP_NO_TLSv1_1',
+ )
+ copy_attributes(info_add, ssl, 'ssl.%s', attributes, formatter=format_attr)
+
+
+def collect_socket(info_add):
+ import socket
+
+ hostname = socket.gethostname()
+ info_add('socket.hostname', hostname)
+
+
+def collect_sqlite(info_add):
+ try:
+ import sqlite3
+ except ImportError:
+ return
+
+ attributes = ('version', 'sqlite_version')
+ copy_attributes(info_add, sqlite3, 'sqlite3.%s', attributes)
+
+
+def collect_zlib(info_add):
+ try:
+ import zlib
+ except ImportError:
+ return
+
+ attributes = ('ZLIB_VERSION', 'ZLIB_RUNTIME_VERSION')
+ copy_attributes(info_add, zlib, 'zlib.%s', attributes)
+
+
+def collect_info(info):
+ error = False
+ info_add = info.add
+
+ for collect_func in (
+ # collect_os() should be the first, to check the getrandom() status
+ collect_os,
+
+ collect_gdb,
+ collect_locale,
+ collect_platform,
+ collect_readline,
+ collect_socket,
+ collect_sqlite,
+ collect_ssl,
+ collect_sys,
+ collect_sysconfig,
+ collect_time,
+ collect_tkinter,
+ collect_zlib,
+ ):
+ try:
+ collect_func(info_add)
+ except Exception as exc:
+ error = True
+ print("ERROR: %s() failed" % (collect_func.__name__),
+ file=sys.stderr)
+ traceback.print_exc(file=sys.stderr)
+ print(file=sys.stderr)
+ sys.stderr.flush()
+
+ return error
+
+
+def dump_info(info, file=None):
+ title = "Python debug information"
+ print(title)
+ print("=" * len(title))
+ print()
+
+ infos = info.get_infos()
+ infos = sorted(infos.items())
+ for key, value in infos:
+ value = value.replace("\n", " ")
+ print("%s: %s" % (key, value))
+ print()
+
+
+def main():
+ info = PythonInfo()
+ error = collect_info(info)
+ dump_info(info)
+
+ if error:
+ print("Collection failed: exit with error", file=sys.stderr)
+ sys.exit(1)
+
+
+if __name__ == "__main__":
+ main()
1
0
![](https://secure.gravatar.com/avatar/cc7737cd64a84f1b5c61a160798e97ee.jpg?s=120&d=mm&r=g)
Update the language selection in the docs language switch. (GH-3114) (#3117)
by Victor Stinner 17 Aug '17
by Victor Stinner 17 Aug '17
17 Aug '17
https://github.com/python/cpython/commit/3cc46bb3ed8005b1d9b74bcb08a9c01b99…
commit: 3cc46bb3ed8005b1d9b74bcb08a9c01b99e84c63
branch: 2.7
author: Julien Palard <julien(a)palard.fr>
committer: Victor Stinner <victor.stinner(a)gmail.com>
date: 2017-08-17T16:37:56+02:00
summary:
Update the language selection in the docs language switch. (GH-3114) (#3117)
Change the option for `Français` into `French` to be consistent with the other language selections that are already in English.
(cherry picked from commit b616b972999cdd5fe54ef8a43c131a27ca538dee)
files:
M Doc/tools/static/switchers.js
diff --git a/Doc/tools/static/switchers.js b/Doc/tools/static/switchers.js
index ff23202a5e0..bd31faca4c6 100644
--- a/Doc/tools/static/switchers.js
+++ b/Doc/tools/static/switchers.js
@@ -20,7 +20,7 @@
var all_languages = {
'en': 'English',
- 'fr': 'Français',
+ 'fr': 'French',
'ja': 'Japanese',
};
1
0
![](https://secure.gravatar.com/avatar/cc7737cd64a84f1b5c61a160798e97ee.jpg?s=120&d=mm&r=g)
Update the language selection in the docs language switch. (GH-3114) (#3115)
by Victor Stinner 17 Aug '17
by Victor Stinner 17 Aug '17
17 Aug '17
https://github.com/python/cpython/commit/fcecb0e365c35fb0e8410609d78881289b…
commit: fcecb0e365c35fb0e8410609d78881289bee7c26
branch: 3.6
author: Julien Palard <julien(a)palard.fr>
committer: Victor Stinner <victor.stinner(a)gmail.com>
date: 2017-08-17T16:37:00+02:00
summary:
Update the language selection in the docs language switch. (GH-3114) (#3115)
Change the option for `Français` into `French` to be consistent with the other language selections that are already in English.
(cherry picked from commit b616b972999cdd5fe54ef8a43c131a27ca538dee)
files:
M Doc/tools/static/switchers.js
diff --git a/Doc/tools/static/switchers.js b/Doc/tools/static/switchers.js
index ff23202a5e0..bd31faca4c6 100644
--- a/Doc/tools/static/switchers.js
+++ b/Doc/tools/static/switchers.js
@@ -20,7 +20,7 @@
var all_languages = {
'en': 'English',
- 'fr': 'Français',
+ 'fr': 'French',
'ja': 'Japanese',
};
1
0
https://github.com/python/cpython/commit/4a347ce426fe7381885703d9074d7a6b3a…
commit: 4a347ce426fe7381885703d9074d7a6b3aeb2f2b
branch: master
author: Victor Stinner <victor.stinner(a)gmail.com>
committer: GitHub <noreply(a)github.com>
date: 2017-08-17T16:29:15+02:00
summary:
bpo-31221: patchcheck ignores external libraries (#3109)
Tools/scripts/patchcheck.py now ignores changes in directories which
are copies of external libraries:
* Modules/_ctypes/libffi_msvc/
* Modules/_ctypes/libffi_osx/
* Modules/_decimal/libmpdec/
* Modules/expat/
* Modules/zlib/
Drop also support for Mercurial, since CPython migrated to Git.
Exclude also libmpdec
patchcheck: exclude also libffi_osx and libffi_msvc
files:
M Tools/scripts/patchcheck.py
diff --git a/Tools/scripts/patchcheck.py b/Tools/scripts/patchcheck.py
index 2204ba0a605..8f7924fe934 100755
--- a/Tools/scripts/patchcheck.py
+++ b/Tools/scripts/patchcheck.py
@@ -11,8 +11,16 @@
import untabify
+# Excluded directories which are copies of external libraries:
+# don't check their coding style
+EXCLUDE_DIRS = [os.path.join('Modules', '_ctypes', 'libffi_osx'),
+ os.path.join('Modules', '_ctypes', 'libffi_msvc'),
+ os.path.join('Modules', '_decimal', 'libmpdec'),
+ os.path.join('Modules', 'expat'),
+ os.path.join('Modules', 'zlib')]
SRCDIR = sysconfig.get_config_var('srcdir')
+
def n_files_str(count):
"""Return 'N file(s)' with the proper plurality on 'file'."""
return "{} file{}".format(count, "s" if count != 1 else "")
@@ -98,7 +106,7 @@ def changed_files(base_branch=None):
if mq_patches_applied():
cmd += ' --rev qparent'
with subprocess.Popen(cmd.split(), stdout=subprocess.PIPE) as st:
- return [x.decode().rstrip() for x in st.stdout]
+ filenames = [x.decode().rstrip() for x in st.stdout]
elif os.path.exists(os.path.join(SRCDIR, '.git')):
# We just use an existence check here as:
# directory = normal git checkout/clone
@@ -120,10 +128,20 @@ def changed_files(base_branch=None):
# file is renamed
filename = filename.split(' -> ', 2)[1].strip()
filenames.append(filename)
- return filenames
else:
sys.exit('need a Mercurial or git checkout to get modified files')
+ filenames2 = []
+ for filename in filenames:
+ # Normalize the path to be able to match using .startswith()
+ filename = os.path.normpath(filename)
+ if any(filename.startswith(path) for path in EXCLUDE_DIRS):
+ # Exclude the file
+ continue
+ filenames2.append(filename)
+
+ return filenames2
+
def report_modified_files(file_paths):
count = len(file_paths)
1
0