coco-framework 0.1.0__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.
- coco/__init__.py +3 -0
- coco/agents/__init__.py +9 -0
- coco/agents/_subagent_transformer.py +307 -0
- coco/agents/factory.py +2164 -0
- coco/agents/middleware/__init__.py +100 -0
- coco/agents/middleware/_execution.py +385 -0
- coco/agents/middleware/_redaction.py +454 -0
- coco/agents/middleware/_retry.py +134 -0
- coco/agents/middleware/_trace_policy.py +61 -0
- coco/agents/middleware/context_editing.py +306 -0
- coco/agents/middleware/file_search.py +433 -0
- coco/agents/middleware/human_in_the_loop.py +680 -0
- coco/agents/middleware/internal_call_transformer.py +108 -0
- coco/agents/middleware/model_call_limit.py +267 -0
- coco/agents/middleware/model_fallback.py +417 -0
- coco/agents/middleware/model_retry.py +327 -0
- coco/agents/middleware/pii.py +878 -0
- coco/agents/middleware/provider_tool_search.py +307 -0
- coco/agents/middleware/shell_tool.py +949 -0
- coco/agents/middleware/summarization.py +910 -0
- coco/agents/middleware/todo.py +357 -0
- coco/agents/middleware/tool_call_limit.py +495 -0
- coco/agents/middleware/tool_emulator.py +251 -0
- coco/agents/middleware/tool_error.py +209 -0
- coco/agents/middleware/tool_retry.py +421 -0
- coco/agents/middleware/tool_selection.py +515 -0
- coco/agents/middleware/types.py +2201 -0
- coco/agents/structured_output.py +472 -0
- coco/chat_models/__init__.py +7 -0
- coco/chat_models/base.py +1082 -0
- coco/embeddings/__init__.py +18 -0
- coco/embeddings/base.py +279 -0
- coco/mcp/__init__.py +36 -0
- coco/mcp/adapter.py +245 -0
- coco/mcp/elicitation.py +394 -0
- coco/mcp/tools.py +307 -0
- coco/messages/__init__.py +73 -0
- coco/py.typed +0 -0
- coco/rate_limiters/__init__.py +13 -0
- coco/tools/__init__.py +22 -0
- coco/tools/tool_node.py +20 -0
- coco_anthropic/__init__.py +15 -0
- coco_anthropic/_client_utils.py +81 -0
- coco_anthropic/_compat.py +228 -0
- coco_anthropic/_sdk_compat.py +124 -0
- coco_anthropic/_version.py +3 -0
- coco_anthropic/chat_models.py +3349 -0
- coco_anthropic/data/__init__.py +1 -0
- coco_anthropic/data/_profiles.py +491 -0
- coco_anthropic/data/profile_augmentations.toml +69 -0
- coco_anthropic/experimental.py +140 -0
- coco_anthropic/llms.py +428 -0
- coco_anthropic/middleware/__init__.py +25 -0
- coco_anthropic/middleware/anthropic_tools.py +1197 -0
- coco_anthropic/middleware/bash.py +84 -0
- coco_anthropic/middleware/file_search.py +360 -0
- coco_anthropic/middleware/prompt_caching.py +276 -0
- coco_anthropic/output_parsers.py +93 -0
- coco_anthropic/py.typed +0 -0
- coco_core/__init__.py +20 -0
- coco_core/_api/__init__.py +87 -0
- coco_core/_api/beta_decorator.py +266 -0
- coco_core/_api/deprecation.py +637 -0
- coco_core/_api/internal.py +23 -0
- coco_core/_api/path.py +50 -0
- coco_core/_import_utils.py +41 -0
- coco_core/_protocol.py +625 -0
- coco_core/_security/__init__.py +36 -0
- coco_core/_security/_exceptions.py +9 -0
- coco_core/_security/_policy.py +313 -0
- coco_core/_security/_ssrf_protection.py +155 -0
- coco_core/_security/_transport.py +254 -0
- coco_core/agents.py +258 -0
- coco_core/caches.py +272 -0
- coco_core/callbacks/__init__.py +132 -0
- coco_core/callbacks/base.py +1229 -0
- coco_core/callbacks/file.py +266 -0
- coco_core/callbacks/manager.py +2826 -0
- coco_core/callbacks/stdout.py +123 -0
- coco_core/callbacks/streaming_stdout.py +154 -0
- coco_core/callbacks/usage.py +124 -0
- coco_core/chat_history.py +267 -0
- coco_core/chat_loaders.py +26 -0
- coco_core/chat_sessions.py +19 -0
- coco_core/cross_encoders.py +18 -0
- coco_core/document_loaders/__init__.py +36 -0
- coco_core/document_loaders/base.py +155 -0
- coco_core/document_loaders/blob_loaders.py +38 -0
- coco_core/documents/__init__.py +55 -0
- coco_core/documents/base.py +347 -0
- coco_core/documents/compressor.py +74 -0
- coco_core/documents/transformers.py +79 -0
- coco_core/embeddings/__init__.py +31 -0
- coco_core/embeddings/embeddings.py +78 -0
- coco_core/embeddings/fake.py +129 -0
- coco_core/env.py +22 -0
- coco_core/example_selectors/__init__.py +47 -0
- coco_core/example_selectors/base.py +60 -0
- coco_core/example_selectors/length_based.py +131 -0
- coco_core/example_selectors/semantic_similarity.py +366 -0
- coco_core/exceptions.py +166 -0
- coco_core/globals.py +72 -0
- coco_core/indexing/__init__.py +53 -0
- coco_core/indexing/api.py +954 -0
- coco_core/indexing/base.py +661 -0
- coco_core/indexing/in_memory.py +104 -0
- coco_core/language_models/__init__.py +116 -0
- coco_core/language_models/_compat_bridge.py +844 -0
- coco_core/language_models/_utils.py +343 -0
- coco_core/language_models/base.py +500 -0
- coco_core/language_models/chat_model_stream.py +1525 -0
- coco_core/language_models/chat_models.py +2748 -0
- coco_core/language_models/fake.py +137 -0
- coco_core/language_models/fake_chat_models.py +396 -0
- coco_core/language_models/llms.py +1569 -0
- coco_core/language_models/model_profile.py +183 -0
- coco_core/load/__init__.py +44 -0
- coco_core/load/_validation.py +191 -0
- coco_core/load/dump.py +120 -0
- coco_core/load/load.py +828 -0
- coco_core/load/mapping.py +1097 -0
- coco_core/load/serializable.py +406 -0
- coco_core/messages/__init__.py +198 -0
- coco_core/messages/ai.py +854 -0
- coco_core/messages/base.py +518 -0
- coco_core/messages/block_translators/__init__.py +112 -0
- coco_core/messages/block_translators/anthropic.py +534 -0
- coco_core/messages/block_translators/bedrock.py +108 -0
- coco_core/messages/block_translators/bedrock_converse.py +329 -0
- coco_core/messages/block_translators/coco_v0.py +303 -0
- coco_core/messages/block_translators/google_genai.py +610 -0
- coco_core/messages/block_translators/google_vertexai.py +21 -0
- coco_core/messages/block_translators/groq.py +157 -0
- coco_core/messages/block_translators/openai.py +1089 -0
- coco_core/messages/chat.py +64 -0
- coco_core/messages/content.py +1488 -0
- coco_core/messages/function.py +62 -0
- coco_core/messages/human.py +70 -0
- coco_core/messages/modifier.py +33 -0
- coco_core/messages/system.py +70 -0
- coco_core/messages/tool.py +418 -0
- coco_core/messages/utils.py +2442 -0
- coco_core/output_parsers/__init__.py +101 -0
- coco_core/output_parsers/base.py +361 -0
- coco_core/output_parsers/format_instructions.py +16 -0
- coco_core/output_parsers/json.py +139 -0
- coco_core/output_parsers/list.py +249 -0
- coco_core/output_parsers/openai_functions.py +324 -0
- coco_core/output_parsers/openai_tools.py +383 -0
- coco_core/output_parsers/pydantic.py +141 -0
- coco_core/output_parsers/string.py +63 -0
- coco_core/output_parsers/transform.py +175 -0
- coco_core/output_parsers/xml.py +301 -0
- coco_core/outputs/__init__.py +66 -0
- coco_core/outputs/chat_generation.py +147 -0
- coco_core/outputs/chat_result.py +39 -0
- coco_core/outputs/generation.py +80 -0
- coco_core/outputs/llm_result.py +112 -0
- coco_core/outputs/run_info.py +22 -0
- coco_core/prompt_values.py +161 -0
- coco_core/prompts/__init__.py +101 -0
- coco_core/prompts/base.py +508 -0
- coco_core/prompts/chat.py +1495 -0
- coco_core/prompts/dict.py +177 -0
- coco_core/prompts/few_shot.py +483 -0
- coco_core/prompts/few_shot_with_templates.py +239 -0
- coco_core/prompts/image.py +177 -0
- coco_core/prompts/loading.py +296 -0
- coco_core/prompts/message.py +97 -0
- coco_core/prompts/prompt.py +312 -0
- coco_core/prompts/string.py +399 -0
- coco_core/prompts/structured.py +214 -0
- coco_core/py.typed +0 -0
- coco_core/rate_limiters.py +254 -0
- coco_core/retrievers.py +328 -0
- coco_core/runnables/__init__.py +136 -0
- coco_core/runnables/base.py +6725 -0
- coco_core/runnables/branch.py +457 -0
- coco_core/runnables/config.py +712 -0
- coco_core/runnables/configurable.py +710 -0
- coco_core/runnables/fallbacks.py +663 -0
- coco_core/runnables/graph.py +741 -0
- coco_core/runnables/graph_ascii.py +366 -0
- coco_core/runnables/graph_mermaid.py +503 -0
- coco_core/runnables/graph_png.py +215 -0
- coco_core/runnables/history.py +631 -0
- coco_core/runnables/passthrough.py +849 -0
- coco_core/runnables/retry.py +379 -0
- coco_core/runnables/router.py +239 -0
- coco_core/runnables/schema.py +188 -0
- coco_core/runnables/utils.py +798 -0
- coco_core/stores.py +291 -0
- coco_core/structured_query.py +203 -0
- coco_core/sys_info.py +154 -0
- coco_core/tools/__init__.py +95 -0
- coco_core/tools/base.py +1948 -0
- coco_core/tools/convert.py +497 -0
- coco_core/tools/render.py +67 -0
- coco_core/tools/retriever.py +94 -0
- coco_core/tools/simple.py +208 -0
- coco_core/tools/structured.py +322 -0
- coco_core/tracers/__init__.py +47 -0
- coco_core/tracers/_compat.py +95 -0
- coco_core/tracers/_context.py +337 -0
- coco_core/tracers/_streaming.py +53 -0
- coco_core/tracers/base.py +955 -0
- coco_core/tracers/coco.py +279 -0
- coco_core/tracers/context.py +205 -0
- coco_core/tracers/core.py +766 -0
- coco_core/tracers/event_stream.py +1101 -0
- coco_core/tracers/log_stream.py +765 -0
- coco_core/tracers/memory_stream.py +200 -0
- coco_core/tracers/root_listeners.py +130 -0
- coco_core/tracers/run_collector.py +40 -0
- coco_core/tracers/schemas.py +14 -0
- coco_core/tracers/stdout.py +205 -0
- coco_core/utils/__init__.py +111 -0
- coco_core/utils/_gateway.py +274 -0
- coco_core/utils/_merge.py +218 -0
- coco_core/utils/aiter.py +357 -0
- coco_core/utils/env.py +86 -0
- coco_core/utils/formatting.py +80 -0
- coco_core/utils/function_calling.py +869 -0
- coco_core/utils/html.py +132 -0
- coco_core/utils/image.py +15 -0
- coco_core/utils/input.py +82 -0
- coco_core/utils/interactive_env.py +12 -0
- coco_core/utils/iter.py +229 -0
- coco_core/utils/json.py +230 -0
- coco_core/utils/json_schema.py +273 -0
- coco_core/utils/mustache.py +706 -0
- coco_core/utils/pydantic.py +631 -0
- coco_core/utils/strings.py +69 -0
- coco_core/utils/usage.py +61 -0
- coco_core/utils/utils.py +528 -0
- coco_core/utils/uuid.py +57 -0
- coco_core/vectorstores/__init__.py +53 -0
- coco_core/vectorstores/base.py +1111 -0
- coco_core/vectorstores/in_memory.py +546 -0
- coco_core/vectorstores/utils.py +163 -0
- coco_core/version.py +3 -0
- coco_framework-0.1.0.dist-info/METADATA +184 -0
- coco_framework-0.1.0.dist-info/RECORD +400 -0
- coco_framework-0.1.0.dist-info/WHEEL +4 -0
- coco_framework-0.1.0.dist-info/licenses/LICENSE +24 -0
- coco_framework-0.1.0.dist-info/licenses/NOTICE +15 -0
- coco_graph/_internal/__init__.py +4 -0
- coco_graph/_internal/_cache.py +31 -0
- coco_graph/_internal/_config.py +474 -0
- coco_graph/_internal/_constants.py +140 -0
- coco_graph/_internal/_fields.py +213 -0
- coco_graph/_internal/_future.py +220 -0
- coco_graph/_internal/_pydantic.py +275 -0
- coco_graph/_internal/_queue.py +124 -0
- coco_graph/_internal/_replay.py +90 -0
- coco_graph/_internal/_retry.py +29 -0
- coco_graph/_internal/_runnable.py +969 -0
- coco_graph/_internal/_scratchpad.py +19 -0
- coco_graph/_internal/_serde.py +253 -0
- coco_graph/_internal/_timeout.py +25 -0
- coco_graph/_internal/_typing.py +54 -0
- coco_graph/cache/base/__init__.py +48 -0
- coco_graph/cache/base/py.typed +0 -0
- coco_graph/cache/memory/__init__.py +73 -0
- coco_graph/cache/redis/__init__.py +144 -0
- coco_graph/cache/sqlite/__init__.py +120 -0
- coco_graph/callbacks.py +394 -0
- coco_graph/channels/__init__.py +29 -0
- coco_graph/channels/any_value.py +72 -0
- coco_graph/channels/base.py +121 -0
- coco_graph/channels/binop.py +155 -0
- coco_graph/channels/delta.py +202 -0
- coco_graph/channels/ephemeral_value.py +79 -0
- coco_graph/channels/last_value.py +151 -0
- coco_graph/channels/named_barrier_value.py +167 -0
- coco_graph/channels/topic.py +94 -0
- coco_graph/channels/untracked_value.py +73 -0
- coco_graph/checkpoint/base/__init__.py +861 -0
- coco_graph/checkpoint/base/id.py +109 -0
- coco_graph/checkpoint/base/py.typed +0 -0
- coco_graph/checkpoint/memory/__init__.py +698 -0
- coco_graph/checkpoint/memory/py.typed +0 -0
- coco_graph/checkpoint/postgres/__init__.py +602 -0
- coco_graph/checkpoint/postgres/_ainternal.py +23 -0
- coco_graph/checkpoint/postgres/_internal.py +21 -0
- coco_graph/checkpoint/postgres/aio.py +691 -0
- coco_graph/checkpoint/postgres/base.py +666 -0
- coco_graph/checkpoint/postgres/py.typed +0 -0
- coco_graph/checkpoint/postgres/shallow.py +967 -0
- coco_graph/checkpoint/serde/__init__.py +0 -0
- coco_graph/checkpoint/serde/_msgpack.py +97 -0
- coco_graph/checkpoint/serde/base.py +64 -0
- coco_graph/checkpoint/serde/encrypted.py +80 -0
- coco_graph/checkpoint/serde/event_hooks.py +52 -0
- coco_graph/checkpoint/serde/jsonplus.py +883 -0
- coco_graph/checkpoint/serde/py.typed +0 -0
- coco_graph/checkpoint/serde/types.py +68 -0
- coco_graph/checkpoint/sqlite/__init__.py +645 -0
- coco_graph/checkpoint/sqlite/_delta.py +172 -0
- coco_graph/checkpoint/sqlite/aio.py +738 -0
- coco_graph/checkpoint/sqlite/py.typed +0 -0
- coco_graph/checkpoint/sqlite/utils.py +116 -0
- coco_graph/config.py +196 -0
- coco_graph/constants.py +64 -0
- coco_graph/errors.py +241 -0
- coco_graph/func/__init__.py +620 -0
- coco_graph/graph/__init__.py +12 -0
- coco_graph/graph/_branch.py +225 -0
- coco_graph/graph/_node.py +103 -0
- coco_graph/graph/message.py +437 -0
- coco_graph/graph/state.py +1978 -0
- coco_graph/graph/ui.py +227 -0
- coco_graph/managed/__init__.py +3 -0
- coco_graph/managed/base.py +31 -0
- coco_graph/managed/is_last_step.py +24 -0
- coco_graph/prebuilt/__init__.py +23 -0
- coco_graph/prebuilt/_tool_call_stream.py +117 -0
- coco_graph/prebuilt/_tool_call_transformer.py +165 -0
- coco_graph/prebuilt/chat_agent_executor.py +1015 -0
- coco_graph/prebuilt/interrupt.py +105 -0
- coco_graph/prebuilt/py.typed +0 -0
- coco_graph/prebuilt/tool_node.py +2030 -0
- coco_graph/prebuilt/tool_validator.py +221 -0
- coco_graph/pregel/__init__.py +3 -0
- coco_graph/pregel/_algo.py +1460 -0
- coco_graph/pregel/_call.py +298 -0
- coco_graph/pregel/_checkpoint.py +331 -0
- coco_graph/pregel/_config.py +0 -0
- coco_graph/pregel/_draw.py +294 -0
- coco_graph/pregel/_executor.py +223 -0
- coco_graph/pregel/_io.py +174 -0
- coco_graph/pregel/_log.py +3 -0
- coco_graph/pregel/_loop.py +1988 -0
- coco_graph/pregel/_messages.py +461 -0
- coco_graph/pregel/_read.py +312 -0
- coco_graph/pregel/_retry.py +854 -0
- coco_graph/pregel/_runner.py +941 -0
- coco_graph/pregel/_tools.py +268 -0
- coco_graph/pregel/_utils.py +221 -0
- coco_graph/pregel/_validate.py +120 -0
- coco_graph/pregel/_write.py +192 -0
- coco_graph/pregel/debug.py +302 -0
- coco_graph/pregel/main.py +4364 -0
- coco_graph/pregel/protocol.py +288 -0
- coco_graph/pregel/types.py +38 -0
- coco_graph/py.typed +0 -0
- coco_graph/runtime.py +350 -0
- coco_graph/store/base/__init__.py +1322 -0
- coco_graph/store/base/batch.py +371 -0
- coco_graph/store/base/embed.py +436 -0
- coco_graph/store/base/py.typed +0 -0
- coco_graph/store/memory/__init__.py +592 -0
- coco_graph/store/memory/py.typed +0 -0
- coco_graph/store/postgres/__init__.py +4 -0
- coco_graph/store/postgres/aio.py +592 -0
- coco_graph/store/postgres/base.py +1480 -0
- coco_graph/store/postgres/py.typed +0 -0
- coco_graph/store/sqlite/__init__.py +4 -0
- coco_graph/store/sqlite/aio.py +632 -0
- coco_graph/store/sqlite/base.py +1514 -0
- coco_graph/stream/__init__.py +45 -0
- coco_graph/stream/_convert.py +32 -0
- coco_graph/stream/_mux.py +523 -0
- coco_graph/stream/_types.py +330 -0
- coco_graph/stream/run_stream.py +735 -0
- coco_graph/stream/stream_channel.py +341 -0
- coco_graph/stream/transformers.py +1039 -0
- coco_graph/types.py +1079 -0
- coco_graph/typing.py +48 -0
- coco_graph/utils/__init__.py +1 -0
- coco_graph/utils/config.py +4 -0
- coco_graph/utils/runnable.py +3 -0
- coco_graph/version.py +13 -0
- coco_graph/warnings.py +69 -0
- coco_openai/__init__.py +20 -0
- coco_openai/_compat.py +45 -0
- coco_openai/_version.py +3 -0
- coco_openai/chat_models/__init__.py +6 -0
- coco_openai/chat_models/_client_utils.py +684 -0
- coco_openai/chat_models/_compat.py +525 -0
- coco_openai/chat_models/azure.py +1227 -0
- coco_openai/chat_models/base.py +5654 -0
- coco_openai/chat_models/codex.py +557 -0
- coco_openai/chatgpt_oauth.py +1063 -0
- coco_openai/data/__init__.py +1 -0
- coco_openai/data/_profiles.py +1655 -0
- coco_openai/data/profile_augmentations.toml +127 -0
- coco_openai/embeddings/__init__.py +6 -0
- coco_openai/embeddings/azure.py +240 -0
- coco_openai/embeddings/base.py +820 -0
- coco_openai/llms/__init__.py +6 -0
- coco_openai/llms/azure.py +249 -0
- coco_openai/llms/base.py +919 -0
- coco_openai/middleware/__init__.py +11 -0
- coco_openai/middleware/openai_moderation.py +484 -0
- coco_openai/output_parsers/__init__.py +9 -0
- coco_openai/output_parsers/tools.py +9 -0
- coco_openai/py.typed +0 -0
- coco_openai/tools/__init__.py +5 -0
- coco_openai/tools/custom_tool.py +111 -0
coco_core/stores.py
ADDED
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
"""**Store** implements the key-value stores and storage helpers.
|
|
2
|
+
|
|
3
|
+
Module provides implementations of various key-value stores that conform
|
|
4
|
+
to a simple key-value interface.
|
|
5
|
+
|
|
6
|
+
The primary goal of these storages is to support implementation of caching.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from abc import ABC, abstractmethod
|
|
10
|
+
from collections.abc import AsyncIterator, Iterator, Sequence
|
|
11
|
+
from typing import (
|
|
12
|
+
Any,
|
|
13
|
+
Generic,
|
|
14
|
+
TypeVar,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
from typing_extensions import override
|
|
18
|
+
|
|
19
|
+
from coco_core.exceptions import CocoException
|
|
20
|
+
from coco_core.runnables import run_in_executor
|
|
21
|
+
|
|
22
|
+
K = TypeVar("K")
|
|
23
|
+
V = TypeVar("V")
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class BaseStore(ABC, Generic[K, V]):
|
|
27
|
+
"""Abstract interface for a key-value store.
|
|
28
|
+
|
|
29
|
+
This is an interface that's meant to abstract away the details of different
|
|
30
|
+
key-value stores. It provides a simple interface for getting, setting, and deleting
|
|
31
|
+
key-value pairs.
|
|
32
|
+
|
|
33
|
+
The basic methods are `mget`, `mset`, and `mdelete` for getting, setting, and
|
|
34
|
+
deleting multiple key-value pairs at once. The `yield_keys` method is used to
|
|
35
|
+
iterate over keys that match a given prefix.
|
|
36
|
+
|
|
37
|
+
The async versions of these methods are also provided, which are meant to be used in
|
|
38
|
+
async contexts. The async methods are named with an `a` prefix, e.g., `amget`,
|
|
39
|
+
`amset`, `amdelete`, and `ayield_keys`.
|
|
40
|
+
|
|
41
|
+
By default, the `amget`, `amset`, `amdelete`, and `ayield_keys` methods are
|
|
42
|
+
implemented using the synchronous methods. If the store can natively support async
|
|
43
|
+
operations, it should override these methods.
|
|
44
|
+
|
|
45
|
+
By design the methods only accept batches of keys and values, and not single keys or
|
|
46
|
+
values. This is done to force user code to work with batches which will usually be
|
|
47
|
+
more efficient by saving on round trips to the store.
|
|
48
|
+
|
|
49
|
+
Examples:
|
|
50
|
+
```python
|
|
51
|
+
from coco.storage import BaseStore
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class MyInMemoryStore(BaseStore[str, int]):
|
|
55
|
+
def __init__(self) -> None:
|
|
56
|
+
self.store: dict[str, int] = {}
|
|
57
|
+
|
|
58
|
+
def mget(self, keys: Sequence[str]) -> list[int | None]:
|
|
59
|
+
return [self.store.get(key) for key in keys]
|
|
60
|
+
|
|
61
|
+
def mset(self, key_value_pairs: Sequence[tuple[str, int]]) -> None:
|
|
62
|
+
for key, value in key_value_pairs:
|
|
63
|
+
self.store[key] = value
|
|
64
|
+
|
|
65
|
+
def mdelete(self, keys: Sequence[str]) -> None:
|
|
66
|
+
for key in keys:
|
|
67
|
+
if key in self.store:
|
|
68
|
+
del self.store[key]
|
|
69
|
+
|
|
70
|
+
def yield_keys(self, prefix: str | None = None) -> Iterator[str]:
|
|
71
|
+
if prefix is None:
|
|
72
|
+
yield from self.store.keys()
|
|
73
|
+
else:
|
|
74
|
+
for key in self.store.keys():
|
|
75
|
+
if key.startswith(prefix):
|
|
76
|
+
yield key
|
|
77
|
+
```
|
|
78
|
+
"""
|
|
79
|
+
|
|
80
|
+
@abstractmethod
|
|
81
|
+
def mget(self, keys: Sequence[K]) -> list[V | None]:
|
|
82
|
+
"""Get the values associated with the given keys.
|
|
83
|
+
|
|
84
|
+
Args:
|
|
85
|
+
keys: A sequence of keys.
|
|
86
|
+
|
|
87
|
+
Returns:
|
|
88
|
+
A sequence of optional values associated with the keys.
|
|
89
|
+
If a key is not found, the corresponding value will be `None`.
|
|
90
|
+
"""
|
|
91
|
+
|
|
92
|
+
async def amget(self, keys: Sequence[K]) -> list[V | None]:
|
|
93
|
+
"""Async get the values associated with the given keys.
|
|
94
|
+
|
|
95
|
+
Args:
|
|
96
|
+
keys: A sequence of keys.
|
|
97
|
+
|
|
98
|
+
Returns:
|
|
99
|
+
A sequence of optional values associated with the keys.
|
|
100
|
+
If a key is not found, the corresponding value will be `None`.
|
|
101
|
+
"""
|
|
102
|
+
return await run_in_executor(None, self.mget, keys)
|
|
103
|
+
|
|
104
|
+
@abstractmethod
|
|
105
|
+
def mset(self, key_value_pairs: Sequence[tuple[K, V]]) -> None:
|
|
106
|
+
"""Set the values for the given keys.
|
|
107
|
+
|
|
108
|
+
Args:
|
|
109
|
+
key_value_pairs: A sequence of key-value pairs.
|
|
110
|
+
"""
|
|
111
|
+
|
|
112
|
+
async def amset(self, key_value_pairs: Sequence[tuple[K, V]]) -> None:
|
|
113
|
+
"""Async set the values for the given keys.
|
|
114
|
+
|
|
115
|
+
Args:
|
|
116
|
+
key_value_pairs: A sequence of key-value pairs.
|
|
117
|
+
"""
|
|
118
|
+
return await run_in_executor(None, self.mset, key_value_pairs)
|
|
119
|
+
|
|
120
|
+
@abstractmethod
|
|
121
|
+
def mdelete(self, keys: Sequence[K]) -> None:
|
|
122
|
+
"""Delete the given keys and their associated values.
|
|
123
|
+
|
|
124
|
+
Args:
|
|
125
|
+
keys: A sequence of keys to delete.
|
|
126
|
+
"""
|
|
127
|
+
|
|
128
|
+
async def amdelete(self, keys: Sequence[K]) -> None:
|
|
129
|
+
"""Async delete the given keys and their associated values.
|
|
130
|
+
|
|
131
|
+
Args:
|
|
132
|
+
keys: A sequence of keys to delete.
|
|
133
|
+
"""
|
|
134
|
+
return await run_in_executor(None, self.mdelete, keys)
|
|
135
|
+
|
|
136
|
+
@abstractmethod
|
|
137
|
+
def yield_keys(self, *, prefix: str | None = None) -> Iterator[K] | Iterator[str]:
|
|
138
|
+
"""Get an iterator over keys that match the given prefix.
|
|
139
|
+
|
|
140
|
+
Args:
|
|
141
|
+
prefix: The prefix to match.
|
|
142
|
+
|
|
143
|
+
Yields:
|
|
144
|
+
An iterator over keys that match the given prefix.
|
|
145
|
+
|
|
146
|
+
This method is allowed to return an iterator over either K or str
|
|
147
|
+
depending on what makes more sense for the given store.
|
|
148
|
+
"""
|
|
149
|
+
|
|
150
|
+
async def ayield_keys(
|
|
151
|
+
self, *, prefix: str | None = None
|
|
152
|
+
) -> AsyncIterator[K] | AsyncIterator[str]:
|
|
153
|
+
"""Async get an iterator over keys that match the given prefix.
|
|
154
|
+
|
|
155
|
+
Args:
|
|
156
|
+
prefix: The prefix to match.
|
|
157
|
+
|
|
158
|
+
Yields:
|
|
159
|
+
The keys that match the given prefix.
|
|
160
|
+
|
|
161
|
+
This method is allowed to return an iterator over either K or str
|
|
162
|
+
depending on what makes more sense for the given store.
|
|
163
|
+
"""
|
|
164
|
+
iterator = await run_in_executor(None, self.yield_keys, prefix=prefix)
|
|
165
|
+
done = object()
|
|
166
|
+
while True:
|
|
167
|
+
item = await run_in_executor(None, lambda it: next(it, done), iterator)
|
|
168
|
+
if item is done:
|
|
169
|
+
break
|
|
170
|
+
yield item # type: ignore[misc]
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
ByteStore = BaseStore[str, bytes]
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
class InMemoryBaseStore(BaseStore[str, V], Generic[V]):
|
|
177
|
+
"""In-memory implementation of the `BaseStore` using a dictionary."""
|
|
178
|
+
|
|
179
|
+
def __init__(self) -> None:
|
|
180
|
+
"""Initialize an empty store."""
|
|
181
|
+
self.store: dict[str, V] = {}
|
|
182
|
+
|
|
183
|
+
@override
|
|
184
|
+
def mget(self, keys: Sequence[str]) -> list[V | None]:
|
|
185
|
+
return [self.store.get(key) for key in keys]
|
|
186
|
+
|
|
187
|
+
@override
|
|
188
|
+
async def amget(self, keys: Sequence[str]) -> list[V | None]:
|
|
189
|
+
return self.mget(keys)
|
|
190
|
+
|
|
191
|
+
@override
|
|
192
|
+
def mset(self, key_value_pairs: Sequence[tuple[str, V]]) -> None:
|
|
193
|
+
for key, value in key_value_pairs:
|
|
194
|
+
self.store[key] = value
|
|
195
|
+
|
|
196
|
+
@override
|
|
197
|
+
async def amset(self, key_value_pairs: Sequence[tuple[str, V]]) -> None:
|
|
198
|
+
return self.mset(key_value_pairs)
|
|
199
|
+
|
|
200
|
+
@override
|
|
201
|
+
def mdelete(self, keys: Sequence[str]) -> None:
|
|
202
|
+
for key in keys:
|
|
203
|
+
if key in self.store:
|
|
204
|
+
del self.store[key]
|
|
205
|
+
|
|
206
|
+
@override
|
|
207
|
+
async def amdelete(self, keys: Sequence[str]) -> None:
|
|
208
|
+
self.mdelete(keys)
|
|
209
|
+
|
|
210
|
+
def yield_keys(self, *, prefix: str | None = None) -> Iterator[str]:
|
|
211
|
+
"""Get an iterator over keys that match the given prefix.
|
|
212
|
+
|
|
213
|
+
Args:
|
|
214
|
+
prefix: The prefix to match.
|
|
215
|
+
|
|
216
|
+
Yields:
|
|
217
|
+
The keys that match the given prefix.
|
|
218
|
+
"""
|
|
219
|
+
if prefix is None:
|
|
220
|
+
yield from self.store.keys()
|
|
221
|
+
else:
|
|
222
|
+
for key in self.store:
|
|
223
|
+
if key.startswith(prefix):
|
|
224
|
+
yield key
|
|
225
|
+
|
|
226
|
+
async def ayield_keys(self, *, prefix: str | None = None) -> AsyncIterator[str]:
|
|
227
|
+
"""Async get an async iterator over keys that match the given prefix.
|
|
228
|
+
|
|
229
|
+
Args:
|
|
230
|
+
prefix: The prefix to match.
|
|
231
|
+
|
|
232
|
+
Yields:
|
|
233
|
+
The keys that match the given prefix.
|
|
234
|
+
"""
|
|
235
|
+
if prefix is None:
|
|
236
|
+
for key in self.store:
|
|
237
|
+
yield key
|
|
238
|
+
else:
|
|
239
|
+
for key in self.store:
|
|
240
|
+
if key.startswith(prefix):
|
|
241
|
+
yield key
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
class InMemoryStore(InMemoryBaseStore[Any]):
|
|
245
|
+
"""In-memory store for any type of data.
|
|
246
|
+
|
|
247
|
+
Attributes:
|
|
248
|
+
store: The underlying dictionary that stores the key-value pairs.
|
|
249
|
+
|
|
250
|
+
Examples:
|
|
251
|
+
```python
|
|
252
|
+
from coco.storage import InMemoryStore
|
|
253
|
+
|
|
254
|
+
store = InMemoryStore()
|
|
255
|
+
store.mset([("key1", "value1"), ("key2", "value2")])
|
|
256
|
+
store.mget(["key1", "key2"])
|
|
257
|
+
# ['value1', 'value2']
|
|
258
|
+
store.mdelete(["key1"])
|
|
259
|
+
list(store.yield_keys())
|
|
260
|
+
# ['key2']
|
|
261
|
+
list(store.yield_keys(prefix="k"))
|
|
262
|
+
# ['key2']
|
|
263
|
+
```
|
|
264
|
+
"""
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
class InMemoryByteStore(InMemoryBaseStore[bytes]):
|
|
268
|
+
"""In-memory store for bytes.
|
|
269
|
+
|
|
270
|
+
Attributes:
|
|
271
|
+
store: The underlying dictionary that stores the key-value pairs.
|
|
272
|
+
|
|
273
|
+
Examples:
|
|
274
|
+
```python
|
|
275
|
+
from coco.storage import InMemoryByteStore
|
|
276
|
+
|
|
277
|
+
store = InMemoryByteStore()
|
|
278
|
+
store.mset([("key1", b"value1"), ("key2", b"value2")])
|
|
279
|
+
store.mget(["key1", "key2"])
|
|
280
|
+
# [b'value1', b'value2']
|
|
281
|
+
store.mdelete(["key1"])
|
|
282
|
+
list(store.yield_keys())
|
|
283
|
+
# ['key2']
|
|
284
|
+
list(store.yield_keys(prefix="k"))
|
|
285
|
+
# ['key2']
|
|
286
|
+
```
|
|
287
|
+
"""
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
class InvalidKeyException(CocoException):
|
|
291
|
+
"""Raised when a key is invalid; e.g., uses incorrect characters."""
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
"""Internal representation of a structured query language."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from abc import ABC, abstractmethod
|
|
6
|
+
from enum import Enum
|
|
7
|
+
from typing import TYPE_CHECKING, Any
|
|
8
|
+
|
|
9
|
+
from pydantic import BaseModel
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from collections.abc import Sequence
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Visitor(ABC):
|
|
16
|
+
"""Defines interface for IR translation using a visitor pattern."""
|
|
17
|
+
|
|
18
|
+
allowed_comparators: Sequence[Comparator] | None = None
|
|
19
|
+
"""Allowed comparators for the visitor."""
|
|
20
|
+
|
|
21
|
+
allowed_operators: Sequence[Operator] | None = None
|
|
22
|
+
"""Allowed operators for the visitor."""
|
|
23
|
+
|
|
24
|
+
def _validate_func(self, func: Operator | Comparator) -> None:
|
|
25
|
+
if (
|
|
26
|
+
isinstance(func, Operator)
|
|
27
|
+
and self.allowed_operators is not None
|
|
28
|
+
and func not in self.allowed_operators
|
|
29
|
+
):
|
|
30
|
+
msg = (
|
|
31
|
+
f"Received disallowed operator {func}. Allowed "
|
|
32
|
+
f"comparators are {self.allowed_operators}"
|
|
33
|
+
)
|
|
34
|
+
raise ValueError(msg)
|
|
35
|
+
if (
|
|
36
|
+
isinstance(func, Comparator)
|
|
37
|
+
and self.allowed_comparators is not None
|
|
38
|
+
and func not in self.allowed_comparators
|
|
39
|
+
):
|
|
40
|
+
msg = (
|
|
41
|
+
f"Received disallowed comparator {func}. Allowed "
|
|
42
|
+
f"comparators are {self.allowed_comparators}"
|
|
43
|
+
)
|
|
44
|
+
raise ValueError(msg)
|
|
45
|
+
|
|
46
|
+
@abstractmethod
|
|
47
|
+
def visit_operation(self, operation: Operation) -> Any:
|
|
48
|
+
"""Translate an Operation.
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
operation: Operation to translate.
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
@abstractmethod
|
|
55
|
+
def visit_comparison(self, comparison: Comparison) -> Any:
|
|
56
|
+
"""Translate a Comparison.
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
comparison: Comparison to translate.
|
|
60
|
+
"""
|
|
61
|
+
|
|
62
|
+
@abstractmethod
|
|
63
|
+
def visit_structured_query(self, structured_query: StructuredQuery) -> Any:
|
|
64
|
+
"""Translate a StructuredQuery.
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
structured_query: StructuredQuery to translate.
|
|
68
|
+
"""
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _to_snake_case(name: str) -> str:
|
|
72
|
+
"""Convert a name into snake_case."""
|
|
73
|
+
snake_case = ""
|
|
74
|
+
for i, char in enumerate(name):
|
|
75
|
+
if char.isupper() and i != 0:
|
|
76
|
+
snake_case += "_" + char.lower()
|
|
77
|
+
else:
|
|
78
|
+
snake_case += char.lower()
|
|
79
|
+
return snake_case
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class Expr(BaseModel):
|
|
83
|
+
"""Base class for all expressions."""
|
|
84
|
+
|
|
85
|
+
def accept(self, visitor: Visitor) -> Any:
|
|
86
|
+
"""Accept a visitor.
|
|
87
|
+
|
|
88
|
+
Args:
|
|
89
|
+
visitor: visitor to accept.
|
|
90
|
+
|
|
91
|
+
Returns:
|
|
92
|
+
result of visiting.
|
|
93
|
+
"""
|
|
94
|
+
return getattr(visitor, f"visit_{_to_snake_case(self.__class__.__name__)}")(
|
|
95
|
+
self
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class Operator(str, Enum):
|
|
100
|
+
"""Enumerator of the operations."""
|
|
101
|
+
|
|
102
|
+
AND = "and"
|
|
103
|
+
OR = "or"
|
|
104
|
+
NOT = "not"
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
class Comparator(str, Enum):
|
|
108
|
+
"""Enumerator of the comparison operators."""
|
|
109
|
+
|
|
110
|
+
EQ = "eq"
|
|
111
|
+
NE = "ne"
|
|
112
|
+
GT = "gt"
|
|
113
|
+
GTE = "gte"
|
|
114
|
+
LT = "lt"
|
|
115
|
+
LTE = "lte"
|
|
116
|
+
CONTAIN = "contain"
|
|
117
|
+
LIKE = "like"
|
|
118
|
+
IN = "in"
|
|
119
|
+
NIN = "nin"
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
class FilterDirective(Expr, ABC):
|
|
123
|
+
"""Filtering expression."""
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
class Comparison(FilterDirective):
|
|
127
|
+
"""Comparison to a value."""
|
|
128
|
+
|
|
129
|
+
comparator: Comparator
|
|
130
|
+
"""The comparator to use."""
|
|
131
|
+
|
|
132
|
+
attribute: str
|
|
133
|
+
"""The attribute to compare."""
|
|
134
|
+
|
|
135
|
+
value: Any
|
|
136
|
+
"""The value to compare to."""
|
|
137
|
+
|
|
138
|
+
def __init__(
|
|
139
|
+
self, comparator: Comparator, attribute: str, value: Any, **kwargs: Any
|
|
140
|
+
) -> None:
|
|
141
|
+
"""Create a Comparison.
|
|
142
|
+
|
|
143
|
+
Args:
|
|
144
|
+
comparator: The comparator to use.
|
|
145
|
+
attribute: The attribute to compare.
|
|
146
|
+
value: The value to compare to.
|
|
147
|
+
"""
|
|
148
|
+
# super exists from BaseModel
|
|
149
|
+
super().__init__(
|
|
150
|
+
comparator=comparator, attribute=attribute, value=value, **kwargs
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
class Operation(FilterDirective):
|
|
155
|
+
"""Logical operation over other directives."""
|
|
156
|
+
|
|
157
|
+
operator: Operator
|
|
158
|
+
"""The operator to use."""
|
|
159
|
+
|
|
160
|
+
arguments: list[FilterDirective]
|
|
161
|
+
"""The arguments to the operator."""
|
|
162
|
+
|
|
163
|
+
def __init__(
|
|
164
|
+
self, operator: Operator, arguments: list[FilterDirective], **kwargs: Any
|
|
165
|
+
) -> None:
|
|
166
|
+
"""Create an Operation.
|
|
167
|
+
|
|
168
|
+
Args:
|
|
169
|
+
operator: The operator to use.
|
|
170
|
+
arguments: The arguments to the operator.
|
|
171
|
+
"""
|
|
172
|
+
# super exists from BaseModel
|
|
173
|
+
super().__init__(operator=operator, arguments=arguments, **kwargs)
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
class StructuredQuery(Expr):
|
|
177
|
+
"""Structured query."""
|
|
178
|
+
|
|
179
|
+
query: str
|
|
180
|
+
"""Query string."""
|
|
181
|
+
|
|
182
|
+
filter: FilterDirective | None
|
|
183
|
+
"""Filtering expression."""
|
|
184
|
+
|
|
185
|
+
limit: int | None
|
|
186
|
+
"""Limit on the number of results."""
|
|
187
|
+
|
|
188
|
+
def __init__(
|
|
189
|
+
self,
|
|
190
|
+
query: str,
|
|
191
|
+
filter: FilterDirective | None, # noqa: A002
|
|
192
|
+
limit: int | None = None,
|
|
193
|
+
**kwargs: Any,
|
|
194
|
+
) -> None:
|
|
195
|
+
"""Create a StructuredQuery.
|
|
196
|
+
|
|
197
|
+
Args:
|
|
198
|
+
query: The query string.
|
|
199
|
+
filter: The filtering expression.
|
|
200
|
+
limit: The limit on the number of results.
|
|
201
|
+
"""
|
|
202
|
+
# super exists from BaseModel
|
|
203
|
+
super().__init__(query=query, filter=filter, limit=limit, **kwargs)
|
coco_core/sys_info.py
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
"""Print information about the system and coco packages for debugging purposes."""
|
|
2
|
+
|
|
3
|
+
import pkgutil
|
|
4
|
+
import platform
|
|
5
|
+
import re
|
|
6
|
+
import sys
|
|
7
|
+
from collections.abc import Sequence
|
|
8
|
+
from importlib import metadata, util
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _dist_name(pkg: str) -> str:
|
|
12
|
+
"""Map a Coco module name (`coco`, `coco_core`) to its `trustcoco` distribution."""
|
|
13
|
+
dist = pkg.replace("_", "-")
|
|
14
|
+
if pkg == "coco" or pkg.startswith("coco_"):
|
|
15
|
+
return f"trust{dist}"
|
|
16
|
+
return dist
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _get_sub_deps(packages: Sequence[str]) -> list[str]:
|
|
20
|
+
"""Get any specified sub-dependencies."""
|
|
21
|
+
sub_deps = set()
|
|
22
|
+
underscored_packages = {pkg.replace("-", "_") for pkg in packages} | {
|
|
23
|
+
_dist_name(pkg).replace("-", "_") for pkg in packages
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
for pkg in packages:
|
|
27
|
+
try:
|
|
28
|
+
required = metadata.requires(_dist_name(pkg))
|
|
29
|
+
except metadata.PackageNotFoundError:
|
|
30
|
+
try:
|
|
31
|
+
required = metadata.requires(pkg)
|
|
32
|
+
except metadata.PackageNotFoundError:
|
|
33
|
+
continue
|
|
34
|
+
|
|
35
|
+
if not required:
|
|
36
|
+
continue
|
|
37
|
+
|
|
38
|
+
for req in required:
|
|
39
|
+
# Extract package name (e.g., "httpx<1,>=0.23.0" -> "httpx")
|
|
40
|
+
match = re.match(r"^([a-zA-Z0-9_.-]+)", req)
|
|
41
|
+
if match:
|
|
42
|
+
pkg_name = match.group(1)
|
|
43
|
+
if pkg_name.replace("-", "_") not in underscored_packages:
|
|
44
|
+
sub_deps.add(pkg_name)
|
|
45
|
+
|
|
46
|
+
return sorted(sub_deps, key=lambda x: x.lower())
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def print_sys_info(*, additional_pkgs: Sequence[str] = ()) -> None:
|
|
50
|
+
"""Print information about the environment for debugging purposes.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
additional_pkgs: Additional packages to include in the output.
|
|
54
|
+
"""
|
|
55
|
+
# Packages that do not start with "coco" prefix or are namespace packages.
|
|
56
|
+
other_coco_packages = [
|
|
57
|
+
"coco_graph",
|
|
58
|
+
"langsmith",
|
|
59
|
+
"deepagents",
|
|
60
|
+
"deepagents-cli",
|
|
61
|
+
]
|
|
62
|
+
|
|
63
|
+
coco_pkgs = [
|
|
64
|
+
name for _, name, _ in pkgutil.iter_modules() if name.startswith("coco")
|
|
65
|
+
]
|
|
66
|
+
|
|
67
|
+
coco_graph_pkgs = [
|
|
68
|
+
name for _, name, _ in pkgutil.iter_modules() if name.startswith("coco_graph")
|
|
69
|
+
]
|
|
70
|
+
|
|
71
|
+
all_packages = sorted(
|
|
72
|
+
set(
|
|
73
|
+
coco_pkgs
|
|
74
|
+
+ coco_graph_pkgs
|
|
75
|
+
+ other_coco_packages
|
|
76
|
+
+ list(additional_pkgs)
|
|
77
|
+
)
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
# Always surface these packages to the top
|
|
81
|
+
order_by = ["coco_core", "coco", "coco_community", "langsmith"]
|
|
82
|
+
|
|
83
|
+
for pkg in reversed(order_by):
|
|
84
|
+
if pkg in all_packages:
|
|
85
|
+
all_packages.remove(pkg)
|
|
86
|
+
all_packages = [pkg, *list(all_packages)]
|
|
87
|
+
|
|
88
|
+
system_info = {
|
|
89
|
+
"OS": platform.system(),
|
|
90
|
+
"OS Version": platform.version(),
|
|
91
|
+
"Python Version": sys.version,
|
|
92
|
+
}
|
|
93
|
+
print()
|
|
94
|
+
print("System Information")
|
|
95
|
+
print("------------------")
|
|
96
|
+
print("> OS: ", system_info["OS"])
|
|
97
|
+
print("> OS Version: ", system_info["OS Version"])
|
|
98
|
+
print("> Python Version: ", system_info["Python Version"])
|
|
99
|
+
|
|
100
|
+
# Print out only coco packages
|
|
101
|
+
print()
|
|
102
|
+
print("Package Information")
|
|
103
|
+
print("-------------------")
|
|
104
|
+
|
|
105
|
+
not_installed = []
|
|
106
|
+
|
|
107
|
+
for pkg in all_packages:
|
|
108
|
+
try:
|
|
109
|
+
found_package = util.find_spec(pkg)
|
|
110
|
+
except Exception:
|
|
111
|
+
found_package = None
|
|
112
|
+
if found_package is None:
|
|
113
|
+
not_installed.append(pkg)
|
|
114
|
+
continue
|
|
115
|
+
|
|
116
|
+
# Package version
|
|
117
|
+
try:
|
|
118
|
+
package_version = metadata.version(_dist_name(pkg))
|
|
119
|
+
except Exception:
|
|
120
|
+
try:
|
|
121
|
+
package_version = metadata.version(pkg)
|
|
122
|
+
except Exception:
|
|
123
|
+
package_version = None
|
|
124
|
+
|
|
125
|
+
# Print package with version
|
|
126
|
+
if package_version is not None:
|
|
127
|
+
print(f"> {pkg}: {package_version}")
|
|
128
|
+
|
|
129
|
+
if not_installed:
|
|
130
|
+
print()
|
|
131
|
+
print("Optional packages not installed")
|
|
132
|
+
print("-------------------------------")
|
|
133
|
+
for pkg in not_installed:
|
|
134
|
+
print(f"> {pkg}")
|
|
135
|
+
|
|
136
|
+
sub_dependencies = _get_sub_deps(all_packages)
|
|
137
|
+
|
|
138
|
+
if sub_dependencies:
|
|
139
|
+
print()
|
|
140
|
+
print("Other Dependencies")
|
|
141
|
+
print("------------------")
|
|
142
|
+
|
|
143
|
+
for dep in sub_dependencies:
|
|
144
|
+
try:
|
|
145
|
+
dep_version = metadata.version(dep)
|
|
146
|
+
except Exception:
|
|
147
|
+
dep_version = None
|
|
148
|
+
|
|
149
|
+
if dep_version is not None:
|
|
150
|
+
print(f"> {dep}: {dep_version}")
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
if __name__ == "__main__":
|
|
154
|
+
print_sys_info()
|