python:moinmoin

差分

このページの2つのバージョン間の差分を表示します。

この比較画面へのリンク

次のリビジョン
前のリビジョン
python:moinmoin [2019/08/09 04:42] – 作成 ともやんpython:moinmoin [2021/07/05 02:40] (現在) ともやん
行 1: 行 1:
 ====== MoinMoin ====== ====== MoinMoin ======
-{{:python:moinmoin_logo.png?100|MoinMoin Logo}}\\+{{python:moinmoin_logo.png?100|MoinMoin Logo}}\\
 \\ \\
 本家: [[https://moinmo.in/|MoinMoinWiki - MoinMoin]]\\ 本家: [[https://moinmo.in/|MoinMoinWiki - MoinMoin]]\\
行 7: 行 7:
 **MoinMoin** (モインモイン) は Python で書かれたウィキクローンの一つ。ユルゲン・ヘルマン、トーマス・ヴァルドマンらによって開発された。ライセンスはGPLである。Moinとは北部ドイツ等で話されるフリジア語の挨拶の言葉であり、直訳すると「よき一日を!」という意味である。\\ **MoinMoin** (モインモイン) は Python で書かれたウィキクローンの一つ。ユルゲン・ヘルマン、トーマス・ヴァルドマンらによって開発された。ライセンスはGPLである。Moinとは北部ドイツ等で話されるフリジア語の挨拶の言葉であり、直訳すると「よき一日を!」という意味である。\\
 [[https://ja.wikipedia.org/wiki/MoinMoin|MoinMoin - Wikipedia]]\\ [[https://ja.wikipedia.org/wiki/MoinMoin|MoinMoin - Wikipedia]]\\
 +
 +===== MoinMoin 2.0 =====
 +**MoinMoin 2.0** は 2019/08/09 現在、開発中であり Python 3.5 以上に対応している。\\
 +しかし、**quickinstall.py** は **virtualenv** を使用しており、**venv** には対応していない。\\
 +以下の手順では、**quickinstall.py** が **Python 3.7.4** で **venv** を利用するようにハックしてインストールを行う。\\
 +
 +==== ダウンロード ====
 +[[https://github.com/moinwiki/moin|GitHub - moinwiki/moin: MoinMoin Wiki Development (2.0+)]] より、ソースコードをダウンロードする。\\
 +<code>
 +$ git clone --depth 1 https://github.com/moinwiki/moin.git
 +</code>
 +
 +==== quickinstall.py を venv を利用するようにハックする ====
 +GitHub からダウンロードした **quickinstall.py** をリネームする。\\
 +
 +**Linux**\\
 +<WRAP prewrap 100%>
 +<code>
 +$ cd moin
 +$ mv quickinstall.py quickinstall.py.org
 +</code>
 +</WRAP>
 +
 +**Windows**\\
 +<WRAP prewrap 100%>
 +<code>
 +> cd moin
 +> ren quickinstall.py quickinstall.py.org
 +</code>
 +</WRAP>
 +
 +以下の **quickinstall.py** を **moin** ディレクトリにダウンロードする。(**quickinstall.py** を差し替える)\\
 +<WRAP prewrap 100%>
 +<code>
 +$ curl -O https://www.tomoyan.net/_media/python/quickinstall.py
 +$ chmod 775 quickinstall.py
 +</code>
 +</WRAP>
 +<WRAP prewrap 100% mincode_long>
 +<file python moin/quickinstall.py>
 +#!/usr/bin/python
 +# Copyright: 2013 MoinMoin:BastianBlank
 +# Copyright: 2013-2018 MoinMoin:RogerHaase
 +# License: GNU GPL v2 (or any later version), see LICENSE.txt for details.
 +"""
 +Create a virtual environment and install moin2 and all requirements in development mode.
 +
 +Usage for installation:
 +
 +    <python> quickinstall.py (where <python> is any Python 3.5+ executable)
 +
 +Requires: Python 3.5+, virtualenv, pip
 +
 +After initial installation, a menu of frequently used commands is provided for
 +moin2 developers and desktop wiki users.
 +
 +    - wraps a few commonly used moin commands, do "moin --help" for infrequently used commands
 +    - adds default file names for selected moin commands (backup, restore, ...)
 +    - creates log files for functions with large output, extracts success/failure messages
 +    - displays error messages if user tries to run commands out of sequence
 +    - activates the virtual env in a subprocess (no need for user to do ". activate" or "activate")
 +
 +usage (to display a menu of commands):
 +
 +    - unix:     ./m
 +    - windows:  m
 +
 +For the menu to work, it needs to know the name of a python executable and the location of a
 +virtual env. These needs are met by running "python quickinstall.py" after cloning the moin2
 +repository. The first run of quickinstall.py creates these files or symlink in the repo root:
 +
 +    - unix: m, activate
 +    - windows: m.bat, activate.bat, deactivate.bat
 +
 +Executing m.bat or ./m will run this script. The name of the python executable is within the
 +m.bat or ./m script.  The location of the virtual env is within the activate symlink or activate.bat.
 +Depending upon the command to be executed, some mix of the python executable
 +or activate will be used to construct a command string to pass to a subprocess call.
 +
 +One possible command is "./m quickinstall" which the user may use to occasionally
 +update the virtualenv with newly released supporting software. If tests have been run,
 +then the two virtualenvs within the .tox directory are also updated.
 +"""
 +
 +
 +import argparse
 +import logging
 +import os
 +import subprocess
 +import sys
 +import platform
 +import glob
 +import shutil
 +import fnmatch
 +from collections import Counter
 +try:
 +    import virtualenv
 +except ImportError:
 +    sys.exit("""
 +Error: import virtualenv failed, either virtualenv is not installed (see installation docs)
 +or the virtual environment must be deactivated before rerunning quickinstall.py
 +""")
 +
 +if sys.hexversion < 0x3050000:
 +    sys.exit("Error: MoinMoin requires Python 3.5+, current version is %s\n" % (platform.python_version(), ))
 +
 +
 +WIN_INFO = 'm.bat, activate.bat, and deactivate.bat are created by quickinstall.py'
 +NIX_INFO = 'the m bash script and the activate symlink are created by quickinstall.py'
 +
 +# text files created by commands with high volume output
 +QUICKINSTALL = 'm-quickinstall.txt'
 +TOX = 'm-tox.txt'
 +CODING_STD = 'm-coding-std.txt'
 +DOCS = 'm-docs.txt'
 +NEWWIKI = 'm-new-wiki.txt'
 +DELWIKI = 'm-delete-wiki.txt'
 +BACKUPWIKI = 'm-backup-wiki.txt'
 +DUMPHTML = 'm-dump-html.txt'
 +EXTRAS = 'm-extras.txt'
 +DIST = 'm-create-dist.txt'
 +# default files used for backup and restore
 +BACKUP_FILENAME = os.path.normpath('wiki/backup.moin')
 +JUST_IN_CASE_BACKUP = os.path.normpath('wiki/deleted-backup.moin')
 +
 +
 +if os.name == 'nt':
 +    M = 'm'  # customize help to local OS
 +    ACTIVATE = 'activate.bat & '
 +    SEP = ' & '
 +    WINDOWS_OS = True
 +else:
 +    M = './m'
 +    # in terminal "source activate" works, but Ubuntu shell requires ". ./activate"
 +    ACTIVATE = '. ./activate; '
 +    SEP = ';'
 +    WINDOWS_OS = False
 +
 +
 +# commands that create log files
 +CMD_LOGS = {
 +    'quickinstall': QUICKINSTALL,
 +    'tests': TOX,
 +    # 'coding-std': CODING_STD,  # not logged due to small output
 +    'docs': DOCS,
 +    'new-wiki': NEWWIKI,
 +    'del-wiki': DELWIKI,
 +    'backup': BACKUPWIKI,
 +    'dump-html': DUMPHTML,
 +    'extras': EXTRAS,
 +    'dist': DIST,
 +}
 +
 +
 +help = """
 +
 +usage: "{0} <target>" where <target> is:
 +
 +quickinstall    update virtual environment with required packages
 +extras          install packages required for docs and moin development
 +docs            create moin html documentation (requires extras)
 +interwiki       refresh contrib/interwiki/intermap.txt (version control)
 +log <target>    view detailed log generated by <target>, omit to see list
 +
 +new-wiki        create empty wiki
 +sample          create wiki and load sample data
 +restore *       create wiki and restore wiki/backup.moin *option, specify file
 +import19 <dir>  import a moin 1.9 wiki/data instance from <dir>
 +
 +run *           run built-in wiki server *options (--port 8081)
 +backup *        roll 3 prior backups and create new backup *option, specify file
 +dump-html *     create a static HTML image of wiki *options, see docs
 +index           delete and rebuild indexes
 +
 +css             run Stylus and lessc to update theme CSS files
 +tests *         run tests, log output (-v -k my_test)
 +coding-std      correct scripts that taint the repository with trailing spaces..
 +
 +del-all         same as running the 4 del-* commands below
 +del-orig        delete all files matching *.orig
 +del-pyc         delete all files matching *.pyc
 +del-rej         delete all files matching *.rej
 +del-wiki        create a backup, then delete all wiki data
 +""".format(M)
 +
 +
 +def search_for_phrase(filename):
 +    """Search a text file for key phrases and print the lines of interest or print a count by phrase."""
 +    files = {
 +        # filename: (list of phrases)
 +        # Note: phrases must be lower-case
 +        QUICKINSTALL: ('could not find', 'error', 'fail', 'timeout', 'traceback', 'success', 'cache location', 'must be deactivated', 'no such option', ),
 +        NEWWIKI: ('error', 'fail', 'timeout', 'traceback', 'success', ),
 +        BACKUPWIKI: ('error', 'fail', 'timeout', 'traceback', 'success', ),
 +        DUMPHTML: ('fail', 'timeout', 'traceback', 'success', 'cannot', 'denied', ),
 +        # use of 'error ' below is to avoid matching .../Modules/errors.o....
 +        EXTRAS: ('error ', 'error:', 'error.', 'error,', 'fail', 'timeout', 'traceback', 'active version', 'successfully installed', 'finished', ),
 +        # ': e' matches lines similar to: src/moin/converters\_tests\test_moinwiki_in_out.py:294:5: E303 too many blank lines (3)
 +        TOX: ('seconds =', 'internalerror', 'error:', 'traceback', ': e', ': f' ),
 +        CODING_STD: ('remove trailing blanks', 'dos line endings', 'unix line endings', 'remove empty lines', ),
 +        DIST: ('creating', 'copying', 'adding', 'hard linking', ),
 +        DOCS: ('build finished', 'build succeeded', 'traceback', 'failed', 'error', 'usage', 'importerror', 'exception occurred', )
 +    }
 +    # for these file names, display a count of occurrances rather than each found line
 +    print_counts = (CODING_STD, DIST, )
 +
 +    with open(filename, "r") as f:
 +        lines = f.readlines()
 +    name = os.path.split(filename)[1]
 +    phrases = files[name]
 +    counts = Counter()
 +    for idx, line in enumerate(lines):
 +        for phrase in phrases:
 +            if phrase in line.lower():
 +                if filename in print_counts:
 +                    counts[phrase] += 1
 +                else:
 +                    print(idx + 1, line.rstrip())
 +                    break
 +    for key in counts:
 +        print('The phrase "%s" was found %s times.' % (key, counts[key]))
 +
 +
 +def wiki_exists():
 +    """Return true if a wiki exists."""
 +    return bool(glob.glob('wiki/index/_all_revs_*.toc'))
 +
 +
 +def make_wiki(command, mode='w', msg='\nSuccess: a new wiki has been created.'):
 +    """Process command to create a new wiki."""
 +    if wiki_exists() and mode == 'w':
 +        print('Error: a wiki exists, delete it and try again.')
 +    else:
 +        print('Output messages redirected to {0}.'.format(NEWWIKI))
 +        with open(NEWWIKI, mode) as messages:
 +            result = subprocess.call(command, shell=True, stderr=messages, stdout=messages)
 +        if result == 0:
 +            print(msg)
 +            return True
 +        else:
 +            print('Important messages from %s are shown below:' % NEWWIKI)
 +            search_for_phrase(NEWWIKI)
 +            print('\nError: attempt to create wiki failed. Do "%s log new-wiki" to see complete log.' % M)
 +            return False
 +
 +
 +def put_items(dir='contrib/sample/'):
 +    """Load sample items into wiki"""
 +    metas = []
 +    datas = []
 +    files = []
 +    for (dirpath, dirnames, filenames) in os.walk(dir):
 +        files.extend(filenames)
 +        break
 +    for file in files:
 +        if file.endswith('.meta'):
 +            metas.append(file)
 +        if file.endswith('.data'):
 +            datas.append(file)
 +    if not len(datas) == len(metas):
 +        print('Error: the number of .data and .meta files should be equal')
 +        return False
 +    commands = []
 +    command = 'moin item-put --meta {0} --data {1}'
 +    for meta in metas:
 +        data = meta.replace('.meta', '.data')
 +        if data in datas:
 +            commands.append(command.format(dir + meta, dir + data))
 +        else:
 +            print('Error: file "{0} is missing'.format(data))
 +            return False
 +    commands = ACTIVATE + SEP.join(commands)
 +
 +    with open(NEWWIKI, 'a') as messages:
 +        result = subprocess.call(commands, shell=True, stderr=messages, stdout=messages)
 +    if result == 0:
 +        print('{0} items were added to wiki'.format(len(metas)))
 +        return True
 +    else:
 +        print('Important messages from %s are shown below:' % NEWWIKI)
 +        search_for_phrase(NEWWIKI)
 +        print('\nError: attempt to add items to wiki failed. Do "%s log new-wiki" to see complete log.' % M)
 +        return False
 +
 +
 +def delete_files(pattern):
 +    """Recursively delete all files matching pattern."""
 +    matches = 0
 +    for root, dirnames, filenames in os.walk(os.path.abspath(os.path.dirname(__file__))):
 +        for filename in fnmatch.filter(filenames, pattern):
 +            os.remove(os.path.join(root, filename))
 +            matches += 1
 +    print('Deleted %s files matching "%s".' % (matches, pattern))
 +
 +
 +def get_bootstrap_data_location():
 +    """Return the virtualenv site-packages/xstatic/pkg/bootstrap/data location."""
 +    command = ACTIVATE + 'python -c "from xstatic.pkg.bootstrap import BASE_DIR; print(BASE_DIR)"'
 +    return subprocess.check_output(command, shell=True).decode()
 +
 +
 +def get_pygments_data_location():
 +    """Return the virtualenv site-packages/xstatic/pkg/pygments/data location."""
 +    command = ACTIVATE + 'python -c "from xstatic.pkg.pygments import BASE_DIR; print(BASE_DIR)"'
 +    return subprocess.check_output(command, shell=True).decode()
 +
 +
 +def get_sitepackages_location():
 +    """Return the location of the virtualenv site-packages directory."""
 +    command = ACTIVATE + 'python -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())"'
 +    return subprocess.check_output(command, shell=True).decode().strip()
 +
 +
 +def create_m():
 +    """Create an 'm.bat or 'm' bash script that will run make.py using this Python"""
 +    if WINDOWS_OS:
 +        with open('m.bat', 'w') as f:
 +            f.write(':: {0}\n\n@{1} quickinstall.py %* --help\n'.format(WIN_INFO, sys.executable))
 +    else:
 +        with open('m', 'w') as f:
 +            f.write('#!/bin/sh\n# {0}\n\n{1} quickinstall.py $* --help\n'.format(NIX_INFO, sys.executable))
 +            os.fchmod(f.fileno(), 0o775)
 +
 +
 +class Commands:
 +    """Each cmd_ method processes a choice on the menu."""
 +    def __init__(self):
 +        pass
 +
 +    def cmd_quickinstall(self, *args):
 +        """create or update a virtual environment with the required packages"""
 +        if os.path.isdir('.tox'):
 +            # keep tox test virtualenvs in sync with moin-env-python
 +            command = '{0} quickinstall.py --FirstCall {1}{2}tox --recreate --notest'.format(sys.executable, ' '.join(args), SEP)
 +            print('Running quickinstall.py and tox recreate virtualenvs... output messages redirected to {0}'.format(QUICKINSTALL))
 +        else:
 +            command = '{0} quickinstall.py --FirstCall'.format(sys.executable, ' '.join(args), )
 +            print('Running quickinstall.py... output messages redirected to {0}'.format(QUICKINSTALL))
 +        with open(QUICKINSTALL, 'w') as messages:
 +            # we run ourself as a subprocess so we can capture output in a log file
 +            result = subprocess.call(command, shell=True, stderr=messages, stdout=messages)
 +        print('\nSearching {0}, important messages are shown below... Do "{1} log quickinstall" to see complete log.\n'.format(QUICKINSTALL, M))
 +        search_for_phrase(QUICKINSTALL)
 +
 +    def cmd_docs(self, *args):
 +        """create local Sphinx html documentation"""
 +        command = '{0}sphinx-apidoc -f -o docs/devel/api src/moin {1}cd docs{1} make html'.format(ACTIVATE, SEP)
 +        print('Creating HTML docs... output messages written to {0}.'.format(DOCS))
 +        with open(DOCS, 'w') as messages:
 +            result = subprocess.call(command, shell=True, stderr=messages, stdout=messages)
 +        print('\nSearching {0}, important messages are shown below...\n'.format(DOCS))
 +        search_for_phrase(DOCS)
 +        if result == 0:
 +            print('HTML docs successfully created in {0}.'.format(os.path.normpath('docs/_build/html')))
 +        else:
 +            print('Error: creation of HTML docs failed with return code "{0}". Do "{1} log docs" to see complete log.'.format(result, M))
 +
 +    def cmd_extras(self, *args):
 +        """install optional packages: Pillow, sqlalchemy, ldap, requirements.d"""
 +        sp_dir = get_sitepackages_location()
 +        packages = ['pillow', 'sqlalchemy', ]
 +        if not WINDOWS_OS:
 +            packages.append('python-ldap')
 +        installer = 'pip install --upgrade '
 +        reqs = ['requirements.d/development.txt', 'requirements.d/docs.txt', ]
 +        reqs_installer = 'pip install -r '
 +        command = ACTIVATE + SEP.join(list(installer + x for x in packages) + list(reqs_installer + x for x in reqs))
 +        print('Installing {0}.'.format(', '.join(packages + reqs)))
 +        print('Output messages written to {0}.'.format(EXTRAS))
 +        with open(EXTRAS, 'w') as messages:
 +            subprocess.call(command, shell=True, stderr=messages, stdout=messages)
 +        print('\nImportant messages from {0} are shown below. Do "{1} log extras" to see complete log.'.format(EXTRAS, M))
 +        search_for_phrase(EXTRAS)
 +
 +    def cmd_interwiki(self, *args):
 +        """refresh contrib/interwiki/intermap.txt"""
 +        print('Refreshing {0}...'.format(os.path.normpath('contrib/interwiki/intermap.txt')))
 +        command = '{0} scripts/wget.py http://master19.moinmo.in/InterWikiMap?action=raw contrib/interwiki/intermap.txt'.format(sys.executable)
 +        subprocess.call(command, shell=True)
 +
 +    def cmd_log(self, *args):
 +        """View a log file with the default text editor"""
 +
 +        def log_help(logs):
 +            """Print list of available logs to view."""
 +            print("usage: {0} log <target> where <target> is:\n\n".format(M))
 +            choices = '{0: <16}- {1}'
 +            for log in sorted(logs):
 +                if os.path.isfile(CMD_LOGS[log]):
 +                    print(choices.format(log, CMD_LOGS[log]))
 +                else:
 +                    print(choices.format(log, '* file does not exist'))
 +
 +        logs = set(CMD_LOGS.keys())
 +        if args and args[0] in logs and os.path.isfile(CMD_LOGS[args[0]]):
 +            if WINDOWS_OS:
 +                command = 'start {0}'.format(CMD_LOGS[args[0]])
 +            else:
 +                # .format requires {{ and }} to escape { and }
 +                command = '${{VISUAL:-${{FCEDIT:-${{EDITOR:-less}}}}}} {0}'.format(CMD_LOGS[args[0]])
 +            subprocess.call(command, shell=True)
 +        else:
 +            log_help(logs)
 +
 +    def cmd_new_wiki(self, *args):
 +        """create empty wiki"""
 +        command = '{0}moin index-create -s -i'.format(ACTIVATE)
 +        print('Creating a new empty wiki...')
 +        make_wiki(command)  # share code with loading sample data and restoring backups
 +
 +    def cmd_sample(self, *args):
 +        """create wiki and load sample data"""
 +        # load items with non-ASCII names from a serialized backup
 +        command = '{0}moin index-create -s -i{1} moin load --file contrib/sample/unicode.moin'.format(ACTIVATE, SEP)
 +        print('Creating a new wiki populated with sample data...')
 +        success = make_wiki(command, msg='\nSuccess: a new wiki has been created... working...')
 +        # build the index
 +        if success:
 +            command = '{0}moin index-build'.format(ACTIVATE, SEP)
 +            success = make_wiki(command, mode='a', msg='\nSuccess: the index has been created for the sample wiki... working...')
 +        # load individual items from contrib/sample, index will be updated
 +        if success:
 +            success = put_items()
 +
 +    def cmd_restore(self, *args):
 +        """create wiki and load data from wiki/backup.moin or user specified path"""
 +        command = '{0} moin index-create -s -i{1} moin load --file %s{1} moin index-build'.format(ACTIVATE, SEP)
 +        filename = BACKUP_FILENAME
 +        if args:
 +            filename = args[0]
 +        if os.path.isfile(filename):
 +            command = command % filename
 +            print('Creating a new wiki and loading it with data from {0}...'.format(filename))
 +            make_wiki(command)
 +        else:
 +            print('Error: cannot create wiki because {0} does not exist.'.format(filename))
 +
 +    def cmd_import19(self, *args):
 +        """import a moin 1.9 wiki directory named dir"""
 +        if args:
 +            dirname = args[0]
 +            if os.path.isdir(dirname):
 +                command = '{0}moin import19 -s -i --data_dir {1}'.format(ACTIVATE, dirname)
 +                print('Creating a new wiki populated with data from {0}...'.format(dirname))
 +                make_wiki(command)
 +            else:
 +                print('Error: cannot create wiki because {0} does not exist.'.format(dirname))
 +        else:
 +            print('Error: a path to the Moin 1.9 wiki/data data directory is required.')
 +
 +    def cmd_index(self, *args):
 +        """delete and rebuild index"""
 +        if wiki_exists():
 +            command = '{0}moin index-create -i{1} moin index-build'.format(ACTIVATE, SEP)
 +            print('Rebuilding indexes...')
 +            try:
 +                subprocess.call(command, shell=True)
 +            except KeyboardInterrupt:
 +                pass  # eliminates traceback on windows
 +        else:
 +            print('Error: a wiki must be created before rebuilding the indexes.')
 +
 +    def cmd_run(self, *args):
 +        """run built-in wiki server"""
 +        if wiki_exists():
 +            if WINDOWS_OS:
 +                args += ('--threaded', )
 +            command = '{0}moin moin {1}'.format(ACTIVATE, ' '.join(args))
 +            try:
 +                subprocess.call(command, shell=True)
 +            except KeyboardInterrupt:
 +                pass  # eliminates traceback on windows
 +        else:
 +            print('Error: a wiki must be created before running the built-in server.')
 +
 +    def cmd_backup(self, *args):
 +        """roll 3 prior backups and create new wiki/backup.moin or backup to user specified file"""
 +        if wiki_exists():
 +            filename = BACKUP_FILENAME
 +            if args:
 +                filename = args[0]
 +                print('Creating a wiki backup to {0}...'.format(filename))
 +            else:
 +                print('Creating a wiki backup to {0} after rolling 3 prior backups...'.format(filename))
 +                b3 = BACKUP_FILENAME.replace('.', '3.')
 +                b2 = BACKUP_FILENAME.replace('.', '2.')
 +                b1 = BACKUP_FILENAME.replace('.', '1.')
 +                if os.path.exists(b3):
 +                    os.remove(b3)
 +                for src, dst in ((b2, b3), (b1, b2), (BACKUP_FILENAME, b1)):
 +                    if os.path.exists(src):
 +                        os.rename(src, dst)
 +
 +            command = '{0}moin save --all-backends --file {1}'.format(ACTIVATE, filename)
 +            with open(BACKUPWIKI, 'w') as messages:
 +                result = subprocess.call(command, shell=True, stderr=messages, stdout=messages)
 +            if result == 0:
 +                print('Success: wiki was backed up to {0}'.format(filename))
 +            else:
 +                print('Important messages from {0} are shown below. Do "{1} log backup" to see complete log.'.format(BACKUPWIKI, M))
 +                search_for_phrase(BACKUPWIKI)
 +                print('\nError: attempt to backup wiki failed.')
 +        else:
 +            print('Error: cannot backup wiki because it has not been created.')
 +
 +    def cmd_dump_html(self, *args):
 +        """create a static html dump of this wiki"""
 +        if wiki_exists():
 +            print('Creating static HTML image of wiki...')
 +            command = '{0}moin dump-html {1}'.format(ACTIVATE, ' '.join(args))
 +            with open(DUMPHTML, 'w') as messages:
 +                result = subprocess.call(command, shell=True, stderr=messages, stdout=messages)
 +            if result == 0:
 +                print('Success: wiki was dumped to html files')
 +            else:
 +                print('\nError: attempt to dump wiki to html files failed.')
 +            # always show errors because individual items may fail
 +            print('Important messages from {0} are shown below. Do "{1} log dump-html" to see complete log.'.format(DUMPHTML, M))
 +            search_for_phrase(DUMPHTML)
 +        else:
 +            print('Error: cannot dump wiki because it has not been created.')
 +
 +    def cmd_css(self, *args):
 +        """run Stylus and lessc to update CSS files"""
 +        # Note: we use / below within file paths; this works in Windows XP, 2000, 7, 8, 10
 +        bootstrap_loc = get_bootstrap_data_location().strip() + '/less'
 +        pygments_loc = get_pygments_data_location().strip() + '/css'
 +        modernized_loc = 'src/moin/themes/modernized/static/css/stylus'
 +        basic_loc = 'src/moin/themes/basic/static/custom-less'
 +
 +        print('Running Stylus to update Modernized theme CSS files...')
 +        command = 'cd {0}{1}stylus --include {2} --include-css --compress < theme.styl > ../theme.css'.format(modernized_loc, SEP, pygments_loc)
 +        result = subprocess.call(command, shell=True)
 +        if result == 0:
 +            print('Success: Modernized CSS files updated.')
 +        else:
 +            print('Error: stylus failed to update css files, see error messages above.')
 +        # stylus adds too many blank lines at end of modernized theme.css, fix it by running coding_std against css directory
 +        command = '{0}python scripts/coding_std.py src/moin/themes/modernized/static/css'.format(ACTIVATE)
 +        result = subprocess.call(command, shell=True)
 +        if result != 0:
 +            print('Error: failure running coding_std.py against modernized css files')
 +
 +        print('Running lessc to update Basic theme CSS files...')
 +        if WINDOWS_OS:
 +            data_loc = '{0};{1}'.format(bootstrap_loc, pygments_loc)
 +        else:
 +            data_loc = '{0}:{1}'.format(bootstrap_loc, pygments_loc)
 +        include = '--include-path=' + data_loc
 +        command = 'cd {0}{1}lessc {2} theme.less ../css/theme.css'.format(basic_loc, SEP, include)
 +        result = subprocess.call(command, shell=True)
 +        if result == 0:
 +            print('Success: Basic theme CSS files updated.')
 +        else:
 +            print('Error: Basic theme CSS files update failed, see error messages above.')
 +
 +    def cmd_tests(self, *args):
 +        """run tests, output goes to m-tox.txt"""
 +        print('Running tests... output written to {0}.'.format(TOX))
 +        command = '{0}tox -- {2} > {1} 2>&1'.format(ACTIVATE, TOX, ' '.join(args))
 +        result = subprocess.call(command, shell=True)
 +        print('Important messages from {0} are shown below. Do "{1} log tests" to see complete log.'.format(TOX, M))
 +        search_for_phrase(TOX)
 +
 +    def cmd_coding_std(self, *args):
 +        """correct scripts that taint the HG repository and clutter subsequent code reviews"""
 +        print('Checking for trailing blanks, DOS line endings, Unix line endings, empty lines at eof...')
 +        command = '%s scripts/coding_std.py' % sys.executable
 +        subprocess.call(command, shell=True)
 +
 +    # not on menu, rarely used, similar code was in moin 1.9
 +    def cmd_dist(self, *args):
 +        """create distribution archive in dist/"""
 +        print('Deleting wiki data, then creating distribution archive in /dist, output written to {0}.'.format(DIST))
 +        self.cmd_del_wiki(*args)
 +        command = '{0} setup.py sdist'.format(sys.executable)
 +        with open(DIST, 'w') as messages:
 +            result = subprocess.call(command, shell=True, stderr=messages, stdout=messages)
 +        print('Summary message from {0} is shown below:'.format(DIST))
 +        search_for_phrase(DIST)
 +        if result == 0:
 +            print('Success: a distribution archive was created in {0}.'.format(os.path.normpath('/dist')))
 +        else:
 +            print('Error: create dist failed with return code = {0}. Do "{1} log dist" to see complete log.'.format(result, M))
 +
 +    def cmd_del_all(self, *args):
 +        """same as running the 4 del-* commands below"""
 +        self.cmd_del_orig(*args)
 +        self.cmd_del_pyc(*args)
 +        self.cmd_del_rej(*args)
 +        self.cmd_del_wiki(*args)
 +
 +    def cmd_del_orig(self, *args):
 +        """delete all files matching *.orig"""
 +        delete_files('*.orig')
 +
 +    def cmd_del_pyc(self, *args):
 +        """delete all files matching *.pyc"""
 +        delete_files('*.pyc')
 +
 +    def cmd_del_rej(self, *args):
 +        """delete all files matching *.rej"""
 +        delete_files('*.rej')
 +
 +    def cmd_del_wiki(self, *args):
 +        """create a just-in-case backup, then delete all wiki data"""
 +        command = '{0}moin save --all-backends --file {1}'.format(ACTIVATE, JUST_IN_CASE_BACKUP)
 +        if wiki_exists():
 +            print('Creating a backup named {0}; then deleting all wiki data and indexes...'.format(JUST_IN_CASE_BACKUP))
 +            with open(DELWIKI, 'w') as messages:
 +                result = subprocess.call(command, shell=True, stderr=messages, stdout=messages)
 +            if result != 0:
 +                print('Error: backup failed with return code = {0}. Complete log is in {1}.'.format(result, DELWIKI))
 +        # destroy wiki even if backup fails
 +        if os.path.isdir('wiki/data') or os.path.isdir('wiki/index'):
 +            shutil.rmtree('wiki/data')
 +            shutil.rmtree('wiki/index')
 +            if os.path.isdir('wiki/preview'):
 +                shutil.rmtree('wiki/preview')
 +            if os.path.isdir('wiki/sql'):
 +                shutil.rmtree('wiki/sql')
 +            print('Wiki data successfully deleted.')
 +        else:
 +            print('Wiki data not deleted because it does not exist.')
 +
 +
 +class QuickInstall:
 +
 +    def __init__(self, source):
 +        self.dir_source = source
 +        base, source_name = os.path.split(source)
 +        executable = os.path.basename(sys.executable).split('.exe')[0]
 +        venv = os.path.join(base, '{0}-venv-{1}'.format(source_name, executable))
 +        venv = os.path.abspath(venv)
 +        venv_home, venv_lib, venv_inc, venv_bin = virtualenv.path_locations(venv)
 +        self.dir_venv = venv_home
 +        self.dir_venv_bin = venv_bin
 +
 +    def __call__(self):
 +        self.do_venv()
 +        self.do_helpers()
 +        self.do_install()
 +        self.do_catalog()
 +        sys.stdout.write("\n\nSuccessfully created or updated venv at {0}".format(self.dir_venv))
 +
 +    def do_venv(self):
 +        virtualenv.create_environment(self.dir_venv)
 +
 +    def get_pip_version(self):
 +        """Return pip version as a list: [1, 5, 1]"""
 +        command = ACTIVATE + 'pip --version'
 +        pip_txt = subprocess.check_output(command, shell=True)
 +        # expecting pip_txt similar to "pip 1.4.1 from /bitbucket/moin-2.0..."
 +        pip_txt = pip_txt.decode().split()
 +        if pip_txt[0] == 'pip':
 +            pip_version = [int(x) for x in pip_txt[1].split('.')]
 +            return pip_version
 +        else:
 +            sys.exit("Error: 'pip --version' produced unexpected results: '{0}".format(' '.join(pip_txt)))
 +
 +    def do_install(self):
 +        pip_version = self.get_pip_version()
 +        args = [
 +            os.path.join(self.dir_venv_bin, 'pip'),
 +            'install',
 +            '--upgrade',
 +            '--editable',
 +            self.dir_source,
 +        ]
 +        if pip_version >= [9, 0]:
 +            args += ['--upgrade-strategy=eager', ]
 +        subprocess.check_call(args)
 +
 +    def do_catalog(self):
 +        subprocess.check_call((
 +            os.path.join(self.dir_venv_bin, 'python'),
 +            os.path.join(self.dir_source, 'setup.py'),
 +            'compile_catalog', '--statistics',
 +            # needed in case user runs quickinstall.py with a cwd other than the repo root
 +            '--directory', os.path.join(os.path.dirname(__file__), 'src', 'moin', 'translations'),
 +        ))
 +
 +    def create_wrapper(self, filename, target):
 +        """Create files in the repo root that wrap files in <path-to-virtual-env>\Scripts."""
 +        target = os.path.join(self.dir_venv_bin, target)
 +        with open(filename, 'w') as f:
 +            f.write(':: {0}\n\n@call {1} %*\n'.format(WIN_INFO, target))
 +
 +    def do_helpers(self):
 +        """Create small helper scripts or symlinks in repo root, avoid keying the long path to virtual env."""
 +        create_m()  # recreate m.bat or ./m to insure it is consistent with activate
 +        if WINDOWS_OS:
 +            # windows commands are: activate | deactivate
 +            self.create_wrapper('activate.bat', 'activate.bat')
 +            self.create_wrapper('deactivate.bat', 'deactivate.bat')
 +        else:
 +            # linux commands are: source activate | deactivate
 +            if os.path.exists('activate'):
 +                os.unlink('activate')
 +            os.symlink(os.path.join(self.dir_venv_bin, 'activate'), 'activate' # no need to define deactivate on unix
 +
 +
 +if __name__ == '__main__':
 +    # create a set of valid menu choices
 +    commands = Commands()
 +    choices = set()
 +    names = dir(commands)
 +    for name in names:
 +        if name.startswith('cmd_'):
 +            choices.add(name)
 +    args = sys.argv[:]
 +
 +    if len(args) > 2 and args[-1] == '--help':
 +        # m and m.bat have trailing --help so "./m" comes across as "python quickinstall.py --help"
 +        # if user did "./m <option>" we have "python quickinstall.py <option> --help" then we can delete the --help and do <option>
 +        args = args[:-1]
 +
 +    if (os.path.isfile('activate') or os.path.isfile('activate.bat')) and (len(args) == 2 and args[1] in ('-h', '--help')):
 +        # user keyed "./m", "./m -h", or "./m --help"
 +        print(help)
 +
 +    else:
 +        if not (os.path.isfile('m') or os.path.isfile('m.bat')):
 +            # user is running "python quickinstall.py" after fresh clone (or m or m.bat has been deleted)
 +            create_m()  # create "m" or "m.bat" file so above IF will be false next time around
 +            command = getattr(commands, 'cmd_quickinstall')
 +            # run this same script (quickinstall.py) again in a subprocess to create the virtual env
 +            command()
 +            # a few success/failure messages will have printed on users terminal, suggest next step
 +            print('\n> > > Type "%s" for menu < < <' % M)
 +
 +        elif args == ['quickinstall.py', 'quickinstall']:
 +            # user keyed "./m quickinstall" to update virtualenv
 +            command = getattr(commands, 'cmd_quickinstall')
 +            # run this same script (quickinstall.py) again in a subprocess to recreate the virtual env
 +            command()
 +
 +        else:
 +            if args == ['quickinstall.py']:
 +                # user keyed "python quickinstall.py" instead of "./m quickinstall"
 +                # run this same script (quickinstall.py) again in a subprocess to create the virtual env
 +                command = getattr(commands, 'cmd_quickinstall')
 +                command()
 +
 +            elif args == ['quickinstall.py', '--FirstCall']:
 +                # we are in a subprocess call after "python quickinstall.py" or  "./m quickinstall"
 +                orig_stdout = sys.stdout
 +                orig_stderr = sys.stderr
 +                with open(QUICKINSTALL, 'a') as messages:
 +                    sys.stdout = messages
 +                    sys.stderr = messages
 +                    QuickInstall(os.path.dirname(os.path.realpath(args[0])))()
 +                    sys.stdout = orig_stdout
 +                    sys.stderr = orig_stderr
 +
 +            else:
 +                # we have some simple command like "./m css" that does not update virtualenv
 +                choice = 'cmd_%s' % args[1]
 +                choice = choice.replace('-', '_')
 +                if choice in choices:
 +                    choice = getattr(commands, choice)
 +                    choice(*args[2:])
 +                else:
 +                    print(help)
 +                    print('Error: unknown menu selection "%s"' % args[1])
 +</file>
 +</WRAP>
 +
 +==== インストール ====
 +**quickinstall.py** を実行する。\\
 +
 +**Linux**\\
 +<WRAP prewrap 100%>
 +<code>
 +$ python3 quickinstall.py
 +</code>
 +</WRAP>
 +<WRAP prewrap 100% result>
 +<code>
 +Running quickinstall.py... output messages redirected to m-quickinstall.txt
 +
 +Searching m-quickinstall.txt, important messages are shown below... Do "./m log quickinstall" to see complete log.
 +
 +83       Successfully uninstalled setuptools-40.8.0
 +119 Successfully installed Babel-2.7.0 Flask-1.1.1 Flask-Babel-0.12.2 Flask-Caching-1.7.2 Flask-Script-2.0.6 Flask-Theme-0.3.4 Jinja2-2.10.1 Markdown-3.1.1 MarkupSafe-1.1.1 Werkzeug-0.15.5 XStatic-1.0.2 XStatic-AnyWikiDraw-0.14.2 XStatic-Bootstrap-3.1.1.2 XStatic-CKEditor-3.6.4.0 XStatic-Font-Awesome-4.7.0.0 XStatic-JQuery.TableSorter-2.14.5.1 XStatic-Pygments-2.2.0.1 XStatic-TWikiDraw-moin-2004.10.23.2 XStatic-autosize-1.17.2.1 XStatic-jQuery-3.3.1.1 XStatic-jQuery-File-Upload-4.4.2 XStatic-svg-edit-moin-2012.11.27.1 blinker-1.4 chardet-3.0.4 click-7.0 docutils-0.15.2 emeraldtree-0.10.0 flatland-0.9.1 itsdangerous-1.1.0 moin passlib-1.7.1 pdfminer3-2018.12.3.0 pycryptodome-3.8.2 pygments-2.4.2 pytz-2019.2 setuptools-41.0.1 six-1.12.0 sortedcontainers-2.1.0 whoosh-2.7.4
 +131 Successfully created or updated venv at /var/www/vhosts/moin.tomoyan.net/moin-venv-python3
 +
 +> > > Type "./m" for menu < < <
 +</code>
 +</WRAP>
 +
 +**Windows**\\
 +<WRAP prewrap 100%>
 +<code>
 +> python3 quickinstall.py
 +</code>
 +</WRAP>
 +<WRAP prewrap 100% result>
 +<code>
 +Running quickinstall.py... output messages redirected to m-quickinstall.txt
 +
 +Searching m-quickinstall.txt, important messages are shown below... Do "m log quickinstall" to see complete log.
 +
 +139 Successfully installed Babel-2.9.1 Flask-1.1.4 Flask-Babel-2.0.0 Flask-Caching-1.10.1 Flask-Script-2.0.6 Flask-Theme-0.3.5 Jinja2-2.11.3 Markdown-3.3.4 MarkupSafe-2.0.1 Werkzeug-1.0.1 XStatic-1.0.2 XStatic-AnyWikiDraw-0.14.2 XStatic-Bootstrap-3.1.1.2 XStatic-CKEditor-3.6.4.0 XStatic-Font-Awesome-4.7.0.0 XStatic-JQuery.TableSorter-2.14.5.2 XStatic-Pygments-2.9.0.1 XStatic-TWikiDraw-moin-2004.10.23.2 XStatic-autosize-1.17.2.1 XStatic-jQuery-3.5.1.1 XStatic-jQuery-File-Upload-10.31.0.1 XStatic-svg-edit-moin-2012.11.27.1 blinker-1.4 chardet-4.0.0 click-7.1.2 docutils-0.17.1 emeraldtree-0.10.0 feedgen-0.9.0 flatland-0.9.1 greenlet-1.1.0 itsdangerous-1.1.0 lxml-4.6.3 moin passlib-1.7.4 pdfminer3-2018.12.3.0 pycryptodome-3.10.1 pygments-2.9.0 python-dateutil-2.8.1 pytz-2021.1 six-1.16.0 sortedcontainers-2.4.0 sqlalchemy-1.4.17 whoosh-2.7.4
 +143 c:\my_projects\moin\.eggs\setuptools_scm-6.0.1-py3.9.egg\setuptools_scm\git.py:88: UserWarning: "C:\My_Projects\moin" is shallow and may cause errors
 +144   warnings.warn(f'"{wd.path}" is shallow and may cause errors')
 +154 Successfully created or updated venv at C:\My_Projects\moin-venv-python
 +Quickinstall run time (h:mm:ss) 0:02:47
 +
 +> > > Type "activate" to activate venv, then "m" for menu < < <
 +</code>
 +</WRAP>
 +^  ディレクトリ  ^^^  説明  ^
 +| + ^ moin || moin2 ソースコード  |
 +|%%|%%| + | quickinstall.py  | Quick Install プログラム  |
 +|%%|%%| + | m.bat  | menu バッチ |
 +| + ^ moin-venv-python || moin2 仮想環境  |
 +|%%|%%| + | Include  |  |
 +|%%|%%| + | Lib  |  |
 +|%%|%%| + | Scripts  |  |
 +
 +menu を実行する。\\
 +**Linux, PowerShell**\\
 +<WRAP prewrap 100%>
 +<code powershell>
 +$ ./m
 +</code>
 +</WRAP>
 +
 +コマンドプロンプト\\
 +<code>
 +> m
 +</code>
 +</WRAP>
 +<WRAP prewrap 100% result>
 +<code powershell>
 +
 +usage: "m <target>" where <target> is:
 +
 +quickinstall    update virtual environment with required packages
 +extras          install packages required for docs and moin development
 +docs            create moin html documentation (requires extras)
 +interwiki       refresh intermap.txt
 +log <target>    view detailed log generated by <target>, omit to see list
 +
 +new-wiki        create empty wiki
 +sample          create wiki and load sample data
 +restore *       create wiki and restore wiki/backup.moin *option, specify file
 +import19 <dir>  import a moin 1.9 wiki/data instance from <dir>
 +
 +run *           run built-in wiki server *options (--port 8081)
 +backup *        roll 3 prior backups and create new backup *option, specify file
 +dump-html *     create a static HTML image of wiki *options, see docs
 +index           delete and rebuild indexes
 +
 +css             run lessc to update basic theme CSS files
 +tests *         run tests, log output (-v -k my_test)
 +coding-std      correct scripts that taint the repository with trailing spaces..
 +
 +del-all         same as running the 4 del-* commands below
 +del-orig        delete all files matching *.orig
 +del-pyc         delete all files matching *.pyc
 +del-rej         delete all files matching *.rej
 +del-wiki        create a backup, then delete all wiki data
 +
 +</code>
 +
 +==== 空の Wiki を作成 ====
 +**Linux, PowerShell**\\
 +<WRAP prewrap 100%>
 +<code powershell>
 +$ ./m new-wiki
 +</code>
 +
 +コマンドプロンプト\\
 +<code>
 +> m new-wiki
 +</code>
 +</WRAP>
 +
 +<WRAP prewrap 100% result>
 +<code powershell>
 +Creating a new empty wiki...
 +Output messages redirected to m-new-wiki.txt.
 +Important messages from m-new-wiki.txt are shown below:
 +
 +Error: attempt to create wiki failed. Do "m log new-wiki" to see complete log.
 +</code>
 +</WRAP>
 +
 +==== moin2 の実行 ====
 +**Linux**\\
 +<code>
 +$ ./m run
 +</code>
 +**Windows**\\
 +<code>
 +> m run
 +</code>
 +<WRAP prewrap 100% result>
 +<code>
 +2019-08-09 18:54:51,020 INFO moin.log:150 using logging configuration read from built-in fallback in moin.log module!
 +2019-08-09 18:54:51,022 INFO moin.log:155 Running MoinMoin 2.0.0.dev546+g8ad1680d.d20190809 code from c:\my_projects\moin\src\moin
 +2019-08-09 18:54:51,639 INFO moin.utils.clock:45 timer create_app load config(0): 26.98ms
 +2019-08-09 18:54:51,725 INFO moin.utils.clock:45 timer create_app register(0): 85.95ms
 +2019-08-09 18:54:51,738 INFO moin.utils.clock:45 timer create_app flask-cache(0): 12.00ms
 +2019-08-09 18:54:51,770 INFO moin.utils.clock:45 timer create_app init backends(0): 31.98ms
 +2019-08-09 18:54:51,770 INFO moin.utils.clock:45 timer create_app flask-babel(0): 0.00ms
 +2019-08-09 18:54:51,772 INFO moin.utils.clock:45 timer create_app flask-theme(0): 2.00ms
 +2019-08-09 18:54:51,772 INFO moin.utils.clock:45 timer create_app total(0): 160.84ms
 + * Serving Flask app "moin" (lazy loading)
 + * Environment: production
 +   WARNING: This is a development server. Do not use it in a production deployment.
 +   Use a production WSGI server instead.
 + * Debug mode: off
 +2019-08-09 18:54:51,783 INFO werkzeug:122  * Running on http://127.0.0.1:8080/ (Press CTRL+C to quit)
 +</code>
 +</WRAP>
 +[[http://localhost:8080]] にブラウザーでアクセスする。\\
 +{{:python:moinmoin2.png?800|MoinMoin 2}}\\
 +
 +===== ApacheBench してみる =====
 +ローカル簡易実行での ApacheBench の結果。\\
 +**Web Server:** Werkzeug/0.15.6\\
 +**Wiki App:** MoinMoin2\\
 +**Lang:** Python 3.7.4\\
 +**Web Interface:** WSGI\\
 +<WRAP prewrap 100% result>
 +<code>
 +> ab -n 1000 -c 100 http://localhost:8080/Home
 +This is ApacheBench, Version 2.3 <$Revision: 1843412 $>
 +Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
 +Licensed to The Apache Software Foundation, http://www.apache.org/
 +
 +Benchmarking localhost (be patient)
 +Completed 100 requests
 +Completed 200 requests
 +Completed 300 requests
 +Completed 400 requests
 +Completed 500 requests
 +Completed 600 requests
 +Completed 700 requests
 +Completed 800 requests
 +Completed 900 requests
 +Completed 1000 requests
 +Finished 1000 requests
 +
 +
 +Server Software:        Werkzeug/0.15.6
 +Server Hostname:        localhost
 +Server Port:            8080
 +
 +Document Path:          /Home
 +Document Length:        17256 bytes
 +
 +Concurrency Level:      100
 +Time taken for tests:   350.456 seconds
 +Complete requests:      1000
 +Failed requests:        527
 +   (Connect: 0, Receive: 0, Length: 527, Exceptions: 0)
 +Total transferred:      17410304 bytes
 +HTML transferred:       17125304 bytes
 +Requests per second:    2.85 [#/sec] (mean)
 +Time per request:       35045.644 [ms] (mean)
 +Time per request:       350.456 [ms] (mean, across all concurrent requests)
 +Transfer rate:          48.51 [Kbytes/sec] received
 +
 +Connection Times (ms)
 +              min  mean[+/-sd] median   max
 +Connect:        0    0   0.5      0       1
 +Processing:   357 34234 4558.1  35388   40767
 +Waiting:      300 34203 4557.3  35359   40723
 +Total:        357 34234 4558.1  35389   40767
 +
 +Percentage of the requests served within a certain time (ms)
 +  50%  35389
 +  66%  37042
 +  75%  37648
 +  80%  38042
 +  90%  38871
 +  95%  39403
 +  98%  39964
 +  99%  40189
 + 100%  40767 (longest request)
 +</code>
 +</WRAP>
 +
 +1 秒間に平均どれくらいのリクエストをさばけるか?\\
 +<code>
 +Requests per second:    2.85 [#/sec] (mean)
 +</code>
 +1リクエストの平均応答時間は?\\
 +<code>
 +Time per request:       350.456 [ms] (mean, across all concurrent requests)
 +</code>
 +
 +**Web Server:** Apache/2.4.25\\
 +**Wiki App:** DokuWiki Release 2018-04-22b "Greebo"\\
 +**Lang:** PHP 7.3.3\\
 +**Web Interface:** FastCGI\\
 +<WRAP prewrap 100% result>
 +<code>
 +abs -n 1000 -c 100 https://wiki.monsters-g.local:1443/start
 +This is ApacheBench, Version 2.3 <$Revision: 1843412 $>
 +Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
 +Licensed to The Apache Software Foundation, http://www.apache.org/
 +
 +Benchmarking wiki.monsters-g.local (be patient)
 +Completed 100 requests
 +Completed 200 requests
 +Completed 300 requests
 +Completed 400 requests
 +Completed 500 requests
 +Completed 600 requests
 +Completed 700 requests
 +Completed 800 requests
 +Completed 900 requests
 +Completed 1000 requests
 +Finished 1000 requests
 +
 +
 +Server Software:        Apache/2.4.25
 +Server Hostname:        wiki.monsters-g.local
 +Server Port:            1443
 +SSL/TLS Protocol:       TLSv1.2,ECDHE-RSA-AES256-GCM-SHA384,2048,256
 +Server Temp Key:        ECDH P-256 256 bits
 +TLS Server Name:        wiki.monsters-g.local
 +
 +Document Path:          /start
 +Document Length:        19763 bytes
 +
 +Concurrency Level:      100
 +Time taken for tests:   62.989 seconds
 +Complete requests:      1000
 +Failed requests:        0
 +Total transferred:      20331000 bytes
 +HTML transferred:       19763000 bytes
 +Requests per second:    15.88 [#/sec] (mean)
 +Time per request:       6298.879 [ms] (mean)
 +Time per request:       62.989 [ms] (mean, across all concurrent requests)
 +Transfer rate:          315.21 [Kbytes/sec] received
 +
 +Connection Times (ms)
 +              min  mean[+/-sd] median   max
 +Connect:        7 1872 602.1   2033    3737
 +Processing:   100 3565 7886.9    620   58699
 +Waiting:       99 3563 7887.1    619   58698
 +Total:        284 5437 7678.7   2713   58830
 +
 +Percentage of the requests served within a certain time (ms)
 +  50%   2713
 +  66%   3079
 +  75%   3954
 +  80%   5102
 +  90%  11795
 +  95%  23513
 +  98%  33594
 +  99%  40993
 + 100%  58830 (longest request)
 +</code>
 +</WRAP>
 +
 +<code>
 +Requests per second:    15.88 [#/sec] (mean)
 +Time per request:       62.989 [ms] (mean, across all concurrent requests)
 +</code>
 +
 +===== 参考文献 =====
 +[[https://moin-20.readthedocs.io/en/latest/|Introducing MoinMoin — MoinMoin 0.1.dev194+gfdb6906 documentation]]\\
 +
  • python/moinmoin.1565293377.txt.gz
  • 最終更新: 2019/08/09 04:42
  • by ともやん