Commits

Tom Morton committed 8bf9299 Draft

saving current

Comments (0)

Files changed (28)

+# -*- coding: utf-8  -*-
 """
-    Bot Framework
-    
-        By Errant
-   
-   Implements some of my basic framework for bots
-   
+User-interface related functions for building bots
 """
+#
+# (C) Pywikipedia bot team, 2008-2011
+#
+# Distributed under the terms of the MIT license.
+#
+__version__ = '$Id: bot.py 9552 2011-09-25 17:20:50Z xqt $'
+
+# Note: the intention is to develop this module (at some point) into a Bot
+# class definition that can be subclassed to create new, functional bot
+# scripts, instead of writing each one from scratch.
+
+
+import logging, logging.handlers
+       # all output goes thru python std library "logging" module
+import os
+import os.path
+import sys
+
+# logging levels
+_logger = "bot"
+
+from logging import DEBUG, INFO, WARNING, ERROR, CRITICAL
+STDOUT = 16
+VERBOSE = 18
+INPUT = 25
 
 import pywikibot
-import time
-import botconfig
+from pywikibot import config
 
-# counts saves
-savecount = 0
 
-class Page(pywikibot.Page):
+# User interface initialization
+# search for user interface module in the 'userinterfaces' subdirectory
+uiModule = __import__("pywikibot.userinterfaces.%s_interface"
+                        % config.userinterface,
+                        fromlist=['UI'] )
+ui = uiModule.UI()
+
+
+# Logging module configuration
+
+class RotatingFileHandler(logging.handlers.RotatingFileHandler):
+    """Strip trailing newlines before outputting text to file"""
+    def format(self, record):
+        text = logging.handlers.RotatingFileHandler.format(self, record)
+        return text.rstrip("\r\n")
+
+
+class LoggingFormatter(logging.Formatter):
+    """Format LogRecords for output to file.
+
+    This formatter *ignores* the 'newline' key of the LogRecord, because
+    every record written to a file must end with a newline, regardless of
+    whether the output to the user's console does.
+
     """
-        A subclass of Page to allow us to implement a "Development" mode
+    def formatException(self, ei):
+        """
+        Make sure that the exception trace is converted to unicode:
+            * our pywikibot.Error traces are encoded in our
+              console encoding, which is needed for plainly printing them.
+            * but when it comes to logging using logging.exception,
+              the Python logging module will try to use these traces,
+              and it will fail if they are console encoded strings.
+
+        Formatter.formatException also strips the trailing \n, which we need.
+        """
+        strExc = logging.Formatter.formatException(self, ei)
+
+        if isinstance(strExc, str):
+            return strExc.decode(config.console_encoding) + '\n'
+        else:
+            return strExc + '\n'
+
+
+# Initialize the handlers and formatters for the logging system.
+#
+# This relies on the global variable 'ui' which is a UserInterface object
+# defined in the 'userinterface' subpackage.
+#
+# The UserInterface object must define its own init_handlers() method
+# which takes the root logger as its only argument, and which adds to that
+# logger whatever handlers and formatters are needed to process output and
+# display it to the user.  The default (terminal) interface sends level
+# STDOUT to sys.stdout (as all interfaces should) and sends all other
+# levels to sys.stderr; levels WARNING and above are labeled with the
+# level name.
+#
+# UserInterface objects must also define methods input(), inputChoice(),
+# editText(), and askForCaptcha(), all of which are documented in
+# userinterfaces/terminal_interface.py
+
+_handlers_initialized = False
+
+def init_handlers(strm=None):
+    """Initialize logging system for terminal-based bots.
+
+    This function must be called before using pywikibot.output(); and must
+    be called again if the destination stream is changed.
+
+    @param strm: Output stream. If None, re-uses the last stream if one
+        was defined, otherwise uses sys.stderr
+
     """
-    def put(self, newtext, comment=u'', watchArticle=None, minorEdit=True,
-            botflag=None, force=False, async=False, callback=None,bot=None,saveLimit=None):
-        # out own version of put to handle interrupting saves
-        if bot:
-            # we do some pre-processing
-            if bot.is_dev():
-                # in development mode we write to the bot namespace
-                title = "%s/%s" % (bot.usernamespace(),self.title())
-                # make sure bot is None, so we don't go into a circular reference
-                Page(self._link.site,title).put(newtext,comment)
-        if saveLimit:
-            global savecount
-            # now we need to check we have not done too many saves
-            if saveLimit <= savecount:
-                print "Would have modified %s, but bot has performed %s actions" % (self.title(),saveLimit)
-                return
-            savecount += 1
-                
-        return self.save(comment=comment, watch=watchArticle,
-                        minor=minorEdit, botflag=botflag, force=force,
-                        async=async, callback=callback)
-    
+    # Note: this function is called by handleArgs(), so it should normally
+    # not need to be called explicitly
+
+    # All user output is routed through the logging module.
+    # Each type of output is handled by an appropriate handler object.
+    # This structure is used to permit eventual development of other
+    # user interfaces (GUIs) without modifying the core bot code.
+    # The following output levels are defined:
+    #    DEBUG - only for file logging; debugging messages
+    #    STDOUT - output that must be sent to sys.stdout (for bots that may
+    #             have their output redirected to a file or other destination)
+    #    VERBOSE - optional progress information for display to user
+    #    INFO - normal (non-optional) progress information for display to user
+    #    INPUT - prompts requiring user response
+    #    WARN - user warning messages
+    #    ERROR - user error messages
+    #    CRITICAL - fatal error messages
+    # Accordingly, do ''not'' use print statements in bot code; instead,
+    # use pywikibot.output function.
+
+    global _handlers_initialized
+
+    moduleName = calledModuleName()
+    if not moduleName:
+        moduleName = "terminal-interface"
+
+    logging.addLevelName(VERBOSE, "VERBOSE")
+        # for messages to be displayed on terminal at "verbose" setting
+        # use INFO for messages to be displayed even on non-verbose setting
+    logging.addLevelName(STDOUT, "STDOUT")
+        # for messages to be displayed to stdout
+    logging.addLevelName(INPUT, "INPUT")
+        # for prompts requiring user response
+
+    root_logger = logging.getLogger("pywiki")
+    root_logger.setLevel(DEBUG+1) # all records except DEBUG go to logger
+    root_logger.handlers = [] # remove any old handlers
+
+    # configure handler(s) for display to user interface
+    ui.init_handlers(root_logger)
+
+    # if user has enabled file logging, configure file handler
+    if moduleName in config.log or '*' in config.log:
+        if config.logfilename:
+            logfile = config.datafilepath(config.logfilename)
+        else:
+            logfile = config.datafilepath("%s-bot.log" % moduleName)
+        file_handler = RotatingFileHandler(
+                            filename=logfile, maxBytes=2 << 20, backupCount=5)
+
+        file_handler.setLevel(DEBUG)
+        form = LoggingFormatter(
+                   fmt="%(asctime)s %(caller_file)18s, %(caller_line)4s "
+                       "in %(caller_name)18s: %(levelname)-8s %(message)s",
+                   datefmt="%Y-%m-%d %H:%M:%S"
+               )
+        file_handler.setFormatter(form)
+        root_logger.addHandler(file_handler)
+        # Turn on debugging for each component requested by user
+        # or for all components if nothing was specified
+        for component in config.debug_log:
+            if component:
+                debuglogger = logging.getLogger("pywiki."+component)
+            else:
+                debuglogger = logging.getLogger("pywiki")
+            debuglogger.setLevel(DEBUG)
+            debuglogger.addHandler(file_handler)
+
+    _handlers_initialized = True
+
+
+# User output/logging functions
+
+# Six output functions are defined. Each requires a unicode or string
+# argument.  All of these functions generate a message to the log file if
+# logging is enabled ("-log" or "-debug" command line arguments).
+
+# The functions output(), stdout(), warning(), and error() all display a
+# message to the user through the logger object; the only difference is the
+# priority level,  which can be used by the application layer to alter the
+# display. The stdout() function should be used only for data that is
+# the "result" of a script, as opposed to information messages to the
+# user.
+
+# The function log() by default does not display a message to the user, but
+# this can be altered by using the "-verbose" command line option.
+
+# The function debug() only logs its messages, they are never displayed on
+# the user console. debug() takes a required second argument, which is a
+# string indicating the debugging layer.
+
+# next bit filched from 1.5.2's inspect.py
+def currentframe():
+    """Return the frame object for the caller's stack frame."""
+    try:
+        raise Exception
+    except:
+        # go back two levels, one for logoutput and one for whatever called it
+        return sys.exc_traceback.tb_frame.f_back.f_back
+
+if hasattr(sys, '_getframe'):
+    # less portable but more efficient
+    currentframe = lambda: sys._getframe(3)
+    # frame0 is this lambda, frame1 is logoutput() in this module,
+    # frame2 is the convenience function (output(), etc.)
+    # so frame3 is whatever called the convenience function
+
+# done filching
+
+def logoutput(text, decoder=None, newline=True, _level=INFO, _logger="",
+               **kwargs):
+    """Format output and send to the logging module.
+
+    Backend function used by all the user-output convenience functions.
+
+    """
+    if _logger:
+        logger = logging.getLogger("pywiki." + _logger)
+    else:
+        logger = logging.getLogger("pywiki")
+
+    # make sure logging system has been initialized
+    if not _handlers_initialized:
+        init_handlers()
+
+    frame = currentframe()
+    module = os.path.basename(frame.f_code.co_filename)
+    context = {'caller_name': frame.f_code.co_name,
+               'caller_file': module,
+               'caller_line': frame.f_lineno,
+               'newline': ("\n" if newline else "")}
+
+    if decoder:
+        text = unicode(text, decoder)
+    elif not isinstance(text, unicode):
+        if not isinstance(text, str):
+            # looks like text is a non-text object.
+            # Maybe it has a __unicode__ builtin ?
+            # (allows to print Page, Site...)
+            text = unicode(text)
+        else:
+            try:
+                text = unicode(text, 'utf-8')
+            except UnicodeDecodeError:
+                text = unicode(text, 'iso8859-1')
+
+    logger.log(_level, text, extra=context, **kwargs)
+def output(text, decoder=None, newline=True, toStdout=False, **kwargs):
+    """Output a message to the user via the userinterface.
+
+    Works like print, but uses the encoding used by the user's console
+    (console_encoding in the configuration file) instead of ASCII.
+
+    If decoder is None, text should be a unicode string. Otherwise it
+    should be encoded in the given encoding.
+
+    If newline is True, a linebreak will be added after printing the text.
+
+    If toStdout is True, the text will be sent to standard output,
+    so that it can be piped to another process. All other text will
+    be sent to stderr. See: http://en.wikipedia.org/wiki/Pipeline_%28Unix%29
+
+    text can contain special sequences to create colored output. These
+    consist of the escape character \03 and the color name in curly braces,
+    e. g. \03{lightpurple}. \03{default} resets the color.
+
+    Other keyword arguments are passed unchanged to the logger; so far, the
+    only argument that is useful is "exc_info=True", which causes the
+    log message to include an exception traceback.
+
+    """
+    if toStdout:  # maintained for backwards-compatibity only
+        logoutput(text, decoder, newline, STDOUT, **kwargs)
+    else:
+        logoutput(text, decoder, newline, INFO, **kwargs)
+
+def stdout(text, decoder=None, newline=True, **kwargs):
+    """Output script results to the user via the userinterface."""
+    logoutput(text, decoder, newline, STDOUT, **kwargs)
+
+def warning(text, decoder=None, newline=True, **kwargs):
+    """Output a warning message to the user via the userinterface."""
+    logoutput(text, decoder, newline, WARNING, **kwargs)
+
+def error(text, decoder=None, newline=True, **kwargs):
+    """Output an error message to the user via the userinterface."""
+    logoutput(text, decoder, newline, ERROR, **kwargs)
+
+def log(text, decoder=None, newline=True, **kwargs):
+    """Output a record to the log file."""
+    logoutput(text, decoder, newline, VERBOSE, **kwargs)
+
+def debug(text, layer, decoder=None, newline=True, **kwargs):
+    """Output a debug record to the log file."""
+    logoutput(text, decoder, newline, DEBUG, layer, **kwargs)
+
+
+# User input functions
+
+def input(question, password=False):
+    """Ask the user a question, return the user's answer.
+
+    Parameters:
+    * question - a unicode string that will be shown to the user. Don't add a
+                 space after the question mark/colon, this method will do this
+                 for you.
+    * password - if True, hides the user's input (for password entry).
+
+    Returns a unicode string.
+
+    """
+    # make sure logging system has been initialized
+    if not _handlers_initialized:
+        init_handlers()
+
+    data = ui.input(question, password)
+    return data
+
+def inputChoice(question, answers, hotkeys, default=None):
+    """Ask the user a question with several options, return the user's choice.
+
+    The user's input will be case-insensitive, so the hotkeys should be
+    distinctive case-insensitively.
+
+    Parameters:
+    * question - a unicode string that will be shown to the user. Don't add a
+                 space after the question mark, this method will do this
+                 for you.
+    * answers  - a list of strings that represent the options.
+    * hotkeys  - a list of one-letter strings, one for each answer.
+    * default  - an element of hotkeys, or None. The default choice that will
+                 be returned when the user just presses Enter.
+
+    Returns a one-letter string in lowercase.
+
+    """
+    # make sure logging system has been initialized
+    if not _handlers_initialized:
+        init_handlers()
+
+    data = ui.inputChoice(question, answers, hotkeys, default).lower()
+    return data
+
+
+# Command line parsing and help
+
+def calledModuleName():
+    """Return the name of the module calling this function.
+
+    This is required because the -help option loads the module's docstring
+    and because the module name will be used for the filename of the log.
+
+    """
+    # get commandline arguments
+    called = sys.argv[0].strip()
+    if ".py" in called:  # could end with .pyc, .pyw, etc. on some platforms
+        # clip off the '.py?' filename extension
+        called = called[:called.rindex('.py')]
+    return os.path.basename(called)
+
+def _decodeArg(arg):
+    if sys.platform == 'win32':
+        if config.console_encoding in ('cp437', 'cp850'):
+            # Western Windows versions give parameters encoded as windows-1252
+            # even though the console encoding is cp850 or cp437.
+            return unicode(arg, 'windows-1252')
+        elif config.console_encoding == 'cp852':
+            # Central/Eastern European Windows versions give parameters encoded
+            # as windows-1250 even though the console encoding is cp852.
+            return unicode(arg, 'windows-1250')
+        else:
+            return unicode(arg, config.console_encoding)
+    else:
+        # Linux uses the same encoding for both.
+        # I don't know how non-Western Windows versions behave.
+        return unicode(arg, config.console_encoding)
+
+def handleArgs(*args):
+    """Handle standard command line arguments, return the rest as a list.
+
+    Takes the commandline arguments, converts them to Unicode, processes all
+    global parameters such as -lang or -log. Returns a list of all arguments
+    that are not global. This makes sure that global arguments are applied
+    first, regardless of the order in which the arguments were given.
+
+    args may be passed as an argument, thereby overriding sys.argv
+
+    """
+    # get commandline arguments if necessary
+    if not args:
+        args = sys.argv[1:]
+    # get the name of the module calling this function. This is
+    # required because the -help option loads the module's docstring and because
+    # the module name will be used for the filename of the log.
+    moduleName = calledModuleName()
+    if not moduleName:
+        moduleName = "terminal-interface"
+    nonGlobalArgs = []
+    username = None
+    do_help = False
+    for arg in args:
+        arg = _decodeArg(arg)
+        if arg == '-help':
+            do_help = True
+        elif arg.startswith('-family:'):
+            config.family = arg[len("-family:") : ]
+        elif arg.startswith('-lang:'):
+            config.mylang = arg[len("-lang:") : ]
+        elif arg.startswith("-user:"):
+            username = arg[len("-user:") : ]
+        elif arg.startswith('-putthrottle:'):
+            config.put_throttle = int(arg[len("-putthrottle:") : ])
+        elif arg.startswith('-pt:'):
+            config.put_throttle = int(arg[len("-pt:") : ])
+        elif arg.startswith("-maxlag:"):
+            config.maxlag = int(arg[len("-maxlag:") : ])
+        elif arg == '-log':
+            if moduleName not in config.log:
+                config.log.append(moduleName)
+        elif arg.startswith('-log:'):
+            if moduleName not in config.log:
+                config.log.append(moduleName)
+            config.logfilename = arg[len("-log:") : ]
+        elif arg == '-nolog':
+            if moduleName in config.log:
+                config.log.remove(moduleName)
+        #
+        #  DEBUG control:
+        #
+        #    The framework has four layers (by default, others can be added),
+        #    each designated by a string --
+        #
+        #    1.  "comm": the communication layer (http requests, etc.)
+        #    2.  "data": the raw data layer (API requests, XML dump parsing)
+        #    3.  "wiki": the wiki content representation layer (Page and Site
+        #         objects)
+        #    4.  "bot": the application layer (user scripts should always
+        #         send any debug() messages to this layer)
+        #
+        #    The "-debug:layer" flag sets the logger for any specified
+        #    layer to the DEBUG level, causing it to output extensive debugging
+        #    information. Otherwise, the default logging setting is the INFO
+        #    level. "-debug" with no layer specified sets _all_ loggers to
+        #    DEBUG level.
+        #
+        #    This method does not check the 'layer' part of the flag for
+        #    validity.
+        #
+        #    If used, "-debug" turns on file logging, regardless of any
+        #    other settings.
+        #
+        elif arg == "-debug":
+            if moduleName not in config.log:
+                config.log.append(moduleName)
+            if "" not in config.debug_log:
+                config.debug_log.append("")
+        elif arg.startswith("-debug:"):
+            if moduleName not in config.log:
+                config.log.append(moduleName)
+            component = arg[len("-debug:") : ]
+            if component not in config.debug_log:
+                config.debug_log.append(component)
+        elif arg == '-verbose' or arg == "-v":
+            config.verbose_output += 1
+        elif arg == '-daemonize':
+            import daemonize
+            daemonize.daemonize()
+        elif arg.startswith('-daemonize:'):
+            import daemonize
+            daemonize.daemonize(redirect_std = arg[11:])
+        else:
+            # the argument is not global. Let the specific bot script care
+            # about it.
+            nonGlobalArgs.append(arg)
+
+    if username:
+        config.usernames[config.family][config.mylang] = username
+
+    init_handlers()
+
+    if config.verbose_output:
+        import re
+        ver = pywikibot.__version__ # probably can be improved on
+        m = re.search(r"\$Id: .* (\d+ \d+-\d+-\d+ \d+:\d+:\d+Z) .*\$", ver)
+        pywikibot.output(u'Pywikipediabot r%s' % m.group(1))
+        pywikibot.output(u'Python %s' % sys.version)
+
+    if do_help:
+        showHelp()
+        sys.exit(0)
+    pywikibot.debug(u"handleArgs() completed.", _logger)
+    return nonGlobalArgs
+
+def showHelp(name=""):
+    # argument, if given, is ignored
+    modname = calledModuleName()
+    if not modname:
+        try:
+            modname = sys.modules['__main__'].main.__module__
+        except NameError:
+            modname = "no_module"
+
+    globalHelp = u'''
+Global arguments available for all bots:
+
+-dir:PATH         Read the bot's configuration data from directory given by
+                  PATH, instead of from the default directory.
+
+-lang:xx          Set the language of the wiki you want to work on, overriding
+                  the configuration in user-config.py. xx should be the
+                  language code.
+
+-family:xyz       Set the family of the wiki you want to work on, e.g.
+                  wikipedia, wiktionary, wikitravel, ...
+                  This will override the configuration in user-config.py.
+
+-user:xyz         Log in as user 'xyz' instead of the default username.
+
+-daemonize:xyz    Immediately return control to the terminal and redirect
+                  stdout and stderr to xyz (only use for bots that require
+                  no input from stdin).
+
+-help             Show this help text.
+
+-log              Enable the logfile, using the default filename
+                  '%s-bot.log'
+
+-log:xyz          Enable the logfile, using 'xyz' as the filename.
+
+-nolog            Disable the logfile (if it is enabled by default).
+
+-debug:item       Enable the logfile and include extensive debugging data
+-debug            for component "item" (for all components if the second form
+                  is used).
+
+-putthrottle:n    Set the minimum time (in seconds) the bot will wait between
+-pt:n             saving pages.
+
+-verbose          Have the bot provide additional console output that may be
+-v                useful in debugging.
+
+''' % modname
+    try:
+        module = __import__('%s' % modname)
+        helpText = module.__doc__.decode('utf-8')
+        if hasattr(module, 'docuReplacements'):
+            for key, value in module.docuReplacements.iteritems():
+                helpText = helpText.replace(key, value.strip('\n\r'))
+        pywikibot.stdout(helpText) # output to STDOUT
+    except Exception:
+        if modname:
+            pywikibot.stdout(u'Sorry, no help available for %s' % modname)
+        pywikibot.log('showHelp:', exc_info=True)
+    pywikibot.stdout(globalHelp)
+
+
 class Bot(object):
-    BOTUSER = None
-    VERSION = None
-    AUTHOR = None
-    statusbox = "{{User:ErrantX/botstatusbox|%s|%s|%s|%s}}"
-    
-    def __init__(self,homewiki):
-        self.home = homewiki
-        print self.BOTUSER
+    """
+    Generic Bot to be subclassed
+    """
+
+    # Bot configuration.
+    # Only the keys of the dict can be passed as init options
+    # The values are the default values
+    # Extend this in subclasses!
+    availableOptions = {
+        'always': False, # ask for confirmation when putting a page?
+    }
+
+    def __init__(self, **kwargs):
+        """
+        Only accepts options defined in availableOptions
+        """
+        self.setOptions(**kwargs)
+
+    def setOptions(self, **kwargs):
+        """
+        Sets the instance options
+        """
+        # contains the options overriden from defaults
+        self.options = {}
+
+        validOptions = set(self.availableOptions)
+        receivedOptions = set(kwargs)
+
+        for opt in receivedOptions & validOptions:
+            self.options[opt] = kwargs[opt]
+
+        for opt in receivedOptions - validOptions:
+            pywikibot.warning(u'%s is not a valid option. It was ignored.'
+                                % opt)
+
+    def getOption(self, option):
+        """
+        Get the current value of an option.
+        @param option: key defined in Bot.availableOptions
+        """
         try:
-            self.botconfig = botconfig.BotConfig(self.BOTUSER)
-            self.config = self.botconfig.default()
-        except IOError:
-            print "Config file could not be loaded, for some reason; continuing anyway"
-        self.load()
-        
-    """
-        Internal Methods
-    """
-            
-    def _status_page(self,page,code,status,summary):
-        # updates status page
-        status = pywikiself.Page(self.home,"%s/%s" % (self.usernamespace(),page))
-        status.put(self.statusbox % (self.BOTUSER,code,self.VERSION,status,time.strftime("%Y-%m-%d %H:%M")),self.bot_summary(summary))
-        
-    """
-        External Interface
-    """
-    
-    def is_dev(self):
-        # is this bot in dev mode
-        #return (self.config.get("root","mode") == "development")
-        return True
-    
-    def online(self):
-        if self.is_dev():
-            self._status_page("devstatus","DEV","online|#00aa00|#BBDDBB","Bot (development) Online")
-        else:
-            self._status_page("botstatus","BOT","online|#00aa00|#BBDDBB","Bot (production) Online")
-    
-    def offline(self):
-        if self.is_dev():
-            self._status_page("devstatus","DEV","offline|#DD3333|#DDBBBB","Bot (development) Offline")
-        else:
-            self._status_page("botstatus","BOT","offline|#DD3333|#DDBBBB","Bot (production) Offline")
-        
-    def usernamespace(self):
-        # returns the bots user namespace WITHOUT trailing slash
-        return "User:%s" % self.BOTUSER
-    
-    def bot_summary(text):
-        # creates a nice "summary" with appended version number for debugging
-        return "%s (Version [[User:%s/changelog|%s]])" % (text,self.BOTUSER,self.VERSION)
+            return self.options.get(option, self.availableOptions[option])
+        except KeyError:
+            raise pywikibot.Error(u'%s is not a valid bot option.' % option)
+
+    def userPut(self, page, oldtext, newtext):
+        """
+        Print differences, ask user for confirmation,
+        and puts the page if needed.
+
+        Option used:
+            * 'always'
+        """
+        if oldtext == newtext:
+            pywikibot.output(u'No changes were needed on %s' \
+                            % page.title(asLink=True))
+            return
+
+        pywikibot.output(u"\n\n>>> \03{lightpurple}%s\03{default} <<<"
+                         % page.title())
+        pywikibot.showDiff(oldtext, newtext)
+
+        choice = 'a'
+        if not self.getOption('always'):
+            choice = pywikibot.inputChoice(
+                        u'Do you want to accept these changes?',
+                        ['Yes', 'No', 'All'],
+                        ['y', 'N', 'a'], 'N')
+            if choice == 'a':
+                # Remember the choice
+                self.options['always'] = True
+
+        if choice != 'n':
+            page.put(newtext, async=(choice=='a'))
+
 User-interface related functions for building bots
 """
 #
-# (C) Pywikipedia bot team, 2008
+# (C) Pywikipedia bot team, 2008-2011
 #
 # Distributed under the terms of the MIT license.
 #
-__version__ = '$Id: bot.py 8199 2010-05-19 19:14:22Z russblau $'
+__version__ = '$Id: bot.py 9552 2011-09-25 17:20:50Z xqt $'
 
 # Note: the intention is to develop this module (at some point) into a Bot
 # class definition that can be subclassed to create new, functional bot
                 text = unicode(text, 'iso8859-1')
 
     logger.log(_level, text, extra=context, **kwargs)
-
 def output(text, decoder=None, newline=True, toStdout=False, **kwargs):
     """Output a message to the user via the userinterface.
 
     # get commandline arguments
     called = sys.argv[0].strip()
     if ".py" in called:  # could end with .pyc, .pyw, etc. on some platforms
-        called = called[ : called.rindex(".py")]
+        # clip off the '.py?' filename extension
+        called = called[:called.rindex('.py')]
     return os.path.basename(called)
 
 def _decodeArg(arg):
     if sys.platform == 'win32':
-        if config.console_encoding in ("cp437", 'cp850'):
+        if config.console_encoding in ('cp437', 'cp850'):
             # Western Windows versions give parameters encoded as windows-1252
             # even though the console encoding is cp850 or cp437.
             return unicode(arg, 'windows-1252')
     pywikibot.debug(u"handleArgs() completed.", _logger)
     return nonGlobalArgs
 
-
 def showHelp(name=""):
     # argument, if given, is ignored
     modname = calledModuleName()
         except NameError:
             modname = "no_module"
 
-    globalHelp = u'''\
+    globalHelp = u'''
 Global arguments available for all bots:
 
 -dir:PATH         Read the bot's configuration data from directory given by

pywikibot/config2.py

 #
 # Distributed under the terms of the MIT license.
 #
-__version__ = '$Id: config2.py 8810 2010-12-28 22:14:30Z xqt $'
+__version__ = '$Id: config2.py 10165 2012-05-01 15:20:22Z xqt $'
 
 import os, re
 import sys as __sys
 # tkinter isn't yet ready
 userinterface = 'terminal'
 
+# i18n setting for user interface language
+# default is config.mylang or 'en'
+userinterface_lang = None
+
 # Should we transliterate characters that do not exist in the console
 # character set?
 # True: whenever possible

pywikibot/data/api.py

 #
 # Distributed under the terms of the MIT license.
 #
-__version__ = '$Id: api.py 9150 2011-04-08 13:33:17Z russblau $'
+__version__ = '$Id: api.py 9843 2012-01-24 20:42:13Z russblau $'
 
 from UserDict import DictMixin
 from datetime import datetime, timedelta
                     rawdata = http.request(self.site, uri, ssl, method="POST",
                                            headers=mimehead, body=body)
                 else:
-                    #print "here",uri,paramstring
                     rawdata = http.request(self.site, uri, ssl, method="POST",
                                 headers={'Content-Type':
                                          'application/x-www-form-urlencoded'},
 "Non-JSON response received from server %s; the server may be down."
                                  % self.site)
                 pywikibot.debug(rawdata, _logger)
+                # there might also be an overflow, so try a smaller limit
+                for param in self.params:
+                    if param.endswith("limit"):
+                        value = self.params[param]
+                        try:
+                            self.params[param] = str(int(value) // 2)
+                            pywikibot.output(u"Set %s = %s"
+                                             % (param, self.params[param]))
+                        except:
+                            pass
                 self.wait()
                 continue
             if not result:
             if name not in _modules:
                 self.get_module()
                 break
+        kwargs["indexpageids"] = ""  # always ask for list of pageids
         self.request = Request(**kwargs)
         self.prefix = None
         self.update_limit() # sets self.prefix
                                          resultdata.keys(),
                                          self.limit),
                                     _logger)
-                    resultdata = [resultdata[k] for k in sorted(resultdata.keys())]
+                    if "results" in resultdata:
+                        resultdata = resultdata["results"]
+                    elif "pageids" in self.data["query"]:
+                        # this ensures that page data will be iterated
+                        # in the same order as received from server
+                        resultdata = [resultdata[k]
+                                      for k in self.data["query"]["pageids"]]
+                    else:
+                        resultdata = [resultdata[k]
+                                      for k in sorted(resultdata.keys())]
                 else:
                     pywikibot.debug(u"%s received %s; limit=%s"
                                       % (self.__class__.__name__,

pywikibot/date.py

 #
 # Distributed under the terms of the MIT license.
 #
-__version__ = '$Id: date.py 9190 2011-04-21 06:30:32Z xqt $'
+__version__ = '$Id: date.py 9763 2011-11-15 08:22:52Z xqt $'
 
 # used for date recognition
 import types
         'no' :      lambda v: slh( v, [u"januar", u"februar", u"mars", u"april", u"mai", u"juni", u"juli", u"august", u"september", u"oktober", u"november", u"desember"] ),
         'oc' :      lambda v: slh( v, [u"genièr", u"febrièr", u"març", u"abril", u"mai", u"junh", u"julhet", u"agost", u"setembre", u"octobre", u"novembre", u"decembre"] ),
         'os' :      lambda v: slh( v, [u"январь", u"февраль", u"мартъи", u"апрель", u"май", u"июнь", u"июль", u"август", u"сентябрь", u"октябрь", u"ноябрь", u"декабрь"] ),
+        'pdc' :     lambda v: slh( v, [u'Yenner', u'Hanning', u'Matz', u'Abril', u'Moi', u'Yuni', u'Yuli', u'Aagscht', u'September', u'Oktower', u'Nowember', u'Disember'] ),
         'pl' :      lambda v: slh( v, [u"styczeń", u"luty", u"marzec", u"kwiecień", u"maj", u"czerwiec", u"lipiec", u"sierpień", u"wrzesień", u"październik", u"listopad", u"grudzień"] ),
         'pt' :      lambda v: slh( v, [u"Janeiro", u"Fevereiro", u"Março", u"Abril", u"Maio", u"Junho", u"Julho", u"Agosto", u"Setembro", u"Outubro", u"Novembro", u"Dezembro"] ),
         'ro' :      lambda v: slh( v, [u"ianuarie", u"februarie", u"martie", u"aprilie", u"mai", u"iunie", u"iulie", u"august", u"septembrie", u"octombrie", u"noiembrie", u"decembrie"] ),
         'nl' :      lambda v: dh_number( v, u'%d (getal)' ),
         'nn' :      lambda v: dh_number( v, u'Talet %d' ),
         'no' :      lambda v: dh_number( v, u'%d (tall)' ),
+        'nso' :     lambda v: dh_number( v, u'%d (nomoro)' ),
         'pl' :      lambda v: dh_number( v, u'%d (liczba)' ),
         'ro' :      lambda v: dh_number( v, u'%d (cifră)' ),
         'ru' :      lambda v: dh_number( v, u'%d (число)' ),
         'fur':      dh_simpleYearAD,
         'fy' :      dh_simpleYearAD,
         'ga' :      dh_simpleYearAD,
+        'gan' :     lambda v: dh_yearAD( v, u'%d年' ),
         'gd' :      dh_simpleYearAD,
         'gl' :      dh_simpleYearAD,
         'gu' :      lambda v: dh_yearAD( v, u'%G' ),
         'nl' :      dh_simpleYearAD,
         'nn' :      dh_simpleYearAD,
         'no' :      dh_simpleYearAD,
+        'nso' :     dh_simpleYearAD,
         'oc' :      dh_simpleYearAD,
         'os' :      dh_simpleYearAD,
+        'pdc' :     dh_simpleYearAD,
         'pl' :      dh_simpleYearAD,
         'pt' :      dh_simpleYearAD,
         'rm' :      dh_simpleYearAD,
         'fi' :      lambda v: dh_yearAD( v, u'Vuoden %d albumit' ),
         'fr' :      lambda v: dh_yearAD( v, u'Album musical sorti en %d' ),
         'he' :      lambda v: dh_yearAD( v, u'אלבומי %d' ),
+        'no' :      lambda v: dh_yearAD( v, u'Musikkalbum fra %d' ),
         'pl' :      lambda v: dh_yearAD( v, u'Albumy muzyczne wydane w roku %d' ),
         'sl' :      lambda v: dh_yearAD( v, u'Albumi iz %d' ),
         'sv' :      lambda v: dh_yearAD( v, u'%d års musikalbum' ),
     },
 
+    'Cat_BirthsAD': {
+        'an' :      lambda v: dh_yearAD( v, u'%d (naixencias)' ),
+        'ar' :      lambda v: dh_yearAD( v, u'مواليد %d' ),
+        'arz' :     lambda v: dh_yearAD( v, u'مواليد %d'),
+        'bar' :     lambda v: dh_yearAD( v, u'Geboren %d' ),
+        'be' :      lambda v: dh_yearAD( v, u'Нарадзіліся ў %d годзе' ),
+        'be-x-old' : lambda v: dh_yearAD( v, u'Нарадзіліся ў %d годзе' ),
+        'bg' :      lambda v: dh_yearAD( v, u'Родени през %d година' ),
+        'bjn' :     lambda v: dh_yearAD( v, u'Kalahiran %d' ),
+        'bn' :      lambda v: dh_yearAD( v, u'%B-এ জন্ম' ),
+        'bpy' :     lambda v: dh_yearAD( v, u'মারি %B-এ উজ্জিসিতা' ),
+        'br' :      lambda v: dh_yearAD( v, u'Ganedigezhioù %d' ),
+        'bs' :      lambda v: dh_yearAD( v, u'%d rođenja' ),
+        'cbk-zam' : lambda v: dh_yearAD( v, u'Nacidos en %d' ),
+        'crh' :     lambda v: dh_yearAD( v, u'%d senesinde doğğanlar' ),
+        'cs' :      lambda v: dh_yearAD( v, u'Narození %d' ),
+        'cy' :      lambda v: dh_yearAD( v, u'Genedigaethau %d' ),
+        'da' :      lambda v: dh_yearAD( v, u'Født i %d' ),
+        'de' :      lambda v: dh_yearAD( v, u'Geboren %d' ),
+        'dsb' :     lambda v: dh_yearAD( v, u'Roź. %d' ),
+        'el' :      lambda v: dh_yearAD( v, u'Γεννήσεις το %d' ),
+        'en' :      lambda v: dh_yearAD( v, u'%d births' ),
+        'eo' :      lambda v: dh_yearAD( v, u'Naskiĝintoj en %d' ),
+        'es' :      lambda v: dh_yearAD( v, u'Nacidos en %d' ),
+        'et' :      lambda v: dh_yearAD( v, u'Sündinud %d' ),
+        'eu' :      lambda v: dh_yearAD( v, u'%dko jaiotzak' ),
+        'fi' :      lambda v: dh_yearAD( v, u'Vuonna %d syntyneet' ),
+        'fa' :      lambda v: dh_yearAD( v, u'زادگان %F (میلادی)' ),
+        'fr' :      lambda v: dh_yearAD( v, u'Naissance en %d' ),
+        'ga' :      lambda v: dh_yearAD( v, u'Daoine a rugadh i %d' ),
+        'gan' :     lambda v: dh_yearAD( v, u'%d年出世' ),
+        'gv' :      lambda v: dh_yearAD( v, u'Ruggyryn \'sy vlein %d' ),
+        'hsb' :     lambda v: dh_yearAD( v, u'Rodź. %d' ),
+        'hy' :      lambda v: dh_yearAD( v, u'%d ծնունդներ' ),
+        'id' :      lambda v: dh_yearAD( v, u'Kelahiran %d' ),
+        'is' :      lambda v: dh_yearAD( v, u'Fólk fætt árið %d' ),
+        'it' :      lambda v: dh_yearAD( v, u'Nati nel %d' ),
+        'ja' :      lambda v: dh_yearAD( v, u'%d年生' ),
+        'jv' :      lambda v: dh_yearAD( v, u'Lair %d' ),
+        'ka' :      lambda v: dh_yearAD( v, u'დაბადებული %d' ),
+        'kk' :      lambda v: dh_yearAD( v, u'%d жылы туғандар' ),
+        'ko' :      lambda v: dh_yearAD( v, u'%d년 태어남' ),
+        'la' :      lambda v: dh_yearAD( v, u'Nati %d' ),
+        'lb' :      lambda v: dh_yearAD( v, u'Gebuer %d' ),
+        'lv' :      lambda v: dh_yearAD( v, u'%d. gadā dzimušiel' ),
+        'mk' :      lambda v: dh_yearAD( v, u'Родени во %d година' ),
+        'ml' :      lambda v: dh_yearAD( v, u'%d-ൽ ജനിച്ചവർ' ),
+        'mn' :      lambda v: dh_yearAD( v, u'%d онд төрөгсөд' ),
+        'mr' :      lambda v: dh_yearAD( v, u'इ.स. %H मधील जन्म' ),
+        'ms' :      lambda v: dh_yearAD( v, u'Kelahiran %d' ),
+        'mt' :      lambda v: dh_yearAD( v, u'Twieldu fl-%d' ),
+        'nah' :     lambda v: dh_yearAD( v, u'Ōtlācatqueh xiuhpan %d' ),
+        'new' :     lambda v: dh_yearAD( v, u'%Hय् बुगु' ),
+        'nn' :      lambda v: dh_yearAD( v, u'Fødde i %d' ),
+        'no' :      lambda v: dh_yearAD( v, u'Fødsler i %d' ),
+        'oc' :      lambda v: dh_yearAD( v, u'Naissença en %d' ),
+        'pdc' :     lambda v: dh_yearAD( v, u'Gebore %d' ),
+        'pl' :      lambda v: dh_yearAD( v, u'Urodzeni w %d' ),
+        'qu' :      lambda v: dh_yearAD( v, u'Paqarisqa %d' ),
+        'ro' :      lambda v: dh_yearAD( v, u'Nașteri în %d' ),
+        'ru' :      lambda v: dh_yearAD( v, u'Родившиеся в %d году' ),
+        'sah' :     lambda v: dh_yearAD( v, u'%d сыллаахха төрөөбүттэр' ),
+        'se' :      lambda v: dh_yearAD( v, u'Riegádeamit %d' ),
+        'sh' :      lambda v: dh_yearAD( v, u'Rođeni %d.' ),
+        'simple' :  lambda v: dh_yearAD( v, u'%d births' ),
+        'sk' :      lambda v: dh_yearAD( v, u'Narodenia v %d' ),
+        'sl' :      lambda v: dh_yearAD( v, u'Rojeni leta %d' ),
+        'sq' :      lambda v: dh_yearAD( v, u'Lindje %d' ),
+        'sr' :      lambda v: dh_yearAD( v, u'Рођени %d.' ),
+        'sv' :      lambda v: dh_yearAD( v, u'Födda %d' ),
+        'sw' :      lambda v: dh_yearAD( v, u'Waliozaliwa %d' ),
+        'szl' :     lambda v: dh_yearAD( v, u'Rodzyńi we %d' ),
+        'ta' :      lambda v: dh_yearAD( v, u'%d பிறப்புகள்' ),
+        'te' :      lambda v: dh_yearAD( v, u'%d జననాలు' ),
+        'th' :      lambda v: dh_yearAD( v, u'บุคคลที่เกิดในปี พ.ศ. %T' ),
+        'tl' :      lambda v: dh_yearAD( v, u'Ipinanganak noong %d' ),
+        'tr' :      lambda v: dh_yearAD( v, u'%d doğumlular' ),
+        'tt' :      lambda v: dh_yearAD( v, u'%d елда туганнар' ),
+        'uk' :      lambda v: dh_yearAD( v, u'Народились %d' ),
+        'vi' :      lambda v: dh_yearAD( v, u'Sinh %d' ),
+        'war' :     lambda v: dh_yearAD( v, u'Mga natawo han %d' ),
+        'yo' :      lambda v: dh_yearAD( v, u'Àwọn ọjọ́ìbí ní %d' ),
+        'zh' :      lambda v: dh_yearAD( v, u'%d年出生' ),
+        'zh-yue' :  lambda v: dh_yearAD( v, u'%d年出世' ),
+    },
+
+    'Cat_DeathsAD': {
+        'an' :      lambda v: dh_yearAD( v, u'%d (muertes)' ),
+        'ay' :      lambda v: dh_yearAD( v, u'Jiwäwi %d' ),
+        'ar' :      lambda v: dh_yearAD( v, u'وفيات %d' ),
+        'ba' :      lambda v: dh_yearAD( v, u'%d йылда үлгәндәр' ),
+        'bar' :     lambda v: dh_yearAD( v, u'Gestorben %d' ),
+        'be' :      lambda v: dh_yearAD( v, u'Памерлі ў %d годзе' ),
+        'be-x-old' : lambda v: dh_yearAD( v, u'Памерлі ў %d годзе' ),
+        'bg' :      lambda v: dh_yearAD( v, u'Починали през %d година' ),
+        'bn' :      lambda v: dh_yearAD( v, u'%B-এ মৃত্যু' ),
+        'br' :      lambda v: dh_yearAD( v, u'Marvioù %d' ),
+        'bs' :      lambda v: dh_yearAD( v, u'%d smrti' ),
+        'crh' :     lambda v: dh_yearAD( v, u'%d senesinde ölgenler' ),
+        'cs' :      lambda v: dh_yearAD( v, u'Úmrtí %d' ),
+        'cy' :      lambda v: dh_yearAD( v, u'Marwolaethau %d' ),
+        'da' :      lambda v: dh_yearAD( v, u'Døde i %d' ),
+        'de' :      lambda v: dh_yearAD( v, u'Gestorben %d' ),
+        'dsb' :     lambda v: dh_yearAD( v, u'Wum. %d' ),
+        'el' :      lambda v: dh_yearAD( v, u'Θάνατοι το %d' ),
+        'en' :      lambda v: dh_yearAD( v, u'%d deaths' ),
+        'eo' :      lambda v: dh_yearAD( v, u'Mortintoj en %d' ),
+        'es' :      lambda v: dh_yearAD( v, u'Fallecidos en %d' ),
+        'et' :      lambda v: dh_yearAD( v, u'Surnud %d' ),
+        'eu' :      lambda v: dh_yearAD( v, u'%deko heriotzak' ),
+        'fa' :      lambda v: dh_yearAD( v, u'درگذشتگان %F (میلادی)' ),
+        'fi' :      lambda v: dh_yearAD( v, u'Vuonna %d kuolleet' ),
+        'fr' :      lambda v: dh_yearAD( v, u'Décès en %d' ),
+        'ga' :      lambda v: dh_yearAD( v, u'Básanna i %d' ),
+        'gan' :     lambda v: dh_yearAD( v, u'%d年過世' ),
+        'gv' :      lambda v: dh_yearAD( v, u'Baaseyn \'sy vlein %d' ),
+        'hif' :     lambda v: dh_yearAD( v, u'%d maut' ),
+        'hsb' :     lambda v: dh_yearAD( v, u'Zemr. %d' ),
+        'hy' :      lambda v: dh_yearAD( v, u'%d մահեր' ),
+        'id' :      lambda v: dh_yearAD( v, u'Kematian %d' ),
+        'is' :      lambda v: dh_yearAD( v, u'Fólk dáið árið %d' ),
+        'it' :      lambda v: dh_yearAD( v, u'Morti nel %d' ),
+        'ja' :      lambda v: dh_yearAD( v, u'%d年没' ),
+        'jv' :      lambda v: dh_yearAD( v, u'Pati %d' ),
+        'ka' :      lambda v: dh_yearAD( v, u'გარდაცვლილი %d' ),
+        'kk' :      lambda v: dh_yearAD( v, u'%d жылы қайтыс болғандар' ),
+        'ko' :      lambda v: dh_yearAD( v, u'%d년 죽음' ),
+        'krc' :     lambda v: dh_yearAD( v, u'%d джылда ёлгенле' ),
+        'ky' :      lambda v: dh_yearAD( v, u'%d жылы кайтыш болгандар' ),
+        'la' :      lambda v: dh_yearAD( v, u'Mortui %d' ),
+        'lb' :      lambda v: dh_yearAD( v, u'Gestuerwen %d' ),
+        'lv' :      lambda v: dh_yearAD( v, u'%d. gadā mirušie' ),
+        'mk' :      lambda v: dh_yearAD( v, u'Починати во %d година' ),
+        'ml' :      lambda v: dh_yearAD( v, u'%d-ൽ മരിച്ചവർ' ),
+        'mn' :      lambda v: dh_yearAD( v, u'%d онд нас барагсад' ),
+        'ms' :      lambda v: dh_yearAD( v, u'Kematian %d' ),
+        'mt' :      lambda v: dh_yearAD( v, u'Mietu fl-%d' ),
+        'nah' :     lambda v: dh_yearAD( v, u'%d miquiztli' ),
+        'nn' :      lambda v: dh_yearAD( v, u'Døde i %d' ),
+        'no' :      lambda v: dh_yearAD( v, u'Dødsfall i %d' ),
+        'oc' :      lambda v: dh_yearAD( v, u'Decès en %d' ),
+        'pdc' :     lambda v: dh_yearAD( v, u'Gschtaerewe %d' ),
+        'pl' :      lambda v: dh_yearAD( v, u'Zmarli w %d' ),
+        'pt' :      lambda v: dh_yearAD( v, u'Mortos em %d' ),
+        'qu' :      lambda v: dh_yearAD( v, u'Wañusqa %d' ),
+        'ro' :      lambda v: dh_yearAD( v, u'Decese în %d' ),
+        'ru' :      lambda v: dh_yearAD( v, u'Умершие в %d году' ),
+        'sah' :     lambda v: dh_yearAD( v, u'%d сыллаахха өлбүттэр' ),
+        'se' :      lambda v: dh_yearAD( v, u'Jápmimat %d' ),
+        'sh' :      lambda v: dh_yearAD( v, u'Umrli %d.' ),
+        'simple' :  lambda v: dh_yearAD( v, u'%d deaths' ),
+        'sk' :      lambda v: dh_yearAD( v, u'Úmrtia v %d' ),
+        'sl' :      lambda v: dh_yearAD( v, u'Umrli leta %d' ),
+        'sq' :      lambda v: dh_yearAD( v, u'Vdekje %d' ),
+        'sr' :      lambda v: dh_yearAD( v, u'Умрли %d.' ),
+        'sv' :      lambda v: dh_yearAD( v, u'Avlidna %d' ),
+        'sw' :      lambda v: dh_yearAD( v, u'Waliofariki %d' ),
+        'szl' :     lambda v: dh_yearAD( v, u'Umarći we %d' ),
+        'ta' :      lambda v: dh_yearAD( v, u'%d இறப்புகள்' ),
+        'te' :      lambda v: dh_yearAD( v, u'%d మరణాలు' ),
+        'th' :      lambda v: dh_yearAD( v, u'บุคคลที่เสียชีวิตในปี พ.ศ. %T' ),
+        'tl' :      lambda v: dh_yearAD( v, u'Namatay noong %d' ),
+        'tr' :      lambda v: dh_yearAD( v, u'%d yılında ölenler' ),
+        'tt' :      lambda v: dh_yearAD( v, u'%d елда вафатлар' ),
+        'uk' :      lambda v: dh_yearAD( v, u'Померли %d' ),
+        'vi' :      lambda v: dh_yearAD( v, u'Mất %d' ),
+        'war' :     lambda v: dh_yearAD( v, u'Mga namatay han %d' ),
+        'yo' :      lambda v: dh_yearAD( v, u'Àwọn ọjọ́aláìsí ní %d' ),
+        'zh' :      lambda v: dh_yearAD( v, u'%d年逝世' ),
+        'zh-yue' :  lambda v: dh_yearAD( v, u'%d年死' ),
+    },
+
+    'Cat_BirthsBC': {
+        'en' :      lambda v: dh_yearBC( v, u'%d BC births' ),
+        'no' :      lambda v: dh_yearBC( v, u'Fødsler i %d f.Kr.' ),
+    },
+    'Cat_DeathsBC': {
+        'en' :      lambda v: dh_yearBC( v, u'%d BC deaths' ),
+        'fr' :      lambda v: dh_yearBC( v, u'Décès en -%d' ),
+        'no' :      lambda v: dh_yearBC( v, u'Dødsfall i %d f.Kr.' ),
+    },
+
     'CurrEvents': {
         'an' :      lambda v: dh_singVal( v, u'Autualidá' ),
         'ang':      lambda v: dh_singVal( v, u'Efenealde belimpas' ),
     'MillenniumBC'      : (lambda v: 1 <=v and v < 20,                 1, 2),     # And only 1 BC Millenium
     'CenturyAD_Cat'     : (lambda v: 1 <=v and v < 41,                 1, 23),    # Some centuries use Roman numerals or a given list - do not exceed them in testing
     'CenturyBC_Cat'     : (lambda v: 1 <=v and v < 41,                 1, 23),    # Some centuries use Roman numerals or a given list - do not exceed them in testing
-    'Cat_Year_MusicAlbums'  : (lambda v: 1950 <= v and v < 2021,        1950, 2021),
-    'CurrEvents'            : (lambda v: 0 <= v and v < 1,              0, 1),
+    'Cat_Year_MusicAlbums' : (lambda v: 1950 <= v and v < 2021,        1950, 2021),
+    'Cat_BirthsAD'      : (lambda v: 0 <=v and v < 2501,               0, 2501),
+    'Cat_DeathsAD'      : (lambda v: 0 <=v and v < 2501,               0, 2501),
+    'Cat_BirthsBC'      : (lambda v: 0 <=v and v < 4001,               0, 501),
+    'Cat_DeathsBC'      : (lambda v: 0 <=v and v < 4001,               0, 501),
+    'CurrEvents'        : (lambda v: 0 <= v and v < 1,                 0, 1),
 }
 
 # All month of year articles are in the same format
     """
     """
     for s in makeMonthNamedList( lang, pattern, capitalize ):
-        print( s )
-
+        pywikibot.output(s)
 
 def testMapEntry( formatName, showAll = True, value = None ):
     """This is a test function, to be used interactivelly to test the validity of the above maps.

pywikibot/families/commons_family.py

 # -*- coding: utf-8  -*-
 
-__version__ = '$Id: commons_family.py 8964 2011-02-16 08:38:48Z xqt $'
+__version__ = '$Id: commons_family.py 10142 2012-04-20 16:12:35Z xqt $'
 
 from pywikibot import family
 
             'meta', 'mediawiki', 'test', 'incubator', 'species',
         ]
 
-
-    def version(self, code):
-        return '1.17wmf1'
-
     def dbName(self, code):
         return 'commonswiki_p'
 

pywikibot/families/i18n_family.py

 # -*- coding: utf-8  -*-
 
-__version__ = '$Id: i18n_family.py 8870 2011-01-21 08:30:07Z xqt $'
+__version__ = '$Id: i18n_family.py 10212 2012-05-13 14:55:14Z xqt $'
 
 from pywikibot import family
 
         }
 
     def version(self, code):
-        return "1.18alpha"
+        return "1.20alpha"

pywikibot/families/incubator_family.py

 # -*- coding: utf-8  -*-
 
-__version__ = '$Id: incubator_family.py 8970 2011-02-16 11:57:50Z xqt $'
+__version__ = '$Id: incubator_family.py 9589 2011-10-06 07:28:32Z xqt $'
 
 from pywikibot import family
 
             'incubator': 'incubator.wikimedia.org',
         }
 
-    def version(self, code):
-        return '1.17wmf1'
-
     def shared_image_repository(self, code):
         return ('commons', 'commons')
 

pywikibot/families/mediawiki_family.py

 # -*- coding: utf-8  -*-
 
-__version__ = '$Id: mediawiki_family.py 8957 2011-02-15 15:44:21Z xqt $'
+__version__ = '$Id: mediawiki_family.py 9577 2011-10-03 14:53:28Z xqt $'
 
 from pywikibot import family
 
             'species',
         ]
 
-    def version(self, code):
-        return '1.17wmf1'
-
     def shared_image_repository(self, code):
         return ('commons', 'commons')
 

pywikibot/families/meta_family.py

 # -*- coding: utf-8  -*-
 
-__version__ = '$Id: meta_family.py 8956 2011-02-15 15:42:47Z xqt $'
+__version__ = '$Id: meta_family.py 10142 2012-04-20 16:12:35Z xqt $'
 
 from pywikibot import family
 
             'commons', 'species',
         ]
 
-    def version(self,code):
-        return '1.17wmf1'
-
     def shared_image_repository(self, code):
         return ('commons', 'commons')
 

pywikibot/families/species_family.py

 # -*- coding: utf-8  -*-
 
-__version__ = '$Id: species_family.py 8970 2011-02-16 11:57:50Z xqt $'
+__version__ = '$Id: species_family.py 9589 2011-10-06 07:28:32Z xqt $'
 
 from pywikibot import family
 
         }
         self.interwiki_forward = 'wikipedia'
         self.cross_projects = [
-            'wikipedia', 'wiktionary', 'wikibooks', 'wikiquote', 'wikisource', 'wikinews', 'wikiversity',
-            'meta', 'mediawiki', 'test', 'incubator', 'commons',
+            'wikipedia', 'wiktionary', 'wikibooks', 'wikiquote', 'wikisource',
+            'wikinews', 'wikiversity', 'meta', 'mediawiki', 'test', 'incubator',
+            'commons',
         ]
 
-    def version(self,code):
-        return '1.17wmf1'
-
     def shared_image_repository(self, code):
         return ('commons', 'commons')
 

pywikibot/families/strategy_family.py

 # -*- coding: utf-8  -*-
 
-__version__ = '$Id: strategy_family.py 8964 2011-02-16 08:38:48Z xqt $'
+__version__ = '$Id: strategy_family.py 9577 2011-10-03 14:53:28Z xqt $'
 
 from pywikibot import family
 
         }
         self.interwiki_forward = 'wikipedia'
 
-    def version(self, code):
-        return '1.17wmf1'
-
     def dbName(self, code):
         return 'strategywiki_p'
 

pywikibot/families/test_family.py

 # -*- coding: utf-8  -*-
 
-__version__ = '$Id: test_family.py 8970 2011-02-16 11:57:50Z xqt $'
+__version__ = '$Id: test_family.py 10142 2012-04-20 16:12:35Z xqt $'
 
 from pywikibot import family
 
+
 # The test wikipedia family
-
 class Family(family.Family):
     def __init__(self):
         family.Family.__init__(self)
             'test': 'test.wikipedia.org',
         }
 
-    def version(self, code):
-        return '1.17wmf1'
-
     def shared_image_repository(self, code):
         return ('commons', 'commons')
 

pywikibot/families/wikia_family.py

 # -*- coding: utf-8  -*-
 
-__version__ = '$Id: wikia_family.py 9064 2011-03-13 14:29:45Z xqt $'
+__version__ = '$Id: wikia_family.py 9444 2011-08-20 16:26:30Z xqt $'
 
 import family
 
         return u'www.wikia.com'
 
     def version(self, code):
-        return "1.16.2"
+        return "1.16.5"
 
     def scriptpath(self, code):
         return ''

pywikibot/families/wikibooks_family.py

 # -*- coding: utf-8  -*-
 from pywikibot import family
 
-__version__ = '$Id: wikibooks_family.py 9192 2011-04-23 15:37:11Z xqt $'
+__version__ = '$Id: wikibooks_family.py 10229 2012-05-20 13:21:59Z xqt $'
 
 # The Wikimedia family that is known as Wikibooks
 
         self.name = 'wikibooks'
 
         self.languages_by_size = [
-            'en', 'de', 'fr', 'hu', 'ja', 'pt', 'nl', 'es', 'pl', 'it', 'he',
-            'sq', 'fi', 'ca', 'ru', 'vi', 'cs', 'zh', 'hr', 'sv', 'tr', 'ko',
-            'da', 'id', 'th', 'gl', 'fa', 'sr', 'tl', 'no', 'mk', 'ar', 'is',
-            'ta', 'ka', 'lt', 'eo', 'bg', 'ro', 'sk', 'uk', 'el', 'az', 'si',
-            'li', 'la', 'tt', 'ang', 'ia', 'cv', 'sl', 'mr', 'et', 'ur', 'oc',
-            'ml', 'bn', 'ms', 'hi', 'eu', 'fy', 'ie', 'te', 'hy', 'tg', 'af',
-            'ne', 'pa', 'bs', 'sa', 'ky', 'be', 'ast', 'zh-min-nan', 'mg', 'cy',
-            'ku', 'co', 'tk', 'su', 'uz', 'vo', 'kk', 'mn', 'my',
+            'en', 'de', 'fr', 'hu', 'ja', 'pt', 'nl', 'pl', 'it', 'es', 'he',
+            'sq', 'fi', 'ca', 'id', 'ru', 'vi', 'cs', 'zh', 'hr', 'sv', 'tr',
+            'da', 'th', 'gl', 'fa', 'ta', 'no', 'ko', 'sr', 'ar', 'tl', 'mk',
+            'is', 'tt', 'lt', 'ka', 'az', 'eo', 'ro', 'bg', 'uk', 'hy', 'sl',
+            'sk', 'el', 'si', 'li', 'la', 'ang', 'ia', 'cv', 'et', 'ur', 'mr',
+            'bn', 'ms', 'hi', 'oc', 'ml', 'kk', 'eu', 'fy', 'ie', 'ne', 'te',
+            'af', 'tg', 'sa', 'pa', 'bs', 'ky', 'mg', 'cy', 'be', 'zh-min-nan',
+            'ast', 'ku', 'tk', 'uz', 'su', 'vo', 'mn', 'my',
         ]
 
-        for lang in self.languages_by_size:
-            self.langs[lang] = '%s.wikibooks.org' % lang
+        self.langs = dict([(lang, '%s.wikibooks.org' % lang) for lang in self.languages_by_size])
+
+        # CentralAuth cross avaliable projects.
+        self.cross_projects = [
+            'wiktionary', 'wikibooks', 'wikiquote', 'wikisource', 'wikinews',
+            'wikiversity', 'meta', 'mediawiki', 'test', 'incubator', 'commons',
+            'species',
+        ]
+
+        # Global bot allowed languages on http://meta.wikimedia.org/wiki/Bot_policy/Implementation#Current_implementation
+        self.cross_allowed = [
+            'af', 'ang', 'ca', 'fa', 'fy', 'it', 'nl', 'ru', 'th', 'zh',
+        ]
 
         # Which languages have a special order for putting interlanguage links,
         # and what order is it? If a language is not in interwiki_putfirst,
         # alphabetical order on language code is used. For languages that are in
         # interwiki_putfirst, interwiki_putfirst is checked first, and
-        # languages are put in the order given there. All other languages are put
-        # after those, in code-alphabetical order.
-
-        alphabetic = ['af', 'ar', 'roa-rup', 'om', 'bg', 'be', 'bn', 'bs', 'ca',
-                      'chr', 'co', 'cs', 'cy', 'da', 'de', 'als', 'et', 'el',
-                      'en', 'es', 'eo', 'eu', 'fa', 'fr', 'fy', 'gv', 'gd',
-                      'gl', 'ko', 'hi', 'hr', 'io', 'id', 'ia', 'is', 'it',
-                      'he', 'jv', 'ka', 'csb', 'sw', 'la', 'lv', 'lt', 'hu',
-                      'mk', 'mg', 'ml', 'mi', 'mr', 'ms', 'zh-cfr', 'mn', 'nah',
-                      'na', 'nl', 'ja', 'no', 'nb', 'oc', 'nds', 'pl', 'pt',
-                      'ro', 'ru', 'sa', 'st', 'sq', 'si', 'simple', 'sk', 'sl',
-                      'sr', 'su', 'fi', 'sv', 'ta', 'tt', 'th', 'ur', 'vi',
-                      'tpi', 'tr', 'uk', 'vo', 'yi', 'za', 'zh', 'zh-cn',
-                      'zh-tw']
+        # languages are put in the order given there. All other languages are
+        # put after those, in code-alphabetical order.
+        self.interwiki_putfirst = {
+            'en': self.alphabetic,
+            'fi': self.alphabetic,
+            'fr': self.alphabetic,
+            'he': ['en'],
+            'hu': ['en'],
+            'pl': self.alphabetic,
+            'simple': self.alphabetic
+        }
 
         self.obsolete = {
             'aa': None, # http://meta.wikimedia.org/wiki/Proposals_for_closing_projects/Closure_of_Afar_Wikibooks
             'bm': None, # http://meta.wikimedia.org/wiki/Proposals_for_closing_projects/Closure_of_Bambara_Wikibooks
             'bo': None, # http://meta.wikimedia.org/wiki/Proposals_for_closing_projects/Closure_of_Tibetan_Wikibooks
             'ch': None, # http://meta.wikimedia.org/wiki/Proposals_for_closing_projects/Closure_of_Chamorro_Wikibooks
+            'co': None, # https://bugzilla.wikimedia.org/show_bug.cgi?id=28644
             'dk': 'da',
             'ga':None, # http://meta.wikimedia.org/wiki/Proposals_for_closing_projects/Closure_of_Gaeilge_Wikibooks
             'got': None, # http://meta.wikimedia.org/wiki/Proposals_for_closing_projects/Closure_of_Gothic_Wikibooks
             'zu': None, # https://bugzilla.wikimedia.org/show_bug.cgi?id=25425
         }
 
-        self.interwiki_putfirst = {
-            'en': alphabetic,
-            'fi': alphabetic,
-            'fr': alphabetic,
-            'he': ['en'],
-            'hu': ['en'],
-            'pl': alphabetic,
-            'simple': alphabetic
-        }
-        # Global bot allowed languages on http://meta.wikimedia.org/wiki/Bot_policy/Implementation#Current_implementation
-        self.cross_allowed = ['fa', 'fy', 'it', 'nl', 'ru', 'simple', 'zh']
-        # CentralAuth cross avaliable projects.
-        self.cross_projects = [
-            'wikipedia', 'wiktionary', 'wikiquote', 'wikiquote', 'wikinews', 'wikiversity',
-            'meta', 'mediawiki', 'test', 'incubator', 'commons', 'species'
-        ]
-
-    def version(self, code):
-        return '1.17wmf1'
-
     def shared_image_repository(self, code):
         return ('commons', 'commons')

pywikibot/families/wikinews_family.py

 # -*- coding: utf-8  -*-
 from pywikibot import family
 
-__version__ = '$Id: wikinews_family.py 9156 2011-04-10 11:32:53Z xqt $'
+__version__ = '$Id: wikinews_family.py 10229 2012-05-20 13:21:59Z xqt $'
 
 # The Wikimedia family that is known as Wikinews
 
         self.name = 'wikinews'
 
         self.languages_by_size = [
-            'sr', 'en', 'pl', 'de', 'fr', 'it', 'es', 'pt', 'zh', 'ja', 'sv',
-            'ru', 'ta', 'fi', 'cs', 'he', 'ro', 'bg', 'ar', 'hu', 'sd', 'fa',
-            'tr', 'ca', 'uk', 'sq', 'no', 'bs', 'el', 'th', 'ko', 'eo',
+            'sr', 'en', 'pl', 'fr', 'de', 'it', 'es', 'pt', 'zh', 'ru', 'ja',
+            'sv', 'ta', 'ca', 'el', 'cs', 'fa', 'ar', 'fi', 'ro', 'he', 'bg',
+            'tr', 'sd', 'sq', 'uk', 'no', 'bs', 'eo', 'ko',
         ]
 
-        for lang in self.languages_by_size:
-            self.langs[lang] = '%s.wikinews.org' % lang
+        self.langs = dict([(lang, '%s.wikinews.org' % lang) for lang in self.languages_by_size])
 
-        self.obsolete = {
-            'jp': 'ja',
-            'nb': 'no',
-            'nl': None, # https://bugzilla.wikimedia.org/show_bug.cgi?id=20325
-            'zh-tw': 'zh',
-            'zh-cn': 'zh'
-        }
+
+        # CentralAuth cross avaliable projects.
+        self.cross_projects = [
+            'wiktionary', 'wikibooks', 'wikiquote', 'wikisource', 'wikinews',
+            'wikiversity', 'meta', 'mediawiki', 'test', 'incubator', 'commons',
+            'species',
+        ]
+
+        # Global bot allowed languages on http://meta.wikimedia.org/wiki/Bot_policy/Implementation#Current_implementation
+        self.cross_allowed = ['ca', 'cs', 'en', 'fa',]
 
         # Which languages have a special order for putting interlanguage links,
         # and what order is it? If a language is not in interwiki_putfirst,
         # alphabetical order on language code is used. For languages that are in
         # interwiki_putfirst, interwiki_putfirst is checked first, and
-        # languages are put in the order given there. All other languages are put
-        # after those, in code-alphabetical order.
+        # languages are put in the order given there. All other languages are
+        # put after those, in code-alphabetical order.
         self.interwiki_putfirst = {
             'en': self.alphabetic,
             'fi': self.alphabetic,
             'pl': self.alphabetic,
         }
 
-        # Global bot allowed languages on http://meta.wikimedia.org/wiki/Bot_policy/Implementation#Current_implementation
-        self.cross_allowed = ['cs', 'hu',]
-        # CentralAuth cross avaliable projects.
-        self.cross_projects = [
-            'wikipedia', 'wiktionary', 'wikibooks', 'wikiquote', 'wikisource', 'wikiversity',
-            'meta', 'mediawiki', 'test', 'incubator', 'commons', 'species'
-        ]
+        self.obsolete = {
+            'hu': None, # https://bugzilla.wikimedia.org/show_bug.cgi?id=28342
+            'jp': 'ja',
+            'nb': 'no',
+            'nl': None, # https://bugzilla.wikimedia.org/show_bug.cgi?id=20325
+            'th': None, # https://bugzilla.wikimedia.org/show_bug.cgi?id=28341
+            'zh-tw': 'zh',
+            'zh-cn': 'zh'
+        }
 
     def code2encoding(self, code):
         return 'utf-8'
 
-    def version(self, code):
-        return '1.17wmf1'
-
     def shared_image_repository(self, code):
         return ('commons', 'commons')

pywikibot/families/wikipedia_family.py

 # -*- coding: utf-8  -*-
 from pywikibot import family
 
-__version__ = '$Id: wikipedia_family.py 9192 2011-04-23 15:37:11Z xqt $'
+__version__ = '$Id: wikipedia_family.py 10238 2012-05-24 09:47:03Z xqt $'
 
 # The Wikimedia family that is known as Wikipedia, the Free Encyclopedia
 
         self.name = 'wikipedia'
 
         self.languages_by_size = [
-            'en', 'de', 'fr', 'pl', 'it', 'es', 'ja', 'ru', 'nl', 'pt', 'sv',
-            'zh', 'ca', 'no', 'uk', 'fi', 'vi', 'cs', 'hu', 'tr', 'id', 'ko',
-            'ro', 'da', 'ar', 'eo', 'sr', 'lt', 'fa', 'sk', 'ms', 'vo', 'he',
-            'bg', 'sl', 'war', 'hr', 'hi', 'et', 'eu', 'gl', 'simple', 'new',
-            'th', 'nn', 'roa-rup', 'el', 'az', 'ht', 'la', 'tl', 'ka', 'te',
-            'mk', 'ceb', 'sh', 'pms', 'br', 'be-x-old', 'lv', 'mr', 'jv', 'lb',
-            'cy', 'sq', 'is', 'bs', 'ta', 'be', 'an', 'bpy', 'oc', 'bn', 'kk',
-            'io', 'sw', 'lmo', 'fy', 'gu', 'ml', 'af', 'nds', 'ur', 'scn', 'qu',
-            'ku', 'zh-yue', 'su', 'ast', 'nap', 'hy', 'ne', 'yo', 'bat-smg',
-            'cv', 'ga', 'wa', 'pnb', 'kn', 'tg', 'vec', 'roa-tara', 'tt', 'als',
-            'zh-min-nan', 'yi', 'gd', 'bug', 'os', 'uz', 'sah', 'pam', 'am',
-            'arz', 'mi', 'li', 'hsb', 'nah', 'sco', 'gan', 'mn', 'co', 'glk',
-            'my', 'ia', 'bcl', 'fo', 'fiu-vro', 'si', 'nds-nl', 'sa', 'vls',
-            'tk', 'bar', 'ckb', 'mg', 'gv', 'ilo', 'map-bms', 'dv', 'nrm',
-            'pag', 'diq', 'se', 'rm', 'mzn', 'wuu', 'fur', 'ug', 'lij', 'mt',
-            'bo', 'bh', 'mrj', 'hif', 'csb', 'ang', 'nov', 'lad', 'km', 'sc',
-            'zh-classical', 'cbk-zam', 'pi', 'ps', 'ksh', 'frp', 'hak', 'kw',
-            'udm', 'rue', 'mhr', 'nv', 'pa', 'szl', 'xal', 'so', 'rw', 'haw',
-            'ie', 'stq', 'pdc', 'kv', 'ln', 'krc', 'to', 'crh', 'pcd', 'ace',
-            'ky', 'ext', 'myv', 'gn', 'ba', 'ce', 'eml', 'arc', 'pap', 'ay',
-            'bjn', 'kl', 'jbo', 'frr', 'wo', 'tpi', 'kab', 'ty', 'srn', 'zea',
-            'gag', 'or', 'dsb', 'ab', 'koi', 'lo', 'ig', 'av', 'tet', 'mdf',
-            'kg', 'mwl', 'rmy', 'lbe', 'cu', 'ltg', 'kaa', 'kbd', 'sm', 'got',
-            'ks', 'bm', 'pfl', 'ik', 'sd', 'bi', 'as', 'iu', 'na', 'pih', 'pnt',
-            'ss', 'chr', 'cdo', 'ha', 'ee', 'ti', 'zu', 'bxr', 'om', 'za', 've',
-            'ts', 'rn', 'sg', 'dz', 'cr', 'tum', 'ch', 'lg', 'fj', 'ny', 'st',
-            'xh', 'ff', 'tn', 'ki', 'sn', 'chy', 'ak', 'tw',
+            'en', 'de', 'fr', 'nl', 'it', 'pl', 'es', 'ru', 'ja', 'pt', 'zh',
+            'sv', 'vi', 'uk', 'ca', 'no', 'fi', 'cs', 'hu', 'ko', 'fa', 'id',
+            'tr', 'ro', 'ar', 'sk', 'eo', 'da', 'sr', 'lt', 'eu', 'ms', 'sl',
+            'he', 'bg', 'kk', 'vo', 'war', 'hr', 'hi', 'et', 'az', 'gl', 'nn',
+            'simple', 'la', 'th', 'el', 'new', 'roa-rup', 'oc', 'sh', 'ka',
+            'mk', 'tl', 'ht', 'pms', 'te', 'ta', 'be-x-old', 'ceb', 'br', 'be',
+            'lv', 'sq', 'jv', 'mg', 'cy', 'lb', 'mr', 'is', 'bs', 'yo', 'an',
+            'hy', 'bpy', 'fy', 'lmo', 'sw', 'ml', 'pnb', 'bn', 'io', 'af', 'gu',
+            'ne', 'zh-yue', 'nds', 'ast', 'ku', 'ur', 'scn', 'su', 'qu', 'diq',
+            'ba', 'tt', 'my', 'ga', 'cv', 'ia', 'nap', 'bat-smg', 'map-bms',
+            'wa', 'am', 'kn', 'als', 'bug', 'tg', 'gd', 'zh-min-nan', 'yi',
+            'vec', 'hif', 'roa-tara', 'sco', 'os', 'arz', 'nah', 'uz', 'sah',
+            'mzn', 'mn', 'sa', 'pam', 'hsb', 'mi', 'li', 'ky', 'si', 'co',
+            'gan', 'glk', 'ckb', 'bo', 'fo', 'bar', 'bcl', 'ilo', 'mrj',
+            'fiu-vro', 'nds-nl', 'tk', 'vls', 'se', 'gv', 'dv', 'rue', 'ps',
+            'nrm', 'pag', 'koi', 'pa', 'rm', 'km', 'kv', 'udm', 'csb', 'mhr',
+            'fur', 'mt', 'wuu', 'ug', 'lij', 'lad', 'pi', 'zea', 'sc', 'bh',
+            'zh-classical', 'nov', 'ksh', 'or', 'ang', 'kw', 'so', 'nv', 'hak',
+            'xmf', 'ay', 'frp', 'stq', 'frr', 'ext', 'szl', 'pcd', 'ie', 'gag',
+            'haw', 'xal', 'ln', 'rw', 'pdc', 'pfl', 'krc', 'crh', 'eml', 'ace',
+            'to', 'gn', 'ce', 'kl', 'arc', 'myv', 'dsb', 'bjn', 'pap', 'as',
+            'tpi', 'lbe', 'vep', 'wo', 'mdf', 'jbo', 'kab', 'av', 'cbk-zam',
+            'sn', 'ty', 'srn', 'lo', 'kbd', 'ab', 'lez', 'ltg', 'mwl', 'ig',
+            'na', 'kg', 'tet', 'za', 'kaa', 'nso', 'zu', 'rmy', 'cu', 'tn',
+            'chr', 'sm', 'bi', 'bm', 'iu', 'ik', 'pih', 'ss', 'sd', 'chy',
+            'got', 'pnt', 'cdo', 'ee', 'ha', 'ti', 'bxr', 'om', 'ks', 'ts',
+            've', 'sg', 'rn', 'dz', 'cr', 'ki', 'lg', 'ak', 'tum', 'fj', 'st',
+            'ch', 'ff', 'ny', 'xh', 'tw',
         ]
 
-        for lang in self.languages_by_size:
-            self.langs[lang] = '%s.wikipedia.org' % lang
+        self.langs = dict([(lang, '%s.wikipedia.org' % lang) for lang in self.languages_by_size])
+
 
         self.category_redirect_templates = {
             '_default': (),
-            'ar': (u"تحويل تصنيف",
-                   u"تحويلة تصنيف",
-                   u"Category redirect",
-                   u"تحويلة تصنيف",),
+            'ar': (u'تحويل تصنيف',
+                   u'تحويلة تصنيف',
+                   u'Category redirect',),
             'arz': (u'تحويل تصنيف',),
             'cs': (u'Zastaralá kategorie',),
             'da': (u'Kategoriomdirigering',),
-            'de': (u'Kategorieweiterleitung',),
-            'en': (u"Category redirect",
-                   u"Category redirect3",
+            # 'de' has removed its template
+            'en': (u'Category redirect',
+                   u'Category redirect3',
                    u"Categoryredirect",
-                   u"CR",
-                   u"Catredirect",
-                   u"Cat redirect",
-                   u"Catredir",
-                   u"Seecat",),
+                   u'CR',
+                   u'Catredirect',
+                   u'Cat redirect',
+                   u'Catredir',
+                   u'Seecat',),
             'es': (u'Categoría redirigida',),
             'eu': (u'Kategoria redirect',),
             'fa': (u'رده بهتر',
                    u'انتقال رده',
                    u'فیلم‌های امریکایی',),
             'fr': (u'Redirection de catégorie',),
+            'gv': (u'Aastiurey ronney',),
             'hi': (u'श्रेणीअनुप्रेषित',
                    u'Categoryredirect',),
             'hu': (u'Kat-redir',
             'id': (u'Alih kategori',
                    u'Alihkategori',),
             # 'it' has removed its template
-            # 'ja' is discussing to remove this template
-            'ja': (u"Category redirect",),
+            'ja': (u'Category redirect',),
             'ko': (u'분류 넘겨주기',),
             'mk': (u'Премести категорија',),
+            'ml': (u'Category redirect',),
             'ms': (u'Pengalihan kategori',
                    u'Categoryredirect',
                    u'Category redirect',),
             'mt': (u'Redirect kategorija',),
             # 'nl' has removed its template
-            'no': (u"Category redirect",
-                   u"Kategoriomdirigering",
-                   u"Kategori-omdirigering",),
+            'no': (u'Category redirect',
+                   u'Kategoriomdirigering',
+                   u'Kategori-omdirigering',),
             'pl': (u'Przekierowanie kategorii',
                    u'Category redirect',),
             'pt': (u'Redirecionamento de categoria',
                    u'CategoryRedirect',
                    u'Category redirect',
                    u'Catredirect',),
-            'simple': (u"Category redirect",
-                       u"Categoryredirect",
-                       u"Catredirect",),
+            'simple': (u'Category redirect',
+                       u'Categoryredirect',
+                       u'Catredirect',),
+            'sl': (u'Category redirect',),
             'sq': (u'Kategori e zhvendosur',
                    u'Category redirect',),
             'sv': (u'Kategoriomdirigering',
                     u'Kişi adları (anlam ayrımı)',
                     u'Yerleşim yerleri (anlam ayrımı)',
                     u'kısaltmalar (anlam ayrımı)', u'Coğrafya (anlam ayrımı)',
-                    u'Yerleşim yerleri (anlam ayrımı)'],
+                    u'Yerleşim yerleri (anlam ayrımı)', u'Sayılar (anlam ayrımı)',
+                    u"ABD'deki iller (anlam ayrımı)"],
             'vls': [u'Db', u'Dp', u'Dpintro'],
             'wo':  [u'Bokktekki'],
             'yi':  [u'באדייטען'],
             'ms':  u'Nyahkekaburan',
             'mt':  u'Diżambigwazzjoni',
             'nds': u'Mehrdüdig Begreep',
-            'nds-nl': u'Deurverwiespagina',
+            'nds-nl': u'Wikipedie:Deurverwiespagina',
             'nl':  u'Wikipedia:Doorverwijspagina',
             'nn':  u'Fleirtydingssider',
             'no':  u'Pekere',
             'zh-min-nan': u'Khu-pia̍t-ia̍h',
             }
 
+        # families that redirect their interlanguage links here.
+        self.interwiki_forwarded_from = [
+            'commons',
+            'incubator',
+            'meta',
+            'species',
+            'strategy',
+            'test',
+        ]
+
         # CentralAuth cross avaliable projects.
         self.cross_projects = [
-            'wiktionary', 'wikibooks', 'wikiquote', 'wikisource', 'wikinews', 'wikiversity',
-            'meta', 'mediawiki', 'test', 'incubator', 'commons', 'species',
+            'wiktionary', 'wikibooks', 'wikiquote', 'wikisource', 'wikinews',
+            'wikiversity', 'meta', 'mediawiki', 'test', 'incubator', 'commons',
+            'species',
         ]
+
         # Global bot allowed languages on
         # http://meta.wikimedia.org/wiki/Bot_policy/Implementation#Current_implementation
         self.cross_allowed = [
             'ab', 'ace', 'af', 'ak', 'als', 'am', 'an', 'ang', 'arc', 'arz',
-            'as', 'ast', 'av', 'ay', 'az', 'ba', 'bat-smg', 'bar', 'bcl',
-            'be-x-old', 'be', 'bg', 'bh', 'bi', 'bm', 'bo', 'bpy', 'bug', 'bxr',
-            'cbk-zam', 'cdo', 'ce', 'ceb', 'ch', 'chr', 'chy', 'ckb', 'co',
-            'crh', 'cr', 'csb', 'cu', 'cv', 'cy', 'diq', 'dsb', 'dz', 'ee',
-            'el', 'eml', 'eo', 'et', 'eu', 'ext', 'fa', 'ff', 'fj', 'fo', 'frp',
-            'frr', 'fur', 'ga', 'gan', 'gd', 'glk', 'gn', 'got', 'gu', 'gv',
-            'ha', 'hak', 'haw', 'hif', 'hi', 'hr', 'hsb', 'ht', 'hu', 'hy',
-            'ia', 'id', 'ie', 'ig', 'ik', 'ilo', 'iow', 'is', 'iu', 'ja', 'jbo',
-            'jv', 'kaa', 'kab', 'ka', 'kg', 'ki', 'kk', 'kl', 'km', 'kn', 'ko',
-            'koi', 'ks', 'ku', 'kv', 'kw', 'ky', 'lad', 'lb', 'lbe', 'lg', 'li',
-            'lij', 'lmo', 'ln', 'lo', 'lv', 'map-bms', 'mdf', 'mg', 'mhr', 'mi',
-            'mk', 'mn', 'mrj', 'ms', 'mt', 'mwl', 'myv', 'my', 'mzn', 'nah',
-            'na', 'nap', 'nds-nl', 'ne', 'new', 'ng', 'nl', 'nov', 'nrm', 'nv',
-            'ny', 'oc', 'om', 'or', 'os', 'pam', 'pap', 'pa', 'pag', 'pdc',
+            'as', 'ast', 'av', 'ay', 'az', 'ba', 'bar', 'bat-smg', 'bcl', 'be',
+            'be-x-old', 'bg', 'bh', 'bi', 'bjn', 'bm', 'bo', 'bpy', 'bug',
+            'bxr', 'ca', 'cbk-zam', 'cdo', 'ce', 'ceb', 'ch', 'chr', 'chy',
+            'ckb', 'co', 'cr', 'crh', 'csb', 'cu', 'cv', 'cy', 'da', 'diq',
+            'dsb', 'dz', 'ee', 'el', 'eml', 'en', 'eo', 'et', 'eu', 'ext', 'fa',
+            'ff', 'fi', 'fj', 'fo', 'frp', 'frr', 'fur', 'ga', 'gag', 'gan',
+            'gd', 'glk', 'gn', 'got', 'gu', 'gv', 'ha', 'hak', 'haw', 'he',
+            'hi', 'hif', 'hr', 'hsb', 'ht', 'hu', 'hy', 'ia', 'ie', 'ig', 'ik',
+            'ilo', 'io', 'iu', 'ja', 'jbo', 'jv', 'ka', 'kaa', 'kab', 'kg',
+            'ki', 'kk', 'kl', 'km', 'kn', 'ko', 'koi', 'krc', 'ks', 'ku', 'kv',
+            'kw', 'ky', 'la', 'lad', 'lb', 'lbe', 'lez', 'lg', 'li', 'lij',
+            'lmo', 'ln', 'lo', 'ltg', 'lv', 'map-bms', 'mdf', 'mg', 'mhr', 'mi',
+            'mk', 'ml', 'mn', 'mrj', 'ms', 'mwl', 'my', 'myv', 'mzn', 'na',
+            'nah', 'nap', 'nds-nl', 'ne', 'new', 'nl', 'no', 'nov', 'nrm', 'nv',
+            'ny', 'oc', 'om', 'or', 'os', 'pa', 'pag', 'pam', 'pap', 'pdc',
             'pfl', 'pi', 'pih', 'pms', 'pnb', 'pnt', 'ps', 'qu', 'rm', 'rmy',
-            'rn', 'roa-rup', 'roa-tara', 'rw', 'sah', 'sa', 'sc', 'scn', 'sco',
-            'sd', 'se', 'sg', 'sh', 'simple', 'si', 'sk', 'sm', 'sn', 'so',
-            'srn', 'stq', 'st', 'su', 'sw', 'szl', 'ta', 'te', 'tet', 'tg',
-            'th', 'ti', 'tk', 'tl', 'tn', 'to', 'tpi', 'ts', 'tt', 'tum', 'tw',
-            'ty', 'udm', 'ug', 'uz', 've', 'vls', 'wa', 'war', 'wo', 'wuu',
-            'xal', 'xh', 'yi', 'yo', 'za', 'zea', 'zh', 'zh-classical',
-            'zh-min-nan', 'zu',
+            'rn', 'roa-rup', 'roa-tara', 'ru', 'rue', 'rw', 'sa', 'sah', 'sc',
+            'scn', 'sco', 'sd', 'se', 'sg', 'sh', 'si', 'simple', 'sk', 'sm',
+            'sn', 'so', 'srn', 'ss', 'st', 'stq', 'su', 'sv', 'sw', 'szl', 'ta',
+            'te', 'tet', 'tg', 'th', 'ti', 'tk', 'tl', 'tn', 'to', 'tpi', 'tr',
+            'ts', 'tt', 'tum', 'tw', 'ty', 'udm', 'ug', 'uz', 've', 'vec',
+            'vep', 'vls', 'vo', 'wa', 'war', 'wo', 'xal', 'xh', 'yi', 'yo',
+            'za', 'zea', 'zh', 'zh-classical', 'zh-min-nan', 'zh-yue', 'zu',
         ]
 
         # On most Wikipedias page names must start with a capital letter,
         # but some languages don't use this.
         self.nocapitalize = ['jbo',]
 
-        self.alphabetic_latin = [
-            'ace', 'af', 'ak', 'als', 'am', 'ang', 'ab', 'ar', 'an', 'arc',
-            'roa-rup', 'frp', 'arz', 'as', 'ast', 'gn', 'av', 'ay', 'az', 'bjn',
-            'id', 'ms', 'bg', 'bm', 'zh-min-nan', 'nan', 'map-bms', 'jv', 'su',
-            'ba', 'be', 'be-x-old', 'bh', 'bcl', 'bi', 'bn', 'bo', 'bar', 'bs',
-            'bpy', 'br', 'bug', 'bxr', 'ca', 'ceb', 'ch', 'cbk-zam', 'sn',
-            'tum', 'ny', 'cho', 'chr', 'co', 'cy', 'cv', 'cs', 'da', 'dk',
-            'pdc', 'de', 'nv', 'dsb', 'na', 'dv', 'dz', 'mh', 'et', 'el', 'eml',
-            'en', 'myv', 'es', 'eo', 'ext', 'eu', 'ee', 'fa', 'hif', 'fo', 'fr',
-            'fy', 'ff', 'fur', 'ga', 'gv', 'sm', 'gag', 'gd', 'gl', 'gan', 'ki',
-            'glk', 'got', 'gu', 'ha', 'hak', 'xal', 'haw', 'he', 'hi', 'ho',
-            'hsb', 'hr', 'hy', 'io', 'ig', 'ii', 'ilo', 'ia', 'ie', 'iu', 'ik',
-            'os', 'xh', 'zu', 'is', 'it', 'ja', 'ka', 'kl', 'kr', 'pam', 'kbd',
-            'krc', 'csb', 'kk', 'kw', 'rw', 'ky', 'mrj', 'rn', 'sw', 'km', 'kn',
-            'ko', 'kv', 'kg', 'ht', 'ks', 'ku', 'kj', 'lad', 'lbe', 'la', 'ltg',
-            'lv', 'to', 'lb', 'lt', 'lij', 'li', 'ln', 'lo', 'jbo', 'lg', 'lmo',
-            'hu', 'mk', 'mg', 'mt', 'mi', 'cdo', 'mwl', 'ml', 'mdf', 'mo', 'mn',
-            'mr', 'mus', 'my', 'mzn', 'nah', 'fj', 'ne', 'nl', 'nds-nl', 'cr',
-            'new', 'nap', 'ce', 'frr', 'pih', 'no', 'nb', 'nn', 'nrm', 'nov',
-            'oc', 'mhr', 'or', 'om', 'ng', 'hz', 'uz', 'pa', 'pfl', 'pag',
-            'pap', 'koi', 'pi', 'pcd', 'pms', 'nds', 'pnb', 'pl', 'pt', 'pnt',
-            'ps', 'aa', 'kaa', 'crh', 'ty', 'ksh', 'ro', 'rmy', 'rm', 'qu',
-            'ru', 'rue', 'sa', 'sah', 'se', 'sg', 'sc', 'sco', 'sd', 'stq',
-            'st', 'tn', 'sq', 'si', 'scn', 'simple', 'ss', 'sk', 'sl', 'cu',
-            'szl', 'so', 'ckb', 'srn', 'sr', 'sh', 'fi', 'sv', 'ta', 'tl',
-            'kab', 'roa-tara', 'tt', 'te', 'tet', 'th', 'ti', 'vi', 'tg',
-            'tokipona', 'tp', 'tpi', 'chy', 've', 'tr', 'tk', 'tw', 'udm', 'uk',
-            'ur', 'ug', 'za', 'vec', 'vo', 'fiu-vro', 'wa', 'vls', 'war', 'wo',
-            'wuu', 'ts', 'yi', 'yo', 'diq', 'zea', 'zh', 'zh-tw', 'zh-cn',
-            'zh-classical', 'zh-yue', 'bat-smg',
-        ]
-
         # Which languages have a special order for putting interlanguage links,
         # and what order is it? If a language is not in interwiki_putfirst,
         # alphabetical order on language code is used. For languages that are in
         # languages are put in the order given there. All other languages are
         # put after those, in code-alphabetical order.
 
+        self.alphabetic_sr = [
+            'ace', 'kbd', 'af', 'ak', 'als', 'am', 'ang', 'ab', 'ar', 'an',
+            'arc', 'roa-rup', 'frp', 'arz', 'as', 'ast', 'gn', 'av', 'ay', 'az',
+            'bjn', 'id', 'ms', 'bg', 'bm', 'zh-min-nan', 'nan', 'map-bms', 'jv',
+            'su', 'ba', 'be', 'be-x-old', 'bh', 'bcl', 'bi', 'bn', 'bo', 'bar',
+            'bs', 'bpy', 'br', 'bug', 'bxr', 'ca', 'ceb', 'ch', 'cbk-zam', 'sn',
+            'tum', 'ny', 'cho', 'chr', 'co', 'cy', 'cv', 'cs', 'da', 'dk',
+            'pdc', 'de', 'nv', 'dsb', 'na', 'dv', 'dz', 'mh', 'et', 'el', 'eml',
+            'en', 'myv', 'es', 'eo', 'ext', 'eu', 'ee', 'fa', 'hif', 'fo', 'fr',
+            'fy', 'ff', 'fur', 'ga', 'gv', 'sm', 'gag', 'gd', 'gl', 'gan', 'ki',
+            'glk', 'got', 'gu', 'ha', 'hak', 'xal', 'haw', 'he', 'hi', 'ho',
+            'hsb', 'hr', 'hy', 'io', 'ig', 'ii', 'ilo', 'ia', 'ie', 'iu', 'ik',
+            'os', 'xh', 'zu', 'is', 'it', 'ja', 'ka', 'kl', 'kr', 'pam', 'krc',
+            'csb', 'kk', 'kw', 'rw', 'ky', 'mrj', 'rn', 'sw', 'km', 'kn', 'ko',
+            'kv', 'kg', 'ht', 'ks', 'ku', 'kj', 'lad', 'lbe', 'la', 'ltg', 'lv',
+            'to', 'lb', 'lez', 'lt', 'lij', 'li', 'ln', 'lo', 'jbo', 'lg',
+            'lmo', 'hu', 'mk', 'mg', 'mt', 'mi', 'cdo', 'mwl', 'ml', 'mdf',
+            'mo', 'mn', 'mr', 'mus', 'my', 'mzn', 'nah', 'fj', 'ne', 'nl',
+            'nds-nl', 'cr', 'new', 'nap', 'ce', 'frr', 'pih', 'no', 'nb', 'nn',
+            'nrm', 'nov', 'oc', 'mhr', 'or', 'om', 'ng', 'hz', 'uz', 'pa',
+            'pfl', 'pag', 'pap', 'koi', 'pi', 'pcd', 'pms', 'nds', 'pnb', 'pl',
+            'pt', 'pnt', 'ps', 'aa', 'kaa', 'crh', 'ty', 'ksh', 'ro', 'rmy',
+            'rm', 'qu', 'ru', 'rue', 'sa', 'sah', 'se', 'sg', 'sc', 'sco', 'sd',
+            'stq', 'st', 'nso', 'tn', 'sq', 'si', 'scn', 'simple', 'ss', 'sk',
+            'sl', 'cu', 'szl', 'so', 'ckb', 'srn', 'sr', 'sh', 'fi', 'sv', 'ta',
+            'shi', 'tl', 'kab', 'roa-tara', 'tt', 'te', 'tet', 'th', 'ti', 'vi',
+            'tg', 'tokipona', 'tp', 'tpi', 'chy', 've', 'tr', 'tk', 'tw', 'udm',
+            'uk', 'ur', 'ug', 'za', 'vec', 'vep', 'vo', 'fiu-vro', 'wa', 'vls',
+            'war', 'wo', 'wuu', 'ts', 'xmf', 'yi', 'yo', 'diq', 'zea', 'zh',
+            'zh-tw', 'zh-cn', 'zh-classical', 'zh-yue', 'bat-smg',
+        ]
+
         self.interwiki_putfirst = {
             'be-x-old': self.alphabetic,
             'en': self.alphabetic,
             'lb': self.alphabetic,
             'mk': self.alphabetic,
             'ms': self.alphabetic_revised,
-            'nds': ['nds-nl', 'pdt'], # Note: as of 2008-02-24, pdt:
-            'nds-nl': ['nds', 'pdt'], # (Plautdietsch) is still in the Incubator.
-            'nn': ['no', 'nb', 'sv', 'da'] + self.alphabetic,
+            'nds': ['nds-nl'],
+            'nds-nl': ['nds'],
+            'nn': ['no', 'sv', 'da'] + self.alphabetic,
             'no': self.alphabetic,
+            'nv': ['en', 'es'] + self.alphabetic,
             'pdc': ['de', 'en'],
             'pl': self.alphabetic,
             'simple': self.alphabetic,
-            'sr': self.alphabetic_latin,
+            'sr': self.alphabetic_sr,
             'sv': self.alphabetic,
             'te': ['en', 'hi', 'kn', 'ta', 'ml'],
             'ur': ['ar', 'fa', 'en'] + self.alphabetic,
             'kr': None, # http://meta.wikimedia.org/wiki/Proposals_for_closing_projects/Closure_of_Kanuri_Wikipedia
             'mh': None, # http://meta.wikimedia.org/wiki/Proposals_for_closing_projects/Closure_of_Marshallese_Wikipedia
             'minnan': 'zh-min-nan',
-            'mo': None, # http://meta.wikimedia.org/wiki/Proposals_for_closing_projects/Closure_of_Moldovan_Wikipedia
+            'mo': 'ro', # http://meta.wikimedia.org/wiki/Proposals_for_closing_projects/Closure_of_Moldovan_Wikipedia
             'mus': None, # http://meta.wikimedia.org/wiki/Proposals_for_closing_projects/Closure_of_Muscogee_Wikipedia
             'nb': 'no',
             'ng': None, #(not reachable) http://meta.wikimedia.org/wiki/Inactive_wikis
         else:
             return self.known_families
 
-    def version(self, code):
-        return '1.17wmf1'
-
     def dbName(self, code):
         # returns the name of the MySQL database
         # for historic reasons, the databases are called xxwiki instead of

pywikibot/families/wikiquote_family.py

 # -*- coding: utf-8  -*-
 from pywikibot import family
 
-__version__ = '$Id: wikiquote_family.py 9192 2011-04-23 15:37:11Z xqt $'
+__version__ = '$Id: wikiquote_family.py 10229 2012-05-20 13:21:59Z xqt $'
 
 # The Wikimedia family that is known as Wikiquote
 
         self.name = 'wikiquote'
 
         self.languages_by_size = [
-            'en', 'pl', 'it', 'de', 'ru', 'pt', 'sk', 'es', 'bg', 'bs', 'cs',
-            'tr', 'fr', 'sl', 'he', 'eo', 'lt', 'el', 'id', 'zh', 'fa', 'nl',
-            'hu', 'uk', 'fi', 'sv', 'no', 'nn', 'ja', 'hy', 'et', 'ca', 'ar',
-            'li', 'cy', 'hr', 'ka', 'ko', 'sr', 'gl', 'ro', 'ku', 'az', 'ml',
-            'is', 'th', 'te', 'da', 'eu', 'af', 'sq', 'vi', 'hi', 'la', 'br',
-            'be', 'mr', 'ast', 'uz', 'ta', 'ang', 'ur', 'zh-min-nan', 'gu',
-            'su', 'lb', 'kn', 'wo', 'ky', 'am', 'co',
+            'en', 'pl', 'it', 'fr', 'ru', 'de', 'pt', 'es', 'sk', 'bg', 'cs',
+            'bs', 'tr', 'sl', 'he', 'eo', 'lt', 'el', 'id', 'uk', 'zh', 'fa',
+            'hu', 'nl', 'fi', 'sv', 'li', 'no', 'nn', 'ja', 'az', 'hy', 'ca',
+            'et', 'ar', 'hr', 'cy', 'ka', 'ko', 'ml', 'gl', 'sr', 'ro', 'ku',
+            'te', 'th', 'is', 'eu', 'da', 'af', 'sq', 'vi', 'ta', 'hi', 'la',
+            'br', 'be', 'mr', 'uz', 'ur', 'zh-min-nan', 'gu', 'su', 'lb', 'kn',
+            'wo', 'ky', 'co', 'am',
         ]
 
-        for lang in self.languages_by_size:
-            self.langs[lang] = '%s.wikiquote.org' % lang
+        self.langs = dict([(lang, '%s.wikiquote.org' % lang) for lang in self.languages_by_size])
+
 
         self.disambiguationTemplates = {
             '_default': [],
 
         # Global bot allowed languages on http://meta.wikimedia.org/wiki/Bot_policy/Implementation#Current_implementation
         self.cross_allowed = [
-            'af','am','ang','ar','ast','az','bg','bs','ca','cs','da','el','es','eu','fa','fr','fi','he','hu','hy','id','it',
-            'ka','ko','la','lt','nl','nn','no','pt','ro','simple','sv','vi','zh'
+            'af', 'am', 'ar', 'az', 'be', 'bg', 'br', 'bs', 'ca', 'cs', 'da',
+            'el', 'eo', 'es', 'et', 'eu', 'fa', 'fi', 'fr', 'gl', 'he', 'hi',
+            'hu', 'hy', 'id', 'is', 'it', 'ja', 'ka', 'kn', 'ku', 'la', 'li',
+            'lt', 'ml', 'nl', 'nn', 'no', 'pt', 'ro', 'ru', 'sk', 'sl', 'sq',
+            'sr', 'su', 'sv', 'te', 'tr', 'uk', 'uz', 'vi', 'zh', 'zh-min-nan',
         ]
+
         # CentralAuth cross avaliable projects.
         self.cross_projects = [
-            'wikipedia', 'wiktionary', 'wikibooks', 'wikisource', 'wikinews', 'wikiversity',
-            'meta', 'mediawiki', 'test', 'incubator', 'commons', 'species'
+            'wiktionary', 'wikibooks', 'wikiquote', 'wikisource', 'wikinews',
+            'wikiversity', 'meta', 'mediawiki', 'test', 'incubator', 'commons',
+            'species',
         ]
+
         # Which languages have a special order for putting interlanguage links,
         # and what order is it? If a language is not in interwiki_putfirst,
         # alphabetical order on language code is used. For languages that are in
         # interwiki_putfirst, interwiki_putfirst is checked first, and
-        # languages are put in the order given there. All other languages are put
-        # after those, in code-alphabetical order.
-
-        alphabetic = ['af','am','ang','ar','roa-rup','ast','az','bn',
-                    'zh-min-nan','bg','be','bs','br','ca','chr','co','cs','cy',
-                    'da','de','als','et','el','en','es','eo','eu','fa','fr',
-                    'fy','ga','gv','gu','gd','gl','ko','hy','hi','hr','io',
-                    'id','ia','is','it','he','jv','kn','ka','ks','csb','kk',
-                    'ky','sw','ku','la','lb','lt','li','hu','mk','mg','ml',
-                    'mi','mr','zh-cfr','mn','nah','na','nl','ja','no','nb',
-                    'nn','oc','om','nds','uz','pl','pt','ro','ru','sa','st',
-                    'sq','si','simple','sk','sl','sr','su','fi','sv','ta','tt',
-                    'te','th','ur','vi','tpi','tr','uk','vo','yi','yo','wo',
-                    'za','zh','zh-cn','zh-tw']
-
+        # languages are put in the order given there. All other languages are
+        # put after those, in code-alphabetical order.
         self.interwiki_putfirst = {
-            'en': alphabetic,
-            'fi': alphabetic,
-            'fr': alphabetic,
+            'en': self.alphabetic,
+            'fi': self.alphabetic,
+            'fr': self.alphabetic,
             'he': ['en'],
             'hu': ['en'],
-            'pl': alphabetic,
-            'simple': alphabetic,
-            'pt': alphabetic,
+            'pl': self.alphabetic,
+            'simple': self.alphabetic,
+            'pt': self.alphabetic,
         }
 
         self.obsolete = {
             'als': None, # http://meta.wikimedia.org/wiki/Proposals_for_closing_projects/Closure_of_Alemannic_Wikiquote
+            'ang': None, # https://bugzilla.wikimedia.org/show_bug.cgi?id=29150
+            'ast': None, # https://bugzilla.wikimedia.org/show_bug.cgi?id=28964
             'bm': None, # http://meta.wikimedia.org/wiki/Proposals_for_closing_projects/Closure_of_Bambara_Wikiquote
             'cr': None, # http://meta.wikimedia.org/wiki/Proposals_for_closing_projects/Closure_of_Nehiyaw_Wikiquote
             'dk': 'da',
             'zh-cn': 'zh'
         }
 
-    def version(self, code):
-        return '1.17wmf1'
-
     def code2encodings(self, code):
         """
         Return a list of historical encodings for a specific language wikipedia

pywikibot/families/wikisource_family.py

 # -*- coding: utf-8  -*-