contrib/packaging/hg-docker
author Gregory Szorc <gregory.szorc@gmail.com>
Thu, 06 Sep 2018 18:04:27 -0700
changeset 39586 f296c0b366c8
parent 38462 e5916f1236f3
child 41233 92b3811fd15f
permissions -rwxr-xr-x
util: lower water mark when removing nodes after cost limit reached See the inline comment for the reasoning here. This is a pretty common strategy for garbage collectors, other cache-like primtives. The performance impact is substantial: $ hg perflrucachedict --size 4 --gets 1000000 --sets 1000000 --mixed 1000000 --costlimit 100 ! inserts w/ cost limit ! wall 1.659181 comb 1.650000 user 1.650000 sys 0.000000 (best of 7) ! wall 1.722122 comb 1.720000 user 1.720000 sys 0.000000 (best of 6) ! mixed w/ cost limit ! wall 1.139955 comb 1.140000 user 1.140000 sys 0.000000 (best of 9) ! wall 1.182513 comb 1.180000 user 1.180000 sys 0.000000 (best of 9) $ hg perflrucachedict --size 1000 --gets 1000000 --sets 1000000 --mixed 1000000 --costlimit 10000 ! inserts ! wall 0.679546 comb 0.680000 user 0.680000 sys 0.000000 (best of 15) ! sets ! wall 0.825147 comb 0.830000 user 0.830000 sys 0.000000 (best of 13) ! inserts w/ cost limit ! wall 25.105273 comb 25.080000 user 25.080000 sys 0.000000 (best of 3) ! wall 1.724397 comb 1.720000 user 1.720000 sys 0.000000 (best of 6) ! mixed ! wall 0.807096 comb 0.810000 user 0.810000 sys 0.000000 (best of 13) ! mixed w/ cost limit ! wall 12.104470 comb 12.070000 user 12.070000 sys 0.000000 (best of 3) ! wall 1.190563 comb 1.190000 user 1.190000 sys 0.000000 (best of 9) $ hg perflrucachedict --size 1000 --gets 1000000 --sets 1000000 --mixed 1000000 --costlimit 10000 --mixedgetfreq 90 ! inserts ! wall 0.711177 comb 0.710000 user 0.710000 sys 0.000000 (best of 14) ! sets ! wall 0.846992 comb 0.850000 user 0.850000 sys 0.000000 (best of 12) ! inserts w/ cost limit ! wall 25.963028 comb 25.960000 user 25.960000 sys 0.000000 (best of 3) ! wall 2.184311 comb 2.180000 user 2.180000 sys 0.000000 (best of 5) ! mixed ! wall 0.728256 comb 0.730000 user 0.730000 sys 0.000000 (best of 14) ! mixed w/ cost limit ! wall 3.174256 comb 3.170000 user 3.170000 sys 0.000000 (best of 4) ! wall 0.773186 comb 0.770000 user 0.770000 sys 0.000000 (best of 13) $ hg perflrucachedict --size 100000 --gets 1000000 --sets 1000000 --mixed 1000000 --mixedgetfreq 90 --costlimit 5000000 ! gets ! wall 1.191368 comb 1.190000 user 1.190000 sys 0.000000 (best of 9) ! wall 1.195304 comb 1.190000 user 1.190000 sys 0.000000 (best of 9) ! inserts ! wall 0.950995 comb 0.950000 user 0.950000 sys 0.000000 (best of 11) ! inserts w/ cost limit ! wall 1.589732 comb 1.590000 user 1.590000 sys 0.000000 (best of 7) ! sets ! wall 1.094941 comb 1.100000 user 1.090000 sys 0.010000 (best of 9) ! mixed ! wall 0.936420 comb 0.940000 user 0.930000 sys 0.010000 (best of 10) ! mixed w/ cost limit ! wall 0.882780 comb 0.870000 user 0.870000 sys 0.000000 (best of 11) This puts us ~2x slower than caches without cost accounting. And for read-heavy workloads (the prime use cases for caches), performance is nearly identical. In the worst case (pure write workloads with cost accounting enabled), we're looking at ~1.5us per insert on large caches. That seems "fast enough." Differential Revision: https://phab.mercurial-scm.org/D4505
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
38462
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
     1
#!/usr/bin/env python3
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
     2
#
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
     3
# Copyright 2018 Gregory Szorc <gregory.szorc@gmail.com>
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
     4
#
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
     5
# This software may be used and distributed according to the terms of the
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
     6
# GNU General Public License version 2 or any later version.
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
     7
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
     8
import argparse
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
     9
import pathlib
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    10
import shutil
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    11
import subprocess
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    12
import sys
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    13
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    14
def get_docker() -> str:
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    15
    docker = shutil.which('docker.io') or shutil.which('docker')
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    16
    if not docker:
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    17
        print('could not find docker executable')
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    18
        return 1
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    19
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    20
    try:
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    21
        out = subprocess.check_output([docker, '-h'], stderr=subprocess.STDOUT)
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    22
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    23
        if b'Jansens' in out:
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    24
            print('%s is the Docking System Tray; try installing docker.io' %
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    25
                  docker)
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    26
            sys.exit(1)
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    27
    except subprocess.CalledProcessError as e:
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    28
        print('error calling `%s -h`: %s' % (docker, e.output))
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    29
        sys.exit(1)
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    30
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    31
    out = subprocess.check_output([docker, 'version'],
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    32
                                  stderr=subprocess.STDOUT)
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    33
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    34
    lines = out.splitlines()
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    35
    if not any(l.startswith((b'Client:', b'Client version:')) for l in lines):
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    36
        print('`%s version` does not look like Docker' % docker)
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    37
        sys.exit(1)
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    38
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    39
    if not any(l.startswith((b'Server:', b'Server version:')) for l in lines):
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    40
        print('`%s version` does not look like Docker' % docker)
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    41
        sys.exit(1)
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    42
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    43
    return docker
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    44
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    45
def get_dockerfile(path: pathlib.Path, args: list) -> bytes:
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    46
    with path.open('rb') as fh:
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    47
        df = fh.read()
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    48
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    49
    for k, v in args:
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    50
        df = df.replace(b'%%%s%%' % k, v)
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    51
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    52
    return df
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    53
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    54
def build_docker_image(dockerfile: pathlib.Path, params: list, tag: str):
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    55
    """Build a Docker image from a templatized Dockerfile."""
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    56
    docker = get_docker()
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    57
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    58
    dockerfile_path = pathlib.Path(dockerfile)
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    59
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    60
    dockerfile = get_dockerfile(dockerfile_path, params)
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    61
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    62
    print('building Dockerfile:')
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    63
    print(dockerfile.decode('utf-8', 'replace'))
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    64
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    65
    args = [
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    66
        docker,
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    67
        'build',
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    68
        '--build-arg', 'http_proxy',
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    69
        '--build-arg', 'https_proxy',
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    70
        '--tag', tag,
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    71
        '-',
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    72
    ]
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    73
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    74
    print('executing: %r' % args)
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    75
    subprocess.run(args, input=dockerfile, check=True)
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    76
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    77
def command_build(args):
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    78
    build_args = []
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    79
    for arg in args.build_arg:
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    80
        k, v = arg.split('=', 1)
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    81
        build_args.append((k.encode('utf-8'), v.encode('utf-8')))
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    82
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    83
    build_docker_image(pathlib.Path(args.dockerfile),
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    84
                       build_args,
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    85
                       args.tag)
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    86
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    87
def command_docker(args):
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    88
    print(get_docker())
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    89
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    90
def main() -> int:
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    91
    parser = argparse.ArgumentParser()
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    92
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    93
    subparsers = parser.add_subparsers(title='subcommands')
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    94
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    95
    build = subparsers.add_parser('build', help='Build a Docker image')
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    96
    build.set_defaults(func=command_build)
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    97
    build.add_argument('--build-arg', action='append', default=[],
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    98
                        help='Substitution to perform in Dockerfile; '
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
    99
                             'format: key=value')
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   100
    build.add_argument('dockerfile', help='path to Dockerfile to use')
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   101
    build.add_argument('tag', help='Tag to apply to created image')
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   102
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   103
    docker = subparsers.add_parser('docker-path', help='Resolve path to Docker')
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   104
    docker.set_defaults(func=command_docker)
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   105
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   106
    args = parser.parse_args()
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   107
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   108
    return args.func(args)
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   109
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   110
if __name__ == '__main__':
e5916f1236f3 packaging: replace dockerlib.sh with a Python script
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
   111
    sys.exit(main())