Gustavo Tabares wrote:
> Hi all,
>
> I recently upgraded to Wing 3.1.2 and I'm having problems with the
> PyLint integration. In my PyLint Panel configuration file I have this:
>
> args = --rcfile='mycfg.cfg'
>
> It doesn't appear to be reading my configuration file after upgrading
> from 3.1.1. <
http://3.1.1.> I'm seeing some warnings that I usually do
> not see (specifically about line length).
I'm attaching the latest version that I've been working with; there have
been changes in how argument parsing works that have caused problems.
I'd like to get feedback on this before releasing it generally. To use
it, find the scripts subdirectory of your Wing installation, rename the
current pylintpanel.py to pylintpanel.py.orig, copy in the new
pylintpanel.py, and then start or restart Wing.
Thanks,
John
"""
PyLint Panel for Wing IDE 3.x
Copyright (c) 2006-2007 Markus Meyer <
meyer@...>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
----------------------------------------------------------------------------
Change Log
----------------------------------------------------------------------------
Version 1.0 (2006-10-12)
* First release
Version 1.1 (2007-03-19)
* Fix compatibility issues with newer PyLint versions
* Fix error message when PyLint returns no errors
Version 1.2 (2008-06-05) Modifications by Wingware:
* Pass configured environment to the pylint sub-process
(includes also PYTHONPATH if it's set in project)
* Use presence of a settings file to enable/disable for easier upgrades
* Removed obsolete example args
* Renamed "preferences" to "configure" in context menu to avoid confusion
* Renamed command execute_pylint to pylint_execute and pylint_preferences
to pylint_configure
* Changed execution of pylint to be asyncronous
"""
import os
import wingapi
import time
PYLINTPANEL_VERSION = "1.1"
import re
_AI = wingapi.CArgInfo
# Scripts can be internationalized with gettext. Strings to be translated
# are sent to _() as in the code below.
import gettext
_ = gettext.translation('scripts_pylintpanel', fallback = 1).ugettext
# This special attribute is used so that the script manager can translate
# also docstrings for the commands found here
_i18n_module = 'scripts_pylintpanel'
######################################################################
# Utilities
gMessageCategories = [
("errors", _("Errors"), _("Errors that must be fixed")),
("warnings", _("Warnings"), _("Warnings that could indicate problems")),
("information", _("Info"), _("General informative messages"))
]
gTheView = None # Will be set later
######################################################################
# Configuration file support
class PylintConfig:
def __init__(self):
pass
def get(self, name, default=""):
"""
Get configuration option from configuration file
"""
try:
lines = file(self._get_config_file_name(), "rt").readlines()
except IOError:
return default
for line in lines:
words = line.split("=", 1)
if len(words) == 2:
key = words[0].strip()
value = words[1].strip()
if key == name:
return value
return default
def edit(self):
"""
Edit and possibly create configuration file. Currently, this will just
open the configuration file inside WingIDE.
"""
try:
# Check if config file can be opened
cfgfile = file(self._get_config_file_name(), "rt")
except IOError:
# The config file does not yet exist, create it
cfgfile = file(self._get_config_file_name(), "wt")
cfgfile.writelines([
'#\n',
'# PyLint Panel Configuration\n',
'#\n',
'\n',
'# Full path to PyLint executable\n',
'command = \n',
'#command = /usr/bin/pylint\n',
'#command = C:\Python24\Scripts\pylint.bat\n',
'\n',
'# Additional args to give to PyLint (may be blank)\n',
'args = \n',
'\n',
'# Timeout for execution of pylint command\n',
'timeout = 30\n',
'\n',
'# Save current file before running PyLint on it (1=yes, 0=no)\n',
'autosave = 1\n'
])
cfgfile.close()
wingapi.gApplication.OpenEditor(self._get_config_file_name())
def _get_config_file_name(self):
"""
Get full name and path of config file
"""
dir = wingapi.gApplication.GetUserSettingsDir()
return os.path.join(dir, "pylintpanel.cfg")
gTheConfig = PylintConfig()
kResultParseExpr = re.compile("([^:]+):[ ]*([0-9]+):(.*)")
######################################################################
# Commands
def pylint_configure():
"""Show the pylint configuration file so it can be edited"""
gTheConfig.edit()
def pylint_execute():
"""Execute pyline for the current editor"""
if gTheView is None:
# Panel is not visible
return
view = gTheView
app = wingapi.gApplication
pylint_command = gTheConfig.get("command", None)
pylint_args = gTheConfig.get("args", "")
pylint_timeout = gTheConfig.get("timeout", "10000")
pylint_autosave = gTheConfig.get("autosave", "1")
if pylint_command is None:
app.ShowMessageDialog(_('Error'), _('PyLint panel configuration file not found. ' +
'Choose "Configure" from the context menu to edit ' +
'the configuration file. You will need to install pylint '
'separately.'))
return
try:
timeout = int(pylint_timeout)
autosave = int(pylint_autosave) != 0
except ValueError:
app.ShowMessageDialog(_("Error"), _("Invalid values specified in configuration file"))
return
filename = app.GetActiveEditor().GetDocument().GetFilename()
view._lastFilename = filename
# Save active document before executing PyLint
if autosave:
app.ExecuteCommand("save")
# Completion routine that updates the tree when pylint is finished running
def _update_tree(result):
resultLines = result.split('\n')
tree_contents = [ [], [], [] ]
for line in resultLines:
matchobj = kResultParseExpr.match(line)
if matchobj is not None:
msg_type = matchobj.group(1)
msg_line = matchobj.group(2).strip()
msg_descr = matchobj.group(3).strip()
if msg_type[0] == 'F' or msg_type[0] == 'E':
msg_index = 0
elif msg_type[0] == 'W':
msg_index = 1
else:
msg_index = 2
tree_contents[msg_index].append(
((msg_line, msg_type + ": " + msg_descr),))
view.set_tree_contents(tree_contents)
# Show pending execution message in tree column title
view._ShowStatusMessage(_("Updating for %s") % filename)
def arg_split(args, sep):
cur_part = ''
retval = []
in_quote = None
for c in args:
if not in_quote:
if c in '\'"':
in_quote = c
cur_part += c
elif c == sep:
if cur_part:
retval.append(cur_part)
cur_part = ''
else:
cur_part += c
elif in_quote == c:
in_quote = None
cur_part += c
else:
cur_part += c
if cur_part:
retval.append(cur_part)
return retval
# Execute PyLint asyncronously
cmd = pylint_command
args = [cmd, filename.encode(config.kFileSystemEncoding), '--reports=n', '--include-ids=yes']
args.extend(arg_split(pylint_args, ' '))
args = tuple(args)
env = app.GetProject().GetEnvironment(filename, set_pypath=True)
start_time = time.time()
handler = app.AsyncExecuteCommandLineE(cmd, os.path.split(filename)[0], env, *args)
last_dot = [int(start_time)]
dots = []
def poll():
if handler.Iterate():
view._ShowStatusMessage('')
stdout, stderr, err, status = handler.Terminate()
if err:
app.ShowMessageDialog(_("PyLint Failed"), _("Error executing PyLint: Command failed with error=%i; stderr:\n%s") % (err, stderr))
else:
_update_tree(stdout)
return False
elif time.time() > start_time + timeout:
view._ShowStatusMessage('')
stdout, stderr, err, status = handler.Terminate()
app.ShowMessageDialog(_("PyLint Timed Out"), _("PyLint timed out: Command did not complete within timeout of %i seconds. Right click on the PyLint tool to configure this value. Output from PyLint:\n\n%s") % (timeout, stderr + stdout))
return False
else:
if int(time.time()) > last_dot[0]:
dots.append('.')
if len(dots) > 3:
while dots:
dots.pop()
view._ShowStatusMessage(_("Updating for %s%s") % (filename, ''.join(dots)))
last_dot[0] = int(time.time())
return True
wingapi.gApplication.InstallTimeout(100, poll)
def _IsAvailable_pylint_execute():
app = wingapi.gApplication
ed = app.GetActiveEditor()
if not ed:
return False
doc = ed.GetDocument()
if not doc:
return False
filename = doc.GetFilename()
if not filename:
return False
mimetype = doc.GetMimeType()
return mimetype == 'text/x-python'
def pylint_show_docs():
"""Show the Wing IDE documentation section for the PyLint integration"""
wingapi.gApplication.ExecuteCommand('show-document', section='edit/pylint')
######################################################################
# XXX This is advanced scripting that accesses Wing IDE internals, which
# XXX are subject to change from version to version without notice.
from guiutils import wgtk
from guiutils import dockview
from guiutils import wingview
from guiutils import winmgr
from command import commandmgr
import guimgr.menus
# Note that panel IDs must be globally unique so all user-provided panels
# MUST add a random uniquifier after '#'. The panel can still be referred
# to by the portion of the name before '#' and Wing will warn when there
# are multiple panel definitions with the same base name (in which case
# Wing-defined panels win over user-defined panels and otherwise the
# last user-defined panel type wins when referred to w/o the uniquifier).
_kPanelID = 'pylintpanel#02EFWRQK9X24'
class _CPylintPanelDefn(dockview.CPanelDefn):
"""Panel definition for the project manager"""
def __init__(self, singletons):
self.fSingletons = singletons
dockview.CPanelDefn.__init__(self, self.fSingletons.fPanelMgr,
_kPanelID, 'tall', 0)
winmgr.CWindowConfig(self.fSingletons.fWinMgr, 'panel:%s' % _kPanelID,
size=(350, 1000))
def _CreateView(self):
return _CPylintView(self.fSingletons)
def _GetLabel(self, panel_instance):
"""Get display label to use for the given panel instance"""
return _('PyLint')
def _GetTitle(self, panel_instance):
"""Get full title for the given panel instance"""
return _('PyLint Panel')
class _CPylintViewCommands(commandmgr.CClassCommandMap):
def __init__(self, singletons, view):
commandmgr.CClassCommandMap.__init__(self, domain='user', package='pylintpanel',
i18n_module=_i18n_module)
assert isinstance(view, _CPylintView)
self.fSingletons = singletons
self.__fView = view
class _CPylintView(wingview.CViewController):
"""A single template manager view"""
def __init__(self, singletons):
""" Constructor """
global gTheView
# Init inherited
wingview.CViewController.__init__(self, ())
# External managers
self.fSingletons = singletons
self.__fCmdMap = _CPylintViewCommands(self.fSingletons, self)
self.fTrees = {}
self.fLabels = {}
self.__CreateGui()
# Remember that this is the default view now
gTheView = self
def _destroy_impl(self):
for tree, sview in self.fTrees.values():
sview.destroy()
def set_tree_contents(self, tree_contents):
idx = 0
for catkey, labeltext, tooltip in gMessageCategories:
label = gTheView.fLabels[catkey]
label.set_text('%s (%i)' % (labeltext, len(tree_contents[idx])))
tree, sview = gTheView.fTrees[catkey]
tree.set_contents(tree_contents[idx])
idx += 1
##########################################################################
# Inherited calls from wingview.CViewController
##########################################################################
def GetDisplayTitle(self):
""" Returns the title of this view suitable for display. """
return _("PyLint Panel")
def GetCommandMap(self):
""" Get the command map object for this view. """
return self.__fCmdMap
def BecomeActive(self):
pass
##########################################################################
# Popup menu and actions
##########################################################################
def __CreateGui(self):
notebook = wgtk.Notebook()
for catkey, label, tooltip in gMessageCategories:
tree = wgtk.SimpleTree([wgtk.gobject.TYPE_STRING] * 2,
[wgtk.CellRendererText(), wgtk.CellRendererText()],
[_("Line"), _("Message")])
tree.unset_flags(wgtk.CAN_FOCUS)
tree.set_property('headers-visible', True)
tree.connect('button-press-event', self.__CB_ButtonPress)
sel = tree.get_selection()
sel.connect('changed', self.__CB_SelectionChanged)
tree.show()
sview = wgtk.ScrolledWindow()
sview.set_policy(wgtk.POLICY_AUTOMATIC, wgtk.POLICY_AUTOMATIC)
sview.add(tree)
sview.show()
# Event box is needed to make tooltips work in this context (don't ask)
tab_event_box = wgtk.TooltipBox()
tab_label = wgtk.Label(label)
tab_event_box.add(tab_label)
tab_event_box.show_all()
wgtk.set_tooltip(tab_event_box, tooltip)
notebook.append_page(sview, tab_event_box)
self.__fNotebook = notebook
self.fTrees[catkey] = (tree, sview)
self.fLabels[catkey] = tab_label
notebook.set_current_page(0)
self._SetGtkWidget(notebook)
def __CreatePopup(self):
"""Construct popup menu for this object."""
update_label = _("Update")
app = wingapi.gApplication
ed = app.GetActiveEditor()
if ed:
filename = ed.GetDocument().GetFilename()
update_label = _("Update for %s") % os.path.basename(filename)
kPopupDefn = [
( update_label, 'pylint-execute' ),
( _("Configure..."), 'pylint-configure' ),
None,
( _("Show PyLint Tool Documentation"), 'pylint-show-docs' ),
]
# Create menu
defnlist = guimgr.menus.GetMenuDefnList(kPopupDefn, self.fSingletons.fGuiMgr,
self.__fCmdMap, is_popup=1, static=1)
menu = guimgr.menus.CMenu(_("PyLint"), self.fSingletons.fGuiMgr,
defnlist, can_tearoff=0, is_popup=1)
return menu
def __CB_SelectionChanged(self, sel):
pos = self.__fNotebook.get_current_page()
catkey, label, tdir = gMessageCategories[pos]
tree, sview = self.fTrees[catkey]
rows = tree.GetSelectedContent()
def __CB_ButtonPress(self, tree, event):
app = wingapi.gApplication
# Always select the row that the pointer is over
event_path_info = tree.get_path_at_pos(int(event.x), int(event.y))
if event_path_info is not None:
event_path = event_path_info[0]
selected_paths = tree.GetSelectedPaths()
if event_path not in selected_paths:
sel = tree.get_selection()
sel.unselect_all()
sel.select_path(event_path)
# Popup menu on right mouse button
if event.button == 3:
self.__PopupMenu(event, (event.x_root, event.y_root))
return 1
selected = tree.GetSelectedContent()
if selected is not None and len(selected) != 0:
line = int(selected[0][0])
if event.button == 1 and event.type == wgtk.gdk.BUTTON_PRESS:
doc = app.OpenEditor(self._lastFilename)
doc.ScrollToLine(lineno=line-1, pos='center', select=1)
def __PopupMenu(self, event, pos):
"""Callback to display the popup menu"""
menu = self.__CreatePopup()
menu.Popup(event, pos=pos)
def _ShowStatusMessage(self, msg):
for tree, sview in self.fTrees.values():
column = tree.get_column(1)
if msg:
column.set_title(_("Message: %s") % msg)
else:
column.set_title(_("Message"))
# Register this panel type: Note that this needs to be at the
# very end of the module so that all the classes defined here
# are already available
import config
if _kPanelID not in config.kUserPanels:
config.kUserPanels.append(_kPanelID)
_CPylintPanelDefn(wingapi.gApplication.fSingletons)
_________________________________________________
Wing IDE users list
http://wingware.com/lists/wingide