diff -r 1985c7abb006 README
--- a/README	Wed Aug 22 11:42:40 2012 +0200
+++ b/README	Mon Sep 03 12:01:30 2012 +1000
@@ -65,6 +65,7 @@
 * Wolfgang Grafen, Axel Muller, Fabio Zadrozny, Pierre Rouleau,
   Maarten ter Huurne, Mirko Friedenhagen (among others):
   bug reports, feedback, feature requests...
+* Martin Pool (Google): symbolic names for messages (like 'unused')
 * All the Logilab's team: daily use, bug reports, feature requests
 * Other people have contributed by their feedback, if I've forgotten
   you, send me a note !
diff -r 1985c7abb006 checkers/base.py
--- a/checkers/base.py	Wed Aug 22 11:42:40 2012 +0200
+++ b/checkers/base.py	Mon Sep 03 12:01:30 2012 +1000
@@ -242,9 +242,11 @@
     name = 'basic'
     msgs = {
     'W0101': ('Unreachable code',
+              'unreachable',
               'Used when there is some code behind a "return" or "raise" \
               statement, which will never be accessed.'),
     'W0102': ('Dangerous default value %s as argument',
+              'dangerous-default-value',
               'Used when a mutable value as list or dictionary is detected in \
               a default value for an argument.'),
     'W0104': ('Statement seems to have no effect',
@@ -276,6 +278,7 @@
               'like map, or filter , where Python offers now some cleaner '
               'alternative like list comprehension.'),
     'W0142': ('Used * or ** magic',
+              'star-args',
               'Used when a function or method is called using `*args` or '
               '`**kwargs` to dispatch arguments. This doesn\'t improve '
               'readability and should be used with care.'),
@@ -709,10 +712,12 @@
 class DocStringChecker(_BasicChecker):
     msgs = {
     'C0111': ('Missing docstring', # W0131
+              'missing-docstring',
               'Used when a module, function, class or method has no docstring.\
               Some special methods like __init__ doesn\'t necessary require a \
               docstring.'),
     'C0112': ('Empty docstring', # W0132
+              'empty-docstring',
               'Used when a module, function, class or method has an empty \
               docstring (it would be too easy ;).'),
     }
@@ -768,6 +773,7 @@
 class PassChecker(_BasicChecker):
     """check is the pass statement is really necessary"""
     msgs = {'W0107': ('Unnecessary pass statement',
+                      'unnecessary-pass',
                       'Used when a "pass" statement that can be avoided is '
                       'encountered.)'),
             }
diff -r 1985c7abb006 checkers/format.py
--- a/checkers/format.py	Wed Aug 22 11:42:40 2012 +0200
+++ b/checkers/format.py	Mon Sep 03 12:01:30 2012 +1000
@@ -35,28 +35,37 @@
 
 MSGS = {
     'C0301': ('Line too long (%s/%s)',
+              'line-too-long',
               'Used when a line is longer than a given number of characters.'),
     'C0302': ('Too many lines in module (%s)', # was W0302
+              'too-many-lines',
               'Used when a module has too much lines, reducing its readability.'
               ),
 
     'W0311': ('Bad indentation. Found %s %s, expected %s',
+              'bad-indentation',
               'Used when an unexpected number of indentation\'s tabulations or '
               'spaces has been found.'),
     'W0312': ('Found indentation with %ss instead of %ss',
+              'mixed-indentation',
               'Used when there are some mixed tabs and spaces in a module.'),
     'W0301': ('Unnecessary semicolon', # was W0106
+              'unnecessary-semicolon',
               'Used when a statement is ended by a semi-colon (";"), which \
               isn\'t necessary (that\'s python, not C ;).'),
     'C0321': ('More than one statement on a single line',
+              'multiple-statements',
               'Used when more than on statement are found on the same line.'),
     'C0322': ('Operator not preceded by a space\n%s',
+              'no-space-before-operator',
               'Used when one of the following operator (!= | <= | == | >= | < '
               '| > | = | \+= | -= | \*= | /= | %) is not preceded by a space.'),
     'C0323': ('Operator not followed by a space\n%s',
+              'no-space-after-operator',
               'Used when one of the following operator (!= | <= | == | >= | < '
               '| > | = | \+= | -= | \*= | /= | %) is not followed by a space.'),
     'C0324': ('Comma not followed by a space\n%s',
+              'no-space-after-comma',
               'Used when a comma (",") is not followed by a space.'),
     }
 
@@ -64,13 +73,16 @@
 
     MSGS.update({
     'W0331': ('Use of the <> operator',
+              'old-ne-operator',
               'Used when the deprecated "<>" operator is used instead \
               of "!=".'),
     'W0332': ('Use l as long integer identifier',
+              'lowercase-l-suffix'
               'Used when a lower case "l" is used to mark a long integer. You '
               'should use a upper case "L" since the letter "l" looks too much '
               'like the digit "1"'),
     'W0333': ('Use of the `` operator',
+              'backtick',
               'Used when the deprecated "``" (backtick) operator is used '
               'instead  of the str() function.'),
     })
diff -r 1985c7abb006 checkers/imports.py
--- a/checkers/imports.py	Wed Aug 22 11:42:40 2012 +0200
+++ b/checkers/imports.py	Mon Sep 03 12:01:30 2012 +1000
@@ -128,6 +128,7 @@
 
 MSGS = {
     'F0401': ('Unable to import %s',
+              'import-error',
               'Used when pylint has been unable to import a module.'),
     'R0401': ('Cyclic import (%s)',
               'Used when a cyclic import between two or more modules is \
diff -r 1985c7abb006 checkers/misc.py
--- a/checkers/misc.py	Wed Aug 22 11:42:40 2012 +0200
+++ b/checkers/misc.py	Mon Sep 03 12:01:30 2012 +1000
@@ -25,6 +25,7 @@
 
 MSGS = {
     'W0511': ('%s',
+              'fixme',
               'Used when a warning note as FIXME or XXX is detected.'),
     }
 
diff -r 1985c7abb006 doc/FAQ.txt
--- a/doc/FAQ.txt	Wed Aug 22 11:42:40 2012 +0200
+++ b/doc/FAQ.txt	Mon Sep 03 12:01:30 2012 +1000
@@ -243,6 +243,15 @@
      E0202, # I have a good reason, trust me
      C0302  # that's it
 
+4.6 Do I have to remember all these numbers?
+--------------------------------------------
+
+No, starting from 0.25.3, you can use symbolic names for messages::
+
+    # pylint: disable=fixme, line-too-long
+
+You can show these symbols in the output with the `-sy` option.
+
 5. Classes and Inheritance
 ==========================
 
diff -r 1985c7abb006 doc/manual.txt
--- a/doc/manual.txt	Wed Aug 22 11:42:40 2012 +0200
+++ b/doc/manual.txt	Mon Sep 03 12:01:30 2012 +1000
@@ -313,6 +313,8 @@
 --html=y_or_n           Use HTML as output format instead of text.
 --list-msgs             Generate pylint's messages.
 --full-documentation    Generate pylint's full documentation, in reST format.
+--include_ids=y_or_n    Show numeric ids of messages (like 'C0301')
+--symbols=y_or_n        Show symbolic ids of messsages (like 'line-too-long')
 
 .. _features: features.html
 
diff -r 1985c7abb006 lint.py
--- a/lint.py	Wed Aug 22 11:42:40 2012 +0200
+++ b/lint.py	Mon Sep 03 12:01:30 2012 +1000
@@ -184,6 +184,12 @@
                   'group': 'Reports',
                   'help' : 'Include message\'s id in output'}),
 
+                ('symbols',
+                 {'type' : 'yn', 'metavar' : '<y_or_n>', 'default' : 0,
+                  'short': 's',
+                  'group': 'Reports',
+                  'help' : 'Include symbolic ids of messages in output'}),
+
                 ('files-output',
                  {'default': 0, 'type' : 'yn', 'metavar' : '<y_or_n>',
                   'group': 'Reports', 'level': 1,
@@ -483,6 +489,7 @@
         name.
         """
         self.reporter.include_ids = self.config.include_ids
+        self.reporter.symbols = self.config.symbols
         if not isinstance(files_or_modules, (list, tuple)):
             files_or_modules = (files_or_modules,)
         walker = PyLintASTWalker(self)
@@ -791,7 +798,7 @@
             ('list-msgs',
              {'action' : 'callback', 'metavar': '<msg-id>',
               'callback' : self.cb_list_messages,
-              'group': 'Commands', 'level': 1,
+              'group': 'Commands',
               'help' : "Generate pylint's messages."}),
 
             ('full-documentation',
@@ -837,13 +844,14 @@
                                                                                 
         MESSAGE_TYPE: LINE_NUM:[OBJECT:] MESSAGE                                
                                                                                 
-There are 5 kind of message types :                                             
+There are 6 kind of message types :
     * (C) convention, for programming standard violation                        
     * (R) refactor, for bad code smell                                          
     * (W) warning, for python specific problems                                 
     * (E) error, for probable bugs in the code                                  
     * (F) fatal, if an error occurred which prevented pylint from doing further
 processing.
+    * (I) information about the progress of pylint
         ''', level=1)
         linter.add_help_section('Output status code', '''
 Pylint should leave with following status code:                                 
diff -r 1985c7abb006 reporters/__init__.py
--- a/reporters/__init__.py	Wed Aug 22 11:42:40 2012 +0200
+++ b/reporters/__init__.py	Mon Sep 03 12:01:30 2012 +1000
@@ -31,18 +31,38 @@
     """raised when a report is empty and so should not be displayed"""
 
 class BaseReporter:
-    """base class for reporters"""
+    """base class for reporters
+
+    symbols: show short symbolic names for messages.
+    """
 
     extension = ''
 
     def __init__(self, output=None):
         self.linter = None
         self.include_ids = None
+        self.symbols = None
         self.section = 0
         self.out = None
         self.out_encoding = None
         self.set_output(output)
 
+    def make_sigle(self, msg_id):
+        """generate a short prefix for a message.
+
+        The sigle can include the id, the symbol, or both, or it can just be
+        the message class.
+        """
+        if self.include_ids:
+            sigle = msg_id
+        else:
+            sigle = msg_id[0]
+        if self.symbols:
+            symbol = self.linter.check_message_id(msg_id).symbol
+            if symbol:
+                sigle += '(%s)' % symbol
+        return sigle
+
     def set_output(self, output=None):
         """set output stream"""
         self.out = output or sys.stdout
diff -r 1985c7abb006 reporters/guireporter.py
--- a/reporters/guireporter.py	Wed Aug 22 11:42:40 2012 +0200
+++ b/reporters/guireporter.py	Mon Sep 03 12:01:30 2012 +1000
@@ -22,11 +22,7 @@
     def add_message(self, msg_id, location, msg):
         """manage message of different type and in the context of path"""
         module, obj, line, col_offset = location[1:]
-        if self.include_ids:
-            sigle = msg_id
-        else:
-            sigle = msg_id[0]
-
+        sigle = self.make_sigle(msg_id)
         full_msg = [sigle, module, obj, str(line), msg]
         self.msgs += [[sigle, module, obj, str(line)]]
         self.gui.msg_queue.put(full_msg)
diff -r 1985c7abb006 reporters/html.py
--- a/reporters/html.py	Wed Aug 22 11:42:40 2012 +0200
+++ b/reporters/html.py	Mon Sep 03 12:01:30 2012 +1000
@@ -36,10 +36,7 @@
     def add_message(self, msg_id, location, msg):
         """manage message of different type and in the context of path"""
         module, obj, line, col_offset = location[1:]
-        if self.include_ids:
-            sigle = msg_id
-        else:
-            sigle = msg_id[0]
+        sigle = self.make_sigle(msg_id)
         self.msgs += [sigle, module, obj, str(line), str(col_offset), escape(msg)]
 
     def set_output(self, output=None):
diff -r 1985c7abb006 reporters/text.py
--- a/reporters/text.py	Wed Aug 22 11:42:40 2012 +0200
+++ b/reporters/text.py	Mon Sep 03 12:01:30 2012 +1000
@@ -56,10 +56,7 @@
                 self.writeln('************* %s' % module)
         if obj:
             obj = ':%s' % obj
-        if self.include_ids:
-            sigle = msg_id
-        else:
-            sigle = msg_id[0]
+        sigle = self.make_sigle(msg_id)
         self.writeln('%s:%3s,%s%s: %s' % (sigle, line, col_offset, obj, msg))
 
     def _display(self, layout):
@@ -88,10 +85,7 @@
         path, _, obj, line, _ = location
         if obj:
             obj = ', %s' % obj
-        if self.include_ids:
-            sigle = msg_id
-        else:
-            sigle = msg_id[0]
+        sigle = self.make_sigle(msg_id)
         if self._prefix:
             path = path.replace(self._prefix, '')
         self.writeln(self.line_format % locals())
@@ -145,10 +139,7 @@
             self._modules[module] = 1
         if obj:
             obj = ':%s' % obj
-        if self.include_ids:
-            sigle = msg_id
-        else:
-            sigle = msg_id[0]
+        sigle = self.make_sigle(msg_id)
         color, style = self._get_decoration(sigle)
         msg = colorize_ansi(msg, color, style)
         sigle = colorize_ansi(sigle, color, style)
diff -r 1985c7abb006 test/input/func_docstring.py
--- a/test/input/func_docstring.py	Wed Aug 22 11:42:40 2012 +0200
+++ b/test/input/func_docstring.py	Mon Sep 03 12:01:30 2012 +1000
@@ -46,3 +46,6 @@
     def __init__(self):
         AAAA.__init__(self)
  
+# pylint: disable=missing-docstring
+def function4():
+    pass
diff -r 1985c7abb006 test/unittest_lint.py
--- a/test/unittest_lint.py	Wed Aug 22 11:42:40 2012 +0200
+++ b/test/unittest_lint.py	Mon Sep 03 12:01:30 2012 +1000
@@ -177,13 +177,45 @@
         self.assert_(linter.is_message_enabled('E1101', 75))
         self.assert_(linter.is_message_enabled('E1101', 77))
 
+    def test_enable_by_symbol(self):
+        """messages can be controlled by symbolic names.
+
+        The state is consistent across symbols and numbers.
+        """
+        linter = self.linter
+        linter.open()
+        linter.set_current_module('toto')
+        self.assert_(linter.is_message_enabled('W0101'))
+        self.assert_(linter.is_message_enabled('unreachable'))
+        self.assert_(linter.is_message_enabled('W0102'))
+        self.assert_(linter.is_message_enabled('dangerous-default-value'))
+        linter.disable('unreachable', scope='package')
+        linter.disable('dangerous-default-value', scope='module', line=1)
+        self.assert_(not linter.is_message_enabled('W0101'))
+        self.assert_(not linter.is_message_enabled('unreachable'))
+        self.assert_(not linter.is_message_enabled('W0102', 1))
+        self.assert_(not linter.is_message_enabled('dangerous-default-value', 1))
+        linter.set_current_module('tutu')
+        self.assert_(not linter.is_message_enabled('W0101'))
+        self.assert_(not linter.is_message_enabled('unreachable'))
+        self.assert_(linter.is_message_enabled('W0102'))
+        self.assert_(linter.is_message_enabled('dangerous-default-value'))
+        linter.enable('unreachable', scope='package')
+        linter.enable('dangerous-default-value', scope='module', line=1)
+        self.assert_(linter.is_message_enabled('W0101'))
+        self.assert_(linter.is_message_enabled('unreachable'))
+        self.assert_(linter.is_message_enabled('W0102', 1))
+        self.assert_(linter.is_message_enabled('dangerous-default-value', 1))
+
     def test_list_messages(self):
         sys.stdout = StringIO()
         try:
-            # just invoke it, don't check the output
             self.linter.list_messages()
+            output = sys.stdout.getvalue()
         finally:
             sys.stdout = sys.__stdout__
+        # cursory examination of the output: we're mostly testing it completes
+        self.assert_(':C0112 (empty-docstring): *Empty docstring*' in output)
 
     def test_lint_ext_module_with_file_output(self):
         if sys.version_info < (3, 0):
@@ -216,6 +248,9 @@
         self.assert_(not linter.is_message_enabled('C0111'))
         self.assert_(not linter.is_message_enabled('W0142'))
         self.assert_(linter.is_message_enabled('W0113'))
+        self.assert_(not linter.is_message_enabled('missing-docstring'))
+        self.assert_(not linter.is_message_enabled('star-args'))
+        # no name for W0113
 
     def test_set_option_2(self):
         linter = self.linter
@@ -223,6 +258,9 @@
         self.assert_(not linter.is_message_enabled('C0111'))
         self.assert_(not linter.is_message_enabled('W0142'))
         self.assert_(linter.is_message_enabled('W0113'))
+        self.assert_(not linter.is_message_enabled('missing-docstring'))
+        self.assert_(not linter.is_message_enabled('star-args'))
+        # no name for W0113
 
     def test_enable_checkers(self):
         self.linter.disable('design')
diff -r 1985c7abb006 utils.py
--- a/utils.py	Wed Aug 22 11:42:40 2012 +0200
+++ b/utils.py	Mon Sep 03 12:01:30 2012 +1000
@@ -93,7 +93,7 @@
 
 
 class Message:
-    def __init__(self, checker, msgid, msg, descr):
+    def __init__(self, checker, msgid, msg, descr, symbol):
         assert len(msgid) == 5, 'Invalid message id %s' % msgid
         assert msgid[0] in MSG_TYPES, \
                'Bad message type %s in %r' % (msgid[0], msgid)
@@ -101,6 +101,7 @@
         self.msg = msg
         self.descr = descr
         self.checker = checker
+        self.symbol = symbol
 
 class MessagesHandlerMixIn:
     """a mix-in class containing all the messages related methods for the main
@@ -110,6 +111,8 @@
     def __init__(self):
         # dictionary of registered messages
         self._messages = {}
+        # dictionary from string symbolic id to Message object.
+        self._messages_by_symbol = {}
         self._msgs_state = {}
         self._module_msgs_state = {} # None
         self._msgs_by_category = {}
@@ -126,14 +129,22 @@
         """
         msgs_dict = checker.msgs
         chkid = None
-        for msgid, (msg, msgdescr) in msgs_dict.items():
+        for msgid, msg_tuple in msgs_dict.items():
+            if len(msg_tuple) == 2:
+                (msg, msgdescr) = msg_tuple
+                msgsymbol = None
+            else:
+                # optionally a symbolic name can be given
+                (msg, msgsymbol, msgdescr) = msg_tuple
             # avoid duplicate / malformed ids
             assert msgid not in self._messages, \
                    'Message id %r is already defined' % msgid
             assert chkid is None or chkid == msgid[1:3], \
                    'Inconsistent checker part in message id %r' % msgid
             chkid = msgid[1:3]
-            self._messages[msgid] = Message(checker, msgid, msg, msgdescr)
+            msg = Message(checker, msgid, msg, msgdescr, msgsymbol)
+            self._messages[msgid] = msg
+            self._messages_by_symbol[msgsymbol] = msg
             self._msgs_by_category.setdefault(msgid[0], []).append(msgid)
 
     def get_message_help(self, msgid, checkerref=False):
@@ -144,10 +155,14 @@
             desc += ' This message belongs to the %s checker.' % \
                    msg.checker.name
         title = msg.msg
+        if msg.symbol:
+            symbol_part = ' (%s)' % msg.symbol
+        else:
+            symbol_part = ''
         if title != '%s':
             title = title.splitlines()[0]
-            return ':%s: *%s*\n%s' % (msg.msgid, title, desc)
-        return ':%s:\n%s' % (msg.msgid, desc)
+            return ':%s%s: *%s*\n%s' % (msg.msgid, symbol_part, title, desc)
+        return ':%s%s:\n%s' % (msg.msgid, symbol_part, desc)
 
     def disable(self, msgid, scope='package', line=None):
         """don't output message of the given id"""
@@ -168,7 +183,7 @@
         if msgid.lower().startswith('rp'):
             self.disable_report(msgid)
             return
-        # msgid is a msgid.
+        # msgid is a symbolic or numeric msgid.
         msg = self.check_message_id(msgid)
         if scope == 'module':
             assert line > 0
@@ -205,7 +220,7 @@
         if msgid.lower().startswith('rp'):
             self.enable_report(msgid)
             return
-        # msgid is a msgid.
+        # msgid is a symbolic or numeric msgid.
         msg = self.check_message_id(msgid)
         if scope == 'module':
             assert line > 0
@@ -221,7 +236,14 @@
             self.config.enable = [mid for mid, val in msgs.items() if val]
 
     def check_message_id(self, msgid):
-        """raise UnknownMessage if the message id is not defined"""
+        """returns the Message object for this message.
+
+        msgid may be either a numeric or symbolic id.
+
+        Raises UnknownMessage if the message id is not defined.
+        """
+        if msgid in self._messages_by_symbol:
+            return self._messages_by_symbol[msgid]
         msgid = msgid.upper()
         try:
             return self._messages[msgid]
@@ -231,7 +253,11 @@
     def is_message_enabled(self, msgid, line=None):
         """return true if the message associated to the given message id is
         enabled
+
+        msgid may be either a numeric or symbolic message id.
         """
+        if msgid in self._messages_by_symbol:
+            msgid = self._messages_by_symbol[msgid].msgid
         if line is None:
             return self._msgs_state.get(msgid, True)
         try:
@@ -525,4 +551,3 @@
             self.walk(child)
         for cb in self.leave_events.get(cid, ()):
             cb(astng)
-
