···11+# this module only has to exist because cpython has a global interpreter lock
22+# and markdown-it is pure python code. ideally we'd just use thread pools, but
33+# the GIL prohibits this.
44+55+import multiprocessing
66+77+from typing import Any, Callable, ClassVar, Iterable, Optional, TypeVar
88+99+R = TypeVar('R')
1010+S = TypeVar('S')
1111+T = TypeVar('T')
1212+A = TypeVar('A')
1313+1414+pool_processes: Optional[int] = None
1515+1616+# this thing is impossible to type because there's so much global state involved.
1717+# wrapping in a class to get access to Generic[] parameters is not sufficient
1818+# because mypy is too weak, and unnecessarily obscures how much global state is
1919+# needed in each worker to make this whole brouhaha work.
2020+_map_worker_fn: Any = None
2121+_map_worker_state_fn: Any = None
2222+_map_worker_state_arg: Any = None
2323+2424+def _map_worker_init(*args: Any) -> None:
2525+ global _map_worker_fn, _map_worker_state_fn, _map_worker_state_arg
2626+ (_map_worker_fn, _map_worker_state_fn, _map_worker_state_arg) = args
2727+2828+# NOTE: the state argument is never passed by any caller, we only use it as a localized
2929+# cache for the created state in lieu of another global. it is effectively a global though.
3030+def _map_worker_step(arg: Any, state: Any = []) -> Any:
3131+ global _map_worker_fn, _map_worker_state_fn, _map_worker_state_arg
3232+ # if a Pool initializer throws it'll just be retried, leading to endless loops.
3333+ # doing the proper initialization only on first use avoids this.
3434+ if not state:
3535+ state.append(_map_worker_state_fn(_map_worker_state_arg))
3636+ return _map_worker_fn(state[0], arg)
3737+3838+def map(fn: Callable[[S, T], R], d: Iterable[T], chunk_size: int,
3939+ state_fn: Callable[[A], S], state_arg: A) -> list[R]:
4040+ """
4141+ `[ fn(state, i) for i in d ]` where `state = state_fn(state_arg)`, but using multiprocessing
4242+ if `pool_processes` is not `None`. when using multiprocessing is used the state function will
4343+ be run once in ever worker process and `multiprocessing.Pool.imap` will be used.
4444+4545+ **NOTE:** neither `state_fn` nor `fn` are allowed to mutate global state! doing so will cause
4646+ discrepancies if `pool_processes` is not None, since each worker will have its own copy.
4747+4848+ **NOTE**: all data types that potentially cross a process boundary (so, all of them) must be
4949+ pickle-able. this excludes lambdas, bound functions, local functions, and a number of other
5050+ types depending on their exact internal structure. *theoretically* the pool constructor
5151+ can transfer non-pickleable data to worker processes, but this only works when using the
5252+ `fork` spawn method (and is thus not available on darwin or windows).
5353+ """
5454+ if pool_processes is None:
5555+ state = state_fn(state_arg)
5656+ return [ fn(state, i) for i in d ]
5757+ with multiprocessing.Pool(pool_processes, _map_worker_init, (fn, state_fn, state_arg)) as p:
5858+ return list(p.imap(_map_worker_step, d, chunk_size))
+3-3
pkgs/tools/security/grype/default.nix
···8899buildGoModule rec {
1010 pname = "grype";
1111- version = "0.56.0";
1111+ version = "0.57.1";
12121313 src = fetchFromGitHub {
1414 owner = "anchore";
1515 repo = pname;
1616 rev = "v${version}";
1717- hash = "sha256-xNv4pI6iT6lNmjeUIW8ObPFJw9H1SiVTg9fRx6Osiwc=";
1717+ hash = "sha256-NACasOoCABoHmb4U5LvQ8EPO7G10A7uQtX4th/WJqrw=";
1818 # populate values that require us to use git. By doing this in postFetch we
1919 # can delete .git afterwards and maintain better reproducibility of the src.
2020 leaveDotGit = true;
···2828 };
2929 proxyVendor = true;
30303131- vendorHash = "sha256-Sez5jNFdL11cHBBPcY0b8qUiupmjPo9MHwUUi7FaNiA=";
3131+ vendorHash = "sha256-DLY0tcacGFcP17IqUVvpVkUjd2xQMO5JZxltmL4b+Wo=";
32323333 nativeBuildInputs = [
3434 installShellFiles