refactor: split code into multiple files (#4044)
A followup to #4043 to split code into multiple files to make the
interaction
and reuse more intentional and explicit.
Work towards #2948.
---------
Co-authored-by: Richard Levasseur <richardlev@gmail.com>
diff --git a/docs/BUILD.bazel b/docs/BUILD.bazel
index baa1a1e..fd52473 100644
--- a/docs/BUILD.bazel
+++ b/docs/BUILD.bazel
@@ -146,7 +146,9 @@
"//python/private/api:py_common_api",
"//python/private/pypi:config_settings",
"//python/private/pypi:env_marker_info",
+ "//python/private/pypi:pip_archive",
"//python/private/pypi:pkg_aliases",
+ "//python/private/pypi:whl_archive",
"//python/private/pypi:whl_config_setting",
"//python/private/pypi:whl_library",
"//python/private/zipapp:py_zipapp_rule",
diff --git a/python/private/pypi/BUILD.bazel b/python/private/pypi/BUILD.bazel
index 3c07135..2d774e5 100644
--- a/python/private/pypi/BUILD.bazel
+++ b/python/private/pypi/BUILD.bazel
@@ -473,20 +473,8 @@
name = "whl_library",
srcs = ["whl_library.bzl"],
deps = [
- ":attrs",
- ":deps",
- ":generate_whl_library_build_bazel",
- ":patch_whl",
- ":pep508_requirement",
- ":pypi_repo_utils",
- ":urllib",
- ":whl_extract",
- ":whl_metadata",
- "//python/private:auth",
- "//python/private:envsubst",
- "//python/private:is_standalone_interpreter",
- "//python/private:normalize_name",
- "//python/private:repo_utils",
+ ":pip_archive",
+ ":whl_archive",
],
)
@@ -548,6 +536,61 @@
)
bzl_library(
+ name = "pip_archive",
+ srcs = ["pip_archive.bzl"],
+ deps = [
+ ":attrs",
+ ":deps",
+ ":patch_and_extract_whl",
+ ":pypi_repo_utils",
+ ":urllib",
+ ":whl_archive",
+ "//python/private:auth",
+ "//python/private:envsubst",
+ "//python/private:is_standalone_interpreter",
+ "//python/private:repo_utils",
+ ],
+)
+
+bzl_library(
+ name = "whl_archive",
+ srcs = ["whl_archive.bzl"],
+ deps = [
+ ":attrs",
+ ":patch_and_extract_whl",
+ ":urllib",
+ ":whl_deps_repo",
+ "//python/private:auth",
+ "//python/private:repo_utils",
+ ],
+)
+
+bzl_library(
+ name = "whl_deps_repo",
+ srcs = ["whl_deps_repo.bzl"],
+ deps = [
+ ":generate_whl_library_build_bazel",
+ ":pep508_requirement",
+ "//python/private:repo_utils",
+ ],
+)
+
+bzl_library(
+ name = "patch_and_extract_whl",
+ srcs = ["patch_and_extract_whl.bzl"],
+ deps = [
+ ":generate_whl_library_build_bazel",
+ ":patch_whl",
+ ":pep508_requirement",
+ ":pypi_repo_utils",
+ ":whl_extract",
+ ":whl_metadata",
+ "//python/private:normalize_name",
+ "//python/private:repo_utils",
+ ],
+)
+
+bzl_library(
name = "argparse",
srcs = ["argparse.bzl"],
)
diff --git a/python/private/pypi/patch_and_extract_whl.bzl b/python/private/pypi/patch_and_extract_whl.bzl
new file mode 100644
index 0000000..b4ecb2f
--- /dev/null
+++ b/python/private/pypi/patch_and_extract_whl.bzl
@@ -0,0 +1,164 @@
+""
+
+load("//python/private:normalize_name.bzl", "normalize_name")
+load("//python/private:repo_utils.bzl", "repo_utils")
+load(":generate_whl_library_build_bazel.bzl", "generate_whl_library_build_bazel")
+load(":patch_whl.bzl", "patch_whl")
+load(":pep508_requirement.bzl", "requirement")
+load(":pypi_repo_utils.bzl", "pypi_repo_utils")
+load(":whl_extract.bzl", "whl_extract")
+load(":whl_metadata.bzl", "parse_entry_points", "whl_metadata")
+
+def _get_entry_points(rctx, install_dir_path, metadata):
+ dist_info_dir = "{}-{}.dist-info".format(
+ metadata.name.replace("-", "_"),
+ metadata.version.replace("-", "_"),
+ )
+ entry_points_txt = install_dir_path.get_child(dist_info_dir).get_child("entry_points.txt")
+ if entry_points_txt.exists:
+ return parse_entry_points(rctx.read(entry_points_txt))
+ return {}
+
+def _move_scripts_needing_shebang_rewrite(rctx, entry_points):
+ bin_dir = rctx.path("bin")
+ if not bin_dir.exists:
+ return
+
+ ep_names = {name.lower(): True for name in entry_points}
+ for script in bin_dir.readdir():
+ if script.is_dir:
+ continue
+ if script.basename.lower() in ep_names:
+ rctx.delete(script)
+ continue
+ if script.basename.endswith(".exe") or script.basename.endswith(".dll"):
+ continue
+ content = rctx.read(script)
+ if content.startswith("#!python"):
+ rewrite_bin_dir = rctx.path("rewrite-bin")
+ repo_utils.mkdir(rctx, rewrite_bin_dir)
+ repo_utils.rename(rctx, script, rctx.path("rewrite-bin/" + script.basename))
+
+def _to_purl(*, index, metadata, filename):
+ """
+ Produce a PyPI PURL from the metadata.
+
+ https://github.com/package-url/purl-spec/blob/main/types-doc/pypi-definition.md
+ """
+
+ # https://github.com/package-url/purl-spec/blob/main/types-doc/pypi-definition.md#name-definition
+ name = normalize_name(metadata.name).replace("_", "-")
+
+ qualifiers = {}
+ if index:
+ qualifiers["repository_url"] = index
+ if filename:
+ qualifiers["file_name"] = filename
+
+ return "pkg:pypi/{}@{}?{}".format(name, metadata.version, "&".join(["{}={}".format(key, val) for key, val in qualifiers.items()]))
+
+def _remove_files(rctx, *basenames):
+ paths = list(rctx.path(".").readdir())
+ for _ in range(10000000):
+ if not paths:
+ break
+ path = paths.pop()
+
+ if path.basename in basenames:
+ rctx.delete(path)
+ elif path.is_dir:
+ paths.extend(path.readdir())
+
+def patch_and_extract_whl(rctx, *, whl_path, logger, sdist_filename = None):
+ """Extract the wheel, apply patches and generate BUILD.bazel files.
+
+ Reused in pip and http wheel download code.
+
+ Args:
+ rctx: the repository ctx.
+ whl_path: the whl path to extract.
+ logger: The logger to use
+ sdist_filename: The filename to ignore in the BUILD.bazel files as sources.
+
+ Returns:
+ The repository metadata if the extraction is reproducible
+ """
+ if rctx.attr.whl_patches:
+ patches = {}
+ for patch_file, json_args in rctx.attr.whl_patches.items():
+ patch_dst = struct(**json.decode(json_args))
+ if whl_path.basename in patch_dst.whls:
+ patches[patch_file] = patch_dst.patch_strip
+
+ if patches:
+ whl_path = patch_whl(
+ rctx,
+ whl_path = whl_path,
+ patches = patches,
+ )
+
+ whl_extract(rctx, whl_path = whl_path, logger = logger)
+
+ install_dir_path = whl_path.dirname.get_child("site-packages")
+ metadata = whl_metadata(
+ install_dir = install_dir_path,
+ read_fn = rctx.read,
+ logger = logger,
+ )
+ rctx.file("metadata.json", json.encode_indent({
+ "name": metadata.name,
+ "provides_extra": metadata.provides_extra,
+ "requires_dist": metadata.requires_dist,
+ "version": metadata.version,
+ }))
+ namespace_package_files = pypi_repo_utils.find_namespace_package_files(rctx, install_dir_path)
+
+ entry_points = _get_entry_points(rctx, install_dir_path, metadata)
+ _move_scripts_needing_shebang_rewrite(rctx, entry_points)
+
+ build_file_contents = generate_whl_library_build_bazel(
+ name = whl_path.basename,
+ dep_template = rctx.attr.dep_template,
+ sdist_filename = sdist_filename,
+ config_load = rctx.attr.config_load,
+ metadata_name = metadata.name,
+ metadata_version = metadata.version,
+ requires_dist = metadata.requires_dist,
+ # TODO @aignas 2025-05-17: maybe have a build flag for this instead
+ enable_implicit_namespace_pkgs = rctx.attr.enable_implicit_namespace_pkgs,
+ # TODO @aignas 2025-04-14: load through the hub:
+ annotation = None if not rctx.attr.annotation else struct(**json.decode(rctx.read(rctx.attr.annotation))),
+ data_exclude = rctx.attr.pip_data_exclude,
+ group_deps = rctx.attr.group_deps,
+ group_name = rctx.attr.group_name,
+ namespace_package_files = namespace_package_files,
+ extras = requirement(rctx.attr.requirement).extras,
+ entry_points = entry_points,
+ purl = _to_purl(
+ index = rctx.attr.index_url,
+ metadata = metadata,
+ filename = sdist_filename or whl_path.basename,
+ ),
+ )
+
+ # Delete these in case the wheel had them. They generally don't cause
+ # a problem, but let's avoid the chance of that happening.
+ rctx.file("WORKSPACE")
+ rctx.file("WORKSPACE.bazel")
+ rctx.file("MODULE.bazel")
+ rctx.file("REPO.bazel", """\
+repo(
+ default_package_metadata = [
+ "//:package_metadata",
+ ],
+)
+""")
+
+ # BUILD files interfere with globbing and Bazel package boundaries.
+ _remove_files(rctx, "BUILD", "BUILD.bazel")
+ rctx.file("BUILD.bazel", build_file_contents)
+
+ if hasattr(rctx, "repo_metadata"):
+ return rctx.repo_metadata(reproducible = True)
+
+ return None
diff --git a/python/private/pypi/pip_archive.bzl b/python/private/pypi/pip_archive.bzl
new file mode 100644
index 0000000..9fc50cc
--- /dev/null
+++ b/python/private/pypi/pip_archive.bzl
@@ -0,0 +1,401 @@
+""
+
+load("//python/private:auth.bzl", "get_auth")
+load("//python/private:envsubst.bzl", "envsubst")
+load("//python/private:is_standalone_interpreter.bzl", "is_standalone_interpreter")
+load("//python/private:repo_utils.bzl", "REPO_DEBUG_ENV_VAR", "repo_utils")
+load(":attrs.bzl", "ATTRS", "use_isolated")
+load(":deps.bzl", "all_repo_names", "record_files")
+load(":patch_and_extract_whl.bzl", "patch_and_extract_whl")
+load(":pypi_repo_utils.bzl", "pypi_repo_utils")
+load(":urllib.bzl", "urllib")
+load(":whl_archive.bzl", "whl_archive_attrs")
+
+_CPPFLAGS = "CPPFLAGS"
+_COMMAND_LINE_TOOLS_PATH_SLUG = "commandlinetools"
+
+def _get_xcode_location_cflags(rctx, logger = None):
+ """Query the xcode sdk location to update cflags
+
+ Figure out if this interpreter target comes from rules_python, and patch the xcode sdk location if so.
+ Pip won't be able to compile c extensions from sdists with the pre built python distributions from astral-sh
+ otherwise. See https://github.com/astral-sh/python-build-standalone/issues/103
+ """
+
+ # Only run on MacOS hosts
+ if not rctx.os.name.lower().startswith("mac os"):
+ return []
+
+ xcode_sdk_location = repo_utils.execute_unchecked(
+ rctx,
+ op = "GetXcodeLocation",
+ arguments = [repo_utils.which_checked(rctx, "xcode-select"), "--print-path"],
+ logger = logger,
+ )
+ if xcode_sdk_location.return_code != 0:
+ return []
+
+ xcode_root = xcode_sdk_location.stdout.strip()
+ if _COMMAND_LINE_TOOLS_PATH_SLUG not in xcode_root.lower():
+ # This is a full xcode installation somewhere like /Applications/Xcode13.0.app/Contents/Developer
+ # so we need to change the path to to the macos specific tools which are in a different relative
+ # path than xcode installed command line tools.
+ xcode_sdks_json = repo_utils.execute_checked(
+ rctx,
+ op = "LocateXCodeSDKs",
+ arguments = [
+ repo_utils.which_checked(rctx, "xcrun"),
+ "xcodebuild",
+ "-showsdks",
+ "-json",
+ ],
+ environment = {
+ "DEVELOPER_DIR": xcode_root,
+ },
+ logger = logger,
+ ).stdout
+ xcode_sdks = json.decode(xcode_sdks_json)
+ potential_sdks = [
+ sdk
+ for sdk in xcode_sdks
+ if "productName" in sdk and
+ sdk["productName"] == "macOS" and
+ "darwinos" not in sdk["canonicalName"]
+ ]
+
+ # Now we'll get two entries here (one for internal and another one for public)
+ # It shouldn't matter which one we pick.
+ xcode_sdk_path = potential_sdks[0]["sdkPath"]
+ else:
+ xcode_sdk_path = "{}/SDKs/MacOSX.sdk".format(xcode_root)
+
+ return [
+ "-isysroot {}".format(xcode_sdk_path),
+ ]
+
+def _get_toolchain_unix_cflags(rctx, python_interpreter, logger = None):
+ """Gather cflags from a standalone toolchain for unix systems.
+
+ Pip won't be able to compile c extensions from sdists with the pre built python distributions from astral-sh
+ otherwise. See https://github.com/astral-sh/python-build-standalone/issues/103
+ """
+
+ # Only run on Unix systems
+ if not rctx.os.name.lower().startswith(("mac os", "linux")):
+ return []
+
+ # Only update the location when using a standalone toolchain.
+ if not is_standalone_interpreter(rctx, python_interpreter, logger = logger):
+ return []
+
+ stdout = pypi_repo_utils.execute_checked_stdout(
+ rctx,
+ op = "GetPythonVersionForUnixCflags",
+ # python_interpreter by default points to a symlink, however when using bazel in vendor mode,
+ # and the vendored directory moves around, the execution of python fails, as it's getting confused
+ # where it's running from. More to the fact that we are executing it in isolated mode "-I", which
+ # results in PYTHONHOME being ignored. The solution is to run python from it's real directory.
+ python = python_interpreter.realpath,
+ arguments = [
+ # Run the interpreter in isolated mode, this options implies -E, -P and -s.
+ # Ensures environment variables are ignored that are set in userspace, such as PYTHONPATH,
+ # which may interfere with this invocation.
+ "-I",
+ "-c",
+ "import sys; print(f'{sys.version_info[0]}.{sys.version_info[1]}', end='')",
+ ],
+ srcs = [],
+ logger = logger,
+ )
+ _python_version = stdout
+ include_path = "{}/include/python{}".format(
+ python_interpreter.dirname,
+ _python_version,
+ )
+
+ return ["-isystem {}".format(include_path)]
+
+def _parse_optional_attrs(rctx, args, extra_pip_args = None):
+ """Helper function to parse common attributes of pip_repository and whl_library repository rules.
+
+ This function also serializes the structured arguments as JSON
+ so they can be passed on the command line to subprocesses.
+
+ Args:
+ rctx: Handle to the rule repository context.
+ args: A list of parsed args for the rule.
+ extra_pip_args: The pip args to pass.
+ Returns: Augmented args list.
+ """
+
+ if use_isolated(rctx, rctx.attr):
+ args.append("--isolated")
+
+ # Check for None so we use empty default types from our attrs.
+ # Some args want to be list, and some want to be dict.
+ if extra_pip_args != None:
+ args += [
+ "--extra_pip_args",
+ json.encode(struct(arg = [
+ envsubst(pip_arg, rctx.attr.envsubst, rctx.getenv)
+ for pip_arg in extra_pip_args
+ ])),
+ ]
+
+ if rctx.attr.download_only:
+ args.append("--download_only")
+
+ if rctx.attr.pip_data_exclude != None:
+ args += [
+ "--pip_data_exclude",
+ json.encode(struct(arg = rctx.attr.pip_data_exclude)),
+ ]
+
+ env = {}
+ if rctx.attr.environment != None:
+ for key, value in rctx.attr.environment.items():
+ env[key] = value
+
+ # This is super hacky, but working out something nice is tricky.
+ # This is in particular needed for psycopg2 which attempts to link libpython.a,
+ # in order to point the linker at the correct python intepreter.
+ if rctx.attr.add_libdir_to_library_search_path:
+ if "LDFLAGS" in env:
+ fail("Can't set both environment LDFLAGS and add_libdir_to_library_search_path")
+ command = [
+ pypi_repo_utils.resolve_python_interpreter(rctx),
+ "-c",
+ "import sys ; sys.stdout.write('{}/lib'.format(sys.exec_prefix))",
+ ]
+ result = rctx.execute(command)
+ if result.return_code != 0:
+ fail("Failed to get LDFLAGS path: command: {}, exit code: {}, stdout: {}, stderr: {}".format(command, result.return_code, result.stdout, result.stderr))
+ libdir = result.stdout
+ env["LDFLAGS"] = "-L{}".format(libdir)
+
+ args += [
+ "--environment",
+ json.encode(struct(arg = env)),
+ ]
+
+ return args
+
+def _get_python_home(rctx, python_interpreter, logger = None):
+ """Get the PYTHONHOME directory from the selected python interpretter
+
+ Args:
+ rctx (repository_ctx): The repository context.
+ python_interpreter (path): The resolved python interpreter.
+ logger: Optional logger to use for operations.
+ Returns:
+ String of PYTHONHOME directory.
+ """
+
+ return pypi_repo_utils.execute_checked_stdout(
+ rctx,
+ op = "GetPythonHome",
+ # python_interpreter by default points to a symlink, however when using bazel in vendor mode,
+ # and the vendored directory moves around, the execution of python fails, as it's getting confused
+ # where it's running from. More to the fact that we are executing it in isolated mode "-I", which
+ # results in PYTHONHOME being ignored. The solution is to run python from it's real directory.
+ python = python_interpreter.realpath,
+ arguments = [
+ # Run the interpreter in isolated mode, this options implies -E, -P and -s.
+ # Ensures environment variables are ignored that are set in userspace, such as PYTHONPATH,
+ # which may interfere with this invocation.
+ "-I",
+ "-c",
+ "import sys; print(f'{sys.prefix}', end='')",
+ ],
+ srcs = [],
+ logger = logger,
+ )
+
+def _create_repository_execution_environment(rctx, python_interpreter, logger = None):
+ """Create a environment dictionary for processes we spawn with rctx.execute.
+
+ Args:
+ rctx (repository_ctx): The repository context.
+ python_interpreter (path): The resolved python interpreter.
+ logger: Optional logger to use for operations.
+ Returns:
+ Dictionary of environment variable suitable to pass to rctx.execute.
+ """
+
+ env = {
+ "PYTHONHOME": _get_python_home(rctx, python_interpreter, logger),
+ "PYTHONPATH": pypi_repo_utils.construct_pythonpath(
+ rctx,
+ entries = rctx.attr._python_path_entries,
+ ),
+ }
+
+ # Gather any available CPPFLAGS values
+ #
+ # We may want to build in an environment without a cc toolchain.
+ # In those cases, we're limited to --download-only, but we should respect that here.
+ is_wheel = rctx.attr.filename and rctx.attr.filename.endswith(".whl")
+ if not (rctx.attr.download_only or is_wheel):
+ cppflags = []
+ cppflags.extend(_get_xcode_location_cflags(rctx, logger = logger))
+ cppflags.extend(_get_toolchain_unix_cflags(rctx, python_interpreter, logger = logger))
+ env[_CPPFLAGS] = " ".join(cppflags)
+ return env
+
+def _pip_archive_impl(rctx):
+ logger = repo_utils.logger(rctx)
+
+ sdist_filename = None
+ extra_pip_args = []
+ extra_pip_args.extend(rctx.attr.extra_pip_args)
+ if rctx.attr.urls and rctx.attr.filename:
+ filename = rctx.attr.filename
+ urls = rctx.attr.urls
+ urls = [
+ urllib.absolute_url(
+ rctx.attr.index_url,
+ url,
+ envsubst = rctx.attr.envsubst,
+ getenv = rctx.getenv,
+ )
+ for url in urls
+ ]
+ result = rctx.download(
+ url = urls,
+ output = filename,
+ sha256 = rctx.attr.sha256,
+ integrity = rctx.attr.integrity if not rctx.attr.sha256 else "",
+ auth = get_auth(rctx, urls),
+ )
+ if not rctx.attr.sha256 and not rctx.attr.integrity:
+ # this is only seen when there is a direct URL reference without a hash
+ logger.warn("Please update the requirement line to include the hash:\n{} \\\n --hash=sha256:{}".format(
+ rctx.attr.requirement,
+ result.sha256,
+ ))
+
+ if not result.success:
+ fail("could not download the '{}' from {}:\n{}".format(filename, urls, result))
+
+ if filename.endswith(".whl"):
+ fail("Only sdists are supported")
+ else:
+ sdist_filename = filename
+
+ # It is an sdist and we need to tell PyPI to use a file in this directory
+ # and, allow getting build dependencies from PYTHONPATH, which we
+ # setup in this repository rule, but still download any necessary
+ # build deps from PyPI (e.g. `flit_core`) if they are missing.
+ extra_pip_args.extend(["--find-links", "."])
+
+ # When we already have a wheel, Python isn't used,
+ # so there's no need to setup env vars to run Python, unless we need to
+ # build an sdist or resolve a requirement.
+ python_interpreter = pypi_repo_utils.resolve_python_interpreter(
+ rctx,
+ python_interpreter = rctx.attr.python_interpreter,
+ python_interpreter_target = rctx.attr.python_interpreter_target,
+ )
+ args = [
+ "-m",
+ "python.private.pypi.whl_installer.wheel_installer",
+ "--requirement",
+ rctx.attr.requirement,
+ ]
+ args = _parse_optional_attrs(rctx, args, extra_pip_args)
+
+ # Manually construct the PYTHONPATH since we cannot use the toolchain here
+ environment = _create_repository_execution_environment(rctx, python_interpreter, logger = logger)
+
+ if rctx.attr.urls:
+ op_tmpl = "whl_library.BuildWheelFromSource({name}, {requirement})"
+ elif rctx.attr.download_only:
+ op_tmpl = "whl_library.DownloadWheel({name}, {requirement})"
+ else:
+ op_tmpl = "whl_library.ResolveRequirement({name}, {requirement})"
+
+ pypi_repo_utils.execute_checked(
+ rctx,
+ # truncate the requirement value when logging it / reporting
+ # progress since it may contain several ' --hash=sha256:...
+ # --hash=sha256:...' substrings that fill up the console
+ python = python_interpreter,
+ op = op_tmpl.format(name = rctx.attr.name, requirement = rctx.attr.requirement.split(" ", 1)[0]),
+ arguments = args,
+ environment = environment,
+ srcs = rctx.attr._python_srcs,
+ quiet = rctx.attr.quiet,
+ timeout = rctx.attr.timeout,
+ logger = logger,
+ )
+
+ whl_path = rctx.path(json.decode(rctx.read("whl_file.json"))["whl_file"])
+ if not rctx.delete("whl_file.json"):
+ fail("failed to delete the whl_file.json file")
+
+ # NOTE @aignas 2026-08-14: We never return rctx.metadata for pip archives because the result may
+ # not be reproducible across all machines given the input args to the repository rule.
+ patch_and_extract_whl(rctx, whl_path = whl_path, logger = logger, sdist_filename = sdist_filename)
+
+# NOTE @aignas 2024-03-21: The usage of dict({}, **common) ensures that all args to `dict` are unique
+_attrs = whl_archive_attrs | {
+ k: ATTRS[k]
+ for k in [
+ # used for pulling deps with pip
+ "download_only",
+ "add_libdir_to_library_search_path",
+ "environment",
+ "extra_pip_args",
+ "isolated",
+ "python_interpreter",
+ "python_interpreter_target",
+ "quiet",
+ "timeout",
+ ]
+} | {
+ "_python_path_entries": attr.label_list(
+ # Get the root directory of these rules and keep them as a default attribute
+ # in order to avoid unnecessary repository fetching restarts.
+ #
+ # This is very similar to what was done in https://github.com/bazelbuild/rules_go/pull/3478
+ default = [
+ Label("//:BUILD.bazel"),
+ ] + [
+ # Includes all the external dependencies from repositories.bzl
+ Label("@" + repo + "//:BUILD.bazel")
+ for repo in all_repo_names
+ ],
+ ),
+ "_python_srcs": attr.label_list(
+ # Used as a default value in a rule to ensure we fetch the dependencies.
+ default = [
+ Label("//python/private/pypi/whl_installer:wheel_installer.py"),
+ Label("//python/private/pypi/whl_installer:arguments.py"),
+ ] + record_files.values(),
+ ),
+}
+
+pip_archive = repository_rule(
+ attrs = _attrs | {
+ "_rule_name": attr.string(default = "pip_archive"),
+ },
+ doc = """
+Download and extracts a single wheel based into a bazel repo based on the requirement string passed in.
+Instantiated from pip_repository and inherits config options from there.
+
+:::{versionchanged} 1.9.0
+The `whl_library` is marked as reproducible if using starlark to extract and parse the
+wheel contents without building an `sdist` first.
+:::
+
+:::{versionchanged} 2.3.0
+The whl-only pure Starlark operations have been refactored into {obj}`whl_archive` and the
+previously named {obj}`whl_library` repository became renamed to `pip_archive`.
+:::
+""",
+ implementation = _pip_archive_impl,
+ environ = [
+ "RULES_PYTHON_PIP_ISOLATED",
+ REPO_DEBUG_ENV_VAR,
+ ],
+)
diff --git a/python/private/pypi/whl_archive.bzl b/python/private/pypi/whl_archive.bzl
new file mode 100644
index 0000000..75a2e6b
--- /dev/null
+++ b/python/private/pypi/whl_archive.bzl
@@ -0,0 +1,143 @@
+""
+
+load("//python/private:auth.bzl", "AUTH_ATTRS", "get_auth")
+load("//python/private:repo_utils.bzl", "REPO_DEBUG_ENV_VAR", "repo_utils")
+load(":attrs.bzl", "ATTRS")
+load(":patch_and_extract_whl.bzl", "patch_and_extract_whl")
+load(":urllib.bzl", "urllib")
+load(":whl_deps_repo.bzl", "whl_deps_attrs")
+
+def _whl_archive_impl(rctx):
+ logger = repo_utils.logger(rctx)
+
+ whl_path = None
+ if rctx.attr.whl_file:
+ rctx.watch(rctx.attr.whl_file)
+ whl_path = rctx.path(rctx.attr.whl_file)
+
+ # Simulate the behaviour where the whl is present in the current directory.
+ rctx.symlink(whl_path, whl_path.basename)
+ whl_path = rctx.path(whl_path.basename)
+ elif rctx.attr.urls and rctx.attr.filename:
+ filename = rctx.attr.filename
+ urls = rctx.attr.urls
+ urls = [
+ urllib.absolute_url(
+ rctx.attr.index_url,
+ url,
+ envsubst = rctx.attr.envsubst,
+ getenv = rctx.getenv,
+ )
+ for url in urls
+ ]
+ result = rctx.download(
+ url = urls,
+ output = filename,
+ sha256 = rctx.attr.sha256,
+ integrity = rctx.attr.integrity if not rctx.attr.sha256 else "",
+ auth = get_auth(rctx, urls),
+ )
+ if not rctx.attr.sha256 and not rctx.attr.integrity:
+ # this is only seen when there is a direct URL reference without a hash
+ logger.warn("Please update the requirement line to include the hash:\n{} \\\n --hash=sha256:{}".format(
+ rctx.attr.requirement,
+ result.sha256,
+ ))
+
+ if not result.success:
+ fail("could not download the '{}' from {}:\n{}".format(filename, urls, result))
+
+ if filename.endswith(".whl"):
+ whl_path = rctx.path(filename)
+ else:
+ fail("Only wheels are supported")
+ else:
+ fail("Either 'whl_file' or 'urls' and 'filename' needs to be specified")
+
+ return patch_and_extract_whl(rctx, whl_path = whl_path, logger = logger)
+
+whl_archive_attrs = whl_deps_attrs | {
+ "annotation": attr.label(
+ doc = (
+ "Optional json encoded file containing annotation to apply to the extracted wheel. " +
+ "See `package_annotation`"
+ ),
+ allow_files = True,
+ ),
+ "filename": attr.string(
+ doc = "Download the whl file to this filename. Only used when the `urls` is passed. If not specified, will be auto-detected from the `urls`.",
+ ),
+ "index_url": attr.string(
+ doc = "The index_url that the package will be downloaded from.",
+ ),
+ "integrity": attr.string(
+ doc = """\
+The expected checksum of the downloaded whl in Subresource Integrity format
+(e.g. `sha256-...` or `sha512-...`). Only used when `urls` is passed. If
+`sha256` is also set, it takes precedence over this attribute.
+
+:::{versionadded} 2.3.0
+:::
+""",
+ ),
+ "sha256": attr.string(
+ doc = "The sha256 of the downloaded whl. Only used when the `urls` is passed.",
+ ),
+ "urls": attr.string_list(
+ doc = """\
+The list of urls of the whl to be downloaded using bazel downloader. Using this
+attr makes `extra_pip_args` and `download_only` ignored.""",
+ ),
+ "whl_patches": attr.label_keyed_string_dict(
+ doc = """
+A label-keyed-string dict with patch files as keys and json-strings as values.
+
+The keys are labels to the patch file to apply.
+
+The values describe what to apply the patch to and how to apply it.
+It is encoded as `json.encode(struct([whls], patch_strip])`,
+where `whls` is a `list[str`] of wheel filenames, and `patch_strip`
+is a number.
+
+So it will look something like this:
+```
+"//path/to/package:my.patch": json.encode(struct(
+ whls = ["something-2.7.1-py3-none-any.whl"],
+ patch_strip = 1,
+)),
+```
+The patch is applied within the scope of the .whl file.
+I.e. you should create the patch from the same place you unziped the wheel.
+
+
+This is to maintain flexibility and correct bzlmod extension interface until we have a better
+way to define whl_library and move whl patching to a separate place. INTERNAL USE ONLY.""",
+ ),
+} | {
+ k: ATTRS[k]
+ for k in [
+ # legacy parameters that are global to the entire hub.
+ "enable_implicit_namespace_pkgs",
+ "envsubst",
+ "pip_data_exclude",
+ ]
+} | AUTH_ATTRS
+
+whl_archive = repository_rule(
+ attrs = whl_archive_attrs | {
+ # attributes only relevant to this rule and not reusable outside
+ "whl_file": attr.label(
+ doc = "The whl file that should be used instead of downloading or building the whl.",
+ ),
+ "_rule_name": attr.string(default = "whl_archive"),
+ },
+ doc = """
+Download and extracts a single wheel based into a bazel repo based on the requirement string passed in.
+
+Does not depend on any python.
+""",
+ implementation = _whl_archive_impl,
+ environ = [
+ REPO_DEBUG_ENV_VAR,
+ ],
+)
diff --git a/python/private/pypi/whl_deps_repo.bzl b/python/private/pypi/whl_deps_repo.bzl
new file mode 100644
index 0000000..20891da
--- /dev/null
+++ b/python/private/pypi/whl_deps_repo.bzl
@@ -0,0 +1,93 @@
+""
+
+load("//python/private:repo_utils.bzl", "REPO_DEBUG_ENV_VAR", "repo_utils")
+load(":generate_whl_library_build_bazel.bzl", "generate_whl_library_build_bazel")
+load(":pep508_requirement.bzl", "requirement")
+
+# Reusable common attributes for generating BUILD.bazel from Requires-Dist in the wheel METADATA.
+whl_deps_attrs = {
+ "config_load": attr.string(
+ doc = "The load location for configuration for pipstar.",
+ ),
+ "dep_template": attr.string(
+ doc = """
+The dep template to use for referencing the dependencies. It should have `{name}`
+and `{target}` tokens that will be replaced with the normalized distribution name
+and the target that we need respectively.
+
+For example if your whl depends on `numpy` and your Python package repo is named
+`pip` so that you would normally do `@pip//numpy`, then this should be: `@pip//{name}`.
+""",
+ ),
+ "group_deps": attr.string_list(
+ doc = "List of dependencies to skip in order to break the cycles within a dependency group.",
+ default = [],
+ ),
+ "group_name": attr.string(
+ doc = "Name of the group, if any.",
+ ),
+ "requirement": attr.string(
+ mandatory = True,
+ doc = "Python requirement string describing the package to make available, if 'urls' or 'whl_file' is given, then this only needs to include foo[any_extras] as a bare minimum.",
+ ),
+}
+
+def _whl_deps_repo_impl(rctx):
+ logger = repo_utils.logger(rctx)
+
+ if rctx.attr.metadata_file and rctx.attr.metadata:
+ logger.fail("Only one of 'metadata_file' and 'metadata' can be specified")
+ return
+ if not (rctx.attr.metadata_file or rctx.attr.metadata):
+ logger.fail("At least one of 'metadata_file' and 'metadata' must be specified")
+ return
+
+ if rctx.attr.metadata_file:
+ metadata_contents = rctx.read(rctx.attr.metadata_file)
+ else:
+ metadata_contents = rctx.attr.metadata
+
+ metadata = struct(**json.decode(metadata_contents))
+
+ build_file_contents = generate_whl_library_build_bazel(
+ dep_template = rctx.attr.dep_template or "@{}{{name}}//:{{target}}".format(
+ rctx.attr.repo_prefix,
+ ),
+ config_load = rctx.attr.config_load,
+ metadata_name = metadata.name,
+ metadata_version = metadata.version,
+ requires_dist = metadata.requires_dist,
+ group_deps = rctx.attr.group_deps,
+ group_name = rctx.attr.group_name,
+ repo = rctx.attr.repo or (
+ str(rctx.attr.metadata_file) if rctx.attr.metadata_file else None
+ ),
+ extras = requirement(rctx.attr.requirement).extras,
+ )
+ rctx.file("BUILD.bazel", build_file_contents)
+
+whl_deps_repo = repository_rule(
+ attrs = whl_deps_attrs | {
+ "metadata": attr.string(
+ doc = """
+The subset of the METADATA contents that is needed for generation of the dependencies.
+* name: {type}`str`
+* version: {type}`str`
+* provides_extra: {type}`list[str]`
+* requires_dist: {type}`list[str]`
+""",
+ ),
+ "metadata_file": attr.label(doc = "An alternative way to pass {attr}`metadata` but as a file."),
+ "repo": attr.label(doc = "A label at the root of the repo to get stuff from."),
+ } | {
+ "_rule_name": attr.string(default = "whl_deps_repo"),
+ },
+ doc = """
+A repo rule that reuses the sources from a different place and then creates the necessary targets
+so that this can be used in the repo.
+
+Does not depend on any python.
+""",
+ implementation = _whl_deps_repo_impl,
+ environ = [REPO_DEBUG_ENV_VAR],
+)
diff --git a/python/private/pypi/whl_library.bzl b/python/private/pypi/whl_library.bzl
index 281dd86..87ec063 100644
--- a/python/private/pypi/whl_library.bzl
+++ b/python/private/pypi/whl_library.bzl
@@ -14,782 +14,8 @@
""
-load("//python/private:auth.bzl", "AUTH_ATTRS", "get_auth")
-load("//python/private:envsubst.bzl", "envsubst")
-load("//python/private:is_standalone_interpreter.bzl", "is_standalone_interpreter")
-load("//python/private:normalize_name.bzl", "normalize_name")
-load("//python/private:repo_utils.bzl", "REPO_DEBUG_ENV_VAR", "repo_utils")
-load(":attrs.bzl", "ATTRS", "use_isolated")
-load(":deps.bzl", "all_repo_names", "record_files")
-load(":generate_whl_library_build_bazel.bzl", "generate_whl_library_build_bazel")
-load(":patch_whl.bzl", "patch_whl")
-load(":pep508_requirement.bzl", "requirement")
-load(":pypi_repo_utils.bzl", "pypi_repo_utils")
-load(":urllib.bzl", "urllib")
-load(":whl_extract.bzl", "whl_extract")
-load(":whl_metadata.bzl", "parse_entry_points", "whl_metadata")
-
-_CPPFLAGS = "CPPFLAGS"
-_COMMAND_LINE_TOOLS_PATH_SLUG = "commandlinetools"
-
-def _get_xcode_location_cflags(rctx, logger = None):
- """Query the xcode sdk location to update cflags
-
- Figure out if this interpreter target comes from rules_python, and patch the xcode sdk location if so.
- Pip won't be able to compile c extensions from sdists with the pre built python distributions from astral-sh
- otherwise. See https://github.com/astral-sh/python-build-standalone/issues/103
- """
-
- # Only run on MacOS hosts
- if not rctx.os.name.lower().startswith("mac os"):
- return []
-
- xcode_sdk_location = repo_utils.execute_unchecked(
- rctx,
- op = "GetXcodeLocation",
- arguments = [repo_utils.which_checked(rctx, "xcode-select"), "--print-path"],
- logger = logger,
- )
- if xcode_sdk_location.return_code != 0:
- return []
-
- xcode_root = xcode_sdk_location.stdout.strip()
- if _COMMAND_LINE_TOOLS_PATH_SLUG not in xcode_root.lower():
- # This is a full xcode installation somewhere like /Applications/Xcode13.0.app/Contents/Developer
- # so we need to change the path to to the macos specific tools which are in a different relative
- # path than xcode installed command line tools.
- xcode_sdks_json = repo_utils.execute_checked(
- rctx,
- op = "LocateXCodeSDKs",
- arguments = [
- repo_utils.which_checked(rctx, "xcrun"),
- "xcodebuild",
- "-showsdks",
- "-json",
- ],
- environment = {
- "DEVELOPER_DIR": xcode_root,
- },
- logger = logger,
- ).stdout
- xcode_sdks = json.decode(xcode_sdks_json)
- potential_sdks = [
- sdk
- for sdk in xcode_sdks
- if "productName" in sdk and
- sdk["productName"] == "macOS" and
- "darwinos" not in sdk["canonicalName"]
- ]
-
- # Now we'll get two entries here (one for internal and another one for public)
- # It shouldn't matter which one we pick.
- xcode_sdk_path = potential_sdks[0]["sdkPath"]
- else:
- xcode_sdk_path = "{}/SDKs/MacOSX.sdk".format(xcode_root)
-
- return [
- "-isysroot {}".format(xcode_sdk_path),
- ]
-
-def _get_toolchain_unix_cflags(rctx, python_interpreter, logger = None):
- """Gather cflags from a standalone toolchain for unix systems.
-
- Pip won't be able to compile c extensions from sdists with the pre built python distributions from astral-sh
- otherwise. See https://github.com/astral-sh/python-build-standalone/issues/103
- """
-
- # Only run on Unix systems
- if not rctx.os.name.lower().startswith(("mac os", "linux")):
- return []
-
- # Only update the location when using a standalone toolchain.
- if not is_standalone_interpreter(rctx, python_interpreter, logger = logger):
- return []
-
- stdout = pypi_repo_utils.execute_checked_stdout(
- rctx,
- op = "GetPythonVersionForUnixCflags",
- # python_interpreter by default points to a symlink, however when using bazel in vendor mode,
- # and the vendored directory moves around, the execution of python fails, as it's getting confused
- # where it's running from. More to the fact that we are executing it in isolated mode "-I", which
- # results in PYTHONHOME being ignored. The solution is to run python from it's real directory.
- python = python_interpreter.realpath,
- arguments = [
- # Run the interpreter in isolated mode, this options implies -E, -P and -s.
- # Ensures environment variables are ignored that are set in userspace, such as PYTHONPATH,
- # which may interfere with this invocation.
- "-I",
- "-c",
- "import sys; print(f'{sys.version_info[0]}.{sys.version_info[1]}', end='')",
- ],
- srcs = [],
- logger = logger,
- )
- _python_version = stdout
- include_path = "{}/include/python{}".format(
- python_interpreter.dirname,
- _python_version,
- )
-
- return ["-isystem {}".format(include_path)]
-
-def _parse_optional_attrs(rctx, args, extra_pip_args = None):
- """Helper function to parse common attributes of pip_repository and whl_library repository rules.
-
- This function also serializes the structured arguments as JSON
- so they can be passed on the command line to subprocesses.
-
- Args:
- rctx: Handle to the rule repository context.
- args: A list of parsed args for the rule.
- extra_pip_args: The pip args to pass.
- Returns: Augmented args list.
- """
-
- if use_isolated(rctx, rctx.attr):
- args.append("--isolated")
-
- # Check for None so we use empty default types from our attrs.
- # Some args want to be list, and some want to be dict.
- if extra_pip_args != None:
- args += [
- "--extra_pip_args",
- json.encode(struct(arg = [
- envsubst(pip_arg, rctx.attr.envsubst, rctx.getenv)
- for pip_arg in extra_pip_args
- ])),
- ]
-
- if rctx.attr.download_only:
- args.append("--download_only")
-
- if rctx.attr.pip_data_exclude != None:
- args += [
- "--pip_data_exclude",
- json.encode(struct(arg = rctx.attr.pip_data_exclude)),
- ]
-
- env = {}
- if rctx.attr.environment != None:
- for key, value in rctx.attr.environment.items():
- env[key] = value
-
- # This is super hacky, but working out something nice is tricky.
- # This is in particular needed for psycopg2 which attempts to link libpython.a,
- # in order to point the linker at the correct python intepreter.
- if rctx.attr.add_libdir_to_library_search_path:
- if "LDFLAGS" in env:
- fail("Can't set both environment LDFLAGS and add_libdir_to_library_search_path")
- command = [
- pypi_repo_utils.resolve_python_interpreter(rctx),
- "-c",
- "import sys ; sys.stdout.write('{}/lib'.format(sys.exec_prefix))",
- ]
- result = rctx.execute(command)
- if result.return_code != 0:
- fail("Failed to get LDFLAGS path: command: {}, exit code: {}, stdout: {}, stderr: {}".format(command, result.return_code, result.stdout, result.stderr))
- libdir = result.stdout
- env["LDFLAGS"] = "-L{}".format(libdir)
-
- args += [
- "--environment",
- json.encode(struct(arg = env)),
- ]
-
- return args
-
-def _get_python_home(rctx, python_interpreter, logger = None):
- """Get the PYTHONHOME directory from the selected python interpretter
-
- Args:
- rctx (repository_ctx): The repository context.
- python_interpreter (path): The resolved python interpreter.
- logger: Optional logger to use for operations.
- Returns:
- String of PYTHONHOME directory.
- """
-
- return pypi_repo_utils.execute_checked_stdout(
- rctx,
- op = "GetPythonHome",
- # python_interpreter by default points to a symlink, however when using bazel in vendor mode,
- # and the vendored directory moves around, the execution of python fails, as it's getting confused
- # where it's running from. More to the fact that we are executing it in isolated mode "-I", which
- # results in PYTHONHOME being ignored. The solution is to run python from it's real directory.
- python = python_interpreter.realpath,
- arguments = [
- # Run the interpreter in isolated mode, this options implies -E, -P and -s.
- # Ensures environment variables are ignored that are set in userspace, such as PYTHONPATH,
- # which may interfere with this invocation.
- "-I",
- "-c",
- "import sys; print(f'{sys.prefix}', end='')",
- ],
- srcs = [],
- logger = logger,
- )
-
-def _create_repository_execution_environment(rctx, python_interpreter, logger = None):
- """Create a environment dictionary for processes we spawn with rctx.execute.
-
- Args:
- rctx (repository_ctx): The repository context.
- python_interpreter (path): The resolved python interpreter.
- logger: Optional logger to use for operations.
- Returns:
- Dictionary of environment variable suitable to pass to rctx.execute.
- """
-
- env = {
- "PYTHONHOME": _get_python_home(rctx, python_interpreter, logger),
- "PYTHONPATH": pypi_repo_utils.construct_pythonpath(
- rctx,
- entries = rctx.attr._python_path_entries,
- ),
- }
-
- # Gather any available CPPFLAGS values
- #
- # We may want to build in an environment without a cc toolchain.
- # In those cases, we're limited to --download-only, but we should respect that here.
- is_wheel = rctx.attr.filename and rctx.attr.filename.endswith(".whl")
- if not (rctx.attr.download_only or is_wheel):
- cppflags = []
- cppflags.extend(_get_xcode_location_cflags(rctx, logger = logger))
- cppflags.extend(_get_toolchain_unix_cflags(rctx, python_interpreter, logger = logger))
- env[_CPPFLAGS] = " ".join(cppflags)
- return env
-
-def _get_entry_points(rctx, install_dir_path, metadata):
- dist_info_dir = "{}-{}.dist-info".format(
- metadata.name.replace("-", "_"),
- metadata.version.replace("-", "_"),
- )
- entry_points_txt = install_dir_path.get_child(dist_info_dir).get_child("entry_points.txt")
- if entry_points_txt.exists:
- return parse_entry_points(rctx.read(entry_points_txt))
- return {}
-
-def _move_scripts_needing_shebang_rewrite(rctx, entry_points):
- bin_dir = rctx.path("bin")
- if not bin_dir.exists:
- return
-
- ep_names = {name.lower(): True for name in entry_points}
- for script in bin_dir.readdir():
- if script.is_dir:
- continue
- if script.basename.lower() in ep_names:
- rctx.delete(script)
- continue
- if script.basename.endswith(".exe") or script.basename.endswith(".dll"):
- continue
- content = rctx.read(script)
- if content.startswith("#!python"):
- rewrite_bin_dir = rctx.path("rewrite-bin")
- repo_utils.mkdir(rctx, rewrite_bin_dir)
- repo_utils.rename(rctx, script, rctx.path("rewrite-bin/" + script.basename))
-
-def _to_purl(*, index, metadata, filename):
- """
- Produce a PyPI PURL from the metadata.
-
- https://github.com/package-url/purl-spec/blob/main/types-doc/pypi-definition.md
- """
-
- # https://github.com/package-url/purl-spec/blob/main/types-doc/pypi-definition.md#name-definition
- name = normalize_name(metadata.name).replace("_", "-")
-
- qualifiers = {}
- if index:
- qualifiers["repository_url"] = index
- if filename:
- qualifiers["file_name"] = filename
-
- return "pkg:pypi/{}@{}?{}".format(name, metadata.version, "&".join(["{}={}".format(key, val) for key, val in qualifiers.items()]))
-
-def _whl_extract(rctx, *, whl_path, logger, sdist_filename = None):
- """Extract the wheel, apply patches and generate BUILD.bazel files."""
- if rctx.attr.whl_patches:
- patches = {}
- for patch_file, json_args in rctx.attr.whl_patches.items():
- patch_dst = struct(**json.decode(json_args))
- if whl_path.basename in patch_dst.whls:
- patches[patch_file] = patch_dst.patch_strip
-
- if patches:
- whl_path = patch_whl(
- rctx,
- whl_path = whl_path,
- patches = patches,
- )
-
- whl_extract(rctx, whl_path = whl_path, logger = logger)
-
- install_dir_path = whl_path.dirname.get_child("site-packages")
- metadata = whl_metadata(
- install_dir = install_dir_path,
- read_fn = rctx.read,
- logger = logger,
- )
- rctx.file("metadata.json", json.encode_indent({
- "name": metadata.name,
- "provides_extra": metadata.provides_extra,
- "requires_dist": metadata.requires_dist,
- "version": metadata.version,
- }))
- namespace_package_files = pypi_repo_utils.find_namespace_package_files(rctx, install_dir_path)
-
- entry_points = _get_entry_points(rctx, install_dir_path, metadata)
- _move_scripts_needing_shebang_rewrite(rctx, entry_points)
-
- build_file_contents = generate_whl_library_build_bazel(
- name = whl_path.basename,
- dep_template = rctx.attr.dep_template or "@{}{{name}}//:{{target}}".format(
- rctx.attr.repo_prefix,
- ),
- sdist_filename = sdist_filename,
- config_load = rctx.attr.config_load,
- metadata_name = metadata.name,
- metadata_version = metadata.version,
- requires_dist = metadata.requires_dist,
- # TODO @aignas 2025-05-17: maybe have a build flag for this instead
- enable_implicit_namespace_pkgs = rctx.attr.enable_implicit_namespace_pkgs,
- # TODO @aignas 2025-04-14: load through the hub:
- annotation = None if not rctx.attr.annotation else struct(**json.decode(rctx.read(rctx.attr.annotation))),
- data_exclude = rctx.attr.pip_data_exclude,
- group_deps = rctx.attr.group_deps,
- group_name = rctx.attr.group_name,
- namespace_package_files = namespace_package_files,
- extras = requirement(rctx.attr.requirement).extras,
- entry_points = entry_points,
- purl = _to_purl(
- index = rctx.attr.index_url,
- metadata = metadata,
- filename = sdist_filename or whl_path.basename,
- ),
- )
-
- # Delete these in case the wheel had them. They generally don't cause
- # a problem, but let's avoid the chance of that happening.
- rctx.file("WORKSPACE")
- rctx.file("WORKSPACE.bazel")
- rctx.file("MODULE.bazel")
- rctx.file("REPO.bazel", """\
-repo(
- default_package_metadata = [
- "//:package_metadata",
- ],
-)
-""")
-
- # BUILD files interfere with globbing and Bazel package boundaries.
- _remove_files(rctx, "BUILD", "BUILD.bazel")
- rctx.file("BUILD.bazel", build_file_contents)
-
- if hasattr(rctx, "repo_metadata"):
- return rctx.repo_metadata(reproducible = True)
-
- return None
-
-def _whl_archive_impl(rctx):
- logger = repo_utils.logger(rctx)
-
- whl_path = None
- if rctx.attr.whl_file:
- rctx.watch(rctx.attr.whl_file)
- whl_path = rctx.path(rctx.attr.whl_file)
-
- # Simulate the behaviour where the whl is present in the current directory.
- rctx.symlink(whl_path, whl_path.basename)
- whl_path = rctx.path(whl_path.basename)
- elif rctx.attr.urls and rctx.attr.filename:
- filename = rctx.attr.filename
- urls = rctx.attr.urls
- urls = [
- urllib.absolute_url(
- rctx.attr.index_url,
- url,
- envsubst = rctx.attr.envsubst,
- getenv = rctx.getenv,
- )
- for url in urls
- ]
- result = rctx.download(
- url = urls,
- output = filename,
- sha256 = rctx.attr.sha256,
- integrity = rctx.attr.integrity if not rctx.attr.sha256 else "",
- auth = get_auth(rctx, urls),
- )
- if not rctx.attr.sha256 and not rctx.attr.integrity:
- # this is only seen when there is a direct URL reference without a hash
- logger.warn("Please update the requirement line to include the hash:\n{} \\\n --hash=sha256:{}".format(
- rctx.attr.requirement,
- result.sha256,
- ))
-
- if not result.success:
- fail("could not download the '{}' from {}:\n{}".format(filename, urls, result))
-
- if filename.endswith(".whl"):
- whl_path = rctx.path(filename)
- else:
- fail("Only wheels are supported")
- else:
- fail("Either 'whl_file' or 'urls' and 'filename' needs to be specified")
-
- return _whl_extract(rctx, whl_path = whl_path, logger = logger)
-
-def _pip_archive_impl(rctx):
- logger = repo_utils.logger(rctx)
-
- sdist_filename = None
- extra_pip_args = []
- extra_pip_args.extend(rctx.attr.extra_pip_args)
- if rctx.attr.urls and rctx.attr.filename:
- filename = rctx.attr.filename
- urls = rctx.attr.urls
- urls = [
- urllib.absolute_url(
- rctx.attr.index_url,
- url,
- envsubst = rctx.attr.envsubst,
- getenv = rctx.getenv,
- )
- for url in urls
- ]
- result = rctx.download(
- url = urls,
- output = filename,
- sha256 = rctx.attr.sha256,
- integrity = rctx.attr.integrity if not rctx.attr.sha256 else "",
- auth = get_auth(rctx, urls),
- )
- if not rctx.attr.sha256 and not rctx.attr.integrity:
- # this is only seen when there is a direct URL reference without a hash
- logger.warn("Please update the requirement line to include the hash:\n{} \\\n --hash=sha256:{}".format(
- rctx.attr.requirement,
- result.sha256,
- ))
-
- if not result.success:
- fail("could not download the '{}' from {}:\n{}".format(filename, urls, result))
-
- if filename.endswith(".whl"):
- fail("Only sdists are supported")
- else:
- sdist_filename = filename
-
- # It is an sdist and we need to tell PyPI to use a file in this directory
- # and, allow getting build dependencies from PYTHONPATH, which we
- # setup in this repository rule, but still download any necessary
- # build deps from PyPI (e.g. `flit_core`) if they are missing.
- extra_pip_args.extend(["--find-links", "."])
-
- # When we already have a wheel, Python isn't used,
- # so there's no need to setup env vars to run Python, unless we need to
- # build an sdist or resolve a requirement.
- python_interpreter = pypi_repo_utils.resolve_python_interpreter(
- rctx,
- python_interpreter = rctx.attr.python_interpreter,
- python_interpreter_target = rctx.attr.python_interpreter_target,
- )
- args = [
- "-m",
- "python.private.pypi.whl_installer.wheel_installer",
- "--requirement",
- rctx.attr.requirement,
- ]
- args = _parse_optional_attrs(rctx, args, extra_pip_args)
-
- # Manually construct the PYTHONPATH since we cannot use the toolchain here
- environment = _create_repository_execution_environment(rctx, python_interpreter, logger = logger)
-
- if rctx.attr.urls:
- op_tmpl = "whl_library.BuildWheelFromSource({name}, {requirement})"
- elif rctx.attr.download_only:
- op_tmpl = "whl_library.DownloadWheel({name}, {requirement})"
- else:
- op_tmpl = "whl_library.ResolveRequirement({name}, {requirement})"
-
- pypi_repo_utils.execute_checked(
- rctx,
- # truncate the requirement value when logging it / reporting
- # progress since it may contain several ' --hash=sha256:...
- # --hash=sha256:...' substrings that fill up the console
- python = python_interpreter,
- op = op_tmpl.format(name = rctx.attr.name, requirement = rctx.attr.requirement.split(" ", 1)[0]),
- arguments = args,
- environment = environment,
- srcs = rctx.attr._python_srcs,
- quiet = rctx.attr.quiet,
- timeout = rctx.attr.timeout,
- logger = logger,
- )
-
- whl_path = rctx.path(json.decode(rctx.read("whl_file.json"))["whl_file"])
- if not rctx.delete("whl_file.json"):
- fail("failed to delete the whl_file.json file")
-
- # NOTE @aignas 2026-08-14: We never return rctx.metadata for pip archives because the result may
- # not be reproducible across all machines given the input args to the repository rule.
- _whl_extract(rctx, whl_path = whl_path, logger = logger, sdist_filename = sdist_filename)
-
-def _remove_files(rctx, *basenames):
- paths = list(rctx.path(".").readdir())
- for _ in range(10000000):
- if not paths:
- break
- path = paths.pop()
-
- if path.basename in basenames:
- rctx.delete(path)
- elif path.is_dir:
- paths.extend(path.readdir())
-
-# NOTE @aignas 2024-03-21: The usage of dict({}, **common) ensures that all args to `dict` are unique
-_pip_archive_attrs = dict({
- "annotation": attr.label(
- doc = (
- "Optional json encoded file containing annotation to apply to the extracted wheel. " +
- "See `package_annotation`"
- ),
- allow_files = True,
- ),
- "config_load": attr.string(
- doc = "The load location for configuration for pipstar.",
- ),
- "dep_template": attr.string(
- doc = """
-The dep template to use for referencing the dependencies. It should have `{name}`
-and `{target}` tokens that will be replaced with the normalized distribution name
-and the target that we need respectively.
-
-For example if your whl depends on `numpy` and your Python package repo is named
-`pip` so that you would normally do `@pip//numpy`, then this should be: `@pip//{name}`.
-""",
- ),
- "filename": attr.string(
- doc = "Download the whl file to this filename. Only used when the `urls` is passed. If not specified, will be auto-detected from the `urls`.",
- ),
- "group_deps": attr.string_list(
- doc = "List of dependencies to skip in order to break the cycles within a dependency group.",
- default = [],
- ),
- "group_name": attr.string(
- doc = "Name of the group, if any.",
- ),
- "index_url": attr.string(
- doc = "The index_url that the package will be downloaded from.",
- ),
- "integrity": attr.string(
- doc = """\
-The expected checksum of the downloaded whl in Subresource Integrity format
-(e.g. `sha256-...` or `sha512-...`). Only used when `urls` is passed. If
-`sha256` is also set, it takes precedence over this attribute.
-
-:::{versionadded} 2.3.0
-:::
-""",
- ),
- "repo": attr.string(
- doc = "Pointer to parent repo name. Used to make these rules rerun if the parent repo changes.",
- ),
- "repo_prefix": attr.string(
- doc = """
-Prefix for the generated packages will be of the form `@<prefix><sanitized-package-name>//...`
-
-DEPRECATED. Only left for people who vendor requirements.bzl.
-""",
- ),
- "requirement": attr.string(
- mandatory = True,
- doc = "Python requirement string describing the package to make available, if 'urls' or 'whl_file' is given, then this only needs to include foo[any_extras] as a bare minimum.",
- ),
- "sha256": attr.string(
- doc = "The sha256 of the downloaded whl. Only used when the `urls` is passed.",
- ),
- "urls": attr.string_list(
- doc = """\
-The list of urls of the whl to be downloaded using bazel downloader. Using this
-attr makes `extra_pip_args` and `download_only` ignored.""",
- ),
- "whl_patches": attr.label_keyed_string_dict(
- doc = """
-A label-keyed-string dict with patch files as keys and json-strings as values.
-
-The keys are labels to the patch file to apply.
-
-The values describe what to apply the patch to and how to apply it.
-It is encoded as `json.encode(struct([whls], patch_strip])`,
-where `whls` is a `list[str`] of wheel filenames, and `patch_strip`
-is a number.
-
-So it will look something like this:
-```
-"//path/to/package:my.patch": json.encode(struct(
- whls = ["something-2.7.1-py3-none-any.whl"],
- patch_strip = 1,
-)),
-```
-The patch is applied within the scope of the .whl file.
-I.e. you should create the patch from the same place you unziped the wheel.
-
-
-This is to maintain flexibility and correct bzlmod extension interface until we have a better
-way to define whl_library and move whl patching to a separate place. INTERNAL USE ONLY.""",
- ),
- "_python_path_entries": attr.label_list(
- # Get the root directory of these rules and keep them as a default attribute
- # in order to avoid unnecessary repository fetching restarts.
- #
- # This is very similar to what was done in https://github.com/bazelbuild/rules_go/pull/3478
- default = [
- Label("//:BUILD.bazel"),
- ] + [
- # Includes all the external dependencies from repositories.bzl
- Label("@" + repo + "//:BUILD.bazel")
- for repo in all_repo_names
- ],
- ),
- "_python_srcs": attr.label_list(
- # Used as a default value in a rule to ensure we fetch the dependencies.
- default = [
- Label("//python/private/pypi/whl_installer:wheel_installer.py"),
- Label("//python/private/pypi/whl_installer:arguments.py"),
- ] + record_files.values(),
- ),
- "_rule_name": attr.string(default = "whl_library"),
-}, **ATTRS)
-_pip_archive_attrs.update(AUTH_ATTRS)
-
-pip_archive = repository_rule(
- attrs = _pip_archive_attrs,
- doc = """
-Download and extracts a single wheel based into a bazel repo based on the requirement string passed in.
-Instantiated from pip_repository and inherits config options from there.
-
-:::{versionchanged} 1.9.0
-The `whl_library` is marked as reproducible if using starlark to extract and parse the
-wheel contents without building an `sdist` first.
-:::
-
-:::{versionchanged} 2.3.0
-The whl-only pure Starlark operations have been refactored into {obj}`whl_archive` and the
-previously named {obj}`whl_library` repository became renamed to `pip_archive`.
-:::
-""",
- implementation = _pip_archive_impl,
- environ = [
- "RULES_PYTHON_PIP_ISOLATED",
- REPO_DEBUG_ENV_VAR,
- ],
-)
-
-whl_archive = repository_rule(
- attrs = {
- k: _pip_archive_attrs[k]
- for k in [
- "annotation",
- "config_load",
- "dep_template",
- "filename",
- "group_deps",
- "group_name",
- "index_url",
- "integrity",
- "repo_prefix",
- "requirement",
- "sha256",
- "urls",
- "whl_patches",
- "enable_implicit_namespace_pkgs",
- "envsubst",
- "pip_data_exclude",
- ]
- } | {
- "whl_file": attr.label(
- doc = "The whl file that should be used instead of downloading or building the whl.",
- ),
- } | AUTH_ATTRS,
- doc = """
-Download and extracts a single wheel based into a bazel repo based on the requirement string passed in.
-
-Does not depend on any python.
-""",
- implementation = _whl_archive_impl,
- environ = [
- REPO_DEBUG_ENV_VAR,
- ],
-)
-
-def _whl_deps_library_impl(rctx):
- logger = repo_utils.logger(rctx)
-
- if rctx.attr.metadata_file and rctx.attr.metadata:
- logger.fail("Only one of 'metadata_file' and 'metadata' can be specified")
- return
- if not (rctx.attr.metadata_file or rctx.attr.metadata):
- logger.fail("At least one of 'metadata_file' and 'metadata' must be specified")
- return
-
- if rctx.attr.metadata_file:
- metadata_contents = rctx.read(rctx.attr.metadata_file)
- else:
- metadata_contents = rctx.attr.metadata
-
- metadata = struct(**json.decode(metadata_contents))
-
- build_file_contents = generate_whl_library_build_bazel(
- dep_template = rctx.attr.dep_template or "@{}{{name}}//:{{target}}".format(
- rctx.attr.repo_prefix,
- ),
- config_load = rctx.attr.config_load,
- metadata_name = metadata.name,
- metadata_version = metadata.version,
- requires_dist = metadata.requires_dist,
- group_deps = rctx.attr.group_deps,
- group_name = rctx.attr.group_name,
- repo = rctx.attr.repo or (
- str(rctx.attr.metadata_file) if rctx.attr.metadata_file else None
- ),
- extras = requirement(rctx.attr.requirement).extras,
- )
- rctx.file("BUILD.bazel", build_file_contents)
-
-whl_deps_library = repository_rule(
- attrs = {
- k: _pip_archive_attrs[k]
- for k in [
- "config_load",
- "dep_template",
- "group_deps",
- "group_name",
- "requirement",
- ]
- } | {
- "metadata": attr.string(
- doc = """
-The subset of the METADATA contents that is needed for generation of the dependencies.
-* name: {type}`str`
-* version: {type}`str`
-* provides_extra: {type}`list[str]`
-* requires_dist: {type}`list[str]`
-""",
- ),
- "metadata_file": attr.label(doc = "An alternative way to pass {attr}`metadata` but as a file."),
- "repo": attr.label(doc = "A label at the root of the repo to get stuff from."),
- },
- doc = """
-A repo rule that reuses the sources from a different place and then creates the necessary targets
-so that this can be used in the repo.
-
-Does not depend on any python.
-""",
- implementation = _whl_deps_library_impl,
- environ = [REPO_DEBUG_ENV_VAR],
-)
+load(":pip_archive.bzl", "pip_archive")
+load(":whl_archive.bzl", "whl_archive")
def whl_library(name, repo = None, **kwargs):
"""Create a whl_library.
@@ -811,6 +37,10 @@
whl_file = kwargs.get("whl_file")
urls = kwargs.get("urls", [])
filename = kwargs.get("filename")
+
+ # compatibility shim for cases for repo_prefix is still used by the called
+ kwargs.setdefault("dep_template", "@{}{{name}}//:{{target}}".format(kwargs.pop("repo_prefix", "")))
+
if whl_file or (urls and filename and filename.endswith(".whl")):
whl_archive(name = name, **kwargs)
else:
diff --git a/tests/integration/bzlmod_lockfile/MODULE.bazel.lock b/tests/integration/bzlmod_lockfile/MODULE.bazel.lock
index 6b29328..ed252bb 100644
--- a/tests/integration/bzlmod_lockfile/MODULE.bazel.lock
+++ b/tests/integration/bzlmod_lockfile/MODULE.bazel.lock
@@ -294,106 +294,106 @@
"https://files.pythonhosted.org/packages/a3/c2/24167ea9858356b47a87a50d39908bfdb72ceeefe0041586e704e5376b3a/certifi-2026.7.22.tar.gz": "sha256:741e2c3b351ddf169a738da9f2c048608ff7f2c5cc02f1ebc6b118bb090d5d55"
},
"cffi": {
- "https://files.pythonhosted.org/packages/04/8c/b925975448cf20634a9fbd5efceb807219db452653648d2897c0989cab2d/cffi-2.1.0-cp311-cp311-musllinux_1_2_x86_64.whl": "sha256:89095c1968b4ba8285840e131bf2891b09ae137fe2146905acae0354fbce1b5e",
- "https://files.pythonhosted.org/packages/05/ef/6cd4f8c671517162379dc79cfae5aea9106bc38abb89628d5c16adf6a838/cffi-2.1.0-cp315-cp315-win_arm64.whl": "sha256:8d35c139744adb3e727cd51b1a18324bbe44b8bd41bf8322bca4d41289f48eda",
- "https://files.pythonhosted.org/packages/0f/6f/ade5ce9863a57992a6ea3d0d10d7e29b8749fc127204b3d493d667b2815f/cffi-2.1.0-cp314-cp314-win32.whl": "sha256:1854b724d00f6654c742097d5387569021be12d3a0f770eae1df8f8acfcc6acd",
- "https://files.pythonhosted.org/packages/11/b6/12fc55092817a5faa26fb8c40c7f9d662e11a46ee248c137aafc42517d92/cffi-2.1.0-cp315-cp315t-macosx_10_15_x86_64.whl": "sha256:f9912624a0c0b834b7520d7769b3644453aabc0a7e1c839da7359f050750e9bc",
- "https://files.pythonhosted.org/packages/14/d0/117dcd9209255ad8571fbc8c92ef32593a1d294dcec91ddc4e4db50606f2/cffi-2.1.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl": "sha256:eb4e8997a49aa2c08a3e43c9045d224448b8941d88e7ac163c7d383e560cbf98",
- "https://files.pythonhosted.org/packages/14/f0/134c00ce0779ec86dea2aa1aac69339c2741a8045072676763512363a2ea/cffi-2.1.0-cp314-cp314t-macosx_10_15_x86_64.whl": "sha256:7ea6b3e2c4250ff1de21c630fe72d0f63eb95c2c32ffbf64a358cf4a8836d714",
- "https://files.pythonhosted.org/packages/19/e5/d3cc82a4a0be7902af279c04181ad038449c096734464a5ae1de3e1401bd/cffi-2.1.0-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "sha256:0611e7ebf90573a535ebdc33ae9da222d037853983e13359f580fab781ca017f",
- "https://files.pythonhosted.org/packages/1b/dc/5620cf930688be01f2d673804291de757a934c90b946dbdc3d84130c2ea4/cffi-2.1.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl": "sha256:b6422532152adf4e59b110cb2808cee7a033800952f5c036b4af047ee43199e7",
- "https://files.pythonhosted.org/packages/1e/85/990925db5df586ec90beb97529c853497e7f85ba0234830447faf41c3057/cffi-2.1.0-cp312-cp312-macosx_10_15_x86_64.whl": "sha256:df2b82571a1b30f58a87bf4e5a9e78d2b1eff6c6ce8fd3aa3757221f93f0863f",
- "https://files.pythonhosted.org/packages/20/71/7c8372d30e42415602ed9f268f7cfd66f1b855fed881ecd168bcb45dbc0b/cffi-2.1.0-cp314-cp314-macosx_10_15_x86_64.whl": "sha256:1ff3456eab0d889592d1936d6125bbfbc7ae4d3354a700f8bd80450a66445d4d",
- "https://files.pythonhosted.org/packages/22/d7/1a74539db16d8bfd839ff1515948948efbb162e574650fd3d846896eea95/cffi-2.1.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl": "sha256:88023dfe18799507b73f1dbb0d14326a17465de1bc9c9c7655c22845e9ddc3a2",
- "https://files.pythonhosted.org/packages/22/f0/a2fc43084c0433caf7f461bccc013e28f848d04ee1c5ed7fce71423cf4d9/cffi-2.1.0-cp311-cp311-musllinux_1_2_i686.whl": "sha256:7762faa47e8ff7eb80bd261d9a7d8eea2d8baa69de5e95b70c1f338bbe712f02",
- "https://files.pythonhosted.org/packages/28/3b/fad54de07260b93ddeef4b96d0131d57ea900675df1d410ae1deee52d7a6/cffi-2.1.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl": "sha256:33eb1ad83ebe8f313e0df035c406227d55a79456704a863fad9842136af5ad7d",
- "https://files.pythonhosted.org/packages/28/ed/c127d3ac36e899c965e3361357c3befacd6578c03f40125183e41c3b219e/cffi-2.1.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl": "sha256:6ca4919c6e4f89aa99c42510b42cf54596892c00b3f9077f6bdd1505e24b9c8d",
- "https://files.pythonhosted.org/packages/2c/0e/fac738d73728c6cea2a88a2883dca54892496cbba88a1dc1f2909cb8a6f5/cffi-2.1.0-cp315-cp315-manylinux2014_s390x.manylinux_2_17_s390x.whl": "sha256:2b71d409cccee78310ab5dec549aed052aaea483346e282c7b02362596e01bb0",
- "https://files.pythonhosted.org/packages/2c/d8/772b8259bf75749adffb1c546828978381fb516f60cf701f6c83daf60c85/cffi-2.1.0-cp315-cp315-win32.whl": "sha256:0a42c688d19fca6e095a53c6a6e2295a5b050a8b289f109adab02a9e61a25de6",
- "https://files.pythonhosted.org/packages/2e/1a/cc6ae6c2913a03aab8898eee57963cf1035b8df5872ed8b9115fcc7e2be8/cffi-2.1.0-cp314-cp314-win_arm64.whl": "sha256:7d28dff1db6764108bc30788d85d61c876beff416d9a49cb9dd7c5a9f34f5804",
- "https://files.pythonhosted.org/packages/2e/d2/065fcae1c73979fac8e054462478d0ff8a29c40cdc2ed7ea5676a061df53/cffi-2.1.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl": "sha256:276f20fffd7b396e12516ba8edf9509210ac248cbbc5acbc39cd512f9f59ebe6",
- "https://files.pythonhosted.org/packages/2f/dd/afa2191fc6d57fedd26e5844a2fe2fcc0bbfa00961bbaa5a41e4921e7cca/cffi-2.1.0-cp315-cp315-win_amd64.whl": "sha256:bccbbb5ee76a61f9d99b5bf3846a51d7fca4b6a732fe46f89295610edaf41853",
- "https://files.pythonhosted.org/packages/38/37/04f54b8e63a02f3d908332c9effbf8c366167c6f733ed8a3d4f79b7e2a1e/cffi-2.1.0-cp313-cp313-musllinux_1_2_aarch64.whl": "sha256:961be50688f7fba2fa65f63712d3b9b341a22311f5253460ce933f52f0de1c8c",
- "https://files.pythonhosted.org/packages/38/66/04781a77b411f0bb5b234d62c1814754ab75ebe455ccff1b08e8d7aae98f/cffi-2.1.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "sha256:4d433a51f1870e43a13b6732f92aaf540ff77c2015097c78556f75a2d6c030e0",
- "https://files.pythonhosted.org/packages/3a/a6/e879bb68cc23a2bc9ba8f4b7d8019f0c2694bad2ab6c4a3701d429439f58/cffi-2.1.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl": "sha256:ff067a8d8d880e7809e4ac88eb009bb848870115317b306666502ccad30b147f",
- "https://files.pythonhosted.org/packages/3b/30/c806937ed5e4c2c7ac30d9d6b76b5dc57ff8b75d83800d9bb11a8253cf2a/cffi-2.1.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl": "sha256:a016194dbe13d14ee9556e734b772d8d67b947092b268d757fd4290e3ba2dfc2",
- "https://files.pythonhosted.org/packages/41/aa/3c1409cdd26094efacd1c36c66e0a6eb9d4296e4fd4f9901b8b2042f4323/cffi-2.1.0-cp310-cp310-musllinux_1_2_i686.whl": "sha256:c5f5df567f6eb216de69be06ce55c8b714090fae02b18a3b40da8163b8c5fa9c",
- "https://files.pythonhosted.org/packages/41/de/92b9eeed4ae4a21d6fd9b2a2c8505cbed573299902ea73981cc13f7ff62c/cffi-2.1.0-cp314-cp314-win_amd64.whl": "sha256:1b96bfe2c4bd825681b7d311ad6d9b7280a091f43e8f63da5729638083cd3bfb",
- "https://files.pythonhosted.org/packages/45/ca/f91641185cdd90c36d317a9dc7f85e88ef8682d8b300977baff5e23c35d8/cffi-2.1.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl": "sha256:19c54ac121cad98450b4896fa9a43ee0180d57bc4bc911a33db6cab1efab6cd3",
- "https://files.pythonhosted.org/packages/4b/92/e7bb136ad6b5352603732cf907ef862ca103f20f2031c1735a46300c20c9/cffi-2.1.0-cp312-cp312-macosx_11_0_arm64.whl": "sha256:78474632761faa0fb96f30b1c928c84ebcf68713cbb80d15bab09dfe61640fde",
- "https://files.pythonhosted.org/packages/4b/a4/77b53abbf7a1e0beb9637edbef2a94d15f9c822f591e85d439ffd91519a6/cffi-2.1.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl": "sha256:46b1c8db8f6122420f32d02fffb924c2fe9bc772d228c7c711748fff56aabb2b",
- "https://files.pythonhosted.org/packages/50/d8/3b86aba791cb610d24e8a3e1b2cd529e71fa15096b04e4d4e360049d4a4c/cffi-2.1.0-cp314-cp314t-macosx_11_0_arm64.whl": "sha256:6af371f3767faeffc6ac1ef57cdfd25844403e9d3f476c5537caee499de96376",
- "https://files.pythonhosted.org/packages/55/c7/8c8c50cb11c6750051daf12164098a9a6f027ac4356967fd4d800a07f242/cffi-2.1.0-cp315-cp315-ios_13_0_arm64_iphoneos.whl": "sha256:2e9dabb9abcb7ad15938c7196ad5c1718a4e6d33cc79b4c0209bdb64c4a54a5c",
- "https://files.pythonhosted.org/packages/57/5f/ff100cae70ebe9d8df1c01a00e510e45d9adb5c1fdda84791b199141de97/cffi-2.1.0.tar.gz": "sha256:efc1cdd798b1aaf39b4610bba7aad28c9bea9b910f25c784ccf9ec1fa719d1f9",
- "https://files.pythonhosted.org/packages/58/0c/f528df19cc94b675087324d4760d9e6d5bfae97d6217aa4fac43de4f5fcc/cffi-2.1.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl": "sha256:d9fafc5aa2e2a39aaf7f8cc0c1f044a9b07fca12e558dca53a3cc5c654ad67a7",
- "https://files.pythonhosted.org/packages/58/85/7ae00d5c8dd6266f4e944c3db630f3c5c9a98b61d469c714d848b1d8138a/cffi-2.1.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl": "sha256:a95b05f9baf29b91171b3a8bd2020b028835243e7b0ff6bb23e2a3c228518b1b",
- "https://files.pythonhosted.org/packages/5a/02/d5e6c43ea85c41bda2a184a3418f195fe7cf602967a8d2b94e085b83deef/cffi-2.1.0-cp315-cp315-musllinux_1_2_x86_64.whl": "sha256:af5e2915d41fe6c961694d7bfdc8562942638200f3ce2765dfb8b745cf997629",
- "https://files.pythonhosted.org/packages/5a/47/59eb7975cb0e4ef0afa764ea945b29a5bb4537a9f771cb7d6c8a5dd74c95/cffi-2.1.0-cp313-cp313-win_amd64.whl": "sha256:8e74a6135550c4748af665b1b1118b6aab33b1fc6a16f9aff630af107c3b4512",
- "https://files.pythonhosted.org/packages/5a/67/9e6e09409336d9e515c58367e7cfcf4f89df06ad25252675595a58eb59d5/cffi-2.1.0-cp315-cp315t-manylinux2014_s390x.manylinux_2_17_s390x.whl": "sha256:762f99479dcb369f60ab9017ad4ab97a36a1dd7c1ee5a3b15db0f4b8659120cd",
- "https://files.pythonhosted.org/packages/5a/af/34fee85c48f8d94efc8597bc09470c9dd274c145f1c12e0fbc6ab6d38d74/cffi-2.1.0-cp313-cp313-win_arm64.whl": "sha256:2282cd5e38aa8accd03e99d1256af8411c84cdbee6a89d841b563fdbd1f3e50f",
- "https://files.pythonhosted.org/packages/5d/7c/b7379a5704c79eda57ce075869ba70a0368d1c850f803b3c0d078d39dcaf/cffi-2.1.0-cp315-cp315-musllinux_1_2_aarch64.whl": "sha256:8f9ec95b8a043d3dfbc74d9abc6f7baf524dd27a8dc160b0a32ff9cdab650c28",
- "https://files.pythonhosted.org/packages/62/f2/c9522a81c32132799a1972c39f5c5f8b4c8b9f00488a23feaa6c06f07741/cffi-2.1.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "sha256:1e9f50d192a3e525b15a75ab5114e442d83d657b7ec29182a991bc9a88fd3a66",
- "https://files.pythonhosted.org/packages/65/68/9f3ef890cf3c6ab97bd531c5677f67613d302165d16f8142b2811782a614/cffi-2.1.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl": "sha256:30b65779d598c370374fefabf138d456fd6f3216bfa7bedfab1ba82025b0cd93",
- "https://files.pythonhosted.org/packages/68/5a/e536c528bc8057496c360c0978559a2dc45653f89dd6151078aa7d8fca1a/cffi-2.1.0-cp315-cp315t-win32.whl": "sha256:cb96698e3c7413d906ce83f8ffd245ec1bd94707541f299d0ce4d6b0193e982b",
- "https://files.pythonhosted.org/packages/69/aa/24580a278de21fd7322635556334d9b535f1cbc00b0a3919447cdf464c65/cffi-2.1.0-cp310-cp310-macosx_11_0_arm64.whl": "sha256:164bff1657b2a74f0b6d54e11c9b375bc97b931f2ca9c43fcf875838da1570dd",
- "https://files.pythonhosted.org/packages/6c/d0/47e338384ab6b1004241002fa616301020cea4fc95f283506565d252f276/cffi-2.1.0-cp314-cp314t-musllinux_1_2_aarch64.whl": "sha256:c16914df9fb7f500e440e6875fa23ff5e0b31db01fa9c06af98d59a91f0dc2e4",
- "https://files.pythonhosted.org/packages/6e/28/bd53988b9833e8f8ad539d26f4c07a6b3f6bcb1e9e02e7ca038250b3428d/cffi-2.1.0-cp312-cp312-musllinux_1_2_aarch64.whl": "sha256:98fff996e983a36d3aa2eca83af40c5821202e7e6f32d13ae94e3d2286f10cfe",
- "https://files.pythonhosted.org/packages/6f/08/f2e7d62c460faae0926f2d6e423694aa409ced3bc1fe2927a0a6e5f05416/cffi-2.1.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "sha256:799416bae98336e400981ff6e532d67d5c709cfb30afb79865a1315f94b0e224",
- "https://files.pythonhosted.org/packages/70/25/65bd5b58ea4bfdfc15cde02cb5365f89ef8ab8b2adfb8fe5c4bd4233382f/cffi-2.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl": "sha256:5ecbd0499275d57506d397eebe1981cee87b47fcd9ef5c22cab7ed7644a39a94",
- "https://files.pythonhosted.org/packages/70/b6/9003c33a3e7d2c1306f5962e646457dcfe5a8cd8fce6bbe02d7af25db783/cffi-2.1.0-cp310-cp310-win32.whl": "sha256:9d72af0cf10a76a600a9690078fe31c63b9588c8e86bf9fd353f713c84b5db0f",
- "https://files.pythonhosted.org/packages/79/99/0d0fd37f055224085f42bbb2c022d002e17dde4a97972822327b07d84101/cffi-2.1.0-cp312-cp312-musllinux_1_2_x86_64.whl": "sha256:379de10ce1ba048b1448599d1b37b24caee16309d1ac98d3982fc997f768700b",
- "https://files.pythonhosted.org/packages/7d/95/8de304305cd9204974b0ca051b86d307cafca13aa575a0ef1b44d92c0d8c/cffi-2.1.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl": "sha256:702c436735fbe99d59ada02a1f65cfc0d31c0ee8b7290912f8fbc5cd1e4b16c3",
- "https://files.pythonhosted.org/packages/84/4c/82f132cb4418ee6d953d982b19191e87e2a6372c8a4ce36e50b69d6ade4a/cffi-2.1.0-cp313-cp313-macosx_11_0_arm64.whl": "sha256:716ff8ec22f20b4d988b12884086bcef0fc99737043e503f7a3935a6be99b1ea",
- "https://files.pythonhosted.org/packages/88/a9/02cae418ec4beb282ace11958d9d4737793439d561fadc7e6d56f2e2b354/cffi-2.1.0-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl": "sha256:c941bb58d5a6e1c3892d86e42927ed6c180302f07e6d395d08c416e594b98b46",
- "https://files.pythonhosted.org/packages/88/f6/01890cfd63c08f8eb96a8319b0443690197d240a8bd6346048cf7bde9190/cffi-2.1.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl": "sha256:3b926723c13eba9f81d2ef3820d63aeceec3b2d4639906047bf675cb8a7a500d",
- "https://files.pythonhosted.org/packages/8a/26/710688310447531c7a22f857c7f79d9855ec18b03e04494ced723fb37e2f/cffi-2.1.0-cp310-cp310-win_amd64.whl": "sha256:fb62edb5bb52cca65fab91a63afa7561607120d26090a7e8fda6fb9f064726da",
- "https://files.pythonhosted.org/packages/8b/31/e115c985105dd7ffb32444505f18ceb874bb42d992af05d5dced7ecf1980/cffi-2.1.0-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl": "sha256:3681e031db29958a7502f5c0c9d6bbc4c36cb20f7b104086fa642d1799631ff8",
- "https://files.pythonhosted.org/packages/8c/e9/45c3a76ad8d43ad9261f4c95436da61128d3ca545d72b9612c0ab5be0b1c/cffi-2.1.0-cp313-cp313-macosx_10_15_x86_64.whl": "sha256:15faec4adfff450819f3aee0e2e02c812de6edb88203aa58807955db2003472a",
- "https://files.pythonhosted.org/packages/8d/2e/cdac88979f295fde5daa69622c7d2111e56e7ceb94f211357fbe452339e4/cffi-2.1.0-cp315-cp315t-macosx_11_0_arm64.whl": "sha256:df92f2aba50eb4d96718b68ef76f2e57a57b54f2fa62333496d16c6d585a85ca",
- "https://files.pythonhosted.org/packages/96/88/a996879e2eeccb815f6e3a5967b12a308257412acec882039d386bd2aa7b/cffi-2.1.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl": "sha256:10537b1df4967ca26d21e5072d7d54188354483b91dc75058968d3f0cf13fbda",
- "https://files.pythonhosted.org/packages/99/e2/67680bf19a6b60d2bb7ff83baefa2a4c3d2d7dc0f3277034b802e1fc504c/cffi-2.1.0-cp315-cp315-ios_13_0_arm64_iphonesimulator.whl": "sha256:37f525a7e7e50c017fdebe58b787be310ad59357ae43a053943a6e1a6c526001",
- "https://files.pythonhosted.org/packages/9e/4e/e8d7cb5783f1841a3c8fb3a7735838d7484d08ec08c9f984b14cac1ac0e9/cffi-2.1.0-cp311-cp311-win_arm64.whl": "sha256:35aaea0c7ee0e58a5cd8c2fd1a48fdf7ece0d2699b7ecdda08194e9ce5dd9b3d",
- "https://files.pythonhosted.org/packages/a0/17/1073b53b68c9b5ca6914adf5f8bf55aacc2d3be102418c90700160ea8605/cffi-2.1.0-cp315-cp315t-win_arm64.whl": "sha256:cbb7640ce37159548d2147b5b8c241f962143d4c71231431820783f4dc78f210",
- "https://files.pythonhosted.org/packages/a0/1c/4ed5a0e5bdca6cbc275556de3328dd1b76fd0c11cc13c88fe66d1d8715f2/cffi-2.1.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl": "sha256:63960549e4f8dc41e31accb97b975abaecfc44c03e396c093a6436763c2ea7db",
- "https://files.pythonhosted.org/packages/a2/a5/d4fe77b589e5e82d43ebc809bf2e6474afe8e48e32ea050b9357645b6471/cffi-2.1.0-cp311-cp311-musllinux_1_2_aarch64.whl": "sha256:9d8272c0e483b024e1b9ad029821470ed8ec65631dbd90217469da0e7cd89f1c",
- "https://files.pythonhosted.org/packages/a6/cf/2b684132056f438567b61e19d690dd31cd0921ace051e0a458be6074369e/cffi-2.1.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl": "sha256:47ff3a8bfd8cb9da1af7524b965127095055654c177fcfc7578debcb015eecd0",
- "https://files.pythonhosted.org/packages/a8/eb/f636456ff21a83fc13c032b58cc5dde061691546ac79efa284b2989b7982/cffi-2.1.0-cp312-cp312-win_amd64.whl": "sha256:c97f080ea627e2863524c5af3836e2270b5f5dfff1f104392b959f8df0c5d384",
- "https://files.pythonhosted.org/packages/a9/d6/c72eecca433cd3e681c65ed313ab4835d9d4a379704d0f628a6a05f51c2e/cffi-2.1.0-cp313-cp313-musllinux_1_2_x86_64.whl": "sha256:bf5c6cf48238b0eb4c086978c492ad1cbc22373fc5b2d7353b3a598ce6db887a",
- "https://files.pythonhosted.org/packages/b0/80/c138990aa2a70b1a269f6e06348729836d733d6f970867943f61d367f8cc/cffi-2.1.0-cp312-cp312-win32.whl": "sha256:9b8f0f26ca4e7513c534d351eca551947d053fac438f2a04ac96d882909b0d3a",
- "https://files.pythonhosted.org/packages/b3/c1/6dbd291ee2ae5a50a034aa057207081f545923bbf15dad4511e985aafff5/cffi-2.1.0-cp314-cp314-musllinux_1_2_x86_64.whl": "sha256:dbf7c7a88e2bac086f06d14577332760bdeecc42bdec8ac4077f6260557d9326",
- "https://files.pythonhosted.org/packages/b5/9f/d4dc66ca651eb1145a133314cda721abf13cfac3d28c4a0402263ae6ad75/cffi-2.1.0-cp315-cp315t-musllinux_1_2_x86_64.whl": "sha256:ba00f661f8ba35d075c937174e27c2c421cec3942fd2e0ea3e66996757c0fdd9",
- "https://files.pythonhosted.org/packages/b6/3d/f20f8b886b254e3ad10e15cd4186d3aed49f3e6a35ab37aab9f8f25f7c03/cffi-2.1.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl": "sha256:bf01d8c84cbea96b944c73b22182e6c7c432b3475632b8111dbfdc95ddad6e13",
- "https://files.pythonhosted.org/packages/b9/26/d00496b22de4d4228f32dde94ad996f350c8aad676d63bcca0743c8dea4d/cffi-2.1.0-cp314-cp314t-win_amd64.whl": "sha256:0582a58f3051372229ca8e7f5f589f9e5632678208d8636fea3676711fdf7fe5",
- "https://files.pythonhosted.org/packages/b9/65/b434abc97ce7cecc2c640fde160507c0ecc7e21544b483ba3325d2e2ea17/cffi-2.1.0-cp315-cp315t-musllinux_1_2_aarch64.whl": "sha256:86cf8755a791f72c85dc287128cc62d4f24d392e3f1e15837245623f4a33cccc",
- "https://files.pythonhosted.org/packages/ba/b0/e131a9c41f10607926278453d9596163594fe1c4ebc46efe3b5e5b34eb84/cffi-2.1.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl": "sha256:a5781494d4d400a3f47f8f1da94b324f6e6b440a53387774002890a2a2f4b50f",
- "https://files.pythonhosted.org/packages/c0/e9/6d7724983b3d5a0908dbf74f64038ade77c18646ff6636ec7894fd392ce1/cffi-2.1.0-cp310-cp310-macosx_10_15_x86_64.whl": "sha256:b65f590ef2a44640f9a05dbb548a429b4ade77913ce683ac8b1480777658a6c0",
- "https://files.pythonhosted.org/packages/c3/c0/d1ec30ffb370f748f2fb54425972bfef9871e0132e82fb589c46b6676049/cffi-2.1.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl": "sha256:5972433ad71a9e46516584ef60a0fda12d9dc459938d1539c3ddecf9bdc1368d",
- "https://files.pythonhosted.org/packages/c6/4b/e706f67279140f92939da3475ad610df18bfd52d50f14953a8e5fede71d5/cffi-2.1.0-cp313-cp313-win32.whl": "sha256:db3eb7d46527159a878ec3460e9d40615bc25ba337d477db681aea6e4f05c5d2",
- "https://files.pythonhosted.org/packages/cc/82/3d5c705acb7abbba9bbd7d79b8e62e0f25b6120eb7ae6ac49f1b721722fe/cffi-2.1.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "sha256:ac0f1a2d0cfa7eea3f2aaf006ab6e70e8feeb16b75d65b7e5939982ca2f11056",
- "https://files.pythonhosted.org/packages/cc/d7/97d3136f81db489ec8d1d67748c110d6c994268fd7528014aa9f2b085e4e/cffi-2.1.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "sha256:d53d10f7da99ae46f7373b9150393e9c5eab9b224909982b43832668de4779f5",
- "https://files.pythonhosted.org/packages/d0/9a/bb1d5ed9c3fcae158e9f6391bf309c95d98c2ac37ed56573228471d0af5e/cffi-2.1.0-cp310-cp310-musllinux_1_2_aarch64.whl": "sha256:3d7f118b5adbfdfead90c25822690b02bc8074fba949bb7858bec4ebd55adb43",
- "https://files.pythonhosted.org/packages/d3/0b/0ffe8b82d3875bced5fa1e7986a7a46b748262a40ab7f60b475eb9fb1bb3/cffi-2.1.0-cp315-cp315t-win_amd64.whl": "sha256:f146d154428a2523f9cc7936c02353c2459b8f6cf07d3cd1ee1c0a611109c5d5",
- "https://files.pythonhosted.org/packages/d3/27/93195977168ee63aed233a1a0993a2178798654d1f4bddcdd321d6fd3b21/cffi-2.1.0-cp314-cp314-musllinux_1_2_aarch64.whl": "sha256:c351efb95e832a853a29361675f33a7ce53de1a109cd73fd47af0712213aa4ce",
- "https://files.pythonhosted.org/packages/d3/67/85c89a59ba36a671e79638f44d466749f08179266a57e4f2ffdf92174072/cffi-2.1.0-cp311-cp311-macosx_10_15_x86_64.whl": "sha256:02cb7ff33ded4f1532476731f89ede53e2e488a8e6205515a82144246ffa7dcc",
- "https://files.pythonhosted.org/packages/d5/dd/0c7dbf815a579ff005008a2d815a55d6bb047c349eef536d9dc53d3f0a8d/cffi-2.1.0-cp314-cp314t-win_arm64.whl": "sha256:510aeeeac94811b138077451da1fb18b308a5feab47dd2b603af55804155e1c8",
- "https://files.pythonhosted.org/packages/d6/5c/584e626835f0375c928176c04137c96927165cb8733cdb3150ec04e5ee5e/cffi-2.1.0-cp314-cp314-macosx_11_0_arm64.whl": "sha256:c4165821e131d6d4ca444347c2b694e2311bcfa3fe5a861cc72968f28867beac",
- "https://files.pythonhosted.org/packages/d8/f0/81478e482afa03f6d18dc8f2afb5edc45b3080853b634b5ed91961be0998/cffi-2.1.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl": "sha256:d2117334c3af3bdcb9a88522b844a2bdb5efdc4f71c6c822df55486ae1c3347a",
- "https://files.pythonhosted.org/packages/dc/78/aa01ac599a8a4322533d45a1f9bc93b338276d2d59dabbe7c6d92a775c81/cffi-2.1.0-cp314-cp314t-win32.whl": "sha256:7d034dcffa09e9a46c93fa3a3be402096cb5354ac6e41ab8e5cc9cd8b642ad76",
- "https://files.pythonhosted.org/packages/dd/2c/400ea43e721727dca8a65c4521390e9196757caba4a45643acb2b63271b8/cffi-2.1.0-cp312-cp312-win_arm64.whl": "sha256:6d194185eabd279f1c05ebe3504265ddfc5ad2b58d0714f7db9f01da592e9eb6",
- "https://files.pythonhosted.org/packages/e0/27/1d0b408497e41a74795af122d7b603c418c5fed0171450f899afd04e594f/cffi-2.1.0-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl": "sha256:0520e1f4c35f44e209cbbb421b67eec42e6a157f59444dfb6058874ff3610e5d",
- "https://files.pythonhosted.org/packages/e5/4b/1f4c36ab273980d7aa75bb126ea4f8971f24a96108acad3a0a084028c57b/cffi-2.1.0-cp315-cp315-macosx_11_0_arm64.whl": "sha256:cdf2448aab5f661c9315308ec8b93f4e8a1a67a3c733f8631067a2b67d5913dc",
- "https://files.pythonhosted.org/packages/e6/3f/0b04a700dd64f465c93020253a793a82c9b4dff9961f48facd0df945d9b8/cffi-2.1.0-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "sha256:7d3538f9c0e50670f4deb93dbb696576e60590369cae2faf7de681e597a8a1f1",
- "https://files.pythonhosted.org/packages/ea/dd/e3b0baa2d3d6a857ac72b7efbf18e32e487c9cdafcc13049ad765495b15e/cffi-2.1.0-cp311-cp311-macosx_11_0_arm64.whl": "sha256:f5bce581e6b8c235e566a14768a943b172ada3ed73537bb0c0be1edee312d4e7",
- "https://files.pythonhosted.org/packages/eb/d8/df4543cc087245044ed02ef3ad8e0a26619d0075ac7a77a12dc81177851b/cffi-2.1.0-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl": "sha256:6274dcb2d15cef48daa73ed1be5a40d501d74dccd0cd6db364776d12cb6ba022",
- "https://files.pythonhosted.org/packages/eb/da/5c4918a2d61d86fa927d716cb3d8e4626ef8dc8f605a599d32f33897f59a/cffi-2.1.0-cp311-cp311-win32.whl": "sha256:64c753a0f87a256020004f37a1c8c02c480e725f910f0b2a0f3f07debd1b2479",
- "https://files.pythonhosted.org/packages/ec/d1/9a5b7169499e8e8d8e636de70b97ac7c9447104d2ff1a2cd94790cea5162/cffi-2.1.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl": "sha256:0a96b74cda968eebbad56d973efe5098974f0a9fb323865bf99ea1fd24e3e64c",
- "https://files.pythonhosted.org/packages/ed/a5/e8bbb1ce5b3ac2f53ad6a10bde44318a5a8d99d4f4a000d44a6e39aeb3e4/cffi-2.1.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl": "sha256:7d5980a3433d4b71a5e120f9dd551403d7824e31e2e67124fe2769c404c06913",
- "https://files.pythonhosted.org/packages/ed/da/4bbe583a3b3a5c8c60892124fe17f3fa3656523faf0d3484eae90f091853/cffi-2.1.0-cp315-cp315-macosx_10_15_x86_64.whl": "sha256:95f2954c2c9473d892eca6e0409f3568b37ab62a8eedb122461f73cc273476e3",
- "https://files.pythonhosted.org/packages/ef/c3/ad299dc38f3583f8d916b299f028af418a9ec98bc695fcbebeae7420691c/cffi-2.1.0-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.whl": "sha256:90bec57cf82089383bd06a605b3eb8daebf7e5a668520beaf6e327a83a947699",
- "https://files.pythonhosted.org/packages/f9/c8/6c2de1d55cf35ef8b92885d5ef280790f0fb9634d87ea1cc315176aecd61/cffi-2.1.0-cp311-cp311-win_amd64.whl": "sha256:4f26194e3d95e06501b942642855aed4f953d55e95d7d01b7c4483db3ecff458",
- "https://files.pythonhosted.org/packages/f9/cf/398272b8bbfd58aa314fda5a7f1cdbb26d1d78ae324a11211521315dd1f0/cffi-2.1.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl": "sha256:03e9810d18c646077e501f661b682fbf5dee4676048527ca3cffe66faa9960dd",
- "https://files.pythonhosted.org/packages/fa/75/74dfb7c3fc6ebbd408038476bd4c1d7e925c62614e7b9c534ecc34218288/cffi-2.1.0-cp310-cp310-musllinux_1_2_x86_64.whl": "sha256:11b3fb55f4f8ad92274ed26705f65d8f91457de71f5380061eb6d125a768fecd",
- "https://files.pythonhosted.org/packages/fb/d2/4398416cd699b35167947c6e22aca52c47e69ad5695073c9f1f2c52e04aa/cffi-2.1.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "sha256:aa7a1b53a2a4452ada2d1b5dade9960b2522f1e61293a811a077439e39029565"
+ "https://files.pythonhosted.org/packages/01/9a/11f687cb39d6a3504060d5242f04f48c735afb4d3d533958a20594890cb2/cffi-2.1.1-cp313-cp313-win_arm64.whl": "sha256:63bbfd5ded17c4840ac07cd8f1c21ba9d9708141f840b324f422f41b207e3973",
+ "https://files.pythonhosted.org/packages/02/10/4b3c75dde3d9663c9e02ba05c2668b954f671d4bbe346413ca8c696b295a/cffi-2.1.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl": "sha256:fcd22650c908d7b7da162bbfaab594a1227a15d1643a98c68b122ac642fa2264",
+ "https://files.pythonhosted.org/packages/09/b8/eefc0e06913b70aa153bf74c946094a18f58fd4aff11b7f372bfdfdca050/cffi-2.1.1-cp310-cp310-musllinux_1_2_i686.whl": "sha256:3d22a20b1fb1632cc72c22f95f7b0d2961c3e1c235f245ba4c606c4771035659",
+ "https://files.pythonhosted.org/packages/0b/e9/d0061c364cde06ee43168a0d076ac1da512cbc380d44767b844ba34fe2b6/cffi-2.1.1-cp314-cp314-win32.whl": "sha256:1a18a57b58cfb21fc28d72e876acf10eaed67a1ed96226f92af4df681d571c4c",
+ "https://files.pythonhosted.org/packages/0c/75/2f5207ff6d1a613133b23a5203cc0c2a628313b5eb3974d7956ae3c57950/cffi-2.1.1-cp315-cp315-macosx_11_0_arm64.whl": "sha256:2ae64be792b8966f2c69538199728b290e34726562896df1e5dc8ffd8d8188e8",
+ "https://files.pythonhosted.org/packages/0d/fc/5f74e293fced6edb51af3a46c4ccf6c23c9943774ecb375ddbd522c76add/cffi-2.1.1-cp315-cp315-manylinux2014_s390x.manylinux_2_17_s390x.whl": "sha256:471cee653ae88de62096552e6d24ccb4a5adb8c8c9f10b5054d0122c15bf2779",
+ "https://files.pythonhosted.org/packages/10/69/43965eccfdead3b9220015fd1320e117be8c6ed01a62ffab76eeb752f5d5/cffi-2.1.1-cp312-cp312-macosx_10_15_x86_64.whl": "sha256:c8c69575568085ba0b1b10c0249d779a214aea6f6522e949a0fc9fb0fcb449d0",
+ "https://files.pythonhosted.org/packages/17/0b/ba385d8ccedf926c3cd06e8e2f327027da5afe5f0eb30f1f7bc43ac55125/cffi-2.1.1-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl": "sha256:42e2f76b9455f5a9a844f770bf3e200ed3da0e15f5df3db9c31fe80b04b3d004",
+ "https://files.pythonhosted.org/packages/18/d4/03026f0c850cbbaa9030750490225b4a7f4d524ea4df72c3cc740a90f4ef/cffi-2.1.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl": "sha256:75f80557d1389eddbd0de2681f6a390a0c5338c31ddaa821381c203fc3fd50d9",
+ "https://files.pythonhosted.org/packages/1b/8a/af668013284634733f02d683458a0728739c7d6ddb5e14cb0c20832266fe/cffi-2.1.1-cp315-cp315-macosx_10_15_x86_64.whl": "sha256:fb92203a88b3d3053034db775110081c49d28be6551923805e039924093761e4",
+ "https://files.pythonhosted.org/packages/23/59/40338bf421c5accea1d45158170c87006ef1cd371b05c077e76476949728/cffi-2.1.1-cp314-cp314t-macosx_10_15_x86_64.whl": "sha256:7750c6449dff7864bb9bb27ddfb0267756189201a3afc911d82b3caacd70dfc3",
+ "https://files.pythonhosted.org/packages/28/f7/dabe6da2466ecbd82dc62e7342dc6b1065dad990c06f00f0ede9ebf2a0ed/cffi-2.1.1-cp311-cp311-musllinux_1_2_aarch64.whl": "sha256:7225e4514edb64eb6740324353e0da0711954fd8d7da4576755b1c6e09b697cd",
+ "https://files.pythonhosted.org/packages/2a/9c/92934c3bea9f785b23eba304538c0b4d37a2a96d2431eb3a1bc87a11aa19/cffi-2.1.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl": "sha256:49cbc70e6542d4ccccb936558d1064a8012541e78f821f955cff24e357776c94",
+ "https://files.pythonhosted.org/packages/34/34/a95870b9221e09cf4f2ce3178b1a210abdfe63a1bd357da940418d7b8d15/cffi-2.1.1-cp313-cp313-musllinux_1_2_x86_64.whl": "sha256:e0bcb7e0f677f543555d2adff3bf19c05f66cdb4796e5ff602442ab2fe3c4ef7",
+ "https://files.pythonhosted.org/packages/37/15/180e0dab27b9312c7479003d14c9e547634b7dcb934e2cc4650e1b131a7a/cffi-2.1.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl": "sha256:9f8d177621de5cb38ee3e731eda45d421db093ec0739f46a5594babda7987a98",
+ "https://files.pythonhosted.org/packages/37/6f/3b5ce4c3b2192d250f04908f2bfd91ef34552ec8f7716a5d4abdb8d67bb2/cffi-2.1.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl": "sha256:f16c709686a78c727bbbf059f92b0bf41c6fc60deec706d2dc19f529175a6125",
+ "https://files.pythonhosted.org/packages/43/1f/1c3d90d91811c8f86ced9ed637956c54bfe5b79ca98fe976d7f8c8979f6b/cffi-2.1.1-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl": "sha256:6a8dddef476fab96d066d578fc88526767b836ab5ab21754e1d5bf3879c31c7c",
+ "https://files.pythonhosted.org/packages/44/16/29e6d01b388bef055ecd6ca8244b3f4d336bd09e92d5d892187b9601084e/cffi-2.1.1-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "sha256:aeae0e330c9f6acd681f647d46cefd30c29f93e3392882e792e82080c9691399",
+ "https://files.pythonhosted.org/packages/44/de/f98430906df1545ffde0d543dd124a7a439bc2cd32b36b9c53f805df7333/cffi-2.1.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl": "sha256:68e62fe11f30d5ca8289242866f0a5291402d8529ca2178ab8afc5c9694ae890",
+ "https://files.pythonhosted.org/packages/4d/45/ba4c93527bc38616a8bd36488acb69a2212d60486794f0c1f318949bbb76/cffi-2.1.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl": "sha256:e2d65b31f36619cda3999b78b2aa9632e76b78448e7a56fc4240824200e7c4fc",
+ "https://files.pythonhosted.org/packages/50/e3/f6234a833e6e08c7007003074723c406559eecf9b48dfc97471e5a8eb7a0/cffi-2.1.1-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl": "sha256:246fa40ce8645a614ff682e0b70f37134e460eaf93a775e0cbe3cca585a67a80",
+ "https://files.pythonhosted.org/packages/52/86/2976131c639aead931c5bee5aba67e4b09fbeb8018b6f282f70803f923a7/cffi-2.1.1-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "sha256:363e05fa78e15116c3c32c210ee36884fd6b9afa6d440e47112c3bd511d64cb6",
+ "https://files.pythonhosted.org/packages/53/b2/6187f46f2912276a3ae284076109cc5c8680482f11f766ccf26db4a86427/cffi-2.1.1-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl": "sha256:937c0052c05a31ca1daf18de3158eed4dbfcb9cc107adbea227728d647be701e",
+ "https://files.pythonhosted.org/packages/54/7d/16e5a096677b5e313ca80cd5e5170efa3ea44624a82bb111925522da64b1/cffi-2.1.1-cp312-cp312-macosx_11_0_arm64.whl": "sha256:f81b3b8f3d4e343550fa4baa0e479bba9f2d29ce9c2e9b51d1ce1718d7442fcf",
+ "https://files.pythonhosted.org/packages/55/41/4c7042f317b9217502988f0873af87e16ad606dc20f84e546e3e6ce9764c/cffi-2.1.1-cp313-cp313-macosx_11_0_arm64.whl": "sha256:19ee6127ee34de7d83ce3d371ebc5ed91addbdcc39f9ab15ce4eb35a4e534971",
+ "https://files.pythonhosted.org/packages/56/78/31f00c1bcd97c9bbf55f1bfdf5bc809a5de8887473e90bb9960dca825e80/cffi-2.1.1-cp314-cp314t-win_amd64.whl": "sha256:616f097f2fe415bc92a247f02e11f634e1f9e9a83d327e3c915c15089c87869e",
+ "https://files.pythonhosted.org/packages/56/e6/8941622732edec876dd17d0453dce07317ae96db34f2ec1436c9d3785986/cffi-2.1.1-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl": "sha256:811bd1e21d32de12efca32393a0ab3f5133b54fce9bd44b8bd77ab07da14bf6a",
+ "https://files.pythonhosted.org/packages/60/a6/8b149b2c3f2e11aaa1618ef64500b45f50f22c57a977a4dff1aff1f91042/cffi-2.1.1-cp313-cp313-win_amd64.whl": "sha256:1aa5645c30469b09530c4ebca77ebf8f17618293c58f8549cb1a543a50236e7d",
+ "https://files.pythonhosted.org/packages/64/b3/f8aa4f3e34986c7e4ec45072d1b1b9dd295b6b18007b45518d79726dd725/cffi-2.1.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl": "sha256:3143d81e29e1e20a9ce10901ec369012947876596f75a222235965f2b7ae832e",
+ "https://files.pythonhosted.org/packages/67/b8/b42132ca113dc567d37684437b46ca1dafc885902b02a110a02d5b511857/cffi-2.1.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl": "sha256:58acb8ab8e295e6c5ea12f888cbb13cf21511ef2a3303a23f4325c29d17fe5c1",
+ "https://files.pythonhosted.org/packages/6a/5b/717f1526b9957b34456313c31645c5b82b8fb5c3fe9e4752999be7128bfc/cffi-2.1.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl": "sha256:4a7c934f7360e8cd64fe9efadcbd10c7c6364f531e432b9a4bf5ccbc9e0e8b50",
+ "https://files.pythonhosted.org/packages/6d/cd/a361394c94b2129d604bb846f624a8e88255a3ee33129c434a00d715e64f/cffi-2.1.1-cp315-cp315t-win32.whl": "sha256:06c72bb76605a4b0cd0aad6930b69d4baf7dd5d806cfc409b824191099700e66",
+ "https://files.pythonhosted.org/packages/6f/13/4e56852824a03cdf68523a35686f1c28eacd4bd30a7b0a78e682e6e6e1d3/cffi-2.1.1-cp310-cp310-musllinux_1_2_x86_64.whl": "sha256:1dea0e4d7d4f11f619fe8c1d76caf49e24405b4b5743c0e3be16a500ecd930c9",
+ "https://files.pythonhosted.org/packages/70/c6/d0ea84713fe46b243a436a18fcd47d639732747e21635c8a27191b06dc30/cffi-2.1.1-cp312-cp312-win_arm64.whl": "sha256:7bde5e4cc5c10140859842b9d383af292b22639a4dffb725314baf45968cef80",
+ "https://files.pythonhosted.org/packages/70/d2/16d99a0c4948febc0ebd133a13b2f688ff7f8cb04da971e1128872ce0c03/cffi-2.1.1-cp311-cp311-macosx_10_15_x86_64.whl": "sha256:c8d2c9fd1f2d16f780d15127abb050d13d1a76c03a4bd87d7e4980e45e511e12",
+ "https://files.pythonhosted.org/packages/70/ea/839b50531021a647fb5e929f72cf97bc1ff702b5472166164b5b6e76b851/cffi-2.1.1-cp313-cp313-win32.whl": "sha256:334644fbac4eff73d985a17a91226df55d0f394160c4cfb880e084c8f7161cac",
+ "https://files.pythonhosted.org/packages/73/c0/77ba02423c2f7d7091143c45cd49e0e6575c4c1967394bb542bd923a9b74/cffi-2.1.1-cp311-cp311-win_amd64.whl": "sha256:42f6930c31dc7f50732c9ae793c2786c7b6b044195967bbdde40bb9be81c4cc0",
+ "https://files.pythonhosted.org/packages/75/77/60bebf6f818bec84210ac5b6979ce4eeadce6fbbaabc9c7ab23e506d1ce5/cffi-2.1.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "sha256:194cffa889098ced9976c3fc6340305e43f6303657d298da55366907c05c22d6",
+ "https://files.pythonhosted.org/packages/76/af/2aeb4dbb5fc41a04161ae9ff1518de7cec08e164f44a8ce6a4cf7fd2cd1d/cffi-2.1.1-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl": "sha256:31348097ff5bbe827ccc41795d4dd099d9f0625e7def00ee653c137a490c2a6c",
+ "https://files.pythonhosted.org/packages/7b/1b/58496f2ed0a35de575250c02a43ab3cc2c04d494a88fed31c1cabc0fd176/cffi-2.1.1-cp314-cp314t-win_arm64.whl": "sha256:ad2c86c495b899d862ea0f4b42891b8713a3bd45dd4105c7fd51c2a72f39f3a5",
+ "https://files.pythonhosted.org/packages/7c/47/9f1f85f9672ceda4984dc6c4f8824e8558992a2972c3d3c81fb8eb28d4ba/cffi-2.1.1-cp311-cp311-win_arm64.whl": "sha256:c7659f22557c5a0bc4855cd635f55edec690cc008a40768527762cb9fb263455",
+ "https://files.pythonhosted.org/packages/7d/47/5ecf1023850036e674c77ec4de86182d309ae344e39e7cba984b7df5d647/cffi-2.1.1-cp314-cp314t-macosx_11_0_arm64.whl": "sha256:0beceaabe56af686895136a2de78db54ecd8e4046b236b8fd6d6cb61389e9bf2",
+ "https://files.pythonhosted.org/packages/7e/ae/a56fa8c4686ad50e148fcbc8d3ae0d03915ff5c30d795058988c24118cef/cffi-2.1.1-cp315-cp315t-macosx_11_0_arm64.whl": "sha256:4be96343e422f2dfcd12ab5c9f5aebe03f82f737c6bffeca6830b3875cb44aab",
+ "https://files.pythonhosted.org/packages/80/10/c5c0cbf0a657aecf59ef511409734230bf556f05a0d6c9eed7aa5c0a0166/cffi-2.1.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl": "sha256:456a61fa52d579ebf9df2e9552ead5129855dbaff6c1e5a9b1bc408809bdc062",
+ "https://files.pythonhosted.org/packages/80/e9/b6ef565e452acb932fb0cb5443f44a78efbd1233e566f02b5a83855e9115/cffi-2.1.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl": "sha256:28907ab9bfb6aa13184cfc17c6b8e1023c5ab6fd7076d8c20a35e59fe04f8f29",
+ "https://files.pythonhosted.org/packages/80/fb/0bb75b7039588c074b37ae99f40d9bfddf990ecb2fbc346ebccd2e56b9be/cffi-2.1.1-cp312-cp312-win32.whl": "sha256:046bfc24911b37851ee1b51aab8bffe713d89c68c6a057b09484ce9fd5f69b4e",
+ "https://files.pythonhosted.org/packages/81/10/5dc0e7bdd18e22107054288283380fc97a06ae3f1656a106908d666a3c88/cffi-2.1.1-cp314-cp314-musllinux_1_2_x86_64.whl": "sha256:5c58fe613dc5e5336357eff555824a314d8e43282600435c8d1cb6a7a2fedd13",
+ "https://files.pythonhosted.org/packages/82/1d/a4aaf9babd75acb4d5f223bff71533bee748dd770a382619a798960ee9ba/cffi-2.1.1-cp314-cp314-musllinux_1_2_aarch64.whl": "sha256:64faea20f4e2613363a1a9b9c7dd73058f3ecd00133a511e72ad7c511658f527",
+ "https://files.pythonhosted.org/packages/87/5b/da4e39efe18eeb89cf580ea9cfc66b6a7c3eadb808fc0cc1d3a295cb5a5d/cffi-2.1.1-cp314-cp314-win_arm64.whl": "sha256:ab36d55f9ed2d067327667c2fea18dda018eb628dd6347aa01dda6cf1f5d3836",
+ "https://files.pythonhosted.org/packages/8a/f6/c3ad28bd19f77047a03084424fbd4cbe997303267c14423737324be0385d/cffi-2.1.1-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl": "sha256:df423d40ee8654634421812bc3b196da3f9bd7d32929da813f8394c4348a5358",
+ "https://files.pythonhosted.org/packages/8d/b0/0b44f47c60b01b57b6e2bbd92343f13a85a1d93bc46ccf6e47e244acd99c/cffi-2.1.1-cp314-cp314t-musllinux_1_2_x86_64.whl": "sha256:25792eac27877609e7bb06d42ff88278a6624fff2ba9bbb523c09616b117e80f",
+ "https://files.pythonhosted.org/packages/94/1a/4b2f7c92293ba05cbd4a9a1b28faaf0326272d9488e6354657571c48a7aa/cffi-2.1.1-cp310-cp310-macosx_11_0_arm64.whl": "sha256:ca82be1a1d406ecfe1d25dc16cb33488e5a16bf4438c9fb590484ea29d92478b",
+ "https://files.pythonhosted.org/packages/94/e6/bcc91b283be94735e268487a054004f0aa19947b6348fa367db53230abc8/cffi-2.1.1-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl": "sha256:f24fb43132a4c6b4cb4eb029492919b2db645be6808d738f244fd146c03c32cb",
+ "https://files.pythonhosted.org/packages/95/95/86342356ff5953b3fb06f7ef7c5bee212d45e770abc7218d451b9148313c/cffi-2.1.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "sha256:a931079504ecc49efed7744c476a5c343a92fabf66dec2db95edb1b2fdc770e2",
+ "https://files.pythonhosted.org/packages/99/7f/040f9e163e4acac3ee3d85b02d00b2576e7ca980d8785f0a3a5f1a9bf7f5/cffi-2.1.1-cp310-cp310-win32.whl": "sha256:7ce713ace7c0e4520535b42b77eaa742c16dab813978064913e5a3cf82973b41",
+ "https://files.pythonhosted.org/packages/9a/95/eff5f0cee78d2eabc7eebffec40d3fc1876b5f3c95582e018bb4b99601f2/cffi-2.1.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "sha256:51b31d1c98274844cfd7838ce00bfc27c7423a4dc00fc0772fc3331c2cc90676",
+ "https://files.pythonhosted.org/packages/9b/b5/ba2b299993c26577d529b6ae29841f9e15b9fcf004d65f423f4fcf94ade9/cffi-2.1.1-cp315-cp315t-win_amd64.whl": "sha256:d9c275eaacd24aa73f94ffd6de08fc3f932424d8b6c376f4bed7cde376fe7bc3",
+ "https://files.pythonhosted.org/packages/9d/f4/035513d4117049066b4779dc3b7c0c0fdad175fa13731c9f4003f1cd1478/cffi-2.1.1-cp313-cp313-ios_13_0_arm64_iphoneos.whl": "sha256:b5bdfd1c873d4e093aabc0ca84c4ca6dbc4f752afb5c86f146d9742580c9da2e",
+ "https://files.pythonhosted.org/packages/9e/ef/008a1939e372c06329a3fce4279c02f328488f3526744906eeec3da7ad5f/cffi-2.1.1.tar.gz": "sha256:dd31f52ea1086513bb9df30f8fcee9b8918323ae067a3d5b78bc826a000712be",
+ "https://files.pythonhosted.org/packages/a0/cd/ccac9013a5bd9fd764de118674ab9c805b5ca10c19270d90ee273f8b2240/cffi-2.1.1-cp315-cp315t-manylinux2014_s390x.manylinux_2_17_s390x.whl": "sha256:a730a083190634c65cca36ba5f489531576ebd79bcd5c8e172130f6453127231",
+ "https://files.pythonhosted.org/packages/a0/d2/6cd24ae3be000a634109c247d1475d62e5616d0dc78c82770942ec384248/cffi-2.1.1-cp312-cp312-musllinux_1_2_aarch64.whl": "sha256:208f941bb9d18e768138677f0a6d2ce01f590df56043dda1df1535ac57c88517",
+ "https://files.pythonhosted.org/packages/a3/b9/0f2e58b2cefa33255bff36935d42b13180fe559bba82596540eb404bde7d/cffi-2.1.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl": "sha256:5a59cc1c4442bc3d5c703bf720b51138d0bfc173618807c9ee2490a7541dd3d9",
+ "https://files.pythonhosted.org/packages/a4/18/fa7f1f6857d5eb88a4ca99ffcbfb7c387a287ccc154c64a73e86314745d7/cffi-2.1.1-cp315-cp315-musllinux_1_2_aarch64.whl": "sha256:42a494cee34437f05546455144f2b5d9ac09b1face62bcfce597d2e521066688",
+ "https://files.pythonhosted.org/packages/a5/a7/a67c733254d6e7373f7822f8082d8d6beade791e0cf12a7611f376fa61c7/cffi-2.1.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl": "sha256:a6e721d4b0e45d5b65e87534470e67b18dcd092c83f68fba09f152b9cbc061af",
+ "https://files.pythonhosted.org/packages/a7/06/1c3e01e3ba14c39f6d10bfbac52753b7e22259e38088e5cfe1d704918690/cffi-2.1.1-cp314-cp314-win_amd64.whl": "sha256:3222ba5d678f80a030e6afbcc33dc1ae5cb45facabb61cee2c7016b8432fde48",
+ "https://files.pythonhosted.org/packages/a7/46/2e5fdde8555706dd98139a910ca11be02809f3f605ce956f655d0214e100/cffi-2.1.1-cp313-cp313-macosx_10_15_x86_64.whl": "sha256:9d2055050ea716bd38b7f7f1579c275386646b4894c155a3e2f3cd62ed41b7c6",
+ "https://files.pythonhosted.org/packages/a7/92/500760486c8baab49a7a8a58ba7fc3355ec3974b454b8a09e528efde9e1d/cffi-2.1.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl": "sha256:6e192623c49c94421616a5778fba35cf0d5a8d000650c1967ef4448ee5cdd990",
+ "https://files.pythonhosted.org/packages/aa/29/35e016098c814cd93de9cd320c66b5bfba14dc6ecedd3cb518fa7c408c69/cffi-2.1.1-cp315-cp315t-win_arm64.whl": "sha256:d18e5ac0f2f03f4f518d3e23db0f0cad7faa1da8620e9c09461d443bbf6e6692",
+ "https://files.pythonhosted.org/packages/ac/0c/33a7aeab2f9c76918c52e084beb39c570db3588133412929e8ec06fab90b/cffi-2.1.1-cp315-cp315t-musllinux_1_2_aarch64.whl": "sha256:770de9db11e84213beec501cfcaa013b019820ca881e03344dea5844f7876d94",
+ "https://files.pythonhosted.org/packages/ad/5a/4707a0dc1f203f5dde5a907b0d4e3c25d71120241048bd5bc6f1bb9d4e71/cffi-2.1.1-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl": "sha256:154852545011f779917b11c78db2358d095da62a9a172b78ad0a583ee5adc0d0",
+ "https://files.pythonhosted.org/packages/ad/66/c19feabb28485b6e0bbaaafa90837a1ef5d302e90f2178bd33f17a49879b/cffi-2.1.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl": "sha256:3311ed60d36f83378794e1009ac6258bafbf81f7888b4caa7b35a521e3f95813",
+ "https://files.pythonhosted.org/packages/b0/ae/679bf47e73fd77b352171727f07de559a003f14de5d02b904a6ec1fa73ca/cffi-2.1.1-cp310-cp310-musllinux_1_2_aarch64.whl": "sha256:5bb4e7ea95dcd6a014a6fef62e62467d67d8e582326443f3d68e71d6320a9fcf",
+ "https://files.pythonhosted.org/packages/b1/db/dceb9dd5b231e1da801793f8acc9f3c52a7e1afe40bb1aae37e02b0faad5/cffi-2.1.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "sha256:c1453022f490d2459a11819d83ad1d586e9ff65a12ac3e705ffebd46d3685dcf",
+ "https://files.pythonhosted.org/packages/b3/a9/9db617d05d7367c1ad0ab00b3aa6e6f9281edd689b4ee9ea0e5a84e89c97/cffi-2.1.1-cp314-cp314-macosx_11_0_arm64.whl": "sha256:661c298b4821edebead0c91edd2b00374d67ad7c5a1f7a91d4442633b79d6a72",
+ "https://files.pythonhosted.org/packages/b4/c6/ab025d75d2c26c19b087c0124e75ee31cb65032f4fe345d356d8c507ab97/cffi-2.1.1-cp311-cp311-musllinux_1_2_x86_64.whl": "sha256:f5cfbc5fe74540d335175b656c725d74d90e3730c626d92575eea35029d9afaa",
+ "https://files.pythonhosted.org/packages/b6/d2/2cde336b375f55c76ca670f0be3978cc048e31e24f3b4d7ce8473150a388/cffi-2.1.1-cp310-cp310-macosx_10_15_x86_64.whl": "sha256:baed1e86cc735622097354b9d1281406caf42ff42a886d29faa8e8d1630333be",
+ "https://files.pythonhosted.org/packages/ba/0b/644a2ec1a4eaba49c2939410bb1eb1d25b09d6d0582f5d2f95c537043725/cffi-2.1.1-cp310-cp310-win_amd64.whl": "sha256:a48d62ab9d6f4f98c983223a547af44be6ca3691074c31cecced6facd3ba2dc1",
+ "https://files.pythonhosted.org/packages/c1/8f/9ebe220eab48a093d1a5a5e339ab0dc7316eef3bb04d63c42f0251b61f50/cffi-2.1.1-cp315-cp315-ios_13_0_arm64_iphoneos.whl": "sha256:dddad92b554513a31f272570678ba307fb9f618f05e3d4a5eacafff9eae03e1d",
+ "https://files.pythonhosted.org/packages/cb/52/3fa190537004dd7f0ab860a6dc7c0175b8667f68d1e618a46f5498d30250/cffi-2.1.1-cp312-cp312-musllinux_1_2_x86_64.whl": "sha256:210019b6c7cf07f081b4c54635c8cf744377001350e29cc0f81c4377b4797735",
+ "https://files.pythonhosted.org/packages/cc/4b/b3a2da8570c704ffc0f9762cdc3ec0f02c8573798e0b5cf7f11c82bbb70f/cffi-2.1.1-cp315-cp315-win_arm64.whl": "sha256:27350daa11d4f10c540e6e89dada4c54feb7256ad03e9a4dc075ebad7ba360d1",
+ "https://files.pythonhosted.org/packages/cd/95/31b535a9f0220ae9f357de4a08d57ce89cb417653c2fd9f075f50822a388/cffi-2.1.1-cp311-cp311-macosx_11_0_arm64.whl": "sha256:398aff33cee2767e3e781d2554c54bd0dff386bb437581e0d8011fde1a942ec1",
+ "https://files.pythonhosted.org/packages/ce/87/616202d8e51342c07d2534c510111c4cc37201775ce8f60802c9335d1edd/cffi-2.1.1-cp311-cp311-musllinux_1_2_i686.whl": "sha256:df913725b79db7bcf03448f36b7bf8815363417d5b58deecf9305e3e30f0f21a",
+ "https://files.pythonhosted.org/packages/d0/ef/5443574510a1207e6f6bc38ba6e1f1de36cb48fef07b2728bb896a21f430/cffi-2.1.1-cp315-cp315t-macosx_10_15_x86_64.whl": "sha256:c26608d2222fb1e94487e4a387d85f13eb55d5ed725cb25a0c589ac4ee60e7bc",
+ "https://files.pythonhosted.org/packages/d3/7b/d6bbf82b8b96e7391438898c42f5bd96dd02030fd5b64937d248220003e2/cffi-2.1.1-cp314-cp314-ios_13_0_arm64_iphoneos.whl": "sha256:7dbb61fe3a7699468030f71bbe5f8a0e326a151daa91beb11a6fc1f980c55e1c",
+ "https://files.pythonhosted.org/packages/d5/6c/bfa0b87b03b9238148beca990292843c9396ba069b54496596594173de7b/cffi-2.1.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl": "sha256:a4f00aa42f75d6e4595e8866e748cc1705adc0cddfeb2ca86d0d03993d63ba03",
+ "https://files.pythonhosted.org/packages/d9/79/615cc094e2fb508cade7de88d3b4f6c4ec2bab695c97bce9153dc65aadf5/cffi-2.1.1-cp312-cp312-win_amd64.whl": "sha256:f53e442b08449d42821fa4a4fba000095af9f62742a500f978a9f557ec44339a",
+ "https://files.pythonhosted.org/packages/d9/99/c4b0c17cacdc9c3b8f280026286a9826d6a208c0f047591a3c3ce99b91fd/cffi-2.1.1-cp314-cp314-macosx_10_15_x86_64.whl": "sha256:d28630f5854ab07ab1fd4aba756de52326c82e6be15d414b12793f1975048b54",
+ "https://files.pythonhosted.org/packages/db/e2/7e8109f65445bdc673a7b54f02c677de462db75674220fd1335efc8eb598/cffi-2.1.1-cp311-cp311-win32.whl": "sha256:f8ec5e643a9a937f64e1999eb9f75d072263751912dc5cd06d3c85f8f44be7c3",
+ "https://files.pythonhosted.org/packages/df/62/14f74b9543e605d17701dc797b815958b8bb70b7624ce1b832ddad48ed6c/cffi-2.1.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl": "sha256:aa9511c62d14da7aacc9b4bf51f3f697a621e83b2d6919008243c3aad168eea3",
+ "https://files.pythonhosted.org/packages/e0/31/5158704cc474ab65c1647932e88be78dc0873f47130e253be38bcaf13d01/cffi-2.1.1-cp315-cp315-win_amd64.whl": "sha256:e6e8cff14d6fb0be70a09c0bdc58096f501952d04624ebf867e0e56da2df8960",
+ "https://files.pythonhosted.org/packages/e0/9f/e8e3dfa04a1b4c241f8c91faacad872b4d4efd051d49764ad4e2fd4b9fea/cffi-2.1.1-cp315-cp315-musllinux_1_2_x86_64.whl": "sha256:cc572dace3f60ef98d7b12ff411d20f5362feb31a0439eab0085bbfd349982d7",
+ "https://files.pythonhosted.org/packages/e2/31/9e1313b0a6e30e91b3b3d3fff51ae99c857c07738e3afcce1f7334e1b7ab/cffi-2.1.1-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.whl": "sha256:507a24c282e0f42f8ed737cf048572cbf580468da5555764a8331735e9c736b6",
+ "https://files.pythonhosted.org/packages/e3/26/2cde30fdde421130bfc18f70395731a6e6b2053c6a1978a5258ff04e72fa/cffi-2.1.1-cp315-cp315t-musllinux_1_2_x86_64.whl": "sha256:7da0c5eff80f0197f3b3d1232ec5a682a9325f4ae9016a78f5f5ca35f9ced1f5",
+ "https://files.pythonhosted.org/packages/e9/02/4e7d553a7ac4b4238b38b3c1b80d486e9d4436f8d2acbf87a0997fe3f402/cffi-2.1.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "sha256:b0431303acaea1089ad4b3e9ce4e6518193def1118d4073ca848635ee4ea2e96",
+ "https://files.pythonhosted.org/packages/eb/d2/3b7176cb570a1d3e27faf67b72f591af508036e0d8b2be2ef9af9e8c84bb/cffi-2.1.1-cp314-cp314t-win32.whl": "sha256:8ef53b2de9bcb9197d31854256575d59dbac0cba72ac627bb291ef5eceb74be4",
+ "https://files.pythonhosted.org/packages/eb/ff/7b3429ff53aafe931ed8a5fc69f481bbef7ba6de87ddcbb63d08f483f613/cffi-2.1.1-cp313-cp313-musllinux_1_2_aarch64.whl": "sha256:a2d7755bef5a12ed488f4ef1f1b69ee9191d7396083b755a5d2295f6edb4768b",
+ "https://files.pythonhosted.org/packages/f7/a4/4399daaf8f7dfee9d7c3327fdb0426ee041cc63edc358b93911ceb2bfc7a/cffi-2.1.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "sha256:34e261f78cb6ceaaa36f42f2613f4380d94d9c759a9c73c769ee6e0247364632",
+ "https://files.pythonhosted.org/packages/f8/7e/8debeb04f1ab9fe2a6963964cd6f1aaf7192627b83926586a6a4e089c9fa/cffi-2.1.1-cp315-cp315-win32.whl": "sha256:4f42141fc14250de6dde5ee7ea4432be017252d91f19c5ad043c084cea629cac",
+ "https://files.pythonhosted.org/packages/fa/01/579d39fb8bef00a335a23d83757b44feb24cd6345a2c451b64cb67b9c362/cffi-2.1.1-cp314-cp314t-musllinux_1_2_aarch64.whl": "sha256:5e7cecbaadb83884793e05828cee59b210b24583b9c7425d0ba6a754fe22eb4e",
+ "https://files.pythonhosted.org/packages/ff/69/844bad3ece306c4782c2ecb93597035b6690d48704b803914c199da1e8b3/cffi-2.1.1-cp315-cp315-ios_13_0_arm64_iphonesimulator.whl": "sha256:da0e573f9f97159390c89d9f1a9e41908b66d408cc5b58d08cf3847d844c531b"
},
"charset-normalizer": {
"https://files.pythonhosted.org/packages/00/5e/17398df3a139985ba9d11ed072531986f408c8fca952835ef1ab1820c02b/charset_normalizer-3.4.9-cp314-cp314t-macosx_10_15_universal2.whl": "sha256:609b3ba8fcc0fb5ab7af00719d0fb6ad0cb518e48e7712d12fd68f1327951198",
@@ -491,52 +491,52 @@
"https://files.pythonhosted.org/packages/f7/40/9593d54209765207a7f11073c06494c1721e4ca4a0a426c597679bf7f91e/charset_normalizer-3.4.9-cp314-cp314t-musllinux_1_2_x86_64.whl": "sha256:ee2f2a527e3c1a6e6411eb4209642e138b544a2d72fe5d0d76daf77b24063534"
},
"cryptography": {
- "https://files.pythonhosted.org/packages/09/41/3797cfaf69cae04a13ee78ebd83f0678d9c02b4779d21ce24445326f1a69/cryptography-49.0.0-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl": "sha256:36d1709f992593689b45bda411498d62c6e365f2ca00b84657d4dadd24de16db",
- "https://files.pythonhosted.org/packages/11/2d/5e1fb307cb5931881516b464c98774b3f2c36b5d4bb9a2830253cf553cad/cryptography-49.0.0-cp314-cp314t-manylinux_2_34_x86_64.whl": "sha256:d8ecde755e2e91bf773fc94e8c9d730cd7f2007004cb492263a794ec3899a1c8",
- "https://files.pythonhosted.org/packages/17/50/983e838c7fd0d87fd8c969bcdd328edaf5f756e38df5281637424c155873/cryptography-49.0.0-cp314-cp314t-manylinux_2_31_armv7l.whl": "sha256:07cab27cc7b7e0fd28e5e26bb9eeedde5c135c868b46de4a27845abe94af6122",
- "https://files.pythonhosted.org/packages/19/2a/5bb823f5bedcf80718cea7fbc95ec5515cca3769633c4b01a32be7f30e7c/cryptography-49.0.0-cp39-abi3-macosx_11_0_arm64.whl": "sha256:ec5e529fb80935c94fe7b729f9972b50e351a0e6b50aa294fd5cabb109fcc29a",
- "https://files.pythonhosted.org/packages/1f/09/f42b1d190c5ba75f72062a387f8030d1d75f6ab035788f1d9c4b01de6525/cryptography-49.0.0-cp311-abi3-win_amd64.whl": "sha256:e5dfc1e64de5677cec922ffa8da89c546d0415bf6efdf081842e5d44c84e1f0e",
- "https://files.pythonhosted.org/packages/1f/99/d1c90d6041656cc6ee229dc99cd67fd0cd5aec3c5f7d72fffc27cc750054/cryptography-49.0.0.tar.gz": "sha256:f89660a348f4f78a92366240a61404e337586ef7f5909a2fef59ca88ef505493",
- "https://files.pythonhosted.org/packages/20/2c/0622f20ff02b2ef32558733443805dc82fd4c275be01b2d19d14676f3a1b/cryptography-49.0.0-cp311-abi3-manylinux_2_28_x86_64.whl": "sha256:2afe9051da7ae7bd5905da5a949280c7d2bb75682e188f650a9d0f2756b834c6",
- "https://files.pythonhosted.org/packages/24/01/186c825898477d77e2324d5360fefe622ff1d8d1963ec0554e2cada8ec77/cryptography-49.0.0-cp39-abi3-manylinux_2_28_ppc64le.whl": "sha256:9e82dcc8e56052715fb18b2429e3bca4823b1629136a2084fc45a9a5cecb9b64",
- "https://files.pythonhosted.org/packages/2c/99/2d13299eb3dd27b02dcfaafcc91d6b5cb3329f7cbd6d8f51921acd566c1a/cryptography-49.0.0-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "sha256:35b151772baff2c74cba7fa290ceaff4c3b11c0c881eb93eb5dbc05a7cfbba18",
- "https://files.pythonhosted.org/packages/3d/df/40577043ca124e17012f408ddddaeb213b856336ac82ddb3bc915f39e29f/cryptography-49.0.0-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl": "sha256:f78ff2c9ed8dc2d036b0f4d640e22522213d047c1b14e61205a7e55c80a494d4",
- "https://files.pythonhosted.org/packages/47/f1/1d3eaa243bfc5de4a187b22aa8c048b3e4980bfbe830ac46e6bac2e66947/cryptography-49.0.0-cp314-cp314t-manylinux_2_28_ppc64le.whl": "sha256:f37d847238971164fdbc68ade6f6574aecc9c0af714190e2083429ff68f4ce9d",
- "https://files.pythonhosted.org/packages/4a/91/01ce7303a4579e6d3a6abef01bd322848e9ea7a219adcabc5048b9033571/cryptography-49.0.0-cp311-abi3-manylinux_2_28_aarch64.whl": "sha256:53ecee2e23f7169b6117e99fc8a944e5e50f79e69758a83b52a00cb98ab2b2d2",
- "https://files.pythonhosted.org/packages/4c/fe/93ecac273d3738939d023612ad12cca9a3740a5345d69fda04134c43fd96/cryptography-49.0.0-cp314-cp314t-win_amd64.whl": "sha256:33cd0565932807baddb67b96dbee92f2c374b5c89dee09fd74079aeb8c8dba61",
- "https://files.pythonhosted.org/packages/4f/01/339573cf1023163a400b0b5d16f6d507de413b9f60be6fd1b77feeaf6737/cryptography-49.0.0-cp311-abi3-musllinux_1_2_aarch64.whl": "sha256:b87e65d263b3e5d3bb92a57e2a6638e2f31110fa7aa890c7b2dbba42248d0a3f",
- "https://files.pythonhosted.org/packages/58/39/2d51306721330c486495853eda1c567880ff036de15a14c4b74f399934af/cryptography-49.0.0-cp314-cp314t-manylinux_2_28_x86_64.whl": "sha256:c2bc30226390d60ea19d9f82b19db005fe0452154a23c1c410c12ea801e43561",
- "https://files.pythonhosted.org/packages/62/99/a2c95cf8293f07491e9e27c20cc4dcd18176d944e674679adeb1d0173fd6/cryptography-49.0.0-cp311-abi3-manylinux_2_28_ppc64le.whl": "sha256:2eda353d8a27bcbcaa4cbed18994a74ab4d19a2ca897db188ea269ab9b71419b",
- "https://files.pythonhosted.org/packages/63/d3/4a83af35d65e3fad632c926fad684c193ea4398569ccb0bbbc7fe8f5dc9a/cryptography-49.0.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl": "sha256:fc1e275c2f1d97b1a6450b8b0ea3ebfa6e087a611c2b26cb2404d48588abab7b",
- "https://files.pythonhosted.org/packages/67/d0/a5fcd3515f0bae49a7b6d0413cc1bdccdcc1fc0047037a0d480642cdc5d6/cryptography-49.0.0-cp39-abi3-manylinux_2_34_ppc64le.whl": "sha256:6fc361c34fb6aac015ce19435876635e5c6d21db31998b0920f675f131e043b8",
- "https://files.pythonhosted.org/packages/68/28/8a3ad4653662c93fc44dc4e5d8fd374c25c42e07b34bbfbadf49cf57a5a8/cryptography-49.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "sha256:7abcee80084cda3f7691f3eb1ce480d8df49cec637b429aa35986c1de71738aa",
- "https://files.pythonhosted.org/packages/6c/72/3e798c064bc39e471008075d0f9bc9daf77a80879c092e4a8e170c585ed4/cryptography-49.0.0-cp39-abi3-manylinux_2_31_armv7l.whl": "sha256:8c25ceb16df5b9435f3f6a9829204985b0e0cbee3b48aacd432c7d2c850b44d9",
- "https://files.pythonhosted.org/packages/6c/a0/db537264e234f7273a73ec020873d6d6b39dfd8a53db78b550ca8320440e/cryptography-49.0.0-cp39-abi3-musllinux_1_2_aarch64.whl": "sha256:67e1d20ad9ef3a563c59ef22e7a8a0b8210bd26604369ea4a30a7c66aefe504e",
- "https://files.pythonhosted.org/packages/6d/88/05563c7fe2e914e87d1a536d06fe83e66b4e1d95cb593e05aea375531da8/cryptography-49.0.0-cp311-abi3-manylinux_2_34_aarch64.whl": "sha256:ccac2bfebc306b862133e3bb71f3f6ee8bb525240089b2d952e4144b3a6d5da7",
- "https://files.pythonhosted.org/packages/71/fd/577302e213a1be9468f92d1afef66fcf1ef83d516819d9992ca547f592bd/cryptography-49.0.0-cp311-abi3-musllinux_1_2_x86_64.whl": "sha256:66ec79c3904820572d7e987abdf304281f141d37ad9a489b8e97066e7b9b6459",
- "https://files.pythonhosted.org/packages/86/12/c48a424f38db03027be9f7ed5c7dc5de9933dbee992865f98b13727a009d/cryptography-49.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl": "sha256:196ecd6a36e4e9aa10270393bb98d8df88fccee0bf1e5128b91ae4eb4375896d",
- "https://files.pythonhosted.org/packages/93/77/8df9eb486495979bccecd1062e2eaf435250e84437040295b57d09048b0b/cryptography-49.0.0-cp39-abi3-musllinux_1_2_x86_64.whl": "sha256:42b0684e0e40cf26122427802486f6d93aea593612603a94fbf260c7eb1e9c1b",
- "https://files.pythonhosted.org/packages/94/64/2923570ac1c0bd3a737aa366ac3abbbbde273042308b8cde95e2364a6e6a/cryptography-49.0.0-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl": "sha256:b47db11c2c3525083296069b98ac5221907455e989ae0c2e3008bde851921615",
- "https://files.pythonhosted.org/packages/9b/22/adf66990e63584a68dfb50c24f48a125c07b1699899381c8151e63ed458c/cryptography-49.0.0-cp311-abi3-macosx_11_0_arm64.whl": "sha256:966fe0e9c67490071f14c0d2b1cb2dfb3023c5ce39457343931415f08382f2db",
- "https://files.pythonhosted.org/packages/a0/84/84fe36f19caf857d61cb7fc9c63035a47ffabd84ea12d1d393148efa3615/cryptography-49.0.0-cp39-abi3-manylinux_2_34_x86_64.whl": "sha256:2400ef9c9e2299a25614eb1dea3db54a69b1349efd043bfac9c67630d136df36",
- "https://files.pythonhosted.org/packages/a3/5b/c5246635d5fd3b64e0d45ae10e99fd32fe9676a79915ccfe5a61ba9af1a5/cryptography-49.0.0-cp311-abi3-manylinux_2_31_armv7l.whl": "sha256:0b82e28ee398a386f0807bba7884d30f25218855690f45115831bcce5d90822c",
- "https://files.pythonhosted.org/packages/a5/4d/9c0cd02f95e2602dd5e563da149ee0830abef3537be8b34dc56281ebe27a/cryptography-49.0.0-cp39-abi3-manylinux_2_28_aarch64.whl": "sha256:0f21641cf4b30fca7aee061ced0ec7ad7b073518088b7c9969a297c0ae796c69",
- "https://files.pythonhosted.org/packages/a7/f5/8f571d7e27c55bce9f76f026143bcb1e040a4233149ecca0bea5fa5dd5f7/cryptography-49.0.0-cp314-cp314t-manylinux_2_34_aarch64.whl": "sha256:b20133d204d2bb56ba047642199603876c872026ca53e79c35b83772ab2cc505",
- "https://files.pythonhosted.org/packages/a8/b2/2193fc74f81aee4f9b62733133b73b5176718932ed8f2e4b03fa040480a6/cryptography-49.0.0-cp314-cp314t-manylinux_2_28_aarch64.whl": "sha256:4ae387c9cb68ea569ca17e490d66d8142b81c3cc814bf179974b7d146e490bbb",
- "https://files.pythonhosted.org/packages/a9/3c/f3ad17eecc1a57b0ba236dc01f90e783c51f4a2f35f64777cc4f47a184b2/cryptography-49.0.0-cp311-abi3-manylinux_2_34_x86_64.whl": "sha256:cbc77da8c523d5abd028635ba850a6966fcee2c82e2bf65a41d1d8afe0f98be9",
- "https://files.pythonhosted.org/packages/aa/50/a9caea39ad19c431c1a3f8a31114df65b260cdfe67786b6c7e7c040c4c44/cryptography-49.0.0-pp311-pypy311_pp73-win_amd64.whl": "sha256:be9fcb48a55f023493482827d4f459bd263cc20efde64f204b97c123201850c6",
- "https://files.pythonhosted.org/packages/ab/f8/614dc7e051418cfe53d55173c1e24c6b0085e89996fe90508c2fdf769aef/cryptography-49.0.0-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl": "sha256:084ef1af862eb07ec46d25f68689f2102a9fc0e05ce7b80f14f5fe51e4eef0f6",
- "https://files.pythonhosted.org/packages/b8/7b/62cbbab75d0659865bf0273790031544a0b16c8072d258f9428dcd8190dc/cryptography-49.0.0-cp39-abi3-manylinux_2_28_x86_64.whl": "sha256:6f2debedf9ca60cf1d5bd466475638af5130f89965605cd818484d19987d3a21",
- "https://files.pythonhosted.org/packages/b9/26/814681d14248d95d73d5c3eea0c39a94eb8302df966f670a2c60de90974b/cryptography-49.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl": "sha256:32703d93296f5c1f4b53349ad3a250c2cae0fdecd3a3dd5d47e616d8d616af27",
- "https://files.pythonhosted.org/packages/c2/e6/f60198ea8d9dfa15fff9ed4ca02ce362f6eadd9ba757dcc50634c4257b63/cryptography-49.0.0-cp39-abi3-win_amd64.whl": "sha256:026ac7423e6fa66872d3bf889be5974507da3944f866f704fa200eadacd00001",
- "https://files.pythonhosted.org/packages/c4/b6/d7696e4e890d6ae1469935164c9e5215c557671cb78d6e3f458ccceaa632/cryptography-49.0.0-cp311-abi3-manylinux_2_34_ppc64le.whl": "sha256:d0527ce944105f257f605a827d6ebead966c752038b6e8656abb9c5edee6fc68",
- "https://files.pythonhosted.org/packages/d6/a7/f9dac0ab7f80368c56993a7bf638ef9935f825c91902798481fac0898138/cryptography-49.0.0-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl": "sha256:c83782480a4a9da4d0feb51950131ba32e12e70813848b3343f6e18c28a66838",
- "https://files.pythonhosted.org/packages/d7/70/2ba3769dd0ae167e2f33dfa9592d45db6ff9a61d62ca1a5b3d1bdd09068f/cryptography-49.0.0-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl": "sha256:b39efa323140595abd3ecca8529d321ae50f55f3aa3ba9cc81ea56a6011953d5",
- "https://files.pythonhosted.org/packages/e4/c0/bff5a02ee731d207d6a1ed51732549d8c53d2bc8da1d10ec6f2844201d68/cryptography-49.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl": "sha256:e3fb64c420688e5319ae25113a354015abbd8dffbfbc41781a1ea66fc7622ac3",
- "https://files.pythonhosted.org/packages/e6/8b/43011f7ebe515a8aa20d61f290a326cd890c2e738e16e59eaff8d9c3a412/cryptography-49.0.0-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "sha256:0e959b578856a3924bc0cbb710fc12c387b9412a951389f3ca61704a9e25f325",
- "https://files.pythonhosted.org/packages/e7/84/0e27016a6fc5a0886f797018b26aa42f40c09a82332bff77822a451deaaa/cryptography-49.0.0-cp314-cp314t-manylinux_2_34_ppc64le.whl": "sha256:b970c6da94d5bb18629db453d14f2a1300f6bf59b61e9b82377931ef95504866",
- "https://files.pythonhosted.org/packages/ec/9e/db72b3ae7fc9cfad53e630e56c6ae83b9b6ff0bf3718ffb8012d20b3aabf/cryptography-49.0.0-cp314-cp314t-macosx_11_0_arm64.whl": "sha256:73a205dce83953d131a4aa1e0fd917a2fd1c5b1eef251e9d7152efefcbf5caf7",
- "https://files.pythonhosted.org/packages/f0/ee/6fca21d1ac73e06f8bef71940abfd4d2f6472b4bca284d770f32bd4086f6/cryptography-49.0.0-cp39-abi3-manylinux_2_34_aarch64.whl": "sha256:28d8b15e6275f12c8a207dc309dfa957903c927d08d0cc937ee3f63f200693cc"
+ "https://files.pythonhosted.org/packages/01/77/2cf79bbfc4d12ca106437a6e170d6aaa01a373e93093118aaaef0e801bd4/cryptography-50.0.0-cp314-cp314t-manylinux_2_34_ppc64le.whl": "sha256:9aa87839c383bdbab6ef865787a1fb877af8dd03464c4400322726feaaadfc6d",
+ "https://files.pythonhosted.org/packages/01/b6/0b9e125e90f3d2dcf599a218a899cda7326a3158cfa258723f0b398b08f6/cryptography-50.0.0-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl": "sha256:8eb5e1172eb569ea8a872796576e6a67c276351728b6455d5beb01242b027c6a",
+ "https://files.pythonhosted.org/packages/03/37/73d005be173aff344af30e9fd2a576575cb2391a7101d9cd3842e1fa8cce/cryptography-50.0.0-cp39-abi3-macosx_11_0_arm64.whl": "sha256:ccdc4a71a4dabae05de219404f9f4abc38e3b58422177ff93d0da05967dafa07",
+ "https://files.pythonhosted.org/packages/06/1e/63a1027cb7fec360a182208e1b7767d5aa1fe57be3d6aa856e69a321edc0/cryptography-50.0.0-cp311-abi3-manylinux_2_28_ppc64le.whl": "sha256:8d89f3976b10b4ce31118de72329025f70d2c6ead14a8217c5514dd2c6d5a78f",
+ "https://files.pythonhosted.org/packages/15/37/36a9c479bbe49acea2636c7fd3360d20f7b7e079c300352011c44850b181/cryptography-50.0.0-cp311-abi3-manylinux_2_31_armv7l.whl": "sha256:900131fafd8aead39ac7dd3a7e833be754c17a95cfd91221636949fe4eb0aa8a",
+ "https://files.pythonhosted.org/packages/1a/28/b555a365adff1cca2fbe7b9e487d68a40de6bc67ff2cb587473eb43de0e7/cryptography-50.0.0-cp314-cp314t-manylinux_2_28_aarch64.whl": "sha256:d58c3db7cd6eed54e6c06744db55456b65ebd7492ddeae9c1e93cfca7aa857d3",
+ "https://files.pythonhosted.org/packages/1d/dd/7c77d26285cc7f6991efce64a0f5b4f9383bfa5dd8c5033003eaf7db4cdb/cryptography-50.0.0-cp39-abi3-manylinux_2_28_ppc64le.whl": "sha256:d764dcf130c428ef66786f866dd750f53182bc608813489915e9fc106bb0c82f",
+ "https://files.pythonhosted.org/packages/22/f6/ec13b470172126464a86bf54d2294a46d29837fc51ba3e45d4047946fb5e/cryptography-50.0.0-cp311-abi3-manylinux_2_34_ppc64le.whl": "sha256:f89831ef99dd7dd169ab06d63a831adb9e20a87aac6d380266bbda5823349169",
+ "https://files.pythonhosted.org/packages/27/20/c930314a2ab476d15dec966ec87e2e9637bb02b06106b12c0396c57bb603/cryptography-50.0.0-cp311-abi3-musllinux_1_2_x86_64.whl": "sha256:7cec5b856506da6defb290f30c9ee687d5f5e8cb0bd3f6459dde43b0b4fa40ef",
+ "https://files.pythonhosted.org/packages/30/82/85f0f7425c856b9f96459411eb12e74ef72df9caf6f8f15bf23a33ff131f/cryptography-50.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "sha256:80b63928fa35083b33966ce1efb70e5b9607181e49dcd1c22c8c005e319f667f",
+ "https://files.pythonhosted.org/packages/32/2e/c9db68a0c4bfa28e310707527c0ee3a2bd254104d2e02e68f368e197aa4c/cryptography-50.0.0-cp311-abi3-win_amd64.whl": "sha256:bd1c592e4d5974f0d08d4888e432157adba757c66da0246918e43677fafa2d30",
+ "https://files.pythonhosted.org/packages/32/98/8a151d64367204cbc63ec65d37502f1d9c53cf4bfc6ec3c532614dbec60d/cryptography-50.0.0-cp311-abi3-manylinux_2_34_aarch64.whl": "sha256:07949c449a1abcf60d1ee6e88956d89404c7df3c8258f46589e912988e551987",
+ "https://files.pythonhosted.org/packages/38/14/6120e5bd7c5aa022ad15424ba4d5c5269d0d9448ed4d55e492ea91e3c1c4/cryptography-50.0.0-cp314-cp314t-manylinux_2_28_x86_64.whl": "sha256:11b74db56cdbe3cdee6e3f6982ecb70334fa10dce99ed58bf7894aaaa3b2a037",
+ "https://files.pythonhosted.org/packages/3a/63/504ccfbbe61fd8aa983f7f146399cdf034c72c2fc55f5b2dfdcdcdb20c99/cryptography-50.0.0-cp314-cp314t-manylinux_2_34_aarch64.whl": "sha256:ecfed7367f965a0328cfbdd70da860f15441f002f613185668c6e6ebf5a0ac11",
+ "https://files.pythonhosted.org/packages/46/c9/f60aed34c013f317f92817b6c171c2d22a78270fa41109bd4b08af26b194/cryptography-50.0.0-cp39-abi3-manylinux_2_28_x86_64.whl": "sha256:105110f43a471dbd0060b9c9516cb8a6a79233631a04cc2ba16f28323ac6e025",
+ "https://files.pythonhosted.org/packages/53/c9/a5151588710785a96d7bc4de27d4cd62f263bbbcb203cfe29df537eb6505/cryptography-50.0.0-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl": "sha256:910d11e1a385c654bf738bf3e6b8e6ed5de0f5610fcae2be9e5b398d8081d20e",
+ "https://files.pythonhosted.org/packages/57/30/4a22984d4f1bdfb8c054f07a92bc176b97a3134cc1d6c4b3bffb1f3688b4/cryptography-50.0.0-cp39-abi3-win_amd64.whl": "sha256:d24fead1d4d076e1bfb006dcec392074a3cd8d7b4fc8a595aa64073b2b7a96ba",
+ "https://files.pythonhosted.org/packages/57/ef/8f2df13c7216bcad3e1c74e07f6e193d93e998e114f524a53877c9af27ad/cryptography-50.0.0-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl": "sha256:fd9192b7b70c573d7f214eb1ae35e00d359f6f5e4b27c7e21e30de1fc6204645",
+ "https://files.pythonhosted.org/packages/62/15/219075012ab13e8905f3cd572204f4acb4b111df787104346b9bc0cea789/cryptography-50.0.0-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl": "sha256:07479a1cb08219ab719147e742e76090c9c773321959bb94946fffdd397a6437",
+ "https://files.pythonhosted.org/packages/64/a2/4615c8f7d81a00b1d6e6afe19f694e1543582349fb5f4076f6cb5dc36485/cryptography-50.0.0-cp39-abi3-musllinux_1_2_aarch64.whl": "sha256:c87f62a3d3b9888ed0fdde100ec06aa61ca9cd44bad9057d1dff9a516b5f5bb9",
+ "https://files.pythonhosted.org/packages/6b/16/d3008eff98c764979865834c3d386d4fd041b5f52e7f34fc29ac1a5eb515/cryptography-50.0.0-cp39-abi3-manylinux_2_34_ppc64le.whl": "sha256:6e7d61120573a7f2cd94cc095f9e81f6967c61ccdf194285aa143ecec8e0b708",
+ "https://files.pythonhosted.org/packages/6b/72/a1116d683a6d7ece94590013882515de087edf9ef0e6292aae615a44df73/cryptography-50.0.0-cp311-abi3-manylinux_2_28_x86_64.whl": "sha256:b42a28c1844fd9de8f3f7d540e36b66f3a9c83fceac7170ebc7a6a19edd9dcae",
+ "https://files.pythonhosted.org/packages/72/d8/f52538140cc719df62a01cf87d1c7142318d235817109d6f4054d7c352d6/cryptography-50.0.0-cp314-cp314t-manylinux_2_28_ppc64le.whl": "sha256:df2a58a472f332225671c35b0a830208b86d004f82baa8530fa3782c85646533",
+ "https://files.pythonhosted.org/packages/74/9a/02ffe35b2853d121689871eb5dce862092562b3a1ed5cc98f1aaed441506/cryptography-50.0.0-cp314-cp314t-win_amd64.whl": "sha256:12b9c6996425c76ea6c457ace4f3073e715b8c545add07cd1a8f3a4f90691269",
+ "https://files.pythonhosted.org/packages/7b/20/4fe50b619a48c2525cc46e2dbc1ac490708d704be5d467bdaac6dc955682/cryptography-50.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl": "sha256:3f5735ffe4996d28b809371756219f5354864902a3b9e7c0b9ee87041209fc9c",
+ "https://files.pythonhosted.org/packages/7d/ff/b6ce0954962e7f7b969f850a883744197bb3910bdfd7b6da162eab7d9f68/cryptography-50.0.0-cp311-abi3-manylinux_2_28_aarch64.whl": "sha256:a1b30560f2acc95aa8b2e06e716a13dbfc97314747b80d9707e307f77b40d6b3",
+ "https://files.pythonhosted.org/packages/85/4f/0fa8c2f4428198f15d9ff8d63400e27afbf94ce833f6108da1eb3753f945/cryptography-50.0.0-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "sha256:a91296cb61e8df6f86d0c19cc4068228da256bf59bf86049fbd821084565327f",
+ "https://files.pythonhosted.org/packages/8e/b5/c2c5fce26f0ee40d21bafe7f191d29a34b35a65ac4fe8a1191d1983612e9/cryptography-50.0.0-pp311-pypy311_pp73-win_amd64.whl": "sha256:c99c003e088647b8a5b7c145d6f78c335f6348332b62e142d411c4b63d1460b9",
+ "https://files.pythonhosted.org/packages/92/91/3a31366e183343d3703f8995c095f5734676bd6938118047e50fcf279eb4/cryptography-50.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl": "sha256:1b4a266766514614f8aa60416e71f2fc6e575d36e7bdc90f644fadb2f4b75b95",
+ "https://files.pythonhosted.org/packages/9c/f8/d97f9603efda3888187bfdb893f26c41be4735c10631d05d284ee6b047c4/cryptography-50.0.0-cp39-abi3-manylinux_2_34_x86_64.whl": "sha256:37fdb0d0111f1e2ff07139dfb79f1b49531f8e213c46f1163dd7642979b58c47",
+ "https://files.pythonhosted.org/packages/9d/3e/e54cde8c01631a5a8226ccd617eab9e57fd5cfdad90f1a9e6bb570794631/cryptography-50.0.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl": "sha256:5e34edd123674534acd70147f0ca331eaa2c74e6325fb2028c886aa26ba0b68c",
+ "https://files.pythonhosted.org/packages/be/f3/f9a0173b139372c3a48ed98154b45cc6b9de17c789d5ab552e621c293609/cryptography-50.0.0-cp39-abi3-manylinux_2_31_armv7l.whl": "sha256:828743d939e9629bc267b8e2d08d8bb67cd4319c771a33d4b18b22dd8fb7440a",
+ "https://files.pythonhosted.org/packages/c3/fb/951032a3bf22a5697c83183fb6294a4843772947a70e616c57b3ff5f522e/cryptography-50.0.0-cp314-cp314t-macosx_11_0_arm64.whl": "sha256:49e7d93abdbd2990caced757e5fade25302f719c3c8fb6e6fff2dde98999fc41",
+ "https://files.pythonhosted.org/packages/c5/5c/59086b4aac5e879d38ddbcf74e4be7ade89cebc3eb199a55da998c3bb46a/cryptography-50.0.0-cp311-abi3-macosx_11_0_arm64.whl": "sha256:031e2d5dd4bb9caa3ca9c82e5a197fd8ae680232cee62603d1a813f3f07e3d03",
+ "https://files.pythonhosted.org/packages/c7/1a/15b92b25eb6ce3089cd49377ae990a0f3ad485a510f968aed1f19dbdcdf2/cryptography-50.0.0-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl": "sha256:62598a8a57f815db4c6259a4e97d857dab56697e7de8e8ab02352ab74da1995d",
+ "https://files.pythonhosted.org/packages/ca/dc/bd72b26be8953f80625f63151efd38eee71c76ca6cf591c08ff34615a79e/cryptography-50.0.0-cp311-abi3-musllinux_1_2_aarch64.whl": "sha256:1489e263a8048bb8b6a8bac662eb2d402ea5d2b7b4699b72f385f1e2772db105",
+ "https://files.pythonhosted.org/packages/d1/63/54dd723490ba2dc09b299682c10b38db38f159728bcaae8c591b8af2f22d/cryptography-50.0.0-cp39-abi3-manylinux_2_28_aarch64.whl": "sha256:e722f16708d854fe924790e051061f6704a472c3bac347b6fd88033ea8dd0dc5",
+ "https://files.pythonhosted.org/packages/d2/1a/efcfb02f91407149a0dacffffab791f7e19bf6385f63b3666dc8b5e5c9c8/cryptography-50.0.0-cp39-abi3-musllinux_1_2_x86_64.whl": "sha256:65c2c3add92b45fd0709db8594536aea39c2a67af0e27ffcf049c498501140b7",
+ "https://files.pythonhosted.org/packages/d4/67/91eb047e69c5e845f2f14b8a2e4a1aab0f283cb885531e9e22c8adb176bc/cryptography-50.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl": "sha256:19736989797678c6af1e55cd49055cdbcb55d8f6b5583ac5335f933aba9101dc",
+ "https://files.pythonhosted.org/packages/d8/36/83bb81f6e569bc38e1e4a7bc80f29b46bb9601920bc455fc8e888f5d5742/cryptography-50.0.0-cp39-abi3-manylinux_2_34_aarch64.whl": "sha256:2a8183b489dc1f7f80f135780fadc1108f14b31b8a40411c7a5b17425f65f28b",
+ "https://files.pythonhosted.org/packages/d9/41/029086c34d91052fc3b88bcc8056f709a7c915c7a23b235a54eb800b1c97/cryptography-50.0.0-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "sha256:06a32a980526a6ab9a4b9bf8f7385800791e2bb960903cb6b530e4817509a3b7",
+ "https://files.pythonhosted.org/packages/da/3a/f05e32c99d440c9bb891ea0e36c9091891e36be5a9a87ab2ee6ea20729f6/cryptography-50.0.0-cp311-abi3-manylinux_2_34_x86_64.whl": "sha256:82148ec5bddac30b51a5b3c1945075f896fa022cb93f8e4a01e9f6ee95292c5f",
+ "https://files.pythonhosted.org/packages/de/41/6cbdcf9142d00fe82836fbb51e503e58088575cf7a0fe1dbff6695bf0840/cryptography-50.0.0.tar.gz": "sha256:eeac2acb5a20ed25e0ad6d1df9891a520b78b404266b6d11778f25d5d691a6c9",
+ "https://files.pythonhosted.org/packages/e5/45/8aae2972c520145377ea3559a605a899bebe227bf070b33cdb445929a9b9/cryptography-50.0.0-cp314-cp314t-manylinux_2_34_x86_64.whl": "sha256:6ba6a53445bd3cfa809ef3ef5f1589aa6ba08784a1d962bf47d0940e871dab1c",
+ "https://files.pythonhosted.org/packages/fa/71/190bf38c3ee2e0f8efc9860ae100c9df4169742eef274b91e7aa1cb133b9/cryptography-50.0.0-cp314-cp314t-manylinux_2_31_armv7l.whl": "sha256:f59e38625469987d7ef6d495323c55e7db6c212eaf6112267e0d3b565a2e9c9f",
+ "https://files.pythonhosted.org/packages/ff/c6/7a6202a534e32103a285b7834a120869557fe198d51d7cfe59754c8bda9c/cryptography-50.0.0-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl": "sha256:910e1d2668e7de9648f2bcee30e180db2a6b15c30f887d7c4c93ddf96e3992e3"
},
"docutils": {
"https://files.pythonhosted.org/packages/32/91/30151a39f7570f448ed84529390628a651d7f27c87d73c9b887f8189695e/docutils-0.23-py3-none-any.whl": "sha256:25d013af9bf23bc1c7b2b093dff4208166c53a94786c9e447808335ef1185fea",
@@ -616,8 +616,8 @@
"https://files.pythonhosted.org/packages/f3/ab/a7653bce9a3b204be6a6931767a9e23595807bb84790ce6685e4d7e5bd08/nh3-0.3.6-cp38-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl": "sha256:a43ebd7543555c3ac1bc353023d0794e75cb76f6f18f19c32e95441496c0cc25"
},
"packaging": {
- "https://files.pythonhosted.org/packages/d7/f1/e7a6dd94a8d4a5626c03e4e99c87f241ba9e350cd9e6d75123f992427270/packaging-26.2.tar.gz": "sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661",
- "https://files.pythonhosted.org/packages/df/b2/87e62e8c3e2f4b32e5fe99e0b86d576da1312593b39f47d8ceef365e95ed/packaging-26.2-py3-none-any.whl": "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e"
+ "https://files.pythonhosted.org/packages/63/34/ba1c580383c9eada3711951fef0795c80b829a078d72188184bcab9dd527/packaging-26.3-py3-none-any.whl": "sha256:d7193f7c8e4e93f444fde0262bf90af30e16fa0ad0ad44cb553c87339b23cd1c",
+ "https://files.pythonhosted.org/packages/7d/fa/3944b40b07da9ce895c0e6303a5ab7d53da063554f534556b134a54d6093/packaging-26.3.tar.gz": "sha256:94edc256424af38762eb31306eed28beb9f0efc50a8837492c9d6fd6004aed79"
},
"pycparser": {
"https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl": "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992",
diff --git a/tests/integration/whl_library/BUILD.bazel b/tests/integration/whl_library/BUILD.bazel
index 0c26fee..a53bcc7 100644
--- a/tests/integration/whl_library/BUILD.bazel
+++ b/tests/integration/whl_library/BUILD.bazel
@@ -37,8 +37,8 @@
# Extract all deps
genquery(
name = "whl_deps_target_deps",
- expression = "deps(@whl_deps_library//:pkg)",
- scope = ["@whl_deps_library//:pkg"],
+ expression = "deps(@whl_deps_repo//:pkg)",
+ scope = ["@whl_deps_repo//:pkg"],
)
py_test(
@@ -51,13 +51,13 @@
"@pip_sdist_archive//:srcs",
"@whl_archive//:srcs",
"@whl_archive//:whl",
- "@whl_deps_library//:whl",
+ "@whl_deps_repo//:whl",
],
env = {
"SDIST_SRC_FILES": "$(locations @pip_sdist_archive//:srcs)",
"SRC_FILES": "$(locations @pip_archive//:srcs)",
"WHL_DEPS": "$(location :whl_target_deps)",
- "WHL_DEPS_LOCATION": "$(location @whl_deps_library//:whl)",
+ "WHL_DEPS_LOCATION": "$(location @whl_deps_repo//:whl)",
"WHL_FILES": "$(locations @whl_archive//:srcs)",
"WHL_LOCATION": "$(location @whl_archive//:whl)",
"WHL_TARGET_DEPS": "$(location :whl_deps_target_deps)",
diff --git a/tests/integration/whl_library/MODULE.bazel b/tests/integration/whl_library/MODULE.bazel
index 39a579f..d3886d7 100644
--- a/tests/integration/whl_library/MODULE.bazel
+++ b/tests/integration/whl_library/MODULE.bazel
@@ -13,10 +13,13 @@
)
use_repo(python, pbs_host = "python_3_14_host")
-pip_archive = use_repo_rule("@rules_python//python/private/pypi:whl_library.bzl", "pip_archive")
+pip_archive = use_repo_rule("@rules_python//python/private/pypi:pip_archive.bzl", "pip_archive")
+
+DEP_TEMPLATE = "@integration_test//:{name}_{target}"
pip_archive(
name = "pip_sdist_archive",
+ dep_template = DEP_TEMPLATE,
filename = "requests-2.34.2.tar.gz",
python_interpreter_target = "@pbs_host//:python",
requirement = "requests",
@@ -29,15 +32,16 @@
pip_archive(
name = "pip_archive",
+ dep_template = DEP_TEMPLATE,
python_interpreter_target = "@pbs_host//:python",
requirement = "requests",
)
-whl_archive = use_repo_rule("@rules_python//python/private/pypi:whl_library.bzl", "whl_archive")
+whl_archive = use_repo_rule("@rules_python//python/private/pypi:whl_archive.bzl", "whl_archive")
whl_archive(
name = "whl_archive",
- dep_template = "@integration_test//:{name}_{target}",
+ dep_template = DEP_TEMPLATE,
filename = "requests-2.34.2-py3-none-any.whl",
# https://pypi.org/project/requests/#requests-2.34.2-py3-none-any.whl
requirement = "requests",
@@ -47,11 +51,11 @@
],
)
-whl_deps_library = use_repo_rule("@rules_python//python/private/pypi:whl_library.bzl", "whl_deps_library")
+whl_deps_repo = use_repo_rule("@rules_python//python/private/pypi:whl_deps_repo.bzl", "whl_deps_repo")
-whl_deps_library(
- name = "whl_deps_library",
- dep_template = "@integration_test//:{name}_{target}",
+whl_deps_repo(
+ name = "whl_deps_repo",
+ dep_template = DEP_TEMPLATE,
metadata_file = "@whl_archive//:metadata.json",
requirement = "requests",
)
diff --git a/tests/integration/whl_library/test_contents.py b/tests/integration/whl_library/test_contents.py
index ab352a5..6db7245 100644
--- a/tests/integration/whl_library/test_contents.py
+++ b/tests/integration/whl_library/test_contents.py
@@ -120,7 +120,7 @@
def test_whl_deps_ar_the_same(self):
for var, main_dep in {
"WHL_DEPS": "@whl_archive//:pkg",
- "WHL_TARGET_DEPS": "@whl_deps_library//:pkg",
+ "WHL_TARGET_DEPS": "@whl_deps_repo//:pkg",
}.items():
self.assertEqual(
{