使用Python脚本来给pip换源(支持Python2/Python3)

342 阅读2分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

换源这事,对于经常换机器和经常操作新服务器的我来说,实在是太常见了。但除了豆瓣之外,其他源的地址,我是记不住的。可惜,小明明已不在豆瓣,所以也不是很想用这个源了。

首先,常用的pip源如下:

https://pypi.tuna.tsinghua.edu.cn/simple/
https://pypi.douban.com/simple/
https://mirrors.cloud.tencent.com/pypi/simple/
https://repo.huaweicloud.com/repository/pypi/simple/
https://mirrors.aliyun.com/pypi/simple/

于是,我自己写了一个脚本来,想换哪个就换哪个,都是一行命令搞定:cat pip_conf.py

#!/usr/bin/env python
"""
This script is for pip source config.
Worked both Windows and Linux/Mac, support python2.7 and python3.5+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Usage:
    $ ./pip_conf.py  # default to tencent source

Or:
    $ python pip_conf.py tx
    $ python pip_conf.py huawei
    $ python pip_conf.py aliyun
    $ python pip_conf.py douban
    $ python pip_conf.py qinghua

    $ python pip_conf.py --list  # show choices

    $ sudo python pip_conf.py --etc  # Set conf file to /etc
"""
import os
import platform
import pprint
import re
import socket

"""
A sample of the pip.conf/pip.ini:

[global] index-url = mirrors.cloud.tencent.com/pypi/simple…

[install] trusted-host = mirrors.cloud.tencent.com

"""

TEMPLATE = """
[global]
index-url = https://{}/simple/
[install]
trusted-host = {}
""".strip()
DEFAULT = "tx"
SOURCES = {
    "aliyun": "mirrors.aliyun.com/pypi",
    "douban": "pypi.douban.com",
    "qinghua": "pypi.tuna.tsinghua.edu.cn",
    "tx": "mirrors.cloud.tencent.com/pypi",
    "tx_ecs": "mirrors.tencentyun.com/pypi",
    "huawei": "repo.huaweicloud.com/repository/pypi",
    "ali_ecs": "mirrors.cloud.aliyuncs.com/pypi",
}
SOURCES["tencent"] = SOURCES["tengxun"] = SOURCES["tx"]
SOURCES["ali"] = SOURCES["aliyun"]
CONF_CMD = "pip config set global.index-url https://{}/simple/"


def is_pingable(domain):
    try:
        socket.gethostbyname(domain)
    except Exception:
        return False
    return True


def is_tx_cloud_server():
    return is_pingable("mirrors.tencentyun.com")


def is_ali_cloud_server():
    return is_pingable("mirrors.cloud.aliyuncs.com")


def config_by_cmd(url, conf_cmd=None):
    if conf_cmd is None:
        conf_cmd = CONF_CMD
    if url.startswith("http"):
        cmd = conf_cmd.split("http")[0] + url
    else:
        cmd = conf_cmd.format(url)
    if "https" not in cmd:
        print("cmd = {}".format(repr(cmd)))
        host = cmd.split("://", 1)[1].split("/", 1)[0]
        cmd += " && pip config set install.trusted-host " + host
    print("--> " + cmd)
    os.system(cmd)


def init_pip_conf(
    source=DEFAULT,
    replace=False,
    at_etc=False,
    force=False,
    template=TEMPLATE,
    conf_cmd=CONF_CMD,
):
    if not force:
        if "ali" in source:
            if is_ali_cloud_server():
                source = "ali_ecs"
                template = template.replace("https", "http")
                conf_cmd = conf_cmd.replace("https", "http")
        elif "tx" in source or "ten" in source:
            if is_tx_cloud_server():
                source = "tx_ecs"
                template = template.replace("https", "http")
                conf_cmd = conf_cmd.replace("https", "http")
    is_raw_url = source.startswith("http")
    if is_raw_url:
        url = source
    else:
        url = SOURCES.get(source, SOURCES[DEFAULT])
    is_windows = platform.system() == "Windows"
    if (not at_etc or is_windows) and can_set_global():
        config_by_cmd(url, conf_cmd)
        return
    if is_windows:
        _pip_conf = ("pip", "pip.ini")
        conf_file = os.path.join(os.path.expanduser("~"), *_pip_conf)
    else:
        if at_etc:
            conf_file = os.path.join("/etc", "pip.conf")
        else:
            _pip_conf = (".pip", "pip.conf")
            conf_file = os.path.join(os.path.expanduser("~"), *_pip_conf)
    parent = os.path.dirname(conf_file)
    if not os.path.exists(parent):
        os.mkdir(parent)
    if is_raw_url:
        url = url.split("://")[-1].split("/simple")[0]
    text = template.format(url, url.split("/")[0])
    if os.path.exists(conf_file):
        with open(conf_file) as fp:
            s = fp.read()
        if text in s:
            print("Pip source already be configured as expected.\nSkip!")
            return
        if not replace:
            print("The pip file {} exists! content:".format(conf_file))
            print(s)
            print('If you want to replace it, rerun with "-y" in args.\nExit!')
            return
    with open(conf_file, "w") as fp:
        fp.write(text + "\n")
    print("Write lines to `{}` as below:\n{}\n".format(conf_file, text))
    print("Done!")


def can_set_global():
    with os.popen("pip --version") as p:
        s = p.read()
    version = re.search(r"^pip (\d+)\.(\d+).(\d+)", s)
    if not version:
        version = re.search(r"^pip (\d+)\.(\d+)", s)
    if version and [int(i) for i in version.groups()] >= [10, 1, 0]:
        return True
    return False


def main():
    from argparse import ArgumentParser

    parser = ArgumentParser()
    source_help = "the source of pip, ali/douban/huawei/qinghua or tx(default)"
    parser.add_argument("name", nargs="?", default="", help=source_help)
    # Be compatible with old version
    parser.add_argument("-s", "--source", default=DEFAULT, help=source_help)
    parser.add_argument(
        "-l", "--list", action="store_true", help="Show available mirrors"
    )
    parser.add_argument("-y", action="store_true", help="whether replace existing file")
    parser.add_argument("--etc", action="store_true", help="Set conf file to /etc")
    parser.add_argument("-f", action="store_true", help="Force to skip ecs cloud check")
    args = parser.parse_args()
    if args.list:
        print("There are several mirrors that can be used for pip/poetry:")
        pprint.pprint(SOURCES)
    else:
        init_pip_conf(args.name or args.source, args.y, args.etc, args.f)


if __name__ == "__main__":
    main()

平时,我在新服务器上,是这么给pip换源的:

cd
mkdir archives
cd archives
git clone https://github.com/waketzheng/carstino
cd carstino
./pip_conf.py
python -m pip install -U pip