def post():
    parser = optparse.OptionParser(usage="%prog OPTIONS")
    parser.add_option("-i", "--input", help="ZIP file to POST")
    parser.add_option('-l', '--logfile',
        help='log output to LOGFILE',
    )
    parser.add_option("-p", "--posturl", help="HTTP POST ZIP file to POSTURL")
    parser.add_option("-g", "--getposturl",
        help='get POST URL from PediaPress.com, open upload page in webbrowser',
        action='store_true',
    )
    parser.add_option("-d", "--daemonize", action="store_true",
        help='become a daemon process as soon as possible')
    parser.add_option('--pid-file',
        help='write PID of daemonized process to this file',
    )
    options, args = parser.parse_args()
    
    use_help = 'Use --help for usage information.'
    if not options.input:
        parser.error('Specify --input.\n' + use_help)
    if (options.posturl and options.getposturl)\
        or (not options.posturl and not options.getposturl):
        parser.error('Specify either --posturl or --getposturl.\n' + use_help)
    if options.posturl:
        from mwlib.podclient import PODClient
        podclient = PODClient(options.posturl)
    elif options.getposturl:
        import webbrowser
        from mwlib.podclient import podclient_from_serviceurl
        podclient = podclient_from_serviceurl('http://pediapress.com/api/collections/')
        webbrowser.open(podclient.redirecturl)
    
    from mwlib import utils
    from mwlib.status import Status
    
    if options.logfile:
        utils.start_logging(options.logfile)
    
    if options.daemonize:
        utils.daemonize()
    if options.pid_file:
        open(options.pid_file, 'wb').write('%d\n' % os.getpid())
    
    
    status = Status(podclient=podclient)
    
    try:
        try:
            status(status='uploading', progress=0)
            podclient.post_zipfile(options.input)
            status(status='finished', progress=100)
        except Exception, e:
            status(status='error')
            raise
    finally:
        if options.pid_file:
            utils.safe_unlink(options.pid_file)
def watch():
    parser = optparse.OptionParser(usage="%prog [OPTIONS]")
    parser.add_option('-l', '--logfile',
        help='log output to LOGFILE',
    )
    parser.add_option('-d', '--daemonize',
        action='store_true',
        help='become daemon as soon as possible',
    )
    parser.add_option('--pid-file',
        help='write PID of daemonized process to this file',
    )
    parser.add_option('-q', '--queue-dir',
        help='queue directory, where new job files are written to (default: /var/cache/mw-watch/q/)',
        default='/var/cache/mw-watch/q/',
    )
    parser.add_option('-p', '--processing-dir',
        help='processing directory, where active job files are moved to (must be on same filesystem as --queue-dir, default: /var/cache/mw-watch/p/)',
        default='/var/cache/mw-watch/p/',
    )
    parser.add_option('-n', '--num-jobs',
        help='maximum number of simulataneous jobs (default: 5)',
        default='5',
    )
    options, args = parser.parse_args()
    
    try:
        options.num_jobs = int(options.num_jobs)
    except ValueError:
        parser.error('--num-jobs value must be an integer')
    
    from mwlib import filequeue, utils
    
    if options.logfile:
        utils.start_logging(options.logfile)
    
    if options.daemonize:
        utils.daemonize()
    if options.pid_file:
        open(options.pid_file, 'wb').write('%d\n' % os.getpid())
    
    poller = filequeue.FileJobPoller(
        queue_dir=options.queue_dir,
        processing_dir=options.processing_dir,
        max_num_jobs=options.num_jobs,
    ).run_forever()
    
    if options.pid_file:
        utils.safe_unlink(options.pid_file)
Пример #3
0
    from mwlib import utils, wiki

    filename = None
    status = None
    try:
        env = parser.makewiki()
        assert env.metabook, "no metabook"

        from mwlib.status import Status
        status = Status(options.status_file,
                        podclient=podclient,
                        progress_range=(1, 90))
        status(progress=0)
        output = options.output

        make_zip(output,
                 options,
                 env.metabook,
                 podclient=podclient,
                 status=status)

    except Exception, e:
        if status:
            status(status='error')
        raise
    finally:
        if options.output is None and filename is not None:
            print 'removing %r' % filename
            utils.safe_unlink(filename)
Пример #4
0
def make_zip_file(
    output,
    env,
    status=None,
    num_threads=10,
    imagesize=800,
):
    if status is None:
        status = lambda **kwargs: None

    if output is None:
        fd, output = tempfile.mkstemp(suffix='.zip')
        os.close(fd)

    fd, tmpzip = tempfile.mkstemp(suffix='.zip', dir=os.path.dirname(output))
    os.close(fd)
    zf = zipfile.ZipFile(tmpzip, 'w')

    try:
        articles = metabook.get_item_list(env.metabook, filter_type='article')

        if num_threads > 0:
            z = ThreadedZipCreator(
                zf,
                imagesize=imagesize,
                num_threads=num_threads,
                status=status,
                num_articles=len(articles),
            )
        else:
            z = ZipCreator(
                zf,
                imagesize=imagesize,
                status=status,
                num_articles=len(articles),
            )

        # if articles:
        #     class IncProgress(object):
        #         inc = 100./len(articles)
        #         p = 0
        #         def __call__(self, title):
        #             self.p += self.inc
        #             status(progress=int(self.p), article=title)
        #     inc_progress = IncProgress()
        # else:
        #     inc_progress = None

        for item in articles:
            d = mwapidb.parse_article_url(item['title'].encode('utf-8'))
            if d is not None:
                item['title'] = d['title']
                item['revision'] = d['revision']
                wikidb = mwapidb.WikiDB(api_helper=d['api_helper'])
                imagedb = mwapidb.ImageDB(api_helper=d['api_helper'])
            else:
                wikidb = env.wiki
                imagedb = env.images
            z.addArticle(
                item['title'],
                revision=item.get('revision', None),
                wikidb=wikidb,
                imagedb=imagedb,
            )

        for license in env.get_licenses():
            z.parseArticle(
                title=license['title'],
                raw=license['wikitext'],
                wikidb=env.wiki,
                imagedb=env.images,
            )

        z.join()
        z.addObject('metabook.json', json.dumps(env.metabook))
        zf.close()
        if os.path.exists(output):  # Windows...
            os.unlink(output)
        os.rename(tmpzip, output)

        if env.images and hasattr(env.images, 'clear'):
            env.images.clear()

        status(progress=100)
        return output
    finally:
        if os.path.exists(tmpzip):
            utils.safe_unlink(tmpzip)
def buildzip():
    from mwlib.options import OptionParser

    parser = OptionParser()
    parser.add_option("-o", "--output", help="write output to OUTPUT")
    parser.add_option("-p", "--posturl", help="http post to POSTURL (directly)")
    parser.add_option("-g", "--getposturl",
        help='get POST URL from PediaPress.com, open upload page in webbrowser',
        action='store_true',
    )
    options, args = parser.parse_args()
    
    use_help = 'Use --help for usage information.'
    if parser.metabook is None and options.collectionpage is None:
        parser.error('Neither --metabook nor, --collectionpage or arguments specified.\n' + use_help)
    if options.posturl and options.getposturl:
        parser.error('Specify either --posturl or --getposturl.\n' + use_help)
    if not options.posturl and not options.getposturl and not options.output:
        parser.error('Neither --output, nor --posturl or --getposturl specified.\n' + use_help)
    if options.posturl:
        from mwlib.podclient import PODClient
        podclient = PODClient(options.posturl)
    elif options.getposturl:
        import webbrowser
        from mwlib.podclient import podclient_from_serviceurl
        podclient = podclient_from_serviceurl('http://pediapress.com/api/collections/')
        webbrowser.open(podclient.redirecturl)
    else:
        podclient = None
    
    from mwlib import utils
    
    if options.daemonize:
        utils.daemonize()
    if options.pid_file:
        open(options.pid_file, 'wb').write('%d\n' % os.getpid())

    filename = None
    status = None
    try:
        try:
            env = parser.makewiki()
        
            from mwlib.status import Status
            from mwlib import zipcreator
        
            status = Status(podclient=podclient, progress_range=(1, 90))
            status(progress=0)
            
            filename = zipcreator.make_zip_file(options.output, env,
                status=status,
                num_threads=options.num_threads,
                imagesize=options.imagesize,
            )
            
            status = Status(podclient=podclient, progress_range=(91, 100))
            if podclient:
                status(status='uploading', progress=0)
                podclient.post_zipfile(filename)
            
            status(status='finished', progress=100)
        except Exception, e:
            if status:
                status(status='error')
            raise
    finally:
        if options.output is None and filename is not None:
            print 'removing %r' % filename
            utils.safe_unlink(filename)
        if options.pid_file:
            utils.safe_unlink(options.pid_file)
 response = {
     'collection_id': collection_id,
     'writer': writer,
     'is_cached': False,
 }
 
 pid_path = self.get_path(collection_id, self.pid_filename, writer)
 if os.path.exists(pid_path):
     log.info('mw-render already running for collection %r' % collection_id)
     return response
 
 output_path = self.get_path(collection_id, self.output_filename, writer)
 if os.path.exists(output_path):
     if force_render:
         log.info('removing rendered file %r (forced rendering)' % output_path)
         utils.safe_unlink(output_path)
     else:
         log.info('re-using rendered file %r' % output_path)
         response['is_cached'] = True
         return response
 
 error_path = self.get_path(collection_id, self.error_filename, writer)
 if os.path.exists(error_path):
     log.info('removing error file %r' % error_path)
     utils.safe_unlink(error_path)
     force_render = True
 
 status_path = self.get_path(collection_id, self.status_filename, writer)
 if os.path.exists(status_path):
     if force_render:
         log.info('removing status file %r (forced rendering)' % status_path)
Пример #7
0
def main():
    from gevent import monkey
    monkey.patch_all(thread=False)

    from mwlib.options import OptionParser
    from mwlib import conf

    parser = OptionParser()
    parser.add_option("-o", "--output", help="write output to OUTPUT")
    parser.add_option("-p", "--posturl", help="http post to POSTURL (directly)")
    parser.add_option("-g", "--getposturl",
                      help='get POST URL from PediaPress.com, open upload page in webbrowser',
                      action='count',
                      )
    parser.add_option('--keep-tmpfiles',
                      action='store_true',
                      default=False,
                      help="don't remove  temporary files like images",
                      )

    parser.add_option("-s", "--status-file",
                      help='write status/progress info to this file')

    options, args = parser.parse_args()
    conf.readrc()
    use_help = 'Use --help for usage information.'

    if parser.metabook is None and options.collectionpage is None:
        parser.error(
            'Neither --metabook nor, --collectionpage or arguments specified.\n' +
            use_help)
    if options.posturl and options.getposturl:
        parser.error('Specify either --posturl or --getposturl.\n' + use_help)
    if not options.posturl and not options.getposturl and not options.output:
        parser.error('Neither --output, nor --posturl or --getposturl specified.\n' + use_help)
    if options.posturl:
        from mwlib.podclient import PODClient
        podclient = PODClient(options.posturl)
    elif options.getposturl:
        if options.getposturl > 1:
            serviceurl = 'http://test.pediapress.com/api/collections/'
        else:
            serviceurl = 'http://pediapress.com/api/collections/'
        import webbrowser
        from mwlib.podclient import podclient_from_serviceurl
        podclient = podclient_from_serviceurl(serviceurl)
        pid = os.fork()
        if not pid:
            try:
                webbrowser.open(podclient.redirecturl)
            finally:
                os._exit(0)
        import time
        time.sleep(1)
        try:
            os.kill(pid, 9)
        except BaseException:
            pass

    else:
        podclient = None

    from mwlib import utils, wiki

    filename = None
    status = None
    try:
        env = parser.makewiki()
        assert env.metabook, "no metabook"

        from mwlib.status import Status
        status = Status(options.status_file, podclient=podclient, progress_range=(1, 90))
        status(progress=0)
        output = options.output

        make_zip(output, options, env.metabook, podclient=podclient, status=status)

    except Exception as e:
        if status:
            status(status='error')
        raise
    finally:
        if options.output is None and filename is not None:
            print 'removing %r' % filename
            utils.safe_unlink(filename)
Пример #8
0
def serve():
    from SocketServer import ForkingMixIn, ThreadingMixIn
    from wsgiref.simple_server import make_server, WSGIServer
    from flup.server import fcgi, fcgi_fork, scgi, scgi_fork

    class ForkingWSGIServer(ForkingMixIn, WSGIServer):
        pass

    class ThreadingWSGIServer(ThreadingMixIn, WSGIServer):
        pass

    proto2server = {
        'http': ForkingWSGIServer,
        'http_threaded': ThreadingWSGIServer,
        'fcgi': fcgi_fork.WSGIServer,
        'fcgi_threaded': fcgi.WSGIServer,
        'scgi': scgi_fork.WSGIServer,
        'scgi_threaded': scgi.WSGIServer,
    }

    parser = optparse.OptionParser(usage="%prog [OPTIONS]")
    parser.add_option(
        '-l',
        '--logfile',
        help='log output to LOGFILE',
    )
    parser.add_option(
        '-d',
        '--daemonize',
        action='store_true',
        help='become daemon as soon as possible',
    )
    parser.add_option(
        '--pid-file',
        help='write PID of daemonized process to this file',
    )
    parser.add_option(
        '-P',
        '--protocol',
        help='one of %s (default: http)' % ', '.join(proto2server.keys()),
        default='http',
    )
    parser.add_option(
        '-p',
        '--port',
        help='port to listen on (default: 8899)',
        default='8899',
    )
    parser.add_option(
        '-i',
        '--interface',
        help='interface to listen on (default: 0.0.0.0)',
        default='0.0.0.0',
    )
    parser.add_option(
        '--cache-dir',
        help='cache directory (default: /var/cache/mw-serve/)',
        default='/var/cache/mw-serve/',
    )
    parser.add_option(
        '--mwrender',
        help='(path to) mw-render executable',
        default='mw-render',
    )
    parser.add_option(
        '--mwrender-logfile',
        help='global logfile for mw-render',
        metavar='LOGFILE',
    )
    parser.add_option(
        '--mwzip',
        help='(path to) mw-zip executable',
        default='mw-zip',
    )
    parser.add_option(
        '--mwzip-logfile',
        help='global logfile for mw-zip',
        metavar='LOGFILE',
    )
    parser.add_option(
        '--mwpost',
        help='(path to) mw-post executable',
        default='mw-post',
    )
    parser.add_option(
        '--mwpost-logfile',
        help='global logfile for mw-post',
        metavar='LOGFILE',
    )
    parser.add_option(
        '-q',
        '--queue-dir',
        help='queue dir of mw-watch (if not specified, no queue is used)',
    )
    parser.add_option(
        '-m',
        '--method',
        help='prefork or threaded (default: prefork)',
        default='prefork',
    )
    parser.add_option(
        '--max-requests',
        help=
        'maximum number of requests a child process can handle before it is killed, irrelevant for --method=threaded (default: 0 = no limit)',
        default='0',
        metavar='NUM',
    )
    parser.add_option(
        '--min-spare',
        help='minimum number of spare processes/threads (default: 2)',
        default='2',
        metavar='NUM',
    )
    parser.add_option(
        '--max-spare',
        help='maximum number of spare processes/threads (default: 5)',
        default='5',
        metavar='NUM',
    )
    parser.add_option(
        '--max-children',
        help='maximum number of processes/threads (default: 50)',
        default='50',
        metavar='NUM',
    )
    parser.add_option(
        '--report-from-mail',
        help='sender of error mails (--report-recipient also needed)',
        metavar='EMAIL',
    )
    parser.add_option(
        '--report-recipient',
        help='recipient of error mails (--report-from-mail also needed)',
        metavar='EMAIL',
    )
    parser.add_option(
        '--clean-cache',
        help=
        'clean cache files that have not been touched for at least HOURS hours and exit',
        metavar='HOURS',
    )
    options, args = parser.parse_args()

    if options.clean_cache:
        try:
            options.clean_cache = int(options.clean_cache)
        except ValueError:
            parser.error('--clean-cache value must be an integer')
        from mwlib.serve import clean_cache
        clean_cache(options.clean_cache * 60 * 60, cache_dir=options.cache_dir)
        return

    if options.protocol not in proto2server:
        parser.error('unsupported protocol (must be one of %s)' %
                     (', '.join(proto2server.keys()), ))

    def to_int(opt_name):
        try:
            setattr(options, opt_name, int(getattr(options, opt_name)))
        except ValueError:
            parser.error('--%s value must be an integer' %
                         opt_name.replace('_', '-'))

    to_int('port')
    to_int('max_requests')
    to_int('min_spare')
    to_int('max_spare')
    to_int('max_children')

    if options.method not in ('prefork', 'threaded'):
        parser.error(
            'the only supported values for --method are "prefork" and "threaded"'
        )

    from mwlib import serve, log, utils

    log = log.Log('mw-serve')

    if options.logfile:
        utils.start_logging(options.logfile)

    if options.daemonize:
        utils.daemonize()
    if options.pid_file:
        open(options.pid_file, 'wb').write('%d\n' % os.getpid())

    if options.method == 'threaded':
        options.protocol += '_threaded'
        flup_kwargs = {
            'maxThreads': options.max_children,
        }
    else:
        flup_kwargs = {
            'maxChildren': options.max_children,
            'maxRequests': options.max_requests,
        }

    log.info("serving %s on %s:%s" %
             (options.protocol, options.interface, options.port))

    if options.report_recipient and options.report_from_mail:
        report_from_mail = options.report_from_mail.encode('utf-8')
        report_recipients = [options.report_recipient.encode('utf-8')]
    else:
        report_from_mail = None
        report_recipients = None

    app = serve.Application(
        cache_dir=options.cache_dir,
        mwrender_cmd=options.mwrender,
        mwrender_logfile=options.mwrender_logfile,
        mwzip_cmd=options.mwzip,
        mwzip_logfile=options.mwzip_logfile,
        mwpost_cmd=options.mwpost,
        mwpost_logfile=options.mwpost_logfile,
        queue_dir=options.queue_dir,
        report_from_mail=report_from_mail,
        report_recipients=report_recipients,
    )
    if options.protocol.startswith('http'):
        server = make_server(
            options.interface,
            options.port,
            app,
            server_class=proto2server[options.protocol],
        )
        try:
            server.serve_forever()
        except KeyboardInterrupt:
            pass
    else:
        serverclass = proto2server[options.protocol]
        serverclass(app,
                    bindAddress=(options.interface, options.port),
                    minSpare=options.min_spare,
                    maxSpare=options.max_spare,
                    **flup_kwargs).run()

    if options.pid_file:
        utils.safe_unlink(options.pid_file)

    log.info('exit.')
Пример #9
0
def watch():
    parser = optparse.OptionParser(usage="%prog [OPTIONS]")
    parser.add_option(
        '-l',
        '--logfile',
        help='log output to LOGFILE',
    )
    parser.add_option(
        '-d',
        '--daemonize',
        action='store_true',
        help='become daemon as soon as possible',
    )
    parser.add_option(
        '--pid-file',
        help='write PID of daemonized process to this file',
    )
    parser.add_option(
        '-q',
        '--queue-dir',
        help=
        'queue directory, where new job files are written to (default: /var/cache/mw-watch/q/)',
        default='/var/cache/mw-watch/q/',
    )
    parser.add_option(
        '-p',
        '--processing-dir',
        help=
        'processing directory, where active job files are moved to (must be on same filesystem as --queue-dir, default: /var/cache/mw-watch/p/)',
        default='/var/cache/mw-watch/p/',
    )
    parser.add_option(
        '-n',
        '--num-jobs',
        help='maximum number of simulataneous jobs (default: 5)',
        default='5',
    )
    options, args = parser.parse_args()

    try:
        options.num_jobs = int(options.num_jobs)
    except ValueError:
        parser.error('--num-jobs value must be an integer')

    from mwlib import filequeue, utils

    if options.logfile:
        utils.start_logging(options.logfile)

    if options.daemonize:
        utils.daemonize()
    if options.pid_file:
        open(options.pid_file, 'wb').write('%d\n' % os.getpid())

    poller = filequeue.FileJobPoller(
        queue_dir=options.queue_dir,
        processing_dir=options.processing_dir,
        max_num_jobs=options.num_jobs,
    ).run_forever()

    if options.pid_file:
        utils.safe_unlink(options.pid_file)
Пример #10
0
            fd, tmpout = tempfile.mkstemp(dir=os.path.dirname(options.output))
            os.close(fd)
            writer(env,
                   output=tmpout,
                   status_callback=status,
                   **writer_options)
            os.rename(tmpout, options.output)
            kwargs = {}
            if hasattr(writer, 'content_type'):
                kwargs['content_type'] = writer.content_type
            if hasattr(writer, 'file_extension'):
                kwargs['file_extension'] = writer.file_extension
            status(status='finished', progress=100, **kwargs)
            if options.keep_zip is None and zip_filename is not None:
                utils.safe_unlink(zip_filename)
        except Exception, e:
            status(status='error')
            if options.error_file:
                fd, tmpfile = tempfile.mkstemp(
                    dir=os.path.dirname(options.error_file))
                f = os.fdopen(fd, 'wb')
                if isinstance(e, WriterError) or isinstance(e, MWAPIError):
                    f.write(str(e))
                else:
                    traceback.print_exc(file=f)
                f.close()
                os.rename(tmpfile, options.error_file)
            raise
    finally:
        if env is not None and env.images is not None:
Пример #11
0
def post():
    parser = optparse.OptionParser(usage="%prog OPTIONS")
    parser.add_option("-i", "--input", help="ZIP file to POST")
    parser.add_option(
        '-l',
        '--logfile',
        help='log output to LOGFILE',
    )
    parser.add_option("-p", "--posturl", help="HTTP POST ZIP file to POSTURL")
    parser.add_option(
        "-g",
        "--getposturl",
        help='get POST URL from PediaPress.com, open upload page in webbrowser',
        action='store_true',
    )
    parser.add_option("-d",
                      "--daemonize",
                      action="store_true",
                      help='become a daemon process as soon as possible')
    parser.add_option(
        '--pid-file',
        help='write PID of daemonized process to this file',
    )
    options, args = parser.parse_args()

    use_help = 'Use --help for usage information.'
    if not options.input:
        parser.error('Specify --input.\n' + use_help)
    if (options.posturl and options.getposturl)\
        or (not options.posturl and not options.getposturl):
        parser.error('Specify either --posturl or --getposturl.\n' + use_help)
    if options.posturl:
        from mwlib.podclient import PODClient
        podclient = PODClient(options.posturl)
    elif options.getposturl:
        import webbrowser
        from mwlib.podclient import podclient_from_serviceurl
        podclient = podclient_from_serviceurl(
            'http://pediapress.com/api/collections/')
        webbrowser.open(podclient.redirecturl)

    from mwlib import utils
    from mwlib.status import Status

    if options.logfile:
        utils.start_logging(options.logfile)

    if options.daemonize:
        utils.daemonize()
    if options.pid_file:
        open(options.pid_file, 'wb').write('%d\n' % os.getpid())

    status = Status(podclient=podclient)

    try:
        try:
            status(status='uploading', progress=0)
            podclient.post_zipfile(options.input)
            status(status='finished', progress=100)
        except Exception, e:
            status(status='error')
            raise
    finally:
        if options.pid_file:
            utils.safe_unlink(options.pid_file)
Пример #12
0
def make_zip_file(output, env,
    status=None,
    num_threads=10,
    imagesize=800,
):
    if status is None:
        status = lambda **kwargs: None
    
    if output is None:
        fd, output = tempfile.mkstemp(suffix='.zip')
        os.close(fd)
    
    fd, tmpzip = tempfile.mkstemp(suffix='.zip', dir=os.path.dirname(output))
    os.close(fd)
    zf = zipfile.ZipFile(tmpzip, 'w')
    
    try:
        articles = metabook.get_item_list(env.metabook, filter_type='article')
        
        if num_threads > 0:
            z = ThreadedZipCreator(zf,
                imagesize=imagesize,
                num_threads=num_threads,
                status=status,
                num_articles=len(articles),
            )
        else:
            z = ZipCreator(zf,
                imagesize=imagesize,
                status=status,
                num_articles=len(articles),
            )
        
        # if articles:
        #     class IncProgress(object):
        #         inc = 100./len(articles)
        #         p = 0
        #         def __call__(self, title):
        #             self.p += self.inc
        #             status(progress=int(self.p), article=title)
        #     inc_progress = IncProgress()
        # else:
        #     inc_progress = None
        
        for item in articles:
            d = mwapidb.parse_article_url(item['title'].encode('utf-8'))
            if d is not None:
                item['title'] = d['title']
                item['revision'] = d['revision']
                wikidb = mwapidb.WikiDB(api_helper=d['api_helper'])
                imagedb = mwapidb.ImageDB(api_helper=d['api_helper'])
            else:
                wikidb = env.wiki
                imagedb = env.images
            z.addArticle(item['title'],
                revision=item.get('revision', None),
                wikidb=wikidb,
                imagedb=imagedb,
            )
        
        for license in env.get_licenses():
            z.parseArticle(
                title=license['title'],
                raw=license['wikitext'],
                wikidb=env.wiki,
                imagedb=env.images,
            )
        
        z.join()
        z.addObject('metabook.json', json.dumps(env.metabook))
        zf.close()
        if os.path.exists(output): # Windows...
            os.unlink(output)
        os.rename(tmpzip, output)
    
        if env.images and hasattr(env.images, 'clear'):
            env.images.clear()
    
        status(progress=100)
        return output
    finally:
        if os.path.exists(tmpzip):
            utils.safe_unlink(tmpzip)
Пример #13
0
def main():
    from gevent import monkey
    monkey.patch_all(thread=False)

    from mwlib.options import OptionParser
    from mwlib import conf

    parser = OptionParser()
    parser.add_option("-o", "--output", help="write output to OUTPUT")
    parser.add_option("-p",
                      "--posturl",
                      help="http post to POSTURL (directly)")
    parser.add_option(
        "-g",
        "--getposturl",
        help='get POST URL from PediaPress.com, open upload page in webbrowser',
        action='count',
    )
    parser.add_option(
        '--keep-tmpfiles',
        action='store_true',
        default=False,
        help="don't remove  temporary files like images",
    )

    parser.add_option("-s",
                      "--status-file",
                      help='write status/progress info to this file')

    options, args = parser.parse_args()
    conf.readrc()
    use_help = 'Use --help for usage information.'

    if parser.metabook is None and options.collectionpage is None:
        parser.error(
            'Neither --metabook nor, --collectionpage or arguments specified.\n'
            + use_help)
    if options.posturl and options.getposturl:
        parser.error('Specify either --posturl or --getposturl.\n' + use_help)
    if not options.posturl and not options.getposturl and not options.output:
        parser.error(
            'Neither --output, nor --posturl or --getposturl specified.\n' +
            use_help)
    if options.posturl:
        from mwlib.podclient import PODClient
        podclient = PODClient(options.posturl)
    elif options.getposturl:
        if options.getposturl > 1:
            serviceurl = 'http://test.pediapress.com/api/collections/'
        else:
            serviceurl = 'http://pediapress.com/api/collections/'
        import webbrowser
        from mwlib.podclient import podclient_from_serviceurl
        podclient = podclient_from_serviceurl(serviceurl)
        pid = os.fork()
        if not pid:
            try:
                webbrowser.open(podclient.redirecturl)
            finally:
                os._exit(0)
        import time
        time.sleep(1)
        try:
            os.kill(pid, 9)
        except BaseException:
            pass

    else:
        podclient = None

    from mwlib import utils, wiki

    filename = None
    status = None
    try:
        env = parser.makewiki()
        assert env.metabook, "no metabook"

        from mwlib.status import Status
        status = Status(options.status_file,
                        podclient=podclient,
                        progress_range=(1, 90))
        status(progress=0)
        output = options.output

        make_zip(output,
                 options,
                 env.metabook,
                 podclient=podclient,
                 status=status)

    except Exception as e:
        if status:
            status(status='error')
        raise
    finally:
        if options.output is None and filename is not None:
            print 'removing %r' % filename
            utils.safe_unlink(filename)
def make_zip_file(output, env,
    status=None,
    num_threads=10,
    imagesize=800,
):
    if output is None:
        fd, output = tempfile.mkstemp(suffix='.zip')
        os.close(fd)
    
    fd, tmpzip = tempfile.mkstemp(suffix='.zip', dir=os.path.dirname(output))
    os.close(fd)
    zf = zipfile.ZipFile(tmpzip, 'w')
    
    try:
        articles = metabook.get_item_list(env.metabook, filter_type='article')
        
        z = ZipCreator(zf,
            imagesize=imagesize,
            num_threads=num_threads,
            status=status,
            num_articles=len(articles),
        )
        
        for item in articles:
            d = mwapidb.parse_article_url(item['title'].encode('utf-8'))
            if d is not None:
                item['title'] = d['title']
                item['revision'] = d['revision']
                wikidb = mwapidb.WikiDB(api_helper=d['api_helper'])
                imagedb = mwapidb.ImageDB(api_helper=d['api_helper'])
            else:
                wikidb = env.wiki
                imagedb = env.images
            z.addArticle(item['title'],
                revision=item.get('revision', None),
                wikidb=wikidb,
                imagedb=imagedb,
            )

        for license in env.get_licenses():
            z.parseArticle(
                title=license['title'],
                raw=license['wikitext'],
                wikidb=env.wiki,
                imagedb=env.images,
            )
        
        z.join()
        
        # using check() is a bit rigorous: sometimes articles just cannot be
        # fetched -- PDFs should be generated nevertheless
        #z.check(articles)

        z.addObject('metabook.json', json.dumps(env.metabook))

        # add stats for later analysis
        z.node_stats["Chapter"] = len(metabook.get_item_list(env.metabook, filter_type='chapter'))
        z.addObject('node_stats.json', json.dumps(z.node_stats)) 

        zf.close()
        if os.path.exists(output): # Windows...
            os.unlink(output)
        os.rename(tmpzip, output)
    
        if env.images and hasattr(env.images, 'clear'):
            env.images.clear()
    
        if status is not None:
            status(progress=100)
        return output
    finally:
        if os.path.exists(tmpzip):
            utils.safe_unlink(tmpzip)
Пример #15
0
    from mwlib import utils,  wiki
    
    if options.daemonize:
        utils.daemonize()
    if options.pid_file:
        open(options.pid_file, 'wb').write('%d\n' % os.getpid())

    filename = None
    status = None
    try:
        env = parser.makewiki()
        assert env.metabook, "no metabook"
            
        from mwlib.status import Status
        status = Status(options.status_file, podclient=podclient, progress_range=(1, 90))
        status(progress=0)
        output = options.output
            
        make_zip(output, options, env.metabook, podclient=podclient, status=status)
            
    except Exception, e:
        if status:
            status(status='error')
        raise
    finally:
        if options.output is None and filename is not None:
            print 'removing %r' % filename
            utils.safe_unlink(filename)
        if options.pid_file:
            utils.safe_unlink(options.pid_file)
Пример #16
0
def buildzip():
    from mwlib.options import OptionParser

    parser = OptionParser()
    parser.add_option("-o", "--output", help="write output to OUTPUT")
    parser.add_option("-p",
                      "--posturl",
                      help="http post to POSTURL (directly)")
    parser.add_option(
        "-g",
        "--getposturl",
        help='get POST URL from PediaPress.com, open upload page in webbrowser',
        action='store_true',
    )
    options, args = parser.parse_args()

    use_help = 'Use --help for usage information.'
    if parser.metabook is None and options.collectionpage is None:
        parser.error(
            'Neither --metabook nor, --collectionpage or arguments specified.\n'
            + use_help)
    if options.posturl and options.getposturl:
        parser.error('Specify either --posturl or --getposturl.\n' + use_help)
    if not options.posturl and not options.getposturl and not options.output:
        parser.error(
            'Neither --output, nor --posturl or --getposturl specified.\n' +
            use_help)
    if options.posturl:
        from mwlib.podclient import PODClient
        podclient = PODClient(options.posturl)
    elif options.getposturl:
        import webbrowser
        from mwlib.podclient import podclient_from_serviceurl
        podclient = podclient_from_serviceurl(
            'http://pediapress.com/api/collections/')
        webbrowser.open(podclient.redirecturl)
    else:
        podclient = None

    from mwlib import utils

    if options.daemonize:
        utils.daemonize()
    if options.pid_file:
        open(options.pid_file, 'wb').write('%d\n' % os.getpid())

    filename = None
    status = None
    try:
        try:
            env = parser.makewiki()

            from mwlib.status import Status
            from mwlib import zipcreator

            status = Status(podclient=podclient, progress_range=(1, 90))
            status(status='parsing', progress=0)

            filename = zipcreator.make_zip_file(
                options.output,
                env,
                status=status,
                num_threads=options.num_threads,
                imagesize=options.imagesize,
            )

            status = Status(podclient=podclient, progress_range=(91, 100))
            if podclient:
                status(status='uploading', progress=0)
                podclient.post_zipfile(filename)

            status(status='finished', progress=100)
        except Exception, e:
            if status:
                status(status='error')
            raise
    finally:
        if options.output is None and filename is not None:
            print 'removing %r' % filename
            utils.safe_unlink(filename)
        if options.pid_file:
            utils.safe_unlink(options.pid_file)
Пример #17
0
        except:
            pass
              
    else:
        podclient = None
    
    from mwlib import utils,  wiki
    

    filename = None
    status = None
    try:
        env = parser.makewiki()
        assert env.metabook, "no metabook"
            
        from mwlib.status import Status
        status = Status(options.status_file, podclient=podclient, progress_range=(1, 90))
        status(progress=0)
        output = options.output
            
        make_zip(output, options, env.metabook, podclient=podclient, status=status)
            
    except Exception, e:
        if status:
            status(status='error')
        raise
    finally:
        if options.output is None and filename is not None:
            print 'removing %r' % filename
            utils.safe_unlink(filename)
Пример #18
0
 response = {
     'collection_id': collection_id,
     'writer': writer,
     'is_cached': False,
 }
 
 pid_path = self.get_path(collection_id, self.pid_filename, writer)
 if os.path.exists(pid_path):
     log.info('mw-render already running for collection %r' % collection_id)
     return response
 
 output_path = self.get_path(collection_id, self.output_filename, writer)
 if os.path.exists(output_path):
     if force_render:
         log.info('removing rendered file %r (forced rendering)' % output_path)
         utils.safe_unlink(output_path)
     else:
         log.info('re-using rendered file %r' % output_path)
         response['is_cached'] = True
         return response
 
 status_path = self.get_path(collection_id, self.status_filename, writer)
 if os.path.exists(status_path):
     if force_render:
         log.info('removing status file %r (forced rendering)' % status_path)
         utils.safe_unlink(status_path)
     else:
         log.info('status file exists %r' % status_path)
         return response
 
 error_path = self.get_path(collection_id, self.error_filename, writer)
Пример #19
0
def main():
    from SocketServer import ForkingMixIn, ThreadingMixIn
    from wsgiref.simple_server import make_server, WSGIServer
    from flup.server import fcgi, fcgi_fork, scgi, scgi_fork
    
    class ForkingWSGIServer(ForkingMixIn, WSGIServer):
        pass
    
    class ThreadingWSGIServer(ThreadingMixIn, WSGIServer):
        pass
    
    proto2server = {
        'http': ForkingWSGIServer,
        'http_threaded': ThreadingWSGIServer,
        'fcgi': fcgi_fork.WSGIServer,
        'fcgi_threaded': fcgi.WSGIServer,
        'scgi': scgi_fork.WSGIServer,
        'scgi_threaded': scgi.WSGIServer,
    }
    
    parser = optparse.OptionParser(usage="%prog [OPTIONS]")
    parser.add_option('-l', '--logfile',
        help='log output to LOGFILE',
    )
    parser.add_option('-d', '--daemonize',
        action='store_true',
        help='become daemon as soon as possible',
    )
    parser.add_option('--pid-file',
        help='write PID of daemonized process to this file',
    )
    parser.add_option('-P', '--protocol',
        help='one of %s (default: http)' % ', '.join(proto2server.keys()),
        default='http',
    )
    parser.add_option('-p', '--port',
        help='port to listen on (default: 8899)',
        default='8899',
    )
    parser.add_option('-i', '--interface',
        help='interface to listen on (default: 0.0.0.0)',
        default='0.0.0.0',
    )
    parser.add_option('--cache-dir',
        help='cache directory (default: /var/cache/mw-serve/)',
        default='/var/cache/mw-serve/',
    )
    parser.add_option('--mwrender',
        help='(path to) mw-render executable',
        default='mw-render',
    )
    parser.add_option('--mwrender-logfile',
        help='global logfile for mw-render',
        metavar='LOGFILE',
    )
    parser.add_option('--mwzip',
        help='(path to) mw-zip executable',
        default='mw-zip',
    )
    parser.add_option('--mwzip-logfile',
        help='global logfile for mw-zip',
        metavar='LOGFILE',
    )
    parser.add_option('--mwpost',
        help='(path to) mw-post executable',
        default='mw-post',
    )
    parser.add_option('--mwpost-logfile',
        help='global logfile for mw-post',
        metavar='LOGFILE',
    )
    parser.add_option('-q', '--queue-dir',
        help='queue dir of mw-watch (if not specified, no queue is used)',
    )
    parser.add_option('-m', '--method',
        help='prefork or threaded (default: prefork)',
        default='prefork',
    )
    parser.add_option('--max-requests',
        help='maximum number of requests a child process can handle before it is killed, irrelevant for --method=threaded (default: 0 = no limit)',
        default='0',
        metavar='NUM',
    )
    parser.add_option('--min-spare',
        help='minimum number of spare processes/threads (default: 2)',
        default='2',
        metavar='NUM',
    )
    parser.add_option('--max-spare',
        help='maximum number of spare processes/threads (default: 5)',
        default='5',
        metavar='NUM',
    )
    parser.add_option('--max-children',
        help='maximum number of processes/threads (default: 50)',
        default='50',
        metavar='NUM',
    )
    parser.add_option('--report-from-mail',
        help='sender of error mails (--report-recipient also needed)',
        metavar='EMAIL',
    )
    parser.add_option('--report-recipient',
        help='recipient of error mails (--report-from-mail also needed)',
        metavar='EMAIL',
    )
    options, args = parser.parse_args()

    if args:
        parser.error('no arguments supported')
    
    
    if options.protocol not in proto2server:
        parser.error('unsupported protocol (must be one of %s)' % (
            ', '.join(proto2server.keys()),
        ))

    def to_int(opt_name):
        try:
            setattr(options, opt_name, int(getattr(options, opt_name)))
        except ValueError:
            parser.error('--%s value must be an integer' % opt_name.replace('_', '-'))
    
    to_int('port')
    to_int('max_requests')
    to_int('min_spare')
    to_int('max_spare')
    to_int('max_children')
    
    if options.method not in ('prefork', 'threaded'):
        parser.error('the only supported values for --method are "prefork" and "threaded"')
    
    from mwlib import serve, log, utils
    
    log = log.Log('mw-serve')
    
    if options.logfile:
        utils.start_logging(options.logfile)
    
    if options.daemonize:
        utils.daemonize()
    if options.pid_file:
        open(options.pid_file, 'wb').write('%d\n' % os.getpid())
    
    if options.method == 'threaded':
        options.protocol += '_threaded'
        flup_kwargs = {
            'maxThreads': options.max_children,
        }
    else:
        flup_kwargs = {
            'maxChildren': options.max_children,
            'maxRequests':  options.max_requests,
        }
    
    log.info("serving %s on %s:%s" % (options.protocol, options.interface, options.port))
    
    if options.report_recipient and options.report_from_mail:
        report_from_mail = options.report_from_mail.encode('utf-8')
        report_recipients = [options.report_recipient.encode('utf-8')]
    else:
        report_from_mail = None
        report_recipients = None
    
    app = serve.Application(
        cache_dir=options.cache_dir,
        mwrender_cmd=options.mwrender,
        mwrender_logfile=options.mwrender_logfile,
        mwzip_cmd=options.mwzip,
        mwzip_logfile=options.mwzip_logfile,
        mwpost_cmd=options.mwpost,
        mwpost_logfile=options.mwpost_logfile,
        queue_dir=options.queue_dir,
        report_from_mail=report_from_mail,
        report_recipients=report_recipients,
    )
    if options.protocol.startswith('http'):
        server = make_server(options.interface, options.port, app,
            server_class=proto2server[options.protocol],
        )
        try:
            server.serve_forever()
        except KeyboardInterrupt:
            pass
    else:
        serverclass = proto2server[options.protocol]
        serverclass(app,
            bindAddress=(options.interface, options.port),
            minSpare=options.min_spare,
            maxSpare=options.max_spare,
            **flup_kwargs
        ).run()
    
    if options.pid_file:
        utils.safe_unlink(options.pid_file)
    
    log.info('exit.')
Пример #20
0
    def __call__(self):
        options, args, parser = self.parse_options()
        conf.readrc()

        self.parser = parser
        self.options = options

        import tempfile
        from mwlib.writerbase import WriterError
        from mwlib.status import Status

        use_help = 'Use --help for usage information.'

        if options.list_writers:
            self.list_writers()
            return

        if options.writer_info:
            self.show_writer_info(options.writer_info)
            return

        if options.output is None:
            parser.error('Please specify an output file with --output.\n' +
                         use_help)

        options.output = os.path.abspath(options.output)

        if options.writer is None:
            parser.error('Please specify a writer with --writer.\n' + use_help)

        writer = self.load_writer(options.writer)
        writer_options = {}
        if options.writer_options:
            for wopt in options.writer_options.split(';'):
                if '=' in wopt:
                    key, value = wopt.split('=', 1)
                else:
                    key, value = wopt, True
                writer_options[str(key)] = value
        if options.language:
            writer_options['lang'] = options.language
        for option in writer_options.keys():
            if option not in getattr(writer, 'options', {}):
                print 'Warning: unknown writer option %r' % option
                del writer_options[option]

        init_tmp_cleaner()

        self.status = Status(options.status_file, progress_range=(1, 33))
        self.status(progress=0)

        env = None
        try:
            env = self.get_environment()

            try:
                _locale.set_locale_from_lang(
                    env.wiki.siteinfo["general"]["lang"])
            except BaseException as err:
                print "Error: could not set locale", err

            basename = os.path.basename(options.output)
            if '.' in basename:
                ext = '.' + basename.rsplit('.', 1)[-1]
            else:
                ext = ''
            fd, tmpout = tempfile.mkstemp(dir=os.path.dirname(options.output),
                                          suffix=ext)
            os.close(fd)
            writer(env,
                   output=tmpout,
                   status_callback=self.status,
                   **writer_options)
            os.rename(tmpout, options.output)
            kwargs = {}
            if hasattr(writer, 'content_type'):
                kwargs['content_type'] = writer.content_type
            if hasattr(writer, 'file_extension'):
                kwargs['file_extension'] = writer.file_extension
            self.status(status='finished', progress=100, **kwargs)
            if options.keep_zip is None and self.zip_filename is not None:
                utils.safe_unlink(self.zip_filename)
        except Exception as e:
            import traceback
            self.status(status='error')
            if options.error_file:
                fd, tmpfile = tempfile.mkstemp(
                    dir=os.path.dirname(options.error_file))
                f = os.fdopen(fd, 'wb')
                if isinstance(e, WriterError):
                    f.write(str(e))
                else:
                    f.write('traceback\n')
                    traceback.print_exc(file=f)
                f.write("sys.argv=%r\n" % (utils.garble_password(sys.argv), ))
                f.close()
                os.rename(tmpfile, options.error_file)
            raise
        finally:
            if env is not None and env.images is not None:
                try:
                    if not options.keep_tmpfiles:
                        env.images.clear()
                except OSError as e:
                    if e.errno != errno.ENOENT:
                        print 'ERROR: Could not remove temporary images: %s' % e, e.errno