@@ -237,6 +237,16 @@ def setup_instparser(instparser):
237237 "releaseargs" , nargs = argparse .REMAINDER , help = extraargs_help ("pytest" )
238238 )
239239
240+ patchreleaseparser = subparsers .add_parser (
241+ "update_patch_versions" ,
242+ help = "Updates version numbers during patch release, used by maintainers and CI" ,
243+ )
244+ patchreleaseparser .set_defaults (func = patch_release_args )
245+ patchreleaseparser .add_argument ("--stable_version" , required = True )
246+ patchreleaseparser .add_argument ("--unstable_version" , required = True )
247+ patchreleaseparser .add_argument ("--stable_version_prev" , required = True )
248+ patchreleaseparser .add_argument ("--unstable_version_prev" , required = True )
249+
240250 fmtparser = subparsers .add_parser (
241251 "format" ,
242252 help = "Formats all source code with black and isort." ,
@@ -655,6 +665,27 @@ def update_dependencies(targets, version, packages):
655665 )
656666
657667
668+ def update_patch_dependencies (targets , version , prev_version , packages ):
669+ print ("updating patch dependencies" )
670+ if "all" in packages :
671+ packages .extend (targets )
672+
673+ # PEP 508 allowed specifier operators
674+ operators = ["==" , "!=" , "<=" , ">=" , "<" , ">" , "===" , "~=" , "=" ]
675+ operators_pattern = "|" .join (re .escape (op ) for op in operators )
676+
677+ for pkg in packages :
678+ search = rf"({ basename (pkg )} [^,]*?)(\s?({ operators_pattern } )\s?)(.*{ prev_version } )"
679+ replace = r"\g<1>\g<2>" + version
680+ print (f"{ search = } \t { replace = } \t { pkg = } " )
681+ update_files (
682+ targets ,
683+ "pyproject.toml" ,
684+ search ,
685+ replace ,
686+ )
687+
688+
658689def update_files (targets , filename , search , replace ):
659690 errors = False
660691 for target in targets :
@@ -687,10 +718,12 @@ def release_args(args):
687718 versions = args .versions
688719 updated_versions = []
689720
721+ # remove excluded packages
690722 excluded = cfg ["exclude_release" ]["packages" ].split ()
691723 targets = [
692724 target for target in targets if basename (target ) not in excluded
693725 ]
726+
694727 for group in versions .split ("," ):
695728 mcfg = cfg [group ]
696729 version = mcfg ["version" ]
@@ -707,6 +740,40 @@ def release_args(args):
707740 update_changelogs ("-" .join (updated_versions ))
708741
709742
743+ def patch_release_args (args ):
744+ print ("preparing patch release" )
745+
746+ rootpath = find_projectroot ()
747+ targets = list (find_targets_unordered (rootpath ))
748+ cfg = ConfigParser ()
749+ cfg .read (str (find_projectroot () / "eachdist.ini" ))
750+
751+ # remove excluded packages
752+ excluded = cfg ["exclude_release" ]["packages" ].split ()
753+ targets = [
754+ target for target in targets if basename (target ) not in excluded
755+ ]
756+
757+ # stable
758+ mcfg = cfg ["stable" ]
759+ packages = mcfg ["packages" ].split ()
760+ print (f"update stable packages to { args .stable_version } " )
761+
762+ update_patch_dependencies (
763+ targets , args .stable_version , args .stable_version_prev , packages
764+ )
765+ update_version_files (targets , args .stable_version , packages )
766+
767+ # prerelease
768+ mcfg = cfg ["prerelease" ]
769+ packages = mcfg ["packages" ].split ()
770+ print (f"update prerelease packages to { args .unstable_version } " )
771+ update_patch_dependencies (
772+ targets , args .unstable_version , args .unstable_version_prev , packages
773+ )
774+ update_version_files (targets , args .unstable_version , packages )
775+
776+
710777def test_args (args ):
711778 clean_remainder_args (args .pytestargs )
712779 execute_args (
0 commit comments