维护者信息#

发布#

本节介绍如何准备主要/次要版本、发布候选版本(RC)或错误修复版本。我们遵循PEP440的版本方案来指示不同类型的发布。我们的惯例是遵循“major.minor.micro”方案,尽管实际上主要版本和次要版本之间没有根本区别,并且微版本是错误修复版本。

我们采用以下发布计划

  • 每6个月发布一次主要/次要版本,通常在5月和11月。这些版本编号为X.Y.0,并在此之前有一个或多个发布候选版本X.Y.0rcN

  • 错误修复版本根据需要在主要/次要版本之间进行,并且仅适用于最新的稳定版本。这些版本编号为X.Y.Z

准备工作

  • 确认所有标记为里程碑的阻止项均已解决,并且标记为里程碑的其他问题可以推迟。

  • 确保已处理标记为发布的所有弃用、FIXME和TODO。

  • 确保已提升我们依赖项的最低支持版本,详情请参阅提升我们依赖项最低版本的准则

  • 对于主要/次要的最终版本,请确保已将“发布亮点”页面作为一个可运行的示例完成,并检查其HTML渲染是否正确。它应该从scikit-learn新版本的“whats new”文件中链接。

权限

  • 发布经理必须是scikit-learn/scikit-learn仓库的维护者,才能在pypi.orgtest.pypi.org上发布(通过手动触发专用的Github Actions工作流程)。

  • 发布经理必须是conda-forge/scikit-learn-feedstock仓库的维护者,才能在conda-forge上发布。这可以通过在第一个发布拉取请求中编辑recipe/meta.yaml文件来更改。

参考步骤#

假设我们正在准备发布1.7.0rc1

第一个RC理想情况下算作功能冻结。接下来的每个发布候选版本和之后的最终版本都应只包含小的文档更改和错误修复。任何主要的增强或新功能都应排除在外。

  • 直接在主仓库中创建发布分支1.7.X,其中X确实是字母X,而不是占位符。最终版本和后续的1.7错误修复版本的开发也应在此分支下进行,使用不同的标签。

    git fetch upstream main
    git checkout upstream/main
    git checkout -b 1.7.X
    git push --set-upstream upstream 1.7.X
    
  • 创建一个针对1.7.X分支的PR。将以下发布清单复制到此PR的描述中以跟踪进度。

    * [ ] Update the sklearn dev0 version in main branch
    * [ ] Set the version number in the release branch
    * [ ] Set an upper bound on build dependencies in the release branch
    * [ ] Generate the changelog in the release branch
    * [ ] Check that the wheels for the release can be built successfully
    * [ ] Merge the PR with `[cd build]` commit message to upload wheels to the staging repo
    * [ ] Upload the wheels and source tarball to https://test.pypi.org
    * [ ] Create tag on the main repo
    * [ ] Confirm bot detected at https://github.com/conda-forge/scikit-learn-feedstock
          and wait for merge
    * [ ] Upload the wheels and source tarball to PyPI
    * [ ] Announce on mailing list and on social media platforms (LinkedIn, Bluesky, etc.)
    
  • main创建一个针对main的PR,以准备下一个版本。在此PR中,您需要

    • 增加sklearn/__init__.py中的dev0 __version__变量。这意味着在发布候选期,最新的稳定版本比main分支落后两个版本,而不是一个。

    • doc/whats_new/目录下包含一个新的“whats new”文件。不要忘记在doc/whats_new.rst中为这个新文件添加一个条目。

    • pyproject.tomltool.towncrier部分的filename字段中,将“whats new”文件更改为新创建的文件。

  • 在发布分支中,将sklearn/__init__.py中的版本号__version__更改为1.7.0rc1

  • 仍在发布分支中,在[build-system]部分的pyproject.toml中设置或更新构建依赖项的上限。目标是防止依赖项未来不兼容的发布破坏维护分支中的构建。

    上限应与已发布的最新次要版本依赖项匹配,并应允许未来的微(错误修复)版本。例如,如果numpy 2.2.5是最新版本,则其上限应设置为<2.3.0。

  • 在发布分支中,为即将发布的版本生成更新日志,即doc/whats_new/1.7.rst。在RC期间,我们希望在生成更新日志时保留片段,因为我们将在最终版本中再次生成它,包括期间可能发生的更改。

    towncrier build --keep --version 1.7.0
    
  • 使用[cd build]提交标记触发wheel构建器。另请参阅wheel构建器的工作流程运行

    git commit --allow-empty -m "[cd build] Trigger wheel builder workflow"
    

    注意

    [cd build]中的缩写CD代表持续交付,指的是用于生成发布工件(二进制和源包)的自动化。这可以看作是CI(代表持续集成)的扩展。GitHub Actions上的CD工作流程也用于自动创建每夜构建并发布scikit-learn开发分支的包。另请参阅安装每夜构建

  • 一旦PR中所有CD作业成功完成,使用提交消息中的[cd build]标记合并它。这次结果将上传到暂存区。然后,您应该能够使用PyPI发布工作流程的“运行工作流程”表单将生成的工件(.tar.gz.whl文件)上传到https://test.pypi.org/

    警告

    此PR应以rebase模式而不是通常的squash模式合并,因为我们希望1.7.X分支的历史与主分支的历史保持接近,这将有助于未来的错误修复发布。

    此外,如果在合并时,包含[cd build]标记的最后一次提交是空的,则CD作业将不会被触发。在这种情况下,您可以直接在1.7.X分支中推送一个带有标记的提交来触发它们。

  • 如果上述步骤顺利,请谨慎地为发布创建新标签。这应该只在您几乎确定发布已准备就绪时才进行,因为向主仓库添加新标签可能会触发某些自动化流程。

    git tag -a 1.7.0rc1  # in the 1.7.X branch
    git push git@github.com:scikit-learn/scikit-learn.git 1.7.0rc1
    

    警告

    不要使用github界面发布版本来创建标签,因为它会自动向所有关注该仓库的用户发送通知,即使网站尚未更新且wheels尚未上传。

  • 确认机器人已在conda-forge feedstock仓库conda-forge/scikit-learn-feedstock中检测到该标签。如果未检测到,请提交针对rc分支的发布PR。确保更新PR,使其与main分支同步。特别是,回溯自上次发布以来可能已添加的迁移。

  • 再次触发PyPI发布工作流程,但这次将工件上传到真正的https://pypi.ac.cn/。为此,在“运行工作流程”表单中将testpypi替换为pypi

    或者,可以在本地收集生成的二进制wheel包和源tarball,并将它们全部上传到PyPI。

    从本地上传工件#

    检出发布标签并运行以下命令。

    rm -r dist
    python -m pip install -U wheelhouse_uploader twine
    python -m wheelhouse_uploader fetch \
      --version 1.7.0rc1 --local-folder dist scikit-learn \
      https://pypi.anaconda.org/scikit-learn-wheels-staging/simple/scikit-learn/
    

    这些命令将下载anaconda.org托管服务上的暂存区中积累的所有二进制包,并将它们放在您的本地./dist文件夹中。检查./dist文件夹的内容:它应包含所有wheels以及源tarball .tar.gz。确保该文件夹中没有开发版本或旧版本的scikit-learn包。在上传到PyPI之前,您可以先尝试上传到test.pypi.org

    twine upload --verbose --repository-url https://test.pypi.org/legacy/ dist/*
    

    然后一次性将所有内容上传到pypi.org

    twine upload dist/*
    

假设我们正在准备发布1.7.0

  • main分支创建一个新分支,然后从1.7.X开始交互式rebase以选择需要回溯的提交。

    git rebase -i upstream/1.7.X
    

    这将打开一个包含main上所有最新提交的git-rebase-todo的交互式rebase。在此阶段,您必须与至少另一个人一起执行此交互式rebase(以免遗漏某些内容并避免疑虑)。

    • 不要删除行,而是通过将pick替换为drop来删除提交。

    • 要为错误修复版本选择的提交通常FIXCIDOC为前缀。它们至少应包括已里程碑化为此发布的所有合并PR的提交。

    • 要为错误修复版本drop的提交通常FEATMAINTENHAPI为前缀。不包括它们的原因是为了防止行为更改(这应该只发生在主要/次要版本中)。

    • 在删除或选择提交后,不要退出,而是将git-rebase-todo消息的内容粘贴到PR中。此文件位于.git/rebase-merge/git-rebase-todo

    • 保存并退出以开始交互式rebase。必要时解决合并冲突。

  • 创建一个针对1.7.X分支的PR。将以下发布清单复制到此PR的描述中以跟踪进度。

    * [ ] Set the version number in the release branch
    
    * [ ] Generate the changelog in the release branch
    * [ ] Check that the wheels for the release can be built successfully
    * [ ] Merge the PR with `[cd build]` commit message to upload wheels to the staging repo
    * [ ] Upload the wheels and source tarball to https://test.pypi.org
    * [ ] Create tag on the main repo
    * [ ] Confirm bot detected at https://github.com/conda-forge/scikit-learn-feedstock
          and wait for merge
    * [ ] Upload the wheels and source tarball to PyPI
    * [ ] Update news and what's new date in main branch
    * [ ] Backport news and what's new date in release branch
    * [ ] Update symlink for stable in https://github.com/scikit-learn/scikit-learn.github.io
    * [ ] Publish to https://github.com/scikit-learn/scikit-learn/releases
    * [ ] Announce on mailing list and on social media platforms (LinkedIn, Bluesky, etc.)
    * [ ] Update SECURITY.md in main branch
    
  • 在发布分支中,将sklearn/__init__.py中的版本号__version__更改为1.7.0

  • 在发布分支中,生成即将发布的版本的更新日志,即doc/whats_new/1.7.rst。对于非RC版本,推送一个提交,其中您

    • 生成更新日志,不保留片段。

      towncrier build --version 1.7.0
      
    • 链接发布亮点示例。

    • 添加贡献者名称列表。假设上一个主要/次要版本中最新发布的标签是1.6.1,那么您可以使用以下命令检索贡献者名称列表

      git shortlog -s 1.6.1.. |
        cut -f2- |
        sort --ignore-case |
        tr "\n" ";" |
        sed "s/;/, /g;s/, $//" |
        fold -s
      

    然后创建一个针对main分支的PR,并在那里cherry-pick此提交。

  • 使用[cd build]提交标记触发wheel构建器。另请参阅wheel构建器的工作流程运行

    git commit --allow-empty -m "[cd build] Trigger wheel builder workflow"
    

    注意

    [cd build]中的缩写CD代表持续交付,指的是用于生成发布工件(二进制和源包)的自动化。这可以看作是CI(代表持续集成)的扩展。GitHub Actions上的CD工作流程也用于自动创建每夜构建并发布scikit-learn开发分支的包。另请参阅安装每夜构建

  • 一旦PR中所有CD作业成功完成,使用提交消息中的[cd build]标记合并它。这次结果将上传到暂存区。然后,您应该能够使用PyPI发布工作流程的“运行工作流程”表单将生成的工件(.tar.gz.whl文件)上传到https://test.pypi.org/

    警告

    此PR应以rebase模式而不是通常的squash模式合并,因为我们希望1.7.X分支的历史与主分支的历史保持接近,这将有助于未来的错误修复发布。

    此外,如果在合并时,包含[cd build]标记的最后一次提交是空的,则CD作业将不会被触发。在这种情况下,您可以直接在1.7.X分支中推送一个带有标记的提交来触发它们。

  • 如果上述步骤顺利,请谨慎地为发布创建新标签。这应该只在您几乎确定发布已准备就绪时才进行,因为向主仓库添加新标签可能会触发某些自动化流程。

    git tag -a 1.7.0  # in the 1.7.X branch
    git push git@github.com:scikit-learn/scikit-learn.git 1.7.0
    

    警告

    不要使用github界面发布版本来创建标签,因为它会自动向所有关注该仓库的用户发送通知,即使网站尚未更新且wheels尚未上传。

  • 确认机器人已在conda-forge feedstock仓库conda-forge/scikit-learn-feedstock中检测到该标签。如果未检测到,请提交针对main分支的发布PR。

  • 再次触发PyPI发布工作流程,但这次将工件上传到真正的https://pypi.ac.cn/。为此,在“运行工作流程”表单中将testpypi替换为pypi

    或者,可以在本地收集生成的二进制wheel包和源tarball,并将它们全部上传到PyPI。

    从本地上传工件#

    检出发布标签并运行以下命令。

    rm -r dist
    python -m pip install -U wheelhouse_uploader twine
    python -m wheelhouse_uploader fetch \
      --version 1.7.0 --local-folder dist scikit-learn \
      https://pypi.anaconda.org/scikit-learn-wheels-staging/simple/scikit-learn/
    

    这些命令将下载anaconda.org托管服务上的暂存区中积累的所有二进制包,并将它们放在您的本地./dist文件夹中。检查./dist文件夹的内容:它应包含所有wheels以及源tarball .tar.gz。确保该文件夹中没有开发版本或旧版本的scikit-learn包。在上传到PyPI之前,您可以先尝试上传到test.pypi.org

    twine upload --verbose --repository-url https://test.pypi.org/legacy/ dist/*
    

    然后一次性将所有内容上传到pypi.org

    twine upload dist/*
    
  • main分支中,编辑doc/templates/index.html以更改登陆页面中的“新闻”部分以及发布月份。不要忘记删除旧条目(两年或三个版本前的)并更新“进行中开发”条目。然后将其cherry-pick到发布分支。

  • 更新scikit-learn/scikit-learn.github.io中的stable符号链接和versionwarning.js中的latestStable变量。

    cd /tmp
    git clone --depth 1 --no-checkout git@github.com:scikit-learn/scikit-learn.github.io.git
    cd scikit-learn.github.io
    echo stable > .git/info/sparse-checkout
    git checkout main
    rm stable
    ln -s 1.7 stable
    sed -i "s/latestStable = '.*/latestStable = '1.7';/" versionwarning.js
    git add stable versionwarning.js
    git commit -m "Update stable to point to 1.7"
    git push origin main
    
  • scikit-learn/scikit-learn发布版本,并在邮件列表和社交网络上宣布。请记住在发布说明中添加更新日志的链接。理想情况下,仅当包在PyPI和conda-forge上可用并且网站已更新时才执行此步骤。

  • 更新SECURITY.md以反映最新支持的版本1.7.0

假设我们正在准备发布1.6.2

  • main分支创建一个新分支,然后从1.6.X开始交互式rebase以选择需要回溯的提交。

    git rebase -i upstream/1.6.X
    

    这将打开一个包含main上所有最新提交的git-rebase-todo的交互式rebase。在此阶段,您必须与至少另一个人一起执行此交互式rebase(以免遗漏某些内容并避免疑虑)。

    • 不要删除行,而是通过将pick替换为drop来删除提交。

    • 要为错误修复版本选择的提交通常FIXCIDOC为前缀。它们至少应包括已里程碑化为此发布的所有合并PR的提交。

    • 要为错误修复版本drop的提交通常FEATMAINTENHAPI为前缀。不包括它们的原因是为了防止行为更改(这应该只发生在主要/次要版本中)。

    • 在删除或选择提交后,不要退出,而是将git-rebase-todo消息的内容粘贴到PR中。此文件位于.git/rebase-merge/git-rebase-todo

    • 保存并退出以开始交互式rebase。必要时解决合并冲突。

  • 创建一个针对1.6.X分支的PR。将以下发布清单复制到此PR的描述中以跟踪进度。

    * [ ] Set the version number in the release branch
    
    * [ ] Generate the changelog in the release branch
    * [ ] Check that the wheels for the release can be built successfully
    * [ ] Merge the PR with `[cd build]` commit message to upload wheels to the staging repo
    * [ ] Upload the wheels and source tarball to https://test.pypi.org
    * [ ] Create tag on the main repo
    * [ ] Confirm bot detected at https://github.com/conda-forge/scikit-learn-feedstock
          and wait for merge
    * [ ] Upload the wheels and source tarball to PyPI
    * [ ] Update news and what's new date in main branch
    * [ ] Backport news and what's new date in release branch
    * [ ] Publish to https://github.com/scikit-learn/scikit-learn/releases
    * [ ] Announce on mailing list and on social media platforms (LinkedIn, Bluesky, etc.)
    * [ ] Update SECURITY.md in main branch
    
  • 在发布分支中,将sklearn/__init__.py中的版本号__version__更改为1.6.2

  • 在发布分支中,生成即将发布的版本的更新日志,即doc/whats_new/1.6.rst。对于非RC版本,推送一个提交,其中您

    • 生成更新日志,不保留片段。

      towncrier build --version 1.6.2
      
    • 添加贡献者名称列表。假设上一个主要/次要版本中最新发布的标签是1.7.0,那么您可以使用以下命令检索贡献者名称列表

      git shortlog -s 1.7.0.. |
        cut -f2- |
        sort --ignore-case |
        tr "\n" ";" |
        sed "s/;/, /g;s/, $//" |
        fold -s
      

    然后创建一个针对main分支的PR,并在那里cherry-pick此提交。

  • 使用[cd build]提交标记触发wheel构建器。另请参阅wheel构建器的工作流程运行

    git commit --allow-empty -m "[cd build] Trigger wheel builder workflow"
    

    注意

    [cd build]中的缩写CD代表持续交付,指的是用于生成发布工件(二进制和源包)的自动化。这可以看作是CI(代表持续集成)的扩展。GitHub Actions上的CD工作流程也用于自动创建每夜构建并发布scikit-learn开发分支的包。另请参阅安装每夜构建

  • 一旦PR中所有CD作业成功完成,使用提交消息中的[cd build]标记合并它。这次结果将上传到暂存区。然后,您应该能够使用PyPI发布工作流程的“运行工作流程”表单将生成的工件(.tar.gz.whl文件)上传到https://test.pypi.org/

    警告

    此PR应以rebase模式而不是通常的squash模式合并,因为我们希望1.6.X分支的历史与主分支的历史保持接近,这将有助于未来的错误修复发布。

    此外,如果在合并时,包含[cd build]标记的最后一次提交是空的,则CD作业将不会被触发。在这种情况下,您可以直接在1.6.X分支中推送一个带有标记的提交来触发它们。

  • 如果上述步骤顺利,请谨慎地为发布创建新标签。这应该只在您几乎确定发布已准备就绪时才进行,因为向主仓库添加新标签可能会触发某些自动化流程。

    git tag -a 1.6.2  # in the 1.6.X branch
    git push git@github.com:scikit-learn/scikit-learn.git 1.6.2
    

    警告

    不要使用github界面发布版本来创建标签,因为它会自动向所有关注该仓库的用户发送通知,即使网站尚未更新且wheels尚未上传。

  • 确认机器人已在conda-forge feedstock仓库conda-forge/scikit-learn-feedstock中检测到该标签。如果未检测到,请提交针对main分支的发布PR。

  • 再次触发PyPI发布工作流程,但这次将工件上传到真正的https://pypi.ac.cn/。为此,在“运行工作流程”表单中将testpypi替换为pypi

    或者,可以在本地收集生成的二进制wheel包和源tarball,并将它们全部上传到PyPI。

    从本地上传工件#

    检出发布标签并运行以下命令。

    rm -r dist
    python -m pip install -U wheelhouse_uploader twine
    python -m wheelhouse_uploader fetch \
      --version 1.6.2 --local-folder dist scikit-learn \
      https://pypi.anaconda.org/scikit-learn-wheels-staging/simple/scikit-learn/
    

    这些命令将下载anaconda.org托管服务上的暂存区中积累的所有二进制包,并将它们放在您的本地./dist文件夹中。检查./dist文件夹的内容:它应包含所有wheels以及源tarball .tar.gz。确保该文件夹中没有开发版本或旧版本的scikit-learn包。在上传到PyPI之前,您可以先尝试上传到test.pypi.org

    twine upload --verbose --repository-url https://test.pypi.org/legacy/ dist/*
    

    然后一次性将所有内容上传到pypi.org

    twine upload dist/*
    
  • main分支中,编辑doc/templates/index.html以更改登陆页面中的“新闻”部分以及发布月份。然后将其cherry-pick到发布分支。

  • scikit-learn/scikit-learn发布版本,并在邮件列表和社交网络上宣布。请记住在发布说明中添加更新日志的链接。理想情况下,仅当包在PyPI和conda-forge上可用并且网站已更新时才执行此步骤。

  • 更新SECURITY.md以反映最新支持的版本1.6.2

更新作者列表#

本节介绍更新scikit-learn背后的人。首先在GitHub上创建一个具有read:org权限的经典令牌。然后运行以下脚本并在提示时输入令牌。

cd build_tools
make authors  # Enter the token when prompted

提升我们依赖项最低版本的准则#

  • 最低Python版本:在scikit-learn次要版本发布(X.Y.0)时,我们放弃初始发布日期超过四年的Python版本。换句话说,我们的最低Python版本在3到4年之间。

  • 已编译依赖项(numpy、scipy,以及已编译的可选依赖项(pandas、matplotlib、pyamg、pillow等):我们采用与我们的最低Python版本具有wheels的最旧的次要版本(X.Y.0)。实际上,这意味着我们最低支持的版本大约是3年前的,可能稍短一些。

  • 纯Python依赖项(joblib、threadpoolctl):在scikit-learn发布时,我们最低支持的版本是至少两年前的最新次要版本(X.Y.0)。

  • 在某些特殊情况下,我们可能会决定比此准则不那么保守。这些特殊情况包括:我们某个依赖项中的安全错误修复或关键错误修复使其在维护方面支持成本过高。

maint_tools/bump-dependencies-versions.py实现了这些规则,可用于给出新的最低依赖项版本。它以预期的scikit-learn发布日期作为输入,例如

python maint_tools/bump-dependencies-versions.py 2025-12-01

合并拉取请求#

当PR在GitHub上合并时,单个提交会被squash。在合并之前

  • 如有必要,可以编辑生成的提交标题。请注意,这默认会重命名PR标题。

  • 可以编辑或删除包含所有提交标题的详细描述。

  • 对于具有多个代码贡献者的PR,必须注意在详细描述中保留Co-authored-by: name <name@example.com>标签。这将把PR标记为具有多个共同作者。代码贡献是否足够重要以值得共同作者身份,由维护者自行决定,与更新日志条目相同。

scikit-learn.org网站#

scikit-learn网站(https://scikit-learn.cn)托管在GitHub上,但很少需要通过推送到scikit-learn/scikit-learn.github.io仓库来手动更新。大多数更新可以通过推送到main(针对/dev)或发布分支A.B.X来完成,Circle CI将从那里自动构建和上传文档。

实验性功能#

sklearn.experimental模块在0.21版本中引入,包含实验性功能和估计器,这些功能和估计器可能会在没有弃用周期的情况下进行更改。

要创建实验模块,请参考enable_halving_search_cv.pyenable_iterative_imputer.py的内容。

注意

这些是0.24版本中的永久链接,其中这些估计器仍处于实验阶段。它们在您阅读时可能已稳定,因此使用了永久链接。有关从实验性到稳定版本的过渡说明,请参见下文。

请注意,公共导入路径必须是公共子包(如sklearn/ensemblesklearn/impute),而不仅仅是.py模块。此外,导入的(私有)实验性功能必须位于公共子包的子模块/子包中,例如sklearn/ensemble/_hist_gradient_boosting/sklearn/impute/_iterative.py。这是必需的,以便将来当功能不再是实验性时,pickles仍然有效。

为了避免类型检查器(例如mypy)错误,实验估计器的直接导入应在父模块中完成,并受到if typing.TYPE_CHECKING检查的保护。请参阅sklearn/ensemble/__init__.pysklearn/impute/__init__.py作为示例。另请编写遵循test_enable_hist_gradient_boosting.py中的基本测试。

请确保您编写的每个面向用户的代码都明确提及该功能是实验性的,并添加# noqa注释以避免PEP8相关警告。

# To use this experimental feature, we need to explicitly ask for it
from sklearn.experimental import enable_iterative_imputer  # noqa
from sklearn.impute import IterativeImputer

为了使文档正确渲染,请同时在doc/conf.py中导入enable_my_experimental_feature,否则sphinx将无法检测和导入相应的模块。请注意,使用from sklearn.experimental import *不起作用

注意

某些实验性类和函数可能未包含在sklearn.experimental模块中,例如sklearn.datasets.fetch_openml

一旦该功能变得稳定,请从scikit-learn代码库中删除所有enable_my_experimental_feature的出现,并使enable_my_experimental_feature成为一个仅引发警告的无操作,如enable_hist_gradient_boosting.py中所示。该文件应无限期地保留在那里,因为我们不想破坏用户的代码;我们只是通过警告激励他们删除该导入。另请记住相应地更新测试,请参阅test_enable_hist_gradient_boosting.py