#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
自动生成 pip 源配置文件,支持 Python 2 / Python 3,兼容 Windows / Linux / macOS。
用法:
python configure_pip_mirror.py # 默认使用阿里云源
python configure_pip_mirror.py --source douban # 使用豆瓣源
python configure_pip_mirror.py -s tsinghua # 使用清华源
python configure_pip_mirror.py -s aliyun # 使用阿里云源
python configure_pip_mirror.py --source ustc # 使用中科大源
python configure_pip_mirror.py --list # 列出所有可选源
python configure_pip_mirror.py --url https://xxx/simple # 自定义源地址
python configure_pip_mirror.py --url https://xxx/simple --trusted-host xxx # 自定义
"""
from __future__ import print_function, unicode_literals
import os
import platform
import sys
import json
# 兼容 Python 2 的 argparse 和 io.open
if sys.version_info[0] == 2:
from io import open
try:
import argparse
except ImportError:
print("错误: Python 2 需要安装 argparse 模块: pip install argparse")
sys.exit(1)
else:
import argparse
# ============================================================
# 预定义的镜像源
# ============================================================
MIRRORS = {
"aliyun": {
"name": "阿里云",
"index_url": "http://mirrors.aliyun.com/pypi/simple/",
"trusted_host": "mirrors.aliyun.com",
"homepage": "https://mirrors.aliyun.com",
},
"douban": {
"name": "豆瓣",
"index_url": "http://pypi.douban.com/simple",
"trusted_host": "pypi.douban.com",
"homepage": "http://pypi.douban.com",
},
"tsinghua": {
"name": "清华大学",
"index_url": "https://pypi.tuna.tsinghua.edu.cn/simple",
"trusted_host": "pypi.tuna.tsinghua.edu.cn",
"homepage": "https://mirrors.tuna.tsinghua.edu.cn",
},
"ustc": {
"name": "中国科学技术大学",
"index_url": "https://pypi.mirrors.ustc.edu.cn/simple",
"trusted_host": "pypi.mirrors.ustc.edu.cn",
"homepage": "https://mirrors.ustc.edu.cn",
},
"huawei": {
"name": "华为云",
"index_url": "https://repo.huaweicloud.com/repository/pypi/simple",
"trusted_host": "repo.huaweicloud.com",
"homepage": "https://mirrors.huaweicloud.com",
},
"tencent": {
"name": "腾讯云",
"index_url": "https://mirrors.cloud.tencent.com/pypi/simple",
"trusted_host": "mirrors.cloud.tencent.com",
"homepage": "https://mirrors.cloud.tencent.com",
},
"netease": {
"name": "网易",
"index_url": "http://mirrors.163.com/pypi/simple",
"trusted_host": "mirrors.163.com",
"homepage": "http://mirrors.163.com",
},
"pypi": {
"name": "PyPI 官方",
"index_url": "https://pypi.org/simple",
"trusted_host": "pypi.org",
"homepage": "https://pypi.org",
},
}
def expanduser(path):
"""安全的 expanduser,处理 Windows 下 ~/ 开头的路径问题。"""
expanded = os.path.expanduser(path)
if path.startswith('~/') and expanded.startswith('//'):
expanded = expanded[1:]
return expanded
def get_pip_config_path():
"""获取 pip 配置文件的完整路径(跨平台)。"""
system = platform.system()
user_dir = expanduser('~')
if system == 'Windows':
config_basename = 'pip.ini'
legacy_storage_dir = os.path.join(user_dir, 'pip')
else:
config_basename = 'pip.conf'
legacy_storage_dir = os.path.join(user_dir, '.pip')
return os.path.join(legacy_storage_dir, config_basename)
def build_config_content(index_url, trusted_host):
"""生成 pip 配置文件内容。"""
return r'''[global]
index-url = {index_url}
[install]
trusted-host = {trusted_host}
'''.format(index_url=index_url, trusted_host=trusted_host)
def list_mirrors():
"""打印所有预定义源。"""
header = "{:<15} {:<16} {:<36} {}".format("别名", "名称", "源地址", "可信主机")
print(header)
print("-" * 95)
for key, mirror in sorted(MIRRORS.items()):
print("{:<15} {:<16} {:<36} {}".format(
key, mirror["name"], mirror["index_url"], mirror["trusted_host"]))
print()
print("使用方式: python configure_pip_mirror.py -s <别名>")
print("示例: python configure_pip_mirror.py -s tsinghua")
def create_pip_config(index_url, trusted_host, force=False):
"""生成 pip 源配置文件,如有旧文件则备份。"""
file_path = get_pip_config_path()
dirname = os.path.dirname(file_path)
basename = os.path.basename(file_path)
print('当前系统: {}'.format(platform.system()))
print('配置文件路径: {}'.format(file_path))
# 创建目录(如果不存在)
try:
os.makedirs(dirname)
print('创建目录: {}'.format(dirname))
except OSError:
pass # 目录已存在
# 备份已有配置文件
if os.path.isfile(file_path):
if force:
os.remove(file_path)
print('已删除旧配置 (--force): {}'.format(file_path))
else:
backup_path = os.path.join(dirname, basename + '.bak')
if os.path.isfile(backup_path):
os.remove(backup_path)
os.rename(file_path, backup_path)
print('已备份原配置至: {}'.format(backup_path))
# 写入新配置
content = build_config_content(index_url, trusted_host)
with open(file_path, 'w', encoding='utf-8') as f:
f.write(content)
print('✅ pip 源配置文件已生成: {}'.format(file_path))
print(' 源地址: {}'.format(index_url))
print(' 可信主机: {}'.format(trusted_host))
return file_path
def parse_args(argv=None):
"""解析命令行参数。"""
parser = argparse.ArgumentParser(
description='自动生成 pip 源配置文件(支持 Python 2/3,跨平台)',
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
示例:
%(prog)s 使用默认阿里云源
%(prog)s -s tsinghua 使用清华源
%(prog)s --source douban 使用豆瓣源
%(prog)s --list 列出所有可选源
%(prog)s -u https://pypi.org/simple -t pypi.org 自定义源
%(prog)s -s aliyun -f 使用阿里云源,不备份直接覆盖
""",
)
parser.add_argument(
'-s', '--source',
metavar='别名',
help='使用预定义源 (如 aliyun, douban, tsinghua, ustc 等),'
'使用 --list 查看所有可选源',
)
parser.add_argument(
'-u', '--url',
metavar='地址',
help='自定义 PyPI 源完整地址 (如 https://pypi.tuna.tsinghua.edu.cn/simple)',
)
parser.add_argument(
'-t', '--trusted-host',
metavar='域名',
help='自定义可信主机域名 (如 pypi.tuna.tsinghua.edu.cn)',
)
parser.add_argument(
'-f', '--force',
action='store_true',
help='不备份已有配置文件,直接覆盖',
)
parser.add_argument(
'--list',
action='store_true',
dest='show_list',
help='列出所有预定义源及其别名',
)
parser.add_argument(
'--json',
action='store_true',
dest='show_json',
help='以 JSON 格式输出所有预定义源信息',
)
args = parser.parse_args(argv)
# 处理 --list 和 --json 这两个纯展示参数
if args.show_list:
list_mirrors()
sys.exit(0)
if args.show_json:
print(json.dumps(MIRRORS, indent=2, ensure_ascii=False))
sys.exit(0)
return args
def resolve_source(args):
"""
解析参数,确定最终的 index_url 和 trusted_host。
优先级: --url > --source > 默认 (aliyun)
"""
index_url = None
trusted_host = None
if args.source:
source_key = args.source.lower()
if source_key not in MIRRORS:
print("错误: 未知源 '{}'。使用 --list 查看所有可选源。".format(source_key))
sys.exit(1)
mirror = MIRRORS[source_key]
index_url = mirror["index_url"]
trusted_host = mirror["trusted_host"]
print('使用源: {} ({})'.format(mirror["name"], source_key))
if args.url:
index_url = args.url
if args.trusted_host:
trusted_host = args.trusted_host
elif not trusted_host:
# 尝试从 URL 中提取 host
try:
from urllib.parse import urlparse
except ImportError:
from urlparse import urlparse # Python 2
parsed = urlparse(index_url)
trusted_host = parsed.hostname or index_url
print('自定义源地址: {}'.format(index_url))
if not index_url:
# 默认使用阿里云
mirror = MIRRORS["aliyun"]
index_url = mirror["index_url"]
trusted_host = mirror["trusted_host"]
print('使用默认源: {} ({})'.format(mirror["name"], "aliyun"))
if args.url and not args.trusted_host and not args.source:
print('提示: 未指定 --trusted-host,已自动从 URL 解析为: {}'.format(trusted_host))
return index_url, trusted_host
def main():
args = parse_args()
index_url, trusted_host = resolve_source(args)
create_pip_config(index_url, trusted_host, force=args.force)
if __name__ == '__main__':
main()