自学内容网 自学内容网

YOLOv9-0.1部分代码阅读笔记-downloads.py

downloads.py

utils\downloads.py

目录

downloads.py

1.所需的库和模块

2.def is_url(url, check=True): 

3.def gsutil_getsize(url=''): 

4.def url_getsize(url='https://ultralytics.com/images/bus.jpg'): 

5.def safe_download(file, url, url2=None, min_bytes=1E0, error_msg=''): 

6.def attempt_download(file, repo='ultralytics/yolov5', release='v7.0'): 


1.所需的库和模块

import logging
import os
import subprocess
import urllib
from pathlib import Path

import requests
import torch

2.def is_url(url, check=True): 

# 这段代码定义了一个名为 is_url 的函数,它用来检查一个字符串是否是一个有效的URL,并且如果 check 参数为 True ,它还会检查这个URL是否在线存在。
# 这行定义了一个名为  is_url  的函数,它接受两个参数。
# 1.url :是你想要检查的URL字符串。
# 2.check :是一个布尔值,默认为 True ,表示是否检查URL是否存在于互联网上。
def is_url(url, check=True):
    # Check if string is URL and check if URL exists    检查字符串是否为 URL 并检查 URL 是否存在。
    # 开始了一个  try  块,用于捕获并处理可能发生的异常。
    try:
        # 确保 url 参数是一个字符串,如果不是,它会被转换成字符串。
        url = str(url)

        # result = urlparse(urlstring, scheme='', allow_fragments=True)
        # urlparse() 函数是 Python 标准库 urllib.parse 模块中的一个函数,用于解析 URL(统一资源定位符)并将其分解为组件。这个函数在处理网络地址时非常有用,因为它可以将复杂的 URL 分解成易于管理的部分。
        # 参数 :
        # urlstring : 要解析的 URL 字符串。
        # scheme : (可选)如果提供,将用于覆盖 URL 中的方案部分。
        # allow_fragments : (可选)一个布尔值,指示是否允许解析 URL 的片段部分(即 # 后面的部分)。默认为 True 。
        # 返回值 :
        # urlparse() 函数返回一个 ParseResult 对象,该对象包含以下属性 :
        # scheme : URL 的方案部分(例如 http 、 https )。
        # netloc : 网络位置部分(例如域名和端口)。
        # path : URL 的路径部分。
        # params : URL 的参数部分( ? 后面的部分)。
        # query : URL 的查询部分( ? 后面的部分,不包括 # )。
        # fragment : URL 的片段部分( # 后面的部分)。
        # urlparse() 函数是处理 URL 的基础工具,常用于网络编程、Web 开发和任何需要解析或构造 URL 的场景。

        # 使用 urllib.parse.urlparse 函数来解析 url 字符串。这个函数会返回一个 ParseResult 对象,其中包含了URL的组成部分,如协议(scheme)、网络位置(netloc)等。
        result = urllib.parse.urlparse(url)
        # 使用 assert 语句来确保解析后的URL对象中既包含协议(scheme)也包含网络位置(netloc)。如果这两个条件不满足, assert 会抛出 AssertionError ,表示这不是一个有效的URL。
        assert all([result.scheme, result.netloc])  # check if is url

        # urllib.request.urlopen(url, data=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT, *, cafile=None, capath=None, cadefault=False, context=None)
        # urllib.request.urlopen() 是 Python 标准库 urllib 模块中的一个函数,它用于打开一个 URL 并返回一个类似文件对象的东西,可以用来读取从 URL 获取的数据。这个函数是 urllib.request 模块的一部分,该模块提供了用于处理 URL 的函数和类。
        # 参数 :
        # url : 要打开的 URL 字符串。
        # data : 可选参数,用于发送 POST 请求的数据。如果是字节字符串,将作为请求体发送。如果是字典或序列的元组,将被编码为表单数据并发送。
        # timeout : 可选参数,指定等待服务器响应的超时时间(以秒为单位)。默认值为 socket._GLOBAL_DEFAULT_TIMEOUT 。
        # cafile : 可选参数,指定包含受信任的 CA 证书的文件路径,用于验证 SSL 证书。
        # capath : 可选参数,指定包含多个 CA 证书的目录路径。
        # cadefault : 布尔值,如果为 True ,则使用默认的 CA 证书。
        # context : 可选参数,用于指定 SSL 上下文。
        # 返回值 :
        # 返回一个 urllib.response.addinfourl 对象,该对象继承自 io.IOBase ,提供了一个文件类的接口来读取服务器的响应。这个对象包含了响应的状态码、响应头等信息。
        # 异常 :
        # urllib.error.URLError : 如果无法打开 URL,抛出此异常。
        # urllib.error.HTTPError : 如果 HTTP 请求返回了错误的状态码,抛出此异常。
        # urllib.response.addinfourl 对象中常用的属性和方法 :
        # 属性 :
        # url :返回请求的 URL,这在你处理重定向时非常有用,因为它可以告诉你实际请求的是哪个 URL。
        # status :返回 HTTP 响应的状态码,例如 200、404、500 等。
        # headers :返回一个类似字典的对象,包含了响应的头部信息。
        # version :返回 HTTP 协议的版本,例如 10(HTTP/1.0)或 11(HTTP/1.1)。
        # reason :返回一个字符串,表示与状态码对应的原因短语,例如 "OK"、"Not Found" 等。
        # 方法1 :
        # geturl() :返回资源检索的 URL,如果发生了重定向,将返回最终的 URL。
        # info() :返回页面的元信息,如头部信息,作为一个 email.message_from_string() 实例返回。
        # getcode() :返回响应的 HTTP 状态码。
        # read() :读取响应的内容。如果响应内容是压缩的,可以使用 io.BufferedReader 或 gzip.GzipFile 等来解压缩。
        # readline() :读取响应的一行内容。
        # readlines() :读取所有响应内容,并按行分割返回列表。
        # fileno() :返回文件描述符。
        # close() :关闭响应对象,释放系统资源。

        # 函数的返回部分。如果 check 参数为 True ,它会尝试使用 urllib.request.urlopen 打开URL,并检查返回的HTTP状态码是否为200(表示请求成功)。如果 check 为 False ,则不进行在线检查,直接返回 True 。
        return (urllib.request.urlopen(url).getcode() == 200) if check else True  # check if exists online
    # 定义了 except 块,用于捕获 try 块中可能抛出的 AssertionError 和 urllib.request.HTTPError 异常。
    except (AssertionError, urllib.request.HTTPError):
        # 如果在 try 块中抛出了异常, except 块会捕获这些异常,并返回 False ,表示URL无效或不存在于互联网上。
        return False
# 这个函数用于检查给定的字符串是否是有效的URL,并且如果需要,还可以检查该URL是否能够在线访问。

3.def gsutil_getsize(url=''): 

# 这段 Python 代码定义了一个名为 gsutil_getsize 的函数,其目的是获取通过 Google Cloud Storage 的 gsutil 工具指定 URL 的文件大小(以字节为单位)。
# 定义了一个名为 gsutil_getsize 的函数,它接受一个参数。
# 1.url :默认值为空字符串。这个参数应该是指向 Google Cloud Storage 上的文件或目录的 URL。
def gsutil_getsize(url=''):
    # gs://bucket/file size https://cloud.google.com/storage/docs/gsutil/commands/du

    # subprocess.check_output(*popenargs, input=None, timeout=None, **kwargs)
    # subprocess.check_output 是 Python 标准库 subprocess 模块中的一个函数,用于执行一个命令并获取它的输出。如果命令执行失败(即返回一个非零退出状态),该函数会抛出一个 CalledProcessError 异常。
    # 参数 :
    # *popenargs :这是一组参数,它们将被传递给 subprocess.Popen 构造函数。通常,这些参数包括命令和它的参数,类似于在 shell 中执行命令的方式。
    # input :(可选)一个字符串或字节序列,将被发送到 subprocess 的 stdin。
    # timeout :(可选)一个浮点数或整数,指定等待 subprocess 完成的秒数。如果超时,将抛出 TimeoutExpired 异常。
    # **kwargs :(可选)其他参数将被传递给 subprocess.Popen 构造函数。
    # 返回值 :
    # 函数返回 subprocess 的 stdout 输出,作为一个字节串(bytes)。
    # 异常 :
    # CalledProcessError :如果 subprocess 返回一个非零退出状态。
    # TimeoutExpired :如果 subprocess 超过指定的 timeout 时间。
    # 注意事项 :
    # 默认情况下, check_output 函数返回的输出是字节串。如果你需要字符串,可以指定 text=True 参数(在 Python 3.7+ 中)或者使用 universal_newlines=True 参数(在 Python 3.6 及以下版本中)。
    # 使用 shell=True 参数时需要特别小心,因为它可能会使程序容易受到 shell 注入攻击。只有当你能够完全信任输入数据时,才应该使用 shell=True 。
    # timeout 参数在 Python 3.3 及以上版本中可用。

    # 使用 Python 的 subprocess 模块来执行外部命令。 subprocess.check_output 函数运行指定的命令并捕获其输出。
    # 这里,命令是 gsutil du {url} ,其中 {url} 是函数参数 url 的值,用于指定要检查大小的 Google Cloud Storage 上的对象。 shell=True 参数允许直接在 shell 中运行命令,这可能带来安全风险,因为它可能会受到 shell 注入攻击的影响。输出被解码为 UTF-8 格式的字符串。
    s = subprocess.check_output(f'gsutil du {url}', shell=True).decode('utf-8')

    # eval(expression, globals=None, locals=None)
    # eval() 函数是 Python 的内置函数,用于计算表达式字符串,并返回表达式的值。
    # 参数 :
    # expression :一个字符串形式的 Python 表达式。这个表达式可以包含 Python 中有效的任意表达式,例如数字、变量、运算符、函数调用等。
    # globals :一个字典,包含表达式将会使用的全局变量。如果为 None (默认值),则使用当前环境的全局变量。如果提供了 globals 参数,它将覆盖当前环境的全局变量。
    # locals :一个字典,包含表达式将会使用的局部变量。如果为 None (默认值),则使用当前环境的局部变量。如果提供了 locals 参数,它将覆盖当前环境的局部变量。
    # 返回值 :
    # 返回 expression 表达式计算后的结果。
    # 安全性 :
    # 使用 eval() 时需要特别小心,因为它可以执行任意代码。如果 expression 来自不可信的源,那么它可能会执行恶意代码,导致安全问题。
    # 在实际编程中,通常推荐避免使用 eval() ,除非完全信任输入源,或者确实需要动态执行代码。在很多情况下,可以使用更安全的方法来替代 eval() ,例如使用 ast.literal_eval() 来计算字面量表达式。

    # 首先检查 s (即命令输出的字符串)的长度,如果 s 不为空,则使用 split(' ') 方法将字符串按空格分割成列表,并取列表的第一个元素(即文件大小的值),然后使用 eval 函数将其转换为 Python 表达式。 eval 函数能够将字符串形式的 Python 表达式转换为相应的 Python 对象。
    # 如果 s 为空,则函数返回 0 。这里的注释 # bytes 表示返回的大小是以字节为单位的。
    return eval(s.split(' ')[0]) if len(s) else 0  # bytes
# 这个函数通过调用 gsutil du 命令来获取 Google Cloud Storage 上文件的大小。它使用 subprocess.check_output 来执行命令并捕获输出,然后解析输出以获取文件大小。
# 然而,这个函数存在两个潜在问题 :
# 安全性问题 :使用 shell=True 可能会使函数容易受到 shell 注入攻击。如果 url 参数来自不可信的源,攻击者可能会注入恶意命令。
# 使用 eval :虽然在这个特定场景中可能安全,但 eval 函数通常被认为是不安全的,因为它可以执行任意代码。如果输出格式不可预测或不受信任,使用 eval 可能会导致安全问题。
# 为了提高安全性,可以考虑以下改进 :
# 避免使用 shell=True ,而是将命令参数分开传递给 subprocess.check_output 。
# 使用更安全的方法来解析输出,例如直接转换为整数,而不是使用 eval 。

4.def url_getsize(url='https://ultralytics.com/images/bus.jpg'): 

# 这段代码定义了一个名为 url_getsize 的函数,它用于获取指定 URL 的可下载文件大小(以字节为单位)。
# 定义了一个名为 url_getsize 的函数,它接受一个参数。
# 1.url :默认值为 `'https://ultralytics.com/images/bus.jpg'`。这个参数应该是指向可下载文件的 URL。
def url_getsize(url='https://ultralytics.com/images/bus.jpg'):
    # Return downloadable file size in bytes    返回可下载文件的大小(以字节为单位)。
    # 使用 requests 库的 head 方法发送一个 HEAD 请求到指定的 URL。HEAD 请求用于获取资源的元数据,但不包括资源本身。 allow_redirects=True 参数允许自动处理重定向。
    response = requests.head(url, allow_redirects=True)
    # 从响应头中获取 content-length 字段的值,它表示文件的大小(以字节为单位)。如果 content-length 不存在,则返回 -1 。最后,使用 int() 函数将字符串转换为整数。
    return int(response.headers.get('content-length', -1))
# 这个函数通过发送 HEAD 请求来获取文件的大小。它的优点是不需要下载整个文件,只需获取元数据即可。然而,需要注意的是,并非所有服务器都会在响应头中包含 content-length 字段,特别是在使用压缩或分块传输编码时。

5.def safe_download(file, url, url2=None, min_bytes=1E0, error_msg=''): 

# 这段代码定义了一个名为 safe_download 的函数,它用于安全地从提供的 URL 下载文件,并确保下载的文件大小符合最小字节要求。
# 函数定义。
# 1.file : 要下载的文件的本地路径。
# 2.url : 用于下载文件的主要 URL。
# 3.url2 : 备用 URL,如果主要 URL 下载失败时使用。
# 4.min_bytes : 定义文件的最小字节大小,用于检查下载是否成功。
# 5.error_msg : 如果下载失败,显示的额外错误信息。
def safe_download(file, url, url2=None, min_bytes=1E0, error_msg=''):
    # Attempts to download file from url or url2, checks and removes incomplete downloads < min_bytes    尝试从 url 或 url2 下载文件,检查并删除不完整的下载 < min_bytes。
    # LOGGER -> LOGGER 是一个日志记录器对象,用于记录日志信息。
    from utils.general import LOGGER

    # 将文件路径转换为 Path 对象,以便使用路径操作。
    file = Path(file)
    # 创建一个断言消息,用于检查下载的文件是否存在或大小是否小于 min_bytes 。
    assert_msg = f"Downloaded file '{file}' does not exist or size is < min_bytes={min_bytes}"    # 下载的文件“{file}”不存在或大小 < min_bytes={min_bytes}。
    # 尝试从 url 下载文件。
    try:  # url1
        # 使用 PyTorch 的 torch.hub 模块下载文件,并根据 LOGGER 的日志级别显示进度。
        LOGGER.info(f'Downloading {url} to {file}...')    # 正在下载 {url} 至 {file}...

        # torch.hub.download_url_to_file(url, dst, hash_prefix=None, progress=True)
        # torch.hub.download_url_to_file 是 PyTorch 的一个函数,用于从指定的 URL 下载文件并保存到本地路径。
        # 参数 :
        # url (str): 要下载文件的 URL。
        # dst (str): 文件保存的完整路径,例如 /tmp/temporary_file 。
        # hash_prefix (str, 可选): 如果提供,下载的 SHA256 文件应以 hash_prefix 开头。默认为 None 。
        # progress (bool, 可选): 是否在标准错误输出(stderr)显示进度条。默认为 True 。
        # 返回值 :该函数没有返回值,它会直接将下载的文件保存到指定的本地路径。
        # 注意事项 :
        # 该函数会检查下载文件的完整性,如果提供了 hash_prefix 参数,它会验证文件的 SHA256 哈希值是否与 hash_prefix 匹配。
        # 如果下载过程中出现任何问题,例如网络错误或文件损坏,函数会抛出异常。
        # progress 参数控制是否显示下载进度,这对于大型文件下载特别有用,可以让用户了解下载进度。
        # torch.hub.download_url_to_file 函数是 PyTorch 提供的一个方便的工具,用于从远程服务器下载预训练模型或其他文件,它是 PyTorch 模型加载和迁移学习流程中的一个重要组成部分。

        torch.hub.download_url_to_file(url, str(file), progress=LOGGER.level <= logging.INFO)
        # 断言检查文件是否存在且大小是否大于 min_bytes 。
        assert file.exists() and file.stat().st_size > min_bytes, assert_msg  # check
    # 如果下载过程中出现异常,执行 except 块中的代码。
    except Exception as e:  # url2
        # 如果文件已存在,则删除不完整的下载文件。
        if file.exists():

            # Path.unlink(missing_ok=False)
            # unlink() 函数是 pathlib 模块中 Path 类的一个方法,用于删除文件系统中的一个文件。
            # Path : 这是 pathlib 模块中的 Path 类,用于表示文件系统路径。
            # unlink() : 这是 Path 类的方法,用于删除路径所指向的文件。
            # 参数 :
            # missing_ok : 这是一个可选参数,默认值为 False 。如果设置为 True ,则在文件不存在时不会抛出异常,而是静默地忽略这个错误。
            # 功能 :
            # unlink() 方法用于删除文件系统中的一个文件。如果文件不存在,并且 missing_ok 参数为 False (默认值),则会引发一个 FileNotFoundError 。
            # 注意事项 :
            # 使用 unlink() 方法时要小心,因为一旦文件被删除,就无法恢复。
            # 确保在删除文件之前有适当的错误处理和文件存在性检查,除非你确定文件存在,或者你不在乎文件是否实际存在。
            # 在多线程或多进程环境中,文件可能会在不同的执行线程或进程中被访问或修改,因此在使用 unlink() 时要特别注意同步和竞态条件。

            file.unlink()  # remove partial downloads
        LOGGER.info(f'ERROR: {e}\nRe-attempting {url2 or url} to {file}...')    # 错误:{e}\n重新尝试将 {url2 或 url} 连接到 {file}...

        # os.system(command)
        # os.system() 函数是 Python 的 os 模块中的一个函数,用于执行指定的命令行字符串。这个函数会调用系统的命令行解释器(通常是 shell)来执行命令,并返回命令执行后的退出状态码。
        # 参数说明 :
        # command :一个字符串,包含要执行的命令。
        # 返回值 :返回命令执行后的退出状态码。在 Unix 和类 Unix 系统中,通常 0 表示成功,非 0 表示失败。在 Windows 系统中,返回值的具体含义取决于命令本身。
        # 异常 :
        # 如果发生错误(例如,无法找到命令解释器),可能会抛出 OSError 异常。
        # 需要注意的是, os.system() 函数会创建一个新的 shell 来执行命令,这意味着它可能会受到当前工作目录的影响,并且不会继承当前 Python 进程的环境变量。
        # 此外,由于安全原因,通常不推荐在程序中使用 os.system() ,因为它可能会执行任意命令,存在安全风险。
        # 在可能的情况下,可以考虑使用 subprocess 模块中的函数,如 subprocess.run() 或 subprocess.call() ,因为它们提供了更多的控制和安全性。

        # 使用 curl 命令尝试从 url2 或 url 重新下载文件,包含重试和断点续传选项。
        os.system(f"curl -# -L '{url2 or url}' -o '{file}' --retry 3 -C -")  # curl download, retry and resume on fail
    # 最后,再次检查文件是否存在且大小是否符合要求。
    finally:

        # os.stat(path, *, dir_fd=None, follow_symlinks=True, dir_fd=None)
        # 在Python中, stat() 函数是 os 模块中的一个方法,用于获取文件或目录的状态信息。
        # 参数说明 :
        # path : 要获取状态信息的文件或目录的路径。
        # dir_fd : 可选参数,文件描述符;如果提供,表示 path 是相对于此文件描述符的。
        # follow_symlinks : 可选参数,布尔值;如果为 True (默认值),则 stat() 会跟随软链接,并返回链接目标的状态信息;如果为 False ,则返回链接本身的状态信息。
        # 返回值 :
        # 返回一个对象,该对象包含了文件或目录的许多属性,如修改时间、访问时间、文件大小等。
        # 返回对象的属性 :
        # 返回的对象包含以下属性(这些属性在不同的操作系统中可能有所不同) :
        # st_mode : 文件模式(类型与权限)。
        # st_ino : inode(节点)编号。
        # st_dev : 设备编号。
        # st_nlink : 硬链接数。
        # st_uid : 文件所有者的ID。
        # st_gid : 文件所有者组的ID。
        # st_size : 文件大小,单位为字节。
        # st_atime : 最后访问时间,以Unix时间戳表示。
        # st_mtime : 最后修改时间,以Unix时间戳表示。
        # st_ctime : 最后状态改变时间(inode修改时间),以Unix时间戳表示。
        # 注意事项 :
        # 使用 stat() 函数时,需要确保提供的路径是存在的,否则会抛出 FileNotFoundError 。
        # stat() 函数返回的属性在不同的操作系统中可能有所不同,因此在编写跨平台代码时需要注意这一点。
        # 在使用 os 模块之前,需要先导入该模块。
        # stat() 函数是处理文件系统操作时常用的一个函数,它提供了获取文件或目录状态信息的直接方法。

        # 如果文件不存在或大小小于 min_bytes ,则删除文件并记录错误信息。
        if not file.exists() or file.stat().st_size < min_bytes:  # check
            if file.exists():
                file.unlink()  # remove partial downloads
            LOGGER.info(f"ERROR: {assert_msg}\n{error_msg}")    # 错误:{assert_msg}\n{error_msg}。
        # 在日志中添加一个空行,用于分隔日志信息。
        LOGGER.info('')
# safe_download 函数提供了一个健壮的文件下载机制,它尝试从提供的 URL 下载文件,并确保文件完整且未损坏。如果下载失败,函数会尝试从备用 URL 下载,并在最终检查中删除不完整的文件。这个函数对于确保模型文件和其他重要资源的完整性非常有用。

6.def attempt_download(file, repo='ultralytics/yolov5', release='v7.0'): 

# 这段代码定义了一个名为 attempt_download 的函数,其目的是尝试从 GitHub 仓库的发布资产中下载文件,如果本地找不到该文件。
# 函数定义。
# 1.file : 要下载的文件的路径或 URL。
# 2.repo : GitHub 仓库的名称,默认为 ultralytics/yolov5 。
# 3.release : GitHub 仓库的发布版本,默认为 v7.0 。
def attempt_download(file, repo='ultralytics/yolov5', release='v7.0'):
    # Attempt file download from GitHub release assets if not found locally. release = 'latest', 'v7.0', etc.    如果本地找不到,则尝试从 GitHub 发布资产下载文件。release = 'latest'、'v7.0' 等。
    # 从 utils.general 模块导入 LOGGER 对象,用于记录日志信息。
    from utils.general import LOGGER

    # 定义一个内部函数 github_assets ,它用于从 GitHub 仓库的指定版本中获取发布标签和资产列表。
    # 函数定义。
    # repository : GitHub 仓库的名称,例如 'ultralytics/yolov5' 。
    # version : 指定的版本标签,默认为 'latest' 。
    def github_assets(repository, version='latest'):
        # Return GitHub repo tag (i.e. 'v7.0') and assets (i.e. ['yolov5s.pt', 'yolov5m.pt', ...])    返回 GitHub repo 标签(即 'v7.0')和资产(即 ['yolov5s.pt', 'yolov5m.pt', ...])。
        # 检查 version 参数是否不是 'latest' 。
        if version != 'latest':
            #  如果 version 不是 'latest' ,则将其格式化为 'tags/{version}' ,以便用于 GitHub API 请求。
            version = f'tags/{version}'  # i.e. tags/v7.0
        # 使用 requests 库向 GitHub API 发起 GET 请求,获取指定仓库和版本的发布信息,并解析响应内容为 JSON。
        response = requests.get(f'https://api.github.com/repos/{repository}/releases/{version}').json()  # github api
        # 返回 仓库的标签名称 和 资产列表 。
        return response['tag_name'], [x['name'] for x in response['assets']]  # tag, assets
    # 这个函数依赖于 requests 库来发送 HTTP 请求。 函数假设 GitHub API 返回的 JSON 响应包含 'tag_name' 和 'assets' 键。 函数返回的资产列表是一个包含资产名称的列表。

    # 这段代码是 attempt_download 函数的一部分,它处理从 URL 下载文件的逻辑。
    # 将输入的 file 变量转换为字符串,去除前后空格,并替换掉所有单引号。然后,使用 Path 对象确保路径操作的兼容性和便捷性。
    file = Path(str(file).strip().replace("'", ''))
    # 检查处理后的文件路径是否指向一个已存在的文件。
    if not file.exists():
        # URL specified

        # parse.unquote(s, encoding='utf-8', errors='replace')
        # parse.unquote() 是 Python 标准库 urllib.parse 模块中的一个函数,用于对 URL 编码的字符串进行解码,将百分号编码(%XX)转换回普通字符。
        # 参数 :
        # s :要解码的 URL 编码字符串。
        # encoding :(可选)用于解码的字符编码,默认为 'utf-8' 。
        # errors :(可选)指定如何处理解码错误,默认为 'replace' ,意味着将无法解码的字符替换为一个替代字符(通常是 ? )。
        # 返回值 :
        # 返回解码后的字符串。
        # 函数逻辑 :
        # 解码百分号编码 :将字符串中的 %XX 序列转换为对应的字符。
        # 字符编码转换 :将原始的百分比编码字符串(通常为 ASCII)转换为指定的编码。
        # 在例子中, unquote 函数将 URL 编码的字符串 "Hello%2C%20World%21" 解码为普通字符串 "Hello, World!" 。
        # 注意事项 :
        # 当处理来自用户的 URL 编码数据时,使用 unquote 函数可以确保正确地解释这些数据。
        # 如果 URL 包含非 ASCII 字符,确保指定正确的 encoding 参数,否则解码可能会失败或产生意外结果。
        # 如果遇到无法解码的百分号序列, errors 参数决定了如何处理这些错误。常见的选项包括 'strict' (抛出 UnicodeDecodeError )、 'replace' (用替代字符替换无法解码的字符)和 'ignore' (忽略无法解码的字符)。

        # 如果文件不存在,解析文件的 URL,解码 URL 中的百分号编码(例如将 %2F 解码为 / ),并获取文件名。
        name = Path(urllib.parse.unquote(str(file))).name  # decode '%2F' to '/' etc.
        # 检查文件路径是否以 http:/ 或 https:/ 开头,确定是否需要下载文件。
        if str(file).startswith(('http:/', 'https:/')):  # download
            # 修复路径中的协议部分,将 :/ 替换为 :// ,以形成有效的 URL。
            url = str(file).replace(':/', '://')  # Pathlib turns :// -> :/
            # 从文件名中移除 URL 的查询参数,只保留文件名本身。
            file = name.split('?')[0]  # parse authentication https://url.com/file.txt?auth...
            # 再次检查本地是否存在该文件。
            if Path(file).is_file():
                # 如果文件已存在,使用 LOGGER 记录文件已在本地找到的信息。
                LOGGER.info(f'Found {url} locally at {file}')  # file already exists    在本地的 {file} 上找到了 {url}。  文件已存在。
            # 如果文件不存在,执行下载操作。
            else:
                # 调用 safe_download 函数,下载文件并保存到指定路径。 min_bytes 参数确保下载的文件大小至少为 100,000 字节。
                # def safe_download(file, url, url2=None, min_bytes=1E0, error_msg=''): -> 用于安全地从提供的 URL 下载文件,并确保下载的文件大小符合最小字节要求。
                safe_download(file=file, url=url, min_bytes=1E5)
            # 返回处理后的文件路径。
            return file
        # 这段代码负责处理文件下载的逻辑,包括检查文件是否存在、解析 URL、下载文件,并记录日志信息。 safe_download 函数用于执行实际的下载操作,确保文件被安全地下载到本地。这段代码是 attempt_download 函数的核心部分,用于从给定的 URL 下载文件,如果文件已经在本地存在则直接使用本地文件。

        # 这段代码是尝试从GitHub仓库获取模型资产列表的逻辑,如果直接获取失败,则会尝试其他方法来确定标签(tag)。
        # GitHub assets
        # 创建一个列表 assets ,包含所有可能的YOLOv5模型文件名。这些文件名基于模型大小( n , s , m , l , x )和后缀(空字符串、 6 , -cls , -seg )的组合。
        assets = [f'yolov5{size}{suffix}.pt' for size in 'nsmlx' for suffix in ('', '6', '-cls', '-seg')]  # default
        # 开始第一个 try 块,尝试执行以下代码。
        try:
            # 调用 github_assets 函数,传入仓库名 repo 和版本 release ,尝试获取对应版本的标签和资产列表。
            # def github_assets(repository, version='latest'):
            # -> 用于从 GitHub 仓库的指定版本中获取发布标签和资产列表。返回 仓库的标签名称 和 资产列表 。
            # -> return response['tag_name'], [x['name'] for x in response['assets']]  # tag, assets
            tag, assets = github_assets(repo, release)
        # 如果在尝试获取特定版本的资产列表时发生异常,进入第一个 except 块。
        except Exception:
            # 开始第二个 try 块,尝试执行以下代码。
            try:
                # 再次调用 github_assets 函数,这次没有传入版本号,尝试获取最新版本的 标签 和 资产列表 。
                # def github_assets(repository, version='latest'):
                # -> 用于从 GitHub 仓库的指定版本中获取发布标签和资产列表。返回 仓库的标签名称 和 资产列表 。
                # -> return response['tag_name'], [x['name'] for x in response['assets']]  # tag, assets
                tag, assets = github_assets(repo)  # latest release
            # 如果在尝试获取最新版本的资产列表时发生异常,进入第二个 except 块。
            except Exception:
                # 开始第三个 try 块,尝试执行以下代码。
                try:

                    # subprocess.check_output(cmd, *args, **kwargs)
                    # check_output() 函数是 Python 标准库 subprocess 模块中的一个函数,它用于执行指定的命令并获取命令的输出。如果命令执行成功, check_output() 会返回命令的输出;如果命令执行失败(即返回非零退出状态),则会抛出一个 CalledProcessError 异常。
                    # 参数说明 :
                    # cmd :要执行的命令,可以是字符串或者字符串列表。如果是字符串,会被 shell 解释,这与 shell=True 相同;如果是字符串列表,则直接传递给底层的 execvp() 函数。
                    # *args :传递给 subprocess.Popen() 的其他参数。
                    # **kwargs :传递给 subprocess.Popen() 的其他关键字参数。常用的关键字参数包括 :
                    # shell :如果为 True ,则 cmd 会被 shell 解释。默认为 False 。
                    # stdout :子进程的 stdout 管道。默认为 subprocess.PIPE ,即捕获输出。
                    # stderr :子进程的 stderr 管道。默认为 subprocess.PIPE ,即捕获错误输出。
                    # universal_newlines 或 text :如果设置为 True ,则 check_output() 返回一个字符串而不是字节对象。在 Python 3.7 及更高版本中, text 参数被引入, universal_newlines 被废弃。
                    # 返回值 :
                    # 返回执行命令后的标准输出(stdout)。
                    # 异常 :
                    # 如果命令返回非零退出状态, check_output() 会抛出 subprocess.CalledProcessError 异常。

                    # 使用 subprocess 模块执行 git tag 命令,获取当前git仓库的所有标签,并解码输出,取最后一个标签作为 tag 。
                    tag = subprocess.check_output('git tag', shell=True, stderr=subprocess.STDOUT).decode().split()[-1]
                # 如果在尝试通过 git tag 获取标签时发生异常,进入第三个 except 块。
                except Exception:
                    # 如果所有尝试都失败,将 tag 设置为初始传入的 release 参数。
                    tag = release
        # 这段代码展示了一种健壮的错误处理策略,用于在不同情况下获取GitHub仓库的标签和资产列表。首先尝试获取特定版本的资产列表,如果失败,则尝试获取最新版本的资产列表。如果这两个尝试都失败,最后通过执行 git tag 命令来获取标签。如果所有尝试都失败,则回退到使用初始的 release 参数。这种方法确保了在不同网络条件和环境配置下,代码能够尽可能地获取所需的信息。

        # 这段代码是 attempt_download 函数的一部分,它负责处理从 GitHub 仓库下载模型文件的逻辑。
        # 确保文件 file 的父目录存在。如果父目录不存在,则创建它。 parents=True 表示创建所有必需的父目录。 exist_ok=True 表示如果目录已经存在,则不抛出异常。
        file.parent.mkdir(parents=True, exist_ok=True)  # make parent dir (if required)
        # 检查文件名 name 是否在从 GitHub 仓库获取的资产列表 assets 中。
        if name in assets:
            # 定义一个备用的 Google Drive 链接,用于在 GitHub 链接不可用时作为备份。
            url3 = 'https://drive.google.com/drive/folders/1EFQTEUeXWSFww0luse2jB9M1QNZQGwNl'  # backup gdrive mirror
            # 调用 safe_download 函数,从 GitHub 仓库的指定版本 tag 下载文件 name 。
            # def safe_download(file, url, url2=None, min_bytes=1E0, error_msg=''): -> 用于安全地从提供的 URL 下载文件,并确保下载的文件大小符合最小字节要求。
            safe_download(
                file,
                # url 参数构建了下载链接,指向 GitHub 仓库的特定发布版本的特定文件。
                url=f'https://github.com/{repo}/releases/download/{tag}/{name}',
                # 确保下载的文件至少为 100,000 字节,以避免下载不完整的文件。
                min_bytes=1E5,
                # error_msg 提供了一个错误消息,如果下载失败,建议用户尝试从 GitHub 仓库或备用 Google Drive 链接手动下载。
                error_msg=f'{file} missing, try downloading from https://github.com/{repo}/releases/{tag} or {url3}')    # 缺少 {file},请尝试从 https://github.com/{repo}/releases/{tag} 或 {url3} 下载。

    # 返回文件的路径,以字符串形式。
    return str(file)
    # 这段代码处理了从 GitHub 仓库下载文件的逻辑,包括确保父目录存在、检查文件是否在资产列表中、构建下载链接、调用下载函数,并在下载失败时提供备用下载选项。这种方法确保了即使在网络问题或 GitHub 链接不可用的情况下,用户也能找到并下载所需的文件。
# attempt_download 函数提供了一个方便的方式来下载 GitHub 仓库中的文件,如果本地不存在。它处理了文件名的解析、URL 的构建、直接下载和 GitHub 资产的下载。这个函数是自动化下载和更新模型文件的实用工具,特别是在需要确保模型文件是最新版本时。


原文地址:https://blog.csdn.net/m0_58169876/article/details/144633139

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!