@@ -236,6 +236,16 @@ def setup_instparser(instparser):
236236 "releaseargs" , nargs = argparse .REMAINDER , help = extraargs_help ("pytest" )
237237 )
238238
239+ patchreleaseparser = subparsers .add_parser (
240+ "update_patch_versions" ,
241+ help = "Updates version numbers during patch release, used by maintainers and CI" ,
242+ )
243+ patchreleaseparser .set_defaults (func = patch_release_args )
244+ patchreleaseparser .add_argument ("--stable_version" , required = True )
245+ patchreleaseparser .add_argument ("--unstable_version" , required = True )
246+ patchreleaseparser .add_argument ("--stable_version_prev" , required = True )
247+ patchreleaseparser .add_argument ("--unstable_version_prev" , required = True )
248+
239249 fmtparser = subparsers .add_parser (
240250 "format" ,
241251 help = "Formats all source code with black and isort." ,
@@ -599,6 +609,23 @@ def update_dependencies(targets, version, packages):
599609 )
600610
601611
612+ def update_patch_dependencies (targets , version , prev_version , packages ):
613+ print ("updating patch dependencies" )
614+ # PEP 508 allowed specifier operators
615+ operators = ["==" , "!=" , "<=" , ">=" , "<" , ">" , "===" , "~=" , "=" ]
616+ operators_pattern = "|" .join (re .escape (op ) for op in operators )
617+
618+ for pkg in packages :
619+ search = rf"({ basename (pkg )} [^,]*)(\s?({ operators_pattern } )\s?)(.*{ prev_version } )"
620+ replace = r"\1\2 " + version
621+ update_files (
622+ targets ,
623+ "pyproject.toml" ,
624+ search ,
625+ replace ,
626+ )
627+
628+
602629def update_files (targets , filename , search , replace ):
603630 errors = False
604631 for target in targets :
@@ -640,6 +667,32 @@ def release_args(args):
640667 update_version_files (targets , version , packages )
641668
642669
670+ def patch_release_args (args ):
671+ print ("preparing patch release" )
672+
673+ rootpath = find_projectroot ()
674+ targets = list (find_targets_unordered (rootpath ))
675+ cfg = ConfigParser ()
676+ cfg .read (str (find_projectroot () / "eachdist.ini" ))
677+ # stable
678+ mcfg = cfg ["stable" ]
679+ packages = mcfg ["packages" ].split ()
680+ print (f"update stable packages to { args .stable_version } " )
681+ update_patch_dependencies (
682+ targets , args .stable_version , args .stable_version_prev , packages
683+ )
684+ update_version_files (targets , args .stable_version , packages )
685+
686+ # prerelease
687+ mcfg = cfg ["prerelease" ]
688+ packages = mcfg ["packages" ].split ()
689+ print (f"update prerelease packages to { args .unstable_version } " )
690+ update_patch_dependencies (
691+ targets , args .unstable_version , args .unstable_version_prev , packages
692+ )
693+ update_version_files (targets , args .unstable_version , packages )
694+
695+
643696def test_args (args ):
644697 clean_remainder_args (args .pytestargs )
645698 execute_args (
0 commit comments