scruby 0.9.3__py3-none-any.whl → 0.27.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
scruby/mixins/count.py ADDED
@@ -0,0 +1,62 @@
1
+ # Scruby - Asynchronous library for building and managing a hybrid database, by scheme of key-value.
2
+ # Copyright (c) 2025 Gennady Kostyunin
3
+ # SPDX-License-Identifier: MIT
4
+ #
5
+ """Methods for counting the number of documents."""
6
+
7
+ from __future__ import annotations
8
+
9
+ __all__ = ("Count",)
10
+
11
+ import concurrent.futures
12
+ from collections.abc import Callable
13
+ from typing import Any
14
+
15
+
16
+ class Count:
17
+ """Methods for counting the number of documents."""
18
+
19
+ async def estimated_document_count(self) -> int:
20
+ """Get an estimate of the number of documents in this collection using collection metadata.
21
+
22
+ Returns:
23
+ The number of documents.
24
+ """
25
+ meta = await self.get_meta()
26
+ return meta.counter_documents
27
+
28
+ async def count_documents(
29
+ self,
30
+ filter_fn: Callable,
31
+ ) -> int:
32
+ """Count the number of documents a matching the filter in this collection.
33
+
34
+ The search is based on the effect of a quantum loop.
35
+ The search effectiveness depends on the number of processor threads.
36
+ Ideally, hundreds and even thousands of threads are required.
37
+
38
+ Args:
39
+ filter_fn: A function that execute the conditions of filtering.
40
+
41
+ Returns:
42
+ The number of documents.
43
+ """
44
+ branch_numbers: range = range(1, self._max_branch_number)
45
+ search_task_fn: Callable = self._task_find
46
+ hash_reduce_left: int = self._hash_reduce_left
47
+ db_root: str = self._db_root
48
+ class_model: Any = self._class_model
49
+ counter: int = 0
50
+ with concurrent.futures.ThreadPoolExecutor(self._max_workers) as executor:
51
+ for branch_number in branch_numbers:
52
+ future = executor.submit(
53
+ search_task_fn,
54
+ branch_number,
55
+ filter_fn,
56
+ hash_reduce_left,
57
+ db_root,
58
+ class_model,
59
+ )
60
+ if await future.result() is not None:
61
+ counter += 1
62
+ return counter
@@ -0,0 +1,75 @@
1
+ # Scruby - Asynchronous library for building and managing a hybrid database, by scheme of key-value.
2
+ # Copyright (c) 2025 Gennady Kostyunin
3
+ # SPDX-License-Identifier: MIT
4
+ #
5
+ """Quantum methods for running custom tasks."""
6
+
7
+ from __future__ import annotations
8
+
9
+ __all__ = ("CustomTask",)
10
+
11
+ from collections.abc import Callable
12
+ from typing import Any
13
+
14
+ import orjson
15
+ from anyio import Path
16
+
17
+
18
+ class CustomTask:
19
+ """Quantum methods for running custom tasks."""
20
+
21
+ @staticmethod
22
+ async def _task_get_docs(
23
+ branch_number: int,
24
+ hash_reduce_left: int,
25
+ db_root: str,
26
+ class_model: Any,
27
+ ) -> list[Any]:
28
+ """Get documents for custom task.
29
+
30
+ This method is for internal use.
31
+
32
+ Returns:
33
+ List of documents.
34
+ """
35
+ branch_number_as_hash: str = f"{branch_number:08x}"[hash_reduce_left:]
36
+ separated_hash: str = "/".join(list(branch_number_as_hash))
37
+ leaf_path: Path = Path(
38
+ *(
39
+ db_root,
40
+ class_model.__name__,
41
+ separated_hash,
42
+ "leaf.json",
43
+ ),
44
+ )
45
+ docs: list[Any] = []
46
+ if await leaf_path.exists():
47
+ data_json: bytes = await leaf_path.read_bytes()
48
+ data: dict[str, str] = orjson.loads(data_json) or {}
49
+ for _, val in data.items():
50
+ docs.append(class_model.model_validate_json(val))
51
+ return docs
52
+
53
+ async def run_custom_task(self, custom_task_fn: Callable, limit_docs: int = 1000) -> Any:
54
+ """Running custom task.
55
+
56
+ This method running a task created on the basis of a quantum loop.
57
+ Effectiveness running task depends on the number of processor threads.
58
+ Ideally, hundreds and even thousands of threads are required.
59
+
60
+ Args:
61
+ custom_task_fn: A function that execute the custom task.
62
+ limit_docs: Limiting the number of documents. By default = 1000.
63
+
64
+ Returns:
65
+ The result of a custom task.
66
+ """
67
+ kwargs = {
68
+ "get_docs_fn": self._task_get_docs,
69
+ "branch_numbers": range(1, self._max_branch_number),
70
+ "hash_reduce_left": self._hash_reduce_left,
71
+ "db_root": self._db_root,
72
+ "class_model": self._class_model,
73
+ "limit_docs": limit_docs,
74
+ }
75
+ return await custom_task_fn(**kwargs)
@@ -0,0 +1,96 @@
1
+ # Scruby - Asynchronous library for building and managing a hybrid database, by scheme of key-value.
2
+ # Copyright (c) 2025 Gennady Kostyunin
3
+ # SPDX-License-Identifier: MIT
4
+ #
5
+ """Methods for deleting documents."""
6
+
7
+ from __future__ import annotations
8
+
9
+ __all__ = ("Delete",)
10
+
11
+ import concurrent.futures
12
+ from collections.abc import Callable
13
+ from typing import Any
14
+
15
+ import orjson
16
+ from anyio import Path
17
+
18
+
19
+ class Delete:
20
+ """Methods for deleting documents."""
21
+
22
+ @staticmethod
23
+ async def _task_delete(
24
+ branch_number: int,
25
+ filter_fn: Callable,
26
+ hash_reduce_left: int,
27
+ db_root: str,
28
+ class_model: Any,
29
+ ) -> int:
30
+ """Task for find and delete documents.
31
+
32
+ This method is for internal use.
33
+
34
+ Returns:
35
+ The number of deleted documents.
36
+ """
37
+ branch_number_as_hash: str = f"{branch_number:08x}"[hash_reduce_left:]
38
+ separated_hash: str = "/".join(list(branch_number_as_hash))
39
+ leaf_path: Path = Path(
40
+ *(
41
+ db_root,
42
+ class_model.__name__,
43
+ separated_hash,
44
+ "leaf.json",
45
+ ),
46
+ )
47
+ counter: int = 0
48
+ if await leaf_path.exists():
49
+ data_json: bytes = await leaf_path.read_bytes()
50
+ data: dict[str, str] = orjson.loads(data_json) or {}
51
+ new_state: dict[str, str] = {}
52
+ for key, val in data.items():
53
+ doc = class_model.model_validate_json(val)
54
+ if filter_fn(doc):
55
+ counter -= 1
56
+ else:
57
+ new_state[key] = val
58
+ await leaf_path.write_bytes(orjson.dumps(new_state))
59
+ return counter
60
+
61
+ async def delete_many(
62
+ self,
63
+ filter_fn: Callable,
64
+ ) -> int:
65
+ """Delete one or more documents matching the filter.
66
+
67
+ The search is based on the effect of a quantum loop.
68
+ The search effectiveness depends on the number of processor threads.
69
+ Ideally, hundreds and even thousands of threads are required.
70
+
71
+ Args:
72
+ filter_fn: A function that execute the conditions of filtering.
73
+
74
+ Returns:
75
+ The number of deleted documents.
76
+ """
77
+ branch_numbers: range = range(1, self._max_branch_number)
78
+ search_task_fn: Callable = self._task_delete
79
+ hash_reduce_left: int = self._hash_reduce_left
80
+ db_root: str = self._db_root
81
+ class_model: Any = self._class_model
82
+ counter: int = 0
83
+ with concurrent.futures.ThreadPoolExecutor(self._max_workers) as executor:
84
+ for branch_number in branch_numbers:
85
+ future = executor.submit(
86
+ search_task_fn,
87
+ branch_number,
88
+ filter_fn,
89
+ hash_reduce_left,
90
+ db_root,
91
+ class_model,
92
+ )
93
+ counter += await future.result()
94
+ if counter < 0:
95
+ await self._counter_documents(counter)
96
+ return abs(counter)
scruby/mixins/docs.py ADDED
@@ -0,0 +1,168 @@
1
+ # Scruby - Asynchronous library for building and managing a hybrid database, by scheme of key-value.
2
+ # Copyright (c) 2025 Gennady Kostyunin
3
+ # SPDX-License-Identifier: MIT
4
+ #
5
+ """Methods for working with keys."""
6
+
7
+ from __future__ import annotations
8
+
9
+ __all__ = ("Keys",)
10
+
11
+ import logging
12
+ from typing import Any
13
+
14
+ import orjson
15
+
16
+ from scruby.errors import (
17
+ KeyAlreadyExistsError,
18
+ KeyNotExistsError,
19
+ )
20
+
21
+
22
+ class Keys:
23
+ """Methods for working with keys."""
24
+
25
+ async def add_doc(self, doc: Any) -> None:
26
+ """Asynchronous method for adding document to collection.
27
+
28
+ Args:
29
+ doc: Value of key. Type, derived from `BaseModel`.
30
+
31
+ Returns:
32
+ None.
33
+ """
34
+ # Check if the Model matches the collection
35
+ if not isinstance(doc, self._class_model):
36
+ doc_class_name = doc.__class__.__name__
37
+ collection_name = self._class_model.__name__
38
+ msg = (
39
+ f"(add_doc) Parameter `doc` => Model `{doc_class_name}` does not match collection `{collection_name}`!"
40
+ )
41
+ logging.error(msg)
42
+ raise TypeError(msg)
43
+ # The path to cell of collection.
44
+ leaf_path, prepared_key = await self._get_leaf_path(doc.key)
45
+ doc_json: str = doc.model_dump_json()
46
+ # Write key-value to collection.
47
+ if await leaf_path.exists():
48
+ # Add new key.
49
+ data_json: bytes = await leaf_path.read_bytes()
50
+ data: dict = orjson.loads(data_json) or {}
51
+ try:
52
+ data[prepared_key]
53
+ except KeyError:
54
+ data[prepared_key] = doc_json
55
+ await leaf_path.write_bytes(orjson.dumps(data))
56
+ else:
57
+ err = KeyAlreadyExistsError()
58
+ logging.error(err.message)
59
+ raise err
60
+ else:
61
+ # Add new document to a blank leaf.
62
+ await leaf_path.write_bytes(orjson.dumps({prepared_key: doc_json}))
63
+ await self._counter_documents(1)
64
+
65
+ async def update_doc(self, doc: Any) -> None:
66
+ """Asynchronous method for updating key to collection.
67
+
68
+ Args:
69
+ doc: Value of key. Type `BaseModel`.
70
+
71
+ Returns:
72
+ None.
73
+ """
74
+ # Check if the Model matches the collection
75
+ if not isinstance(doc, self._class_model):
76
+ doc_class_name = doc.__class__.__name__
77
+ collection_name = self._class_model.__name__
78
+ msg = (
79
+ f"(update_doc) Parameter `doc` => Model `{doc_class_name}` "
80
+ f"does not match collection `{collection_name}`!"
81
+ )
82
+ logging.error(msg)
83
+ raise TypeError(msg)
84
+ # The path to cell of collection.
85
+ leaf_path, prepared_key = await self._get_leaf_path(doc.key)
86
+ doc_json: str = doc.model_dump_json()
87
+ # Update the existing key.
88
+ if await leaf_path.exists():
89
+ # Update the existing key.
90
+ data_json: bytes = await leaf_path.read_bytes()
91
+ data: dict = orjson.loads(data_json) or {}
92
+ try:
93
+ data[prepared_key]
94
+ data[prepared_key] = doc_json
95
+ await leaf_path.write_bytes(orjson.dumps(data))
96
+ except KeyError:
97
+ err = KeyNotExistsError()
98
+ logging.error(err.message)
99
+ raise err from None
100
+ else:
101
+ logging.error("The key not exists.")
102
+ raise KeyError()
103
+
104
+ async def get_key(self, key: str) -> Any:
105
+ """Asynchronous method for getting value of key from collection.
106
+
107
+ Args:
108
+ key: Key name.
109
+
110
+ Returns:
111
+ Value of key or KeyError.
112
+ """
113
+ # The path to the database cell.
114
+ leaf_path, prepared_key = await self._get_leaf_path(key)
115
+ # Get value of key.
116
+ if await leaf_path.exists():
117
+ data_json: bytes = await leaf_path.read_bytes()
118
+ data: dict = orjson.loads(data_json) or {}
119
+ obj: Any = self._class_model.model_validate_json(data[prepared_key])
120
+ return obj
121
+ msg: str = "`get_key` - The unacceptable key value."
122
+ logging.error(msg)
123
+ raise KeyError()
124
+
125
+ async def has_key(self, key: str) -> bool:
126
+ """Asynchronous method for checking presence of key in collection.
127
+
128
+ Args:
129
+ key: Key name.
130
+
131
+ Returns:
132
+ True, if the key is present.
133
+ """
134
+ # Get path to cell of collection.
135
+ leaf_path, prepared_key = await self._get_leaf_path(key)
136
+ # Checking whether there is a key.
137
+ if await leaf_path.exists():
138
+ data_json: bytes = await leaf_path.read_bytes()
139
+ data: dict = orjson.loads(data_json) or {}
140
+ try:
141
+ data[prepared_key]
142
+ return True
143
+ except KeyError:
144
+ return False
145
+ return False
146
+
147
+ async def delete_key(self, key: str) -> None:
148
+ """Asynchronous method for deleting key from collection.
149
+
150
+ Args:
151
+ key: Key name.
152
+
153
+ Returns:
154
+ None.
155
+ """
156
+ # The path to the database cell.
157
+ leaf_path, prepared_key = await self._get_leaf_path(key)
158
+ # Deleting key.
159
+ if await leaf_path.exists():
160
+ data_json: bytes = await leaf_path.read_bytes()
161
+ data: dict = orjson.loads(data_json) or {}
162
+ del data[prepared_key]
163
+ await leaf_path.write_bytes(orjson.dumps(data))
164
+ await self._counter_documents(-1)
165
+ return
166
+ msg: str = "`delete_key` - The unacceptable key value."
167
+ logging.error(msg)
168
+ raise KeyError()
scruby/mixins/find.py ADDED
@@ -0,0 +1,149 @@
1
+ # Scruby - Asynchronous library for building and managing a hybrid database, by scheme of key-value.
2
+ # Copyright (c) 2025 Gennady Kostyunin
3
+ # SPDX-License-Identifier: MIT
4
+ #
5
+ """Quantum methods for searching documents."""
6
+
7
+ from __future__ import annotations
8
+
9
+ __all__ = ("Find",)
10
+
11
+ import concurrent.futures
12
+ from collections.abc import Callable
13
+ from typing import Any
14
+
15
+ import orjson
16
+ from anyio import Path
17
+
18
+
19
+ class Find:
20
+ """Quantum methods for searching documents."""
21
+
22
+ @staticmethod
23
+ async def _task_find(
24
+ branch_number: int,
25
+ filter_fn: Callable,
26
+ hash_reduce_left: str,
27
+ db_root: str,
28
+ class_model: Any,
29
+ filter_is_checking: bool = True,
30
+ ) -> list[Any] | None:
31
+ """Task for find documents.
32
+
33
+ This method is for internal use.
34
+
35
+ Returns:
36
+ List of documents or None.
37
+ """
38
+ branch_number_as_hash: str = f"{branch_number:08x}"[hash_reduce_left:]
39
+ separated_hash: str = "/".join(list(branch_number_as_hash))
40
+ leaf_path: Path = Path(
41
+ *(
42
+ db_root,
43
+ class_model.__name__,
44
+ separated_hash,
45
+ "leaf.json",
46
+ ),
47
+ )
48
+ docs: list[Any] = []
49
+ if await leaf_path.exists():
50
+ data_json: bytes = await leaf_path.read_bytes()
51
+ data: dict[str, str] = orjson.loads(data_json) or {}
52
+ for _, val in data.items():
53
+ doc = class_model.model_validate_json(val)
54
+ if not filter_is_checking or filter_fn(doc):
55
+ docs.append(doc)
56
+ return docs or None
57
+
58
+ async def find_one(
59
+ self,
60
+ filter_fn: Callable,
61
+ ) -> Any | None:
62
+ """Finds a single document matching the filter.
63
+
64
+ The search is based on the effect of a quantum loop.
65
+ The search effectiveness depends on the number of processor threads.
66
+ Ideally, hundreds and even thousands of threads are required.
67
+
68
+ Args:
69
+ filter_fn (Callable): A function that execute the conditions of filtering.
70
+
71
+ Returns:
72
+ Document or None.
73
+ """
74
+ branch_numbers: range = range(1, self._max_branch_number)
75
+ search_task_fn: Callable = self._task_find
76
+ hash_reduce_left: int = self._hash_reduce_left
77
+ db_root: str = self._db_root
78
+ class_model: Any = self._class_model
79
+ with concurrent.futures.ThreadPoolExecutor(self._max_workers) as executor:
80
+ for branch_number in branch_numbers:
81
+ future = executor.submit(
82
+ search_task_fn,
83
+ branch_number,
84
+ filter_fn,
85
+ hash_reduce_left,
86
+ db_root,
87
+ class_model,
88
+ )
89
+ docs = await future.result()
90
+ if docs is not None:
91
+ return docs[0]
92
+ return None
93
+
94
+ async def find_many(
95
+ self,
96
+ filter_fn: Callable = lambda _: True,
97
+ limit_docs: int = 1000,
98
+ page_number: int = 1,
99
+ ) -> list[Any] | None:
100
+ """Finds one or more documents matching the filter.
101
+
102
+ The search is based on the effect of a quantum loop.
103
+ The search effectiveness depends on the number of processor threads.
104
+ Ideally, hundreds and even thousands of threads are required.
105
+
106
+ Args:
107
+ filter_fn (Callable): A function that execute the conditions of filtering.
108
+ By default it searches for all documents.
109
+ limit_docs (int): Limiting the number of documents. By default = 1000.
110
+ page_number (int): For pagination output. By default = 1.
111
+ Number of documents per page = limit_docs.
112
+
113
+ Returns:
114
+ List of documents or None.
115
+ """
116
+ branch_numbers: range = range(1, self._max_branch_number)
117
+ search_task_fn: Callable = self._task_find
118
+ hash_reduce_left: int = self._hash_reduce_left
119
+ db_root: str = self._db_root
120
+ class_model: Any = self._class_model
121
+ counter: int = 0
122
+ number_docs_skippe: int = limit_docs * (page_number - 1) if page_number > 1 else 0
123
+ result: list[Any] = []
124
+ filter_is_checking: bool = False
125
+ with concurrent.futures.ThreadPoolExecutor(self._max_workers) as executor:
126
+ for branch_number in branch_numbers:
127
+ if number_docs_skippe == 0 and counter >= limit_docs:
128
+ return result[:limit_docs]
129
+ future = executor.submit(
130
+ search_task_fn,
131
+ branch_number,
132
+ filter_fn,
133
+ hash_reduce_left,
134
+ db_root,
135
+ class_model,
136
+ filter_is_checking,
137
+ )
138
+ docs = await future.result()
139
+ if docs is not None:
140
+ for doc in docs:
141
+ if number_docs_skippe == 0:
142
+ if counter >= limit_docs:
143
+ return result[:limit_docs]
144
+ if filter_fn(doc):
145
+ result.append(doc)
146
+ counter += 1
147
+ else:
148
+ number_docs_skippe -= 1
149
+ return result or None
@@ -0,0 +1,99 @@
1
+ # Scruby - Asynchronous library for building and managing a hybrid database, by scheme of key-value.
2
+ # Copyright (c) 2025 Gennady Kostyunin
3
+ # SPDX-License-Identifier: MIT
4
+ #
5
+ """Methods for updating documents."""
6
+
7
+ from __future__ import annotations
8
+
9
+ __all__ = ("Update",)
10
+
11
+ import concurrent.futures
12
+ from collections.abc import Callable
13
+ from typing import Any
14
+
15
+ import orjson
16
+ from anyio import Path
17
+
18
+
19
+ class Update:
20
+ """Methods for updating documents."""
21
+
22
+ @staticmethod
23
+ async def _task_update(
24
+ branch_number: int,
25
+ filter_fn: Callable,
26
+ hash_reduce_left: str,
27
+ db_root: str,
28
+ class_model: Any,
29
+ new_data: dict[str, Any],
30
+ ) -> int:
31
+ """Task for find documents.
32
+
33
+ This method is for internal use.
34
+
35
+ Returns:
36
+ The number of updated documents.
37
+ """
38
+ branch_number_as_hash: str = f"{branch_number:08x}"[hash_reduce_left:]
39
+ separated_hash: str = "/".join(list(branch_number_as_hash))
40
+ leaf_path: Path = Path(
41
+ *(
42
+ db_root,
43
+ class_model.__name__,
44
+ separated_hash,
45
+ "leaf.json",
46
+ ),
47
+ )
48
+ counter: int = 0
49
+ if await leaf_path.exists():
50
+ data_json: bytes = await leaf_path.read_bytes()
51
+ data: dict[str, str] = orjson.loads(data_json) or {}
52
+ new_state: dict[str, str] = {}
53
+ for _, val in data.items():
54
+ doc = class_model.model_validate_json(val)
55
+ if filter_fn(doc):
56
+ for key, value in new_data.items():
57
+ doc.__dict__[key] = value
58
+ new_state[key] = doc.model_dump_json()
59
+ counter += 1
60
+ await leaf_path.write_bytes(orjson.dumps(new_state))
61
+ return counter
62
+
63
+ async def update_many(
64
+ self,
65
+ filter_fn: Callable,
66
+ new_data: dict[str, Any],
67
+ ) -> int:
68
+ """Updates one or more documents matching the filter.
69
+
70
+ The search is based on the effect of a quantum loop.
71
+ The search effectiveness depends on the number of processor threads.
72
+ Ideally, hundreds and even thousands of threads are required.
73
+
74
+ Args:
75
+ filter_fn: A function that execute the conditions of filtering.
76
+ new_data: New data for the fields that need to be updated.
77
+
78
+ Returns:
79
+ The number of updated documents.
80
+ """
81
+ branch_numbers: range = range(1, self._max_branch_number)
82
+ update_task_fn: Callable = self._task_update
83
+ hash_reduce_left: int = self._hash_reduce_left
84
+ db_root: str = self._db_root
85
+ class_model: Any = self._class_model
86
+ counter: int = 0
87
+ with concurrent.futures.ThreadPoolExecutor(self._max_workers) as executor:
88
+ for branch_number in branch_numbers:
89
+ future = executor.submit(
90
+ update_task_fn,
91
+ branch_number,
92
+ filter_fn,
93
+ hash_reduce_left,
94
+ db_root,
95
+ class_model,
96
+ new_data,
97
+ )
98
+ counter += await future.result()
99
+ return counter
scruby/settings.py ADDED
@@ -0,0 +1,44 @@
1
+ # Scruby - Asynchronous library for building and managing a hybrid database, by scheme of key-value.
2
+ # Copyright (c) 2025 Gennady Kostyunin
3
+ # SPDX-License-Identifier: MIT
4
+ #
5
+ """Database settings.
6
+
7
+ The module contains the following parameters:
8
+
9
+ - `DB_ROOT` - Path to root directory of database. `By default = "ScrubyDB" (in root of project)`.
10
+ - `HASH_REDUCE_LEFT` - The length of the hash reduction on the left side.
11
+ - `0` - 4294967296 branches in collection.
12
+ - `2` - 16777216 branches in collection.
13
+ - `4` - 65536 branches in collection.
14
+ - `6` - 256 branches in collection (by default).
15
+ - `MAX_WORKERS` - The maximum number of processes that can be used `By default = None`.
16
+ """
17
+
18
+ from __future__ import annotations
19
+
20
+ __all__ = (
21
+ "DB_ROOT",
22
+ "HASH_REDUCE_LEFT",
23
+ "MAX_WORKERS",
24
+ )
25
+
26
+ from typing import Literal
27
+
28
+ # Path to root directory of database
29
+ # By default = "ScrubyDB" (in root of project).
30
+ DB_ROOT: str = "ScrubyDB"
31
+
32
+ # The length of the hash reduction on the left side.
33
+ # 0 = 4294967296 branches in collection.
34
+ # 2 = 16777216 branches in collection.
35
+ # 4 = 65536 branches in collection.
36
+ # 6 = 256 branches in collection (by default).
37
+ # Number of branches is number of requests to the hard disk during quantum operations.
38
+ # Quantum operations: find_one, find_many, count_documents, delete_many, run_custom_task.
39
+ HASH_REDUCE_LEFT: Literal[0, 2, 4, 6] = 6
40
+
41
+ # The maximum number of processes that can be used to execute the given calls.
42
+ # If None, then as many worker processes will be
43
+ # created as the machine has processors.
44
+ MAX_WORKERS: int | None = None