1515"Python toolchain module extensions for use with bzlmod."
1616
1717load ("@bazel_features//:features.bzl" , "bazel_features" )
18- load ("//python:versions.bzl" , "DEFAULT_RELEASE_BASE_URL" , "PLATFORMS" , "TOOL_VERSIONS" )
18+ load ("//python:versions.bzl" , "DEFAULT_RELEASE_BASE_URL" , "MINOR_MAPPING" , " PLATFORMS" , "TOOL_VERSIONS" )
1919load (":auth.bzl" , "AUTH_ATTRS" )
2020load (":full_version.bzl" , "full_version" )
2121load (":platform_info.bzl" , "platform_info" )
@@ -84,7 +84,7 @@ def parse_modules(*, module_ctx, logger, _fail = fail):
8484 if not module_ctx .modules [0 ].tags .toolchain :
8585 ignore_root_user_error = True
8686
87- config = _get_toolchain_config (modules = module_ctx . modules , _fail = _fail )
87+ config = _get_toolchain_config (module_ctx = module_ctx , _fail = _fail )
8888
8989 default_python_version = None
9090 for mod in module_ctx .modules :
@@ -598,7 +598,7 @@ def _validate_version(version_str, *, _fail = fail):
598598
599599 return True
600600
601- def _process_single_version_overrides (* , tag , _fail = fail , default ):
601+ def _process_single_version_overrides (* , tag , _fail = fail , default , module_ctx = None ):
602602 if not _validate_version (tag .python_version , _fail = _fail ):
603603 return
604604
@@ -648,31 +648,65 @@ def _process_single_version_overrides(*, tag, _fail = fail, default):
648648 if tag .distutils :
649649 kwargs .setdefault (tag .python_version , {})["distutils" ] = tag .distutils
650650
651- def _process_single_version_platform_overrides (* , tag , _fail = fail , default ):
652- if not _validate_version (tag .python_version , _fail = _fail ):
651+ def _process_single_version_platform_overrides (* , tag , _fail = fail , default , module_ctx ):
652+ python_version = tag .python_version
653+ python_version_env = getattr (tag , "python_version_env" , None )
654+ if python_version_env :
655+ python_version = module_ctx .getenv (python_version_env , python_version )
656+
657+ if not python_version :
658+ _fail ("Either `python_version` or `python_version_env` must be specified and non-empty for python.single_version_platform_override." )
659+ return
660+
661+ parsed_version = version .parse (python_version , _fail = _fail )
662+ if not parsed_version :
663+ _fail ("Failed to parse PEP 440 version identifier '{}'. Parse error at '{}'" .format (python_version , python_version ))
664+ return
665+ if len (parsed_version .release ) < 3 :
666+ if python_version in MINOR_MAPPING :
667+ python_version = MINOR_MAPPING [python_version ]
668+
669+ if not _validate_version (python_version , _fail = _fail ):
653670 return
654671
655672 available_versions = default ["tool_versions" ]
656673
657- if tag .python_version not in available_versions :
658- if not tag .urls or not tag .sha256 or not tag .strip_prefix :
659- _fail ("When introducing a new python_version '{}', 'sha256', 'strip_prefix' and 'urls' must be specified" .format (tag .python_version ))
674+ sha256 = getattr (tag , "sha256" , None )
675+ sha256_env = getattr (tag , "sha256_env" , None )
676+ if sha256_env :
677+ sha256 = module_ctx .getenv (sha256_env , sha256 )
678+
679+ strip_prefix = tag .strip_prefix
680+ strip_prefix_env = getattr (tag , "strip_prefix_env" , None )
681+ if strip_prefix_env :
682+ strip_prefix = module_ctx .getenv (strip_prefix_env , strip_prefix )
683+
684+ urls = getattr (tag , "urls" , None )
685+ url_env = getattr (tag , "url_env" , None )
686+ if url_env :
687+ urls_from_env = module_ctx .getenv (url_env )
688+ if urls_from_env :
689+ urls = [url .strip () for url in urls_from_env .split ("," )]
690+
691+ if python_version not in available_versions :
692+ if not urls or not sha256 or not strip_prefix :
693+ _fail ("When introducing a new python_version '{}', 'sha256', 'strip_prefix' and 'urls' must be specified" .format (python_version ))
660694 return
661- available_versions [tag . python_version ] = {}
695+ available_versions [python_version ] = {}
662696
663697 if tag .coverage_tool :
664- available_versions [tag . python_version ].setdefault ("coverage_tool" , {})[tag .platform ] = tag .coverage_tool
698+ available_versions [python_version ].setdefault ("coverage_tool" , {})[tag .platform ] = tag .coverage_tool
665699 if tag .patch_strip :
666- available_versions [tag . python_version ].setdefault ("patch_strip" , {})[tag .platform ] = tag .patch_strip
700+ available_versions [python_version ].setdefault ("patch_strip" , {})[tag .platform ] = tag .patch_strip
667701 if tag .patches :
668- available_versions [tag . python_version ].setdefault ("patches" , {})[tag .platform ] = list (tag .patches )
669- if tag . sha256 :
670- available_versions [tag . python_version ].setdefault ("sha256" , {})[tag .platform ] = tag . sha256
671- if tag . strip_prefix :
672- available_versions [tag . python_version ].setdefault ("strip_prefix" , {})[tag .platform ] = tag . strip_prefix
702+ available_versions [python_version ].setdefault ("patches" , {})[tag .platform ] = list (tag .patches )
703+ if sha256 :
704+ available_versions [python_version ].setdefault ("sha256" , {})[tag .platform ] = sha256
705+ if strip_prefix :
706+ available_versions [python_version ].setdefault ("strip_prefix" , {})[tag .platform ] = strip_prefix
673707
674- if tag . urls :
675- available_versions [tag . python_version ].setdefault ("url" , {})[tag .platform ] = tag . urls
708+ if urls :
709+ available_versions [python_version ].setdefault ("url" , {})[tag .platform ] = urls
676710
677711 # If platform is customized, or doesn't exist, (re)define one.
678712 if ((tag .target_compatible_with or tag .target_settings or tag .os_name or tag .arch ) or
@@ -720,7 +754,7 @@ def _process_single_version_platform_overrides(*, tag, _fail = fail, default):
720754
721755 default ["platforms" ] = override_first
722756
723- def _process_global_overrides (* , tag , default , _fail = fail ):
757+ def _process_global_overrides (* , tag , default , _fail = fail , module_ctx = None ):
724758 if tag .available_python_versions :
725759 available_versions = default ["tool_versions" ]
726760 all_versions = dict (available_versions )
@@ -755,8 +789,8 @@ def _process_global_overrides(*, tag, default, _fail = fail):
755789 if getattr (tag , key , None ):
756790 default [key ] = getattr (tag , key )
757791
758- def _override_defaults (* overrides , modules , _fail = fail , default ):
759- mod = modules [0 ] if modules else None
792+ def _override_defaults (* overrides , module_ctx , _fail = fail , default ):
793+ mod = module_ctx . modules [0 ] if module_ctx . modules else None
760794 if not mod or not mod .is_root :
761795 return
762796
@@ -774,9 +808,9 @@ def _override_defaults(*overrides, modules, _fail = fail, default):
774808 _fail ("Only a single 'python.{}' can be present" .format (override .name ))
775809 return
776810
777- override .fn (tag = tag , _fail = _fail , default = default )
811+ override .fn (tag = tag , _fail = _fail , default = default , module_ctx = module_ctx )
778812
779- def _get_toolchain_config (* , modules , _fail = fail ):
813+ def _get_toolchain_config (* , module_ctx , _fail = fail ):
780814 """Computes the configs for toolchains.
781815
782816 Args:
@@ -848,7 +882,7 @@ def _get_toolchain_config(*, modules, _fail = fail):
848882 key = lambda t : None ,
849883 fn = _process_global_overrides ,
850884 ),
851- modules = modules ,
885+ module_ctx = module_ctx ,
852886 default = default ,
853887 _fail = _fail ,
854888 )
@@ -1296,18 +1330,45 @@ Arbitrary platform strings allowed.
12961330 ),
12971331 ),
12981332 "python_version" : attr .string (
1299- mandatory = True ,
1333+ mandatory = False ,
13001334 doc = "The python version to override URLs for. Must be in `X.Y.Z` format." ,
13011335 ),
1336+ "python_version_env" : attr .string (
1337+ mandatory = False ,
1338+ doc = """\
1339+ The environment variable for the python version. Overrides `python_version` if set.
1340+
1341+ :::{{versionadded}} 1.6.0
1342+ :::
1343+ """ ,
1344+ ),
13021345 "sha256" : attr .string (
13031346 mandatory = False ,
13041347 doc = "The sha256 for the archive" ,
13051348 ),
1349+ "sha256_env" : attr .string (
1350+ mandatory = False ,
1351+ doc = """\
1352+ The environment variable for the sha256. Overrides `sha256` if set.
1353+
1354+ :::{{versionadded}} 1.6.0
1355+ :::
1356+ """ ,
1357+ ),
13061358 "strip_prefix" : attr .string (
13071359 mandatory = False ,
13081360 doc = "The 'strip_prefix' for the archive, defaults to 'python'." ,
13091361 default = "python" ,
13101362 ),
1363+ "strip_prefix_env" : attr .string (
1364+ mandatory = False ,
1365+ doc = """\
1366+ The environment variable for the strip_prefix. Overrides `strip_prefix` if set.
1367+
1368+ :::{{versionadded}} 1.6.0
1369+ :::
1370+ """ ,
1371+ ),
13111372 "target_compatible_with" : attr .string_list (
13121373 doc = """
13131374The `target_compatible_with` values to use for the toolchain definition.
@@ -1342,6 +1403,15 @@ Docs for [Registering custom runtimes]
13421403 mandatory = False ,
13431404 doc = "The URL template to fetch releases for this Python version. If the URL template results in a relative fragment, default base URL is going to be used. Occurrences of `{python_version}`, `{platform}` and `{build}` will be interpolated based on the contents in the override and the known {attr}`platform` values." ,
13441405 ),
1406+ "url_env" : attr .string (
1407+ mandatory = False ,
1408+ doc = """\
1409+ The environment variable for a comma-separated list of URLs. Overrides `urls` if set.
1410+
1411+ :::{{versionadded}} 1.6.0
1412+ :::
1413+ """ ,
1414+ ),
13451415 },
13461416)
13471417
0 commit comments