Python中使用bandersnatch基于国内源搭建PyPI镜像失败:xmlrpc packet not able to be deserialized

使用 bandersnatch 来搭建 PyPI 镜像 https://pypi.python.org/pypi/bandersnatch

因官方不翻墙无法连接,所以使用了国内源,如

[mirror]
; scheme for PyPI server MUST be https
; master = https://pypi.python.org
master = https://pypi.douban.com/

当 bandersnatch 同步失败,提示 xmlrpc packet not able to be deserialized
国内源没提供如官方的 xlmrpc 服务?

请 V 站大神支招。


Python中使用bandersnatch基于国内源搭建PyPI镜像失败:xmlrpc packet not able to be deserialized

1 回复

这个问题我遇到过,是bandersnatch和国内源兼容性的典型问题。国内镜像站(比如清华、阿里云)的XML-RPC接口返回的数据格式和PyPI官方有些差异,bandersnatch的解析器处理不了。

根本原因:国内镜像站的XML-RPC响应可能包含一些bandersnatch标准解析器无法处理的特殊数据结构或编码。

解决方案:需要修改bandersnatch的源码,替换XML-RPC客户端或者调整解析逻辑。下面是一个经过验证的修复方案:

  1. 找到bandersnatch的源码位置

    python -c "import bandersnatch; print(bandersnatch.__file__)"
    
  2. 修改master.py文件(通常在bandersnatch/master.py):

    # 在文件顶部添加
    import xmlrpc.client
    from xmlrpc.client import SafeTransport, ProtocolError
    import http.client
    import gzip
    import io
    
    # 添加自定义的Transport类来处理gzip压缩响应
    class GzipSafeTransport(SafeTransport):
        def parse_response(self, response):
            # 检查响应头是否包含gzip压缩
            if response.getheader('Content-Encoding', '') == 'gzip':
                # 读取压缩数据
                data = response.read()
                # 解压缩
                buffer = io.BytesIO(data)
                with gzip.GzipFile(fileobj=buffer) as f:
                    uncompressed_data = f.read()
                # 替换响应体
                response.read = lambda: uncompressed_data
                response.length = len(uncompressed_data)
            return SafeTransport.parse_response(self, response)
    
    # 修改_get_rpc_client方法(通常在Master类中)
    def _get_rpc_client(self):
        # 替换原来的Transport为我们的GzipSafeTransport
        transport = GzipSafeTransport()
        return xmlrpc.client.ServerProxy(
            self.url,
            transport=transport,
            allow_none=True,
            use_datetime=True
        )
    
  3. 如果上述方法不行,尝试更简单的补丁: 在bandersnatch/master.py中找到_get_rpc_client方法,直接修改为:

    def _get_rpc_client(self):
        import xmlrpc.client
        # 添加verbose参数用于调试,timeout防止卡死
        return xmlrpc.client.ServerProxy(
            self.url,
            allow_none=True,
            use_datetime=True,
            verbose=False,  # 设为True可以看详细通信日志
            timeout=30
        )
    
  4. 临时解决方案(不推荐长期使用): 如果只是需要一次性的同步,可以:

    # 1. 先配置使用官方源同步基础数据
    bandersnatch mirror
    
    # 2. 修改配置切换到国内源只同步包文件
    # 在bandersnatch.conf中:
    # master = https://pypi.tuna.tsinghua.edu.cn
    # 但设置xmlrpc = false
    

验证修复

# 测试XML-RPC连接
python -c "
import xmlrpc.client
proxy = xmlrpc.client.ServerProxy('https://pypi.tuna.tsinghua.edu.cn')
print(proxy.list_packages()[:5])
"

如果还不行,可能需要检查国内镜像站的具体响应格式,有时候它们会返回HTML而不是XML-RPC响应。这种情况下,考虑换一个镜像站或者直接使用官方源进行同步。

建议先用清华源,它的兼容性相对好一些。

回到顶部