lionagi 0.3.8__py3-none-any.whl → 0.5.0__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- lionagi/__init__.py +14 -46
- lionagi/core/__init__.py +3 -1
- lionagi/core/_class_registry.py +69 -0
- lionagi/core/action/__init__.py +3 -13
- lionagi/core/action/action_manager.py +287 -0
- lionagi/core/action/base.py +109 -0
- lionagi/core/action/function_calling.py +127 -92
- lionagi/core/action/tool.py +172 -70
- lionagi/core/action/types.py +16 -0
- lionagi/core/communication/__init__.py +3 -0
- lionagi/core/communication/action_request.py +163 -0
- lionagi/core/communication/action_response.py +149 -0
- lionagi/core/communication/assistant_response.py +161 -0
- lionagi/core/communication/base_mail.py +49 -0
- lionagi/core/communication/instruction.py +376 -0
- lionagi/core/communication/message.py +286 -0
- lionagi/core/communication/message_manager.py +530 -0
- lionagi/core/communication/system.py +116 -0
- lionagi/core/communication/templates/README.md +28 -0
- lionagi/core/communication/templates/action_request.jinja2 +5 -0
- lionagi/core/communication/templates/action_response.jinja2 +9 -0
- lionagi/core/communication/templates/assistant_response.jinja2 +2 -0
- lionagi/core/communication/templates/instruction_message.jinja2 +61 -0
- lionagi/core/communication/templates/system_message.jinja2 +11 -0
- lionagi/core/communication/templates/tool_schemas.jinja2 +7 -0
- lionagi/core/communication/types.py +27 -0
- lionagi/core/communication/utils.py +254 -0
- lionagi/core/forms/__init__.py +3 -0
- lionagi/core/forms/base.py +232 -0
- lionagi/core/forms/form.py +791 -0
- lionagi/core/forms/report.py +321 -0
- lionagi/core/forms/types.py +13 -0
- lionagi/core/forms/utils.py +26 -0
- lionagi/core/generic/__init__.py +3 -6
- lionagi/core/generic/component.py +422 -0
- lionagi/core/generic/edge.py +143 -101
- lionagi/core/generic/element.py +195 -0
- lionagi/core/generic/graph.py +297 -180
- lionagi/core/generic/log.py +151 -0
- lionagi/core/generic/log_manager.py +320 -0
- lionagi/core/generic/node.py +7 -229
- lionagi/core/generic/pile.py +1017 -0
- lionagi/core/generic/progression.py +388 -0
- lionagi/core/generic/types.py +23 -0
- lionagi/core/generic/utils.py +50 -0
- lionagi/core/models/__init__.py +5 -0
- lionagi/core/models/base.py +85 -0
- lionagi/core/models/field_model.py +122 -0
- lionagi/core/models/new_model_params.py +195 -0
- lionagi/core/models/note.py +351 -0
- lionagi/core/models/operable_model.py +392 -0
- lionagi/core/models/schema_model.py +50 -0
- lionagi/core/models/types.py +10 -0
- lionagi/core/session/__init__.py +3 -0
- lionagi/core/session/branch.py +115 -415
- lionagi/core/session/branch_mixins.py +507 -0
- lionagi/core/session/session.py +122 -257
- lionagi/core/session/types.py +8 -0
- lionagi/core/typing/__init__.py +9 -0
- lionagi/core/typing/concepts.py +132 -0
- lionagi/core/typing/config.py +15 -0
- lionagi/core/typing/id.py +221 -0
- lionagi/core/typing/pydantic_.py +33 -0
- lionagi/core/typing/typing_.py +54 -0
- lionagi/integrations/__init__.py +0 -1
- lionagi/integrations/anthropic_/AnthropicModel.py +268 -0
- lionagi/integrations/anthropic_/AnthropicService.py +113 -0
- lionagi/integrations/anthropic_/__init__.py +3 -0
- lionagi/integrations/anthropic_/anthropic_max_output_token_data.yaml +7 -0
- lionagi/integrations/anthropic_/anthropic_price_data.yaml +14 -0
- lionagi/integrations/anthropic_/api_endpoints/__init__.py +3 -0
- lionagi/integrations/anthropic_/api_endpoints/api_request.py +277 -0
- lionagi/integrations/anthropic_/api_endpoints/data_models.py +40 -0
- lionagi/integrations/anthropic_/api_endpoints/match_response.py +119 -0
- lionagi/integrations/anthropic_/api_endpoints/messages/__init__.py +3 -0
- lionagi/integrations/anthropic_/api_endpoints/messages/request/__init__.py +3 -0
- lionagi/integrations/anthropic_/api_endpoints/messages/request/message_models.py +14 -0
- lionagi/integrations/anthropic_/api_endpoints/messages/request/request_body.py +74 -0
- lionagi/integrations/anthropic_/api_endpoints/messages/response/content_models.py +32 -0
- lionagi/integrations/anthropic_/api_endpoints/messages/response/response_body.py +101 -0
- lionagi/integrations/anthropic_/api_endpoints/messages/response/usage_models.py +25 -0
- lionagi/integrations/anthropic_/version.py +5 -0
- lionagi/integrations/groq_/GroqModel.py +318 -0
- lionagi/integrations/groq_/GroqService.py +147 -0
- lionagi/integrations/groq_/__init__.py +3 -0
- lionagi/integrations/groq_/api_endpoints/data_models.py +187 -0
- lionagi/integrations/groq_/api_endpoints/groq_request.py +288 -0
- lionagi/integrations/groq_/api_endpoints/match_response.py +106 -0
- lionagi/integrations/groq_/api_endpoints/response_utils.py +105 -0
- lionagi/integrations/groq_/groq_max_output_token_data.yaml +21 -0
- lionagi/integrations/groq_/groq_price_data.yaml +58 -0
- lionagi/integrations/groq_/groq_rate_limits.yaml +105 -0
- lionagi/integrations/groq_/version.py +5 -0
- lionagi/integrations/litellm_/__init__.py +3 -0
- lionagi/integrations/litellm_/imodel.py +69 -0
- lionagi/integrations/ollama_/OllamaModel.py +244 -0
- lionagi/integrations/ollama_/OllamaService.py +138 -0
- lionagi/integrations/ollama_/__init__.py +3 -0
- lionagi/integrations/ollama_/api_endpoints/__init__.py +3 -0
- lionagi/integrations/ollama_/api_endpoints/api_request.py +179 -0
- lionagi/integrations/ollama_/api_endpoints/chat_completion/__init__.py +3 -0
- lionagi/integrations/ollama_/api_endpoints/chat_completion/message_models.py +31 -0
- lionagi/integrations/ollama_/api_endpoints/chat_completion/request_body.py +46 -0
- lionagi/integrations/ollama_/api_endpoints/chat_completion/response_body.py +67 -0
- lionagi/integrations/ollama_/api_endpoints/chat_completion/tool_models.py +49 -0
- lionagi/integrations/ollama_/api_endpoints/completion/request_body.py +72 -0
- lionagi/integrations/ollama_/api_endpoints/completion/response_body.py +59 -0
- lionagi/integrations/ollama_/api_endpoints/data_models.py +15 -0
- lionagi/integrations/ollama_/api_endpoints/embedding/request_body.py +33 -0
- lionagi/integrations/ollama_/api_endpoints/embedding/response_body.py +29 -0
- lionagi/integrations/ollama_/api_endpoints/match_data_model.py +62 -0
- lionagi/integrations/ollama_/api_endpoints/match_response.py +190 -0
- lionagi/integrations/ollama_/api_endpoints/model/__init__.py +3 -0
- lionagi/integrations/ollama_/api_endpoints/model/copy_model.py +13 -0
- lionagi/integrations/ollama_/api_endpoints/model/create_model.py +28 -0
- lionagi/integrations/ollama_/api_endpoints/model/delete_model.py +11 -0
- lionagi/integrations/ollama_/api_endpoints/model/list_model.py +60 -0
- lionagi/integrations/ollama_/api_endpoints/model/pull_model.py +34 -0
- lionagi/integrations/ollama_/api_endpoints/model/push_model.py +35 -0
- lionagi/integrations/ollama_/api_endpoints/model/show_model.py +36 -0
- lionagi/integrations/ollama_/api_endpoints/option_models.py +68 -0
- lionagi/integrations/openai_/OpenAIModel.py +414 -0
- lionagi/integrations/openai_/OpenAIService.py +426 -0
- lionagi/integrations/openai_/api_endpoints/__init__.py +3 -0
- lionagi/integrations/openai_/api_endpoints/api_request.py +277 -0
- lionagi/integrations/openai_/api_endpoints/audio/__init__.py +9 -0
- lionagi/integrations/openai_/api_endpoints/audio/speech_models.py +34 -0
- lionagi/integrations/openai_/api_endpoints/audio/transcription_models.py +136 -0
- lionagi/integrations/openai_/api_endpoints/audio/translation_models.py +41 -0
- lionagi/integrations/openai_/api_endpoints/audio/types.py +41 -0
- lionagi/integrations/openai_/api_endpoints/batch/__init__.py +17 -0
- lionagi/integrations/openai_/api_endpoints/batch/batch_models.py +146 -0
- lionagi/integrations/openai_/api_endpoints/batch/cancel_batch.py +7 -0
- lionagi/integrations/openai_/api_endpoints/batch/create_batch.py +26 -0
- lionagi/integrations/openai_/api_endpoints/batch/list_batch.py +37 -0
- lionagi/integrations/openai_/api_endpoints/batch/request_object_models.py +65 -0
- lionagi/integrations/openai_/api_endpoints/batch/retrieve_batch.py +7 -0
- lionagi/integrations/openai_/api_endpoints/batch/types.py +4 -0
- lionagi/integrations/openai_/api_endpoints/chat_completions/__init__.py +1 -0
- lionagi/integrations/openai_/api_endpoints/chat_completions/request/__init__.py +39 -0
- lionagi/integrations/openai_/api_endpoints/chat_completions/request/message_models.py +121 -0
- lionagi/integrations/openai_/api_endpoints/chat_completions/request/request_body.py +221 -0
- lionagi/integrations/openai_/api_endpoints/chat_completions/request/response_format.py +71 -0
- lionagi/integrations/openai_/api_endpoints/chat_completions/request/stream_options.py +14 -0
- lionagi/integrations/openai_/api_endpoints/chat_completions/request/tool_choice_models.py +17 -0
- lionagi/integrations/openai_/api_endpoints/chat_completions/request/tool_models.py +54 -0
- lionagi/integrations/openai_/api_endpoints/chat_completions/request/types.py +18 -0
- lionagi/integrations/openai_/api_endpoints/chat_completions/response/choice_models.py +62 -0
- lionagi/integrations/openai_/api_endpoints/chat_completions/response/function_models.py +16 -0
- lionagi/integrations/openai_/api_endpoints/chat_completions/response/log_prob_models.py +47 -0
- lionagi/integrations/openai_/api_endpoints/chat_completions/response/message_models.py +25 -0
- lionagi/integrations/openai_/api_endpoints/chat_completions/response/response_body.py +99 -0
- lionagi/integrations/openai_/api_endpoints/chat_completions/response/types.py +8 -0
- lionagi/integrations/openai_/api_endpoints/chat_completions/response/usage_models.py +24 -0
- lionagi/integrations/openai_/api_endpoints/chat_completions/util.py +46 -0
- lionagi/integrations/openai_/api_endpoints/data_models.py +23 -0
- lionagi/integrations/openai_/api_endpoints/embeddings/__init__.py +3 -0
- lionagi/integrations/openai_/api_endpoints/embeddings/request_body.py +79 -0
- lionagi/integrations/openai_/api_endpoints/embeddings/response_body.py +67 -0
- lionagi/integrations/openai_/api_endpoints/files/__init__.py +11 -0
- lionagi/integrations/openai_/api_endpoints/files/delete_file.py +20 -0
- lionagi/integrations/openai_/api_endpoints/files/file_models.py +56 -0
- lionagi/integrations/openai_/api_endpoints/files/list_files.py +27 -0
- lionagi/integrations/openai_/api_endpoints/files/retrieve_file.py +9 -0
- lionagi/integrations/openai_/api_endpoints/files/upload_file.py +38 -0
- lionagi/integrations/openai_/api_endpoints/fine_tuning/__init__.py +37 -0
- lionagi/integrations/openai_/api_endpoints/fine_tuning/cancel_jobs.py +9 -0
- lionagi/integrations/openai_/api_endpoints/fine_tuning/create_jobs.py +133 -0
- lionagi/integrations/openai_/api_endpoints/fine_tuning/fine_tuning_job_checkpoint_models.py +58 -0
- lionagi/integrations/openai_/api_endpoints/fine_tuning/fine_tuning_job_event_models.py +31 -0
- lionagi/integrations/openai_/api_endpoints/fine_tuning/fine_tuning_job_models.py +140 -0
- lionagi/integrations/openai_/api_endpoints/fine_tuning/list_fine_tuning_checkpoints.py +51 -0
- lionagi/integrations/openai_/api_endpoints/fine_tuning/list_fine_tuning_events.py +42 -0
- lionagi/integrations/openai_/api_endpoints/fine_tuning/list_fine_tuning_jobs.py +31 -0
- lionagi/integrations/openai_/api_endpoints/fine_tuning/retrieve_jobs.py +9 -0
- lionagi/integrations/openai_/api_endpoints/fine_tuning/training_format.py +30 -0
- lionagi/integrations/openai_/api_endpoints/images/__init__.py +9 -0
- lionagi/integrations/openai_/api_endpoints/images/image_edit_models.py +69 -0
- lionagi/integrations/openai_/api_endpoints/images/image_models.py +56 -0
- lionagi/integrations/openai_/api_endpoints/images/image_variation_models.py +56 -0
- lionagi/integrations/openai_/api_endpoints/images/response_body.py +30 -0
- lionagi/integrations/openai_/api_endpoints/match_data_model.py +197 -0
- lionagi/integrations/openai_/api_endpoints/match_response.py +336 -0
- lionagi/integrations/openai_/api_endpoints/models/__init__.py +7 -0
- lionagi/integrations/openai_/api_endpoints/models/delete_fine_tuned_model.py +17 -0
- lionagi/integrations/openai_/api_endpoints/models/models_models.py +31 -0
- lionagi/integrations/openai_/api_endpoints/models/retrieve_model.py +9 -0
- lionagi/integrations/openai_/api_endpoints/moderations/__init__.py +3 -0
- lionagi/integrations/openai_/api_endpoints/moderations/request_body.py +20 -0
- lionagi/integrations/openai_/api_endpoints/moderations/response_body.py +139 -0
- lionagi/integrations/openai_/api_endpoints/uploads/__init__.py +19 -0
- lionagi/integrations/openai_/api_endpoints/uploads/add_upload_part.py +11 -0
- lionagi/integrations/openai_/api_endpoints/uploads/cancel_upload.py +7 -0
- lionagi/integrations/openai_/api_endpoints/uploads/complete_upload.py +18 -0
- lionagi/integrations/openai_/api_endpoints/uploads/create_upload.py +17 -0
- lionagi/integrations/openai_/api_endpoints/uploads/uploads_models.py +52 -0
- lionagi/integrations/openai_/image_token_calculator/image_token_calculator.py +92 -0
- lionagi/integrations/openai_/image_token_calculator/openai_image_token_data.yaml +15 -0
- lionagi/integrations/openai_/openai_max_output_token_data.yaml +12 -0
- lionagi/integrations/openai_/openai_price_data.yaml +26 -0
- lionagi/integrations/openai_/version.py +1 -0
- lionagi/integrations/pandas_/__init__.py +24 -0
- lionagi/integrations/pandas_/extend_df.py +61 -0
- lionagi/integrations/pandas_/read.py +103 -0
- lionagi/integrations/pandas_/remove_rows.py +61 -0
- lionagi/integrations/pandas_/replace_keywords.py +65 -0
- lionagi/integrations/pandas_/save.py +131 -0
- lionagi/integrations/pandas_/search_keywords.py +69 -0
- lionagi/integrations/pandas_/to_df.py +196 -0
- lionagi/integrations/pandas_/update_cells.py +54 -0
- lionagi/integrations/perplexity_/PerplexityModel.py +269 -0
- lionagi/integrations/perplexity_/PerplexityService.py +109 -0
- lionagi/integrations/perplexity_/__init__.py +3 -0
- lionagi/integrations/perplexity_/api_endpoints/api_request.py +171 -0
- lionagi/integrations/perplexity_/api_endpoints/chat_completions/request/request_body.py +121 -0
- lionagi/integrations/perplexity_/api_endpoints/chat_completions/response/response_body.py +146 -0
- lionagi/integrations/perplexity_/api_endpoints/data_models.py +63 -0
- lionagi/integrations/perplexity_/api_endpoints/match_response.py +26 -0
- lionagi/integrations/perplexity_/perplexity_max_output_token_data.yaml +3 -0
- lionagi/integrations/perplexity_/perplexity_price_data.yaml +10 -0
- lionagi/integrations/perplexity_/version.py +1 -0
- lionagi/integrations/pydantic_/__init__.py +8 -0
- lionagi/integrations/pydantic_/break_down_annotation.py +81 -0
- lionagi/integrations/pydantic_/new_model.py +208 -0
- lionagi/integrations/services.py +17 -0
- lionagi/libs/__init__.py +0 -55
- lionagi/libs/compress/models.py +62 -0
- lionagi/libs/compress/utils.py +81 -0
- lionagi/libs/constants.py +98 -0
- lionagi/libs/file/chunk.py +265 -0
- lionagi/libs/file/file_ops.py +114 -0
- lionagi/libs/file/params.py +212 -0
- lionagi/libs/file/path.py +301 -0
- lionagi/libs/file/process.py +139 -0
- lionagi/libs/file/save.py +90 -0
- lionagi/libs/file/types.py +22 -0
- lionagi/libs/func/async_calls/__init__.py +21 -0
- lionagi/libs/func/async_calls/alcall.py +157 -0
- lionagi/libs/func/async_calls/bcall.py +82 -0
- lionagi/libs/func/async_calls/mcall.py +134 -0
- lionagi/libs/func/async_calls/pcall.py +149 -0
- lionagi/libs/func/async_calls/rcall.py +185 -0
- lionagi/libs/func/async_calls/tcall.py +114 -0
- lionagi/libs/func/async_calls/ucall.py +85 -0
- lionagi/libs/func/decorators.py +277 -0
- lionagi/libs/func/lcall.py +57 -0
- lionagi/libs/func/params.py +64 -0
- lionagi/libs/func/throttle.py +119 -0
- lionagi/libs/func/types.py +39 -0
- lionagi/libs/func/utils.py +96 -0
- lionagi/libs/package/imports.py +162 -0
- lionagi/libs/package/management.py +58 -0
- lionagi/libs/package/params.py +26 -0
- lionagi/libs/package/system.py +18 -0
- lionagi/libs/package/types.py +26 -0
- lionagi/libs/parse/__init__.py +1 -0
- lionagi/libs/parse/flatten/__init__.py +9 -0
- lionagi/libs/parse/flatten/flatten.py +168 -0
- lionagi/libs/parse/flatten/params.py +52 -0
- lionagi/libs/parse/flatten/unflatten.py +79 -0
- lionagi/libs/parse/json/__init__.py +27 -0
- lionagi/libs/parse/json/as_readable.py +104 -0
- lionagi/libs/parse/json/extract.py +102 -0
- lionagi/libs/parse/json/parse.py +179 -0
- lionagi/libs/parse/json/schema.py +227 -0
- lionagi/libs/parse/json/to_json.py +71 -0
- lionagi/libs/parse/nested/__init__.py +33 -0
- lionagi/libs/parse/nested/nfilter.py +55 -0
- lionagi/libs/parse/nested/nget.py +40 -0
- lionagi/libs/parse/nested/ninsert.py +103 -0
- lionagi/libs/parse/nested/nmerge.py +155 -0
- lionagi/libs/parse/nested/npop.py +66 -0
- lionagi/libs/parse/nested/nset.py +89 -0
- lionagi/libs/parse/nested/to_flat_list.py +64 -0
- lionagi/libs/parse/nested/utils.py +185 -0
- lionagi/libs/parse/string_parse/__init__.py +11 -0
- lionagi/libs/parse/string_parse/code_block.py +73 -0
- lionagi/libs/parse/string_parse/docstring.py +179 -0
- lionagi/libs/parse/string_parse/function_.py +92 -0
- lionagi/libs/parse/type_convert/__init__.py +19 -0
- lionagi/libs/parse/type_convert/params.py +145 -0
- lionagi/libs/parse/type_convert/to_dict.py +333 -0
- lionagi/libs/parse/type_convert/to_list.py +186 -0
- lionagi/libs/parse/type_convert/to_num.py +358 -0
- lionagi/libs/parse/type_convert/to_str.py +195 -0
- lionagi/libs/parse/types.py +9 -0
- lionagi/libs/parse/validate/__init__.py +14 -0
- lionagi/libs/parse/validate/boolean.py +96 -0
- lionagi/libs/parse/validate/keys.py +150 -0
- lionagi/libs/parse/validate/mapping.py +109 -0
- lionagi/libs/parse/validate/params.py +62 -0
- lionagi/libs/parse/xml/__init__.py +10 -0
- lionagi/libs/parse/xml/convert.py +56 -0
- lionagi/libs/parse/xml/parser.py +93 -0
- lionagi/libs/string_similarity/__init__.py +32 -0
- lionagi/libs/string_similarity/algorithms.py +219 -0
- lionagi/libs/string_similarity/matcher.py +102 -0
- lionagi/libs/string_similarity/utils.py +15 -0
- lionagi/libs/utils.py +255 -0
- lionagi/operations/__init__.py +3 -6
- lionagi/operations/brainstorm/__init__.py +3 -0
- lionagi/operations/brainstorm/brainstorm.py +204 -0
- lionagi/operations/brainstorm/prompt.py +1 -0
- lionagi/operations/plan/__init__.py +3 -0
- lionagi/operations/plan/plan.py +172 -0
- lionagi/operations/plan/prompt.py +21 -0
- lionagi/operations/select/__init__.py +3 -0
- lionagi/operations/select/prompt.py +1 -0
- lionagi/operations/select/select.py +100 -0
- lionagi/operations/select/utils.py +107 -0
- lionagi/operations/utils.py +35 -0
- lionagi/protocols/adapters/adapter.py +79 -0
- lionagi/protocols/adapters/json_adapter.py +43 -0
- lionagi/protocols/adapters/pandas_adapter.py +96 -0
- lionagi/protocols/configs/__init__.py +15 -0
- lionagi/protocols/configs/branch_config.py +86 -0
- lionagi/protocols/configs/id_config.py +15 -0
- lionagi/protocols/configs/imodel_config.py +73 -0
- lionagi/protocols/configs/log_config.py +93 -0
- lionagi/protocols/configs/retry_config.py +29 -0
- lionagi/protocols/operatives/__init__.py +15 -0
- lionagi/protocols/operatives/action.py +181 -0
- lionagi/protocols/operatives/instruct.py +196 -0
- lionagi/protocols/operatives/operative.py +182 -0
- lionagi/protocols/operatives/prompts.py +232 -0
- lionagi/protocols/operatives/reason.py +56 -0
- lionagi/protocols/operatives/step.py +217 -0
- lionagi/protocols/registries/_component_registry.py +19 -0
- lionagi/protocols/registries/_pile_registry.py +26 -0
- lionagi/service/__init__.py +13 -0
- lionagi/service/complete_request_info.py +11 -0
- lionagi/service/imodel.py +110 -0
- lionagi/service/rate_limiter.py +108 -0
- lionagi/service/service.py +37 -0
- lionagi/service/service_match_util.py +131 -0
- lionagi/service/service_util.py +72 -0
- lionagi/service/token_calculator.py +51 -0
- lionagi/settings.py +136 -0
- lionagi/strategies/base.py +53 -0
- lionagi/strategies/concurrent.py +71 -0
- lionagi/strategies/concurrent_chunk.py +43 -0
- lionagi/strategies/concurrent_sequential_chunk.py +104 -0
- lionagi/strategies/params.py +128 -0
- lionagi/strategies/sequential.py +23 -0
- lionagi/strategies/sequential_chunk.py +89 -0
- lionagi/strategies/sequential_concurrent_chunk.py +100 -0
- lionagi/strategies/types.py +21 -0
- lionagi/strategies/utils.py +49 -0
- lionagi/version.py +1 -1
- lionagi-0.5.0.dist-info/METADATA +348 -0
- lionagi-0.5.0.dist-info/RECORD +373 -0
- {lionagi-0.3.8.dist-info → lionagi-0.5.0.dist-info}/WHEEL +1 -1
- lionagi/core/_setting/_setting.py +0 -59
- lionagi/core/action/README.md +0 -20
- lionagi/core/action/manual.py +0 -1
- lionagi/core/action/node.py +0 -94
- lionagi/core/action/tool_manager.py +0 -342
- lionagi/core/agent/README.md +0 -1
- lionagi/core/agent/base_agent.py +0 -82
- lionagi/core/agent/eval/README.md +0 -1
- lionagi/core/agent/eval/evaluator.py +0 -1
- lionagi/core/agent/eval/vote.py +0 -40
- lionagi/core/agent/learn/learner.py +0 -59
- lionagi/core/agent/plan/unit_template.py +0 -1
- lionagi/core/collections/README.md +0 -23
- lionagi/core/collections/__init__.py +0 -16
- lionagi/core/collections/_logger.py +0 -312
- lionagi/core/collections/abc/README.md +0 -63
- lionagi/core/collections/abc/__init__.py +0 -53
- lionagi/core/collections/abc/component.py +0 -620
- lionagi/core/collections/abc/concepts.py +0 -277
- lionagi/core/collections/abc/exceptions.py +0 -136
- lionagi/core/collections/abc/util.py +0 -45
- lionagi/core/collections/exchange.py +0 -146
- lionagi/core/collections/flow.py +0 -416
- lionagi/core/collections/model.py +0 -465
- lionagi/core/collections/pile.py +0 -1232
- lionagi/core/collections/progression.py +0 -221
- lionagi/core/collections/util.py +0 -73
- lionagi/core/director/README.md +0 -1
- lionagi/core/director/direct.py +0 -298
- lionagi/core/director/director.py +0 -2
- lionagi/core/director/operations/select.py +0 -3
- lionagi/core/director/operations/utils.py +0 -6
- lionagi/core/engine/branch_engine.py +0 -361
- lionagi/core/engine/instruction_map_engine.py +0 -213
- lionagi/core/engine/sandbox_.py +0 -16
- lionagi/core/engine/script_engine.py +0 -89
- lionagi/core/executor/base_executor.py +0 -97
- lionagi/core/executor/graph_executor.py +0 -335
- lionagi/core/executor/neo4j_executor.py +0 -394
- lionagi/core/generic/README.md +0 -0
- lionagi/core/generic/edge_condition.py +0 -17
- lionagi/core/generic/hyperedge.py +0 -1
- lionagi/core/generic/tree.py +0 -49
- lionagi/core/generic/tree_node.py +0 -85
- lionagi/core/mail/__init__.py +0 -11
- lionagi/core/mail/mail.py +0 -26
- lionagi/core/mail/mail_manager.py +0 -185
- lionagi/core/mail/package.py +0 -49
- lionagi/core/mail/start_mail.py +0 -36
- lionagi/core/message/__init__.py +0 -18
- lionagi/core/message/action_request.py +0 -114
- lionagi/core/message/action_response.py +0 -121
- lionagi/core/message/assistant_response.py +0 -80
- lionagi/core/message/instruction.py +0 -194
- lionagi/core/message/message.py +0 -86
- lionagi/core/message/system.py +0 -71
- lionagi/core/message/util.py +0 -274
- lionagi/core/report/__init__.py +0 -4
- lionagi/core/report/base.py +0 -201
- lionagi/core/report/form.py +0 -212
- lionagi/core/report/report.py +0 -150
- lionagi/core/report/util.py +0 -15
- lionagi/core/rule/_default.py +0 -17
- lionagi/core/rule/action.py +0 -87
- lionagi/core/rule/base.py +0 -234
- lionagi/core/rule/boolean.py +0 -56
- lionagi/core/rule/choice.py +0 -48
- lionagi/core/rule/mapping.py +0 -82
- lionagi/core/rule/number.py +0 -73
- lionagi/core/rule/rulebook.py +0 -45
- lionagi/core/rule/string.py +0 -43
- lionagi/core/rule/util.py +0 -0
- lionagi/core/session/directive_mixin.py +0 -307
- lionagi/core/structure/__init__.py +0 -1
- lionagi/core/structure/chain.py +0 -1
- lionagi/core/structure/forest.py +0 -1
- lionagi/core/structure/graph.py +0 -1
- lionagi/core/structure/tree.py +0 -1
- lionagi/core/unit/__init__.py +0 -4
- lionagi/core/unit/parallel_unit.py +0 -234
- lionagi/core/unit/template/action.py +0 -65
- lionagi/core/unit/template/base.py +0 -35
- lionagi/core/unit/template/plan.py +0 -69
- lionagi/core/unit/template/predict.py +0 -95
- lionagi/core/unit/template/score.py +0 -108
- lionagi/core/unit/template/select.py +0 -91
- lionagi/core/unit/unit.py +0 -452
- lionagi/core/unit/unit_form.py +0 -290
- lionagi/core/unit/unit_mixin.py +0 -1166
- lionagi/core/unit/util.py +0 -103
- lionagi/core/validator/validator.py +0 -376
- lionagi/core/work/work.py +0 -59
- lionagi/core/work/work_edge.py +0 -102
- lionagi/core/work/work_function.py +0 -114
- lionagi/core/work/work_function_node.py +0 -50
- lionagi/core/work/work_queue.py +0 -90
- lionagi/core/work/work_task.py +0 -151
- lionagi/core/work/worker.py +0 -410
- lionagi/core/work/worker_engine.py +0 -208
- lionagi/core/work/worklog.py +0 -108
- lionagi/experimental/compressor/base.py +0 -47
- lionagi/experimental/compressor/llm_compressor.py +0 -265
- lionagi/experimental/compressor/llm_summarizer.py +0 -61
- lionagi/experimental/compressor/util.py +0 -70
- lionagi/experimental/directive/README.md +0 -1
- lionagi/experimental/directive/__init__.py +0 -19
- lionagi/experimental/directive/parser/base_parser.py +0 -294
- lionagi/experimental/directive/parser/base_syntax.txt +0 -200
- lionagi/experimental/directive/template/base_template.py +0 -71
- lionagi/experimental/directive/template/schema.py +0 -36
- lionagi/experimental/directive/tokenizer.py +0 -59
- lionagi/experimental/evaluator/README.md +0 -1
- lionagi/experimental/evaluator/ast_evaluator.py +0 -119
- lionagi/experimental/evaluator/base_evaluator.py +0 -213
- lionagi/experimental/knowledge/__init__.py +0 -0
- lionagi/experimental/knowledge/base.py +0 -10
- lionagi/experimental/knowledge/graph.py +0 -0
- lionagi/experimental/memory/__init__.py +0 -0
- lionagi/experimental/strategies/__init__.py +0 -0
- lionagi/experimental/strategies/base.py +0 -1
- lionagi/integrations/bridge/__init__.py +0 -4
- lionagi/integrations/bridge/autogen_/__init__.py +0 -0
- lionagi/integrations/bridge/autogen_/autogen_.py +0 -127
- lionagi/integrations/bridge/langchain_/__init__.py +0 -0
- lionagi/integrations/bridge/langchain_/documents.py +0 -138
- lionagi/integrations/bridge/langchain_/langchain_bridge.py +0 -68
- lionagi/integrations/bridge/llamaindex_/__init__.py +0 -0
- lionagi/integrations/bridge/llamaindex_/index.py +0 -36
- lionagi/integrations/bridge/llamaindex_/llama_index_bridge.py +0 -108
- lionagi/integrations/bridge/llamaindex_/llama_pack.py +0 -256
- lionagi/integrations/bridge/llamaindex_/node_parser.py +0 -92
- lionagi/integrations/bridge/llamaindex_/reader.py +0 -201
- lionagi/integrations/bridge/llamaindex_/textnode.py +0 -59
- lionagi/integrations/bridge/pydantic_/__init__.py +0 -0
- lionagi/integrations/bridge/pydantic_/pydantic_bridge.py +0 -7
- lionagi/integrations/bridge/transformers_/__init__.py +0 -0
- lionagi/integrations/bridge/transformers_/install_.py +0 -39
- lionagi/integrations/chunker/__init__.py +0 -0
- lionagi/integrations/chunker/chunk.py +0 -314
- lionagi/integrations/config/__init__.py +0 -4
- lionagi/integrations/config/mlx_configs.py +0 -1
- lionagi/integrations/config/oai_configs.py +0 -154
- lionagi/integrations/config/ollama_configs.py +0 -1
- lionagi/integrations/config/openrouter_configs.py +0 -74
- lionagi/integrations/langchain_/__init__.py +0 -0
- lionagi/integrations/llamaindex_/__init__.py +0 -0
- lionagi/integrations/loader/__init__.py +0 -0
- lionagi/integrations/loader/load.py +0 -257
- lionagi/integrations/loader/load_util.py +0 -214
- lionagi/integrations/provider/__init__.py +0 -11
- lionagi/integrations/provider/_mapping.py +0 -47
- lionagi/integrations/provider/litellm.py +0 -53
- lionagi/integrations/provider/mistralai.py +0 -1
- lionagi/integrations/provider/mlx_service.py +0 -55
- lionagi/integrations/provider/oai.py +0 -196
- lionagi/integrations/provider/ollama.py +0 -55
- lionagi/integrations/provider/openrouter.py +0 -170
- lionagi/integrations/provider/services.py +0 -138
- lionagi/integrations/provider/transformers.py +0 -108
- lionagi/integrations/storage/__init__.py +0 -3
- lionagi/integrations/storage/neo4j.py +0 -681
- lionagi/integrations/storage/storage_util.py +0 -302
- lionagi/integrations/storage/structure_excel.py +0 -291
- lionagi/integrations/storage/to_csv.py +0 -70
- lionagi/integrations/storage/to_excel.py +0 -91
- lionagi/libs/ln_api.py +0 -944
- lionagi/libs/ln_async.py +0 -208
- lionagi/libs/ln_context.py +0 -37
- lionagi/libs/ln_convert.py +0 -671
- lionagi/libs/ln_dataframe.py +0 -187
- lionagi/libs/ln_func_call.py +0 -1328
- lionagi/libs/ln_image.py +0 -114
- lionagi/libs/ln_knowledge_graph.py +0 -422
- lionagi/libs/ln_nested.py +0 -822
- lionagi/libs/ln_parse.py +0 -750
- lionagi/libs/ln_queue.py +0 -107
- lionagi/libs/ln_tokenize.py +0 -179
- lionagi/libs/ln_validate.py +0 -299
- lionagi/libs/special_tokens.py +0 -172
- lionagi/libs/sys_util.py +0 -710
- lionagi/lions/__init__.py +0 -0
- lionagi/lions/coder/__init__.py +0 -0
- lionagi/lions/coder/add_feature.py +0 -20
- lionagi/lions/coder/base_prompts.py +0 -22
- lionagi/lions/coder/code_form.py +0 -15
- lionagi/lions/coder/coder.py +0 -184
- lionagi/lions/coder/util.py +0 -101
- lionagi/lions/director/__init__.py +0 -0
- lionagi/lions/judge/__init__.py +0 -0
- lionagi/lions/judge/config.py +0 -8
- lionagi/lions/judge/data/__init__.py +0 -0
- lionagi/lions/judge/data/sample_codes.py +0 -526
- lionagi/lions/judge/data/sample_rurbic.py +0 -48
- lionagi/lions/judge/forms/__init__.py +0 -0
- lionagi/lions/judge/forms/code_analysis_form.py +0 -126
- lionagi/lions/judge/rubric.py +0 -34
- lionagi/lions/judge/services/__init__.py +0 -0
- lionagi/lions/judge/services/judge_code.py +0 -49
- lionagi/lions/researcher/__init__.py +0 -0
- lionagi/lions/researcher/data_source/__init__.py +0 -0
- lionagi/lions/researcher/data_source/finhub_.py +0 -192
- lionagi/lions/researcher/data_source/google_.py +0 -207
- lionagi/lions/researcher/data_source/wiki_.py +0 -98
- lionagi/lions/researcher/data_source/yfinance_.py +0 -21
- lionagi/operations/brainstorm.py +0 -87
- lionagi/operations/config.py +0 -6
- lionagi/operations/rank.py +0 -102
- lionagi/operations/score.py +0 -144
- lionagi/operations/select.py +0 -141
- lionagi-0.3.8.dist-info/METADATA +0 -241
- lionagi-0.3.8.dist-info/RECORD +0 -249
- /lionagi/{core/_setting → integrations/anthropic_/api_endpoints/messages/response}/__init__.py +0 -0
- /lionagi/{core/agent → integrations/groq_/api_endpoints}/__init__.py +0 -0
- /lionagi/{core/agent/eval → integrations/ollama_/api_endpoints/completion}/__init__.py +0 -0
- /lionagi/{core/agent/learn → integrations/ollama_/api_endpoints/embedding}/__init__.py +0 -0
- /lionagi/{core/agent/plan → integrations/openai_}/__init__.py +0 -0
- /lionagi/{core/director → integrations/openai_/api_endpoints/chat_completions/response}/__init__.py +0 -0
- /lionagi/{core/director/operations → integrations/openai_/image_token_calculator}/__init__.py +0 -0
- /lionagi/{core/engine → integrations/perplexity_/api_endpoints}/__init__.py +0 -0
- /lionagi/{core/executor → integrations/perplexity_/api_endpoints/chat_completions}/__init__.py +0 -0
- /lionagi/{core/generic/registry/component_registry → integrations/perplexity_/api_endpoints/chat_completions/request}/__init__.py +0 -0
- /lionagi/{core/rule → integrations/perplexity_/api_endpoints/chat_completions/response}/__init__.py +0 -0
- /lionagi/{core/unit/template → libs/compress}/__init__.py +0 -0
- /lionagi/{core/validator → libs/file}/__init__.py +0 -0
- /lionagi/{core/work → libs/func}/__init__.py +0 -0
- /lionagi/{experimental → libs/package}/__init__.py +0 -0
- /lionagi/{core/agent/plan/plan.py → libs/parse/params.py} +0 -0
- /lionagi/{experimental/compressor → protocols}/__init__.py +0 -0
- /lionagi/{experimental/directive/parser → protocols/adapters}/__init__.py +0 -0
- /lionagi/{experimental/directive/template → protocols/registries}/__init__.py +0 -0
- /lionagi/{experimental/evaluator → strategies}/__init__.py +0 -0
- {lionagi-0.3.8.dist-info → lionagi-0.5.0.dist-info/licenses}/LICENSE +0 -0
lionagi/libs/ln_func_call.py
DELETED
@@ -1,1328 +0,0 @@
|
|
1
|
-
from __future__ import annotations
|
2
|
-
|
3
|
-
import asyncio
|
4
|
-
import functools
|
5
|
-
import logging
|
6
|
-
from collections.abc import Callable, Coroutine
|
7
|
-
from concurrent.futures import ThreadPoolExecutor
|
8
|
-
from typing import Any
|
9
|
-
|
10
|
-
from lionagi.libs.ln_async import AsyncUtil
|
11
|
-
from lionagi.libs.ln_convert import to_list
|
12
|
-
from lionagi.libs.sys_util import SysUtil
|
13
|
-
|
14
|
-
|
15
|
-
def lru_cache(*args, **kwargs):
|
16
|
-
return functools.lru_cache(*args, **kwargs)
|
17
|
-
|
18
|
-
|
19
|
-
def lcall(
|
20
|
-
input_: Any,
|
21
|
-
/,
|
22
|
-
func: Callable,
|
23
|
-
*,
|
24
|
-
flatten: bool = False,
|
25
|
-
dropna: bool = False,
|
26
|
-
**kwargs,
|
27
|
-
) -> list[Any]:
|
28
|
-
"""
|
29
|
-
applies a function to each element of the input list, with options to flatten
|
30
|
-
results and drop None values.
|
31
|
-
|
32
|
-
this function facilitates the batch application of a transformation or operation
|
33
|
-
to each input_ in an input list. it is versatile, supporting both flattening of the
|
34
|
-
result list and removal of None values from the output, making it suitable for a
|
35
|
-
wide range of data manipulation tasks. additional arguments to the applied
|
36
|
-
function can be passed dynamically, allowing for flexible function application.
|
37
|
-
|
38
|
-
Args:
|
39
|
-
input_ (Any):
|
40
|
-
The input list or iterable to process. each element will be passed to the
|
41
|
-
provided `func` Callable.
|
42
|
-
func (Callable):
|
43
|
-
The function to apply to each element of `input_`. this function can be any
|
44
|
-
Callable that accepts the elements of `input_` as arguments.
|
45
|
-
flatten (bool, optional):
|
46
|
-
If True, the resulting list is flattened. useful when `func` returns a list.
|
47
|
-
defaults to False.
|
48
|
-
dropna (bool, optional):
|
49
|
-
If True, None values are removed from the final list. defaults to False.
|
50
|
-
**kwargs:
|
51
|
-
Additional keyword arguments to be passed to `func`.
|
52
|
-
|
53
|
-
Returns:
|
54
|
-
list[Any]:
|
55
|
-
The list of results after applying `func` to each input element, modified
|
56
|
-
according to `flatten` and `dropna` options.
|
57
|
-
|
58
|
-
Examples:
|
59
|
-
Apply a doubling function to each element:
|
60
|
-
>>> lcall([1, 2, 3], lambda x: x * 2)
|
61
|
-
[2, 4, 6]
|
62
|
-
|
63
|
-
apply a function that returns lists, then flatten the result:
|
64
|
-
>>> lcall([1, 2, None], lambda x: [x, x] if x else x, flatten=True, dropna=True)
|
65
|
-
[1, 1, 2, 2]
|
66
|
-
"""
|
67
|
-
lst = to_list(input_, dropna=dropna)
|
68
|
-
if len(to_list(func)) != 1:
|
69
|
-
raise ValueError(
|
70
|
-
"There must be one and only one function for list calling."
|
71
|
-
)
|
72
|
-
|
73
|
-
return to_list(
|
74
|
-
[func(i, **kwargs) for i in lst], flatten=flatten, dropna=dropna
|
75
|
-
)
|
76
|
-
|
77
|
-
|
78
|
-
async def alcall(
|
79
|
-
input_: Any | None = None,
|
80
|
-
func: Callable = None,
|
81
|
-
*,
|
82
|
-
flatten: bool = False,
|
83
|
-
dropna=False,
|
84
|
-
**kwargs,
|
85
|
-
) -> list[Any]:
|
86
|
-
# noinspection GrazieInspection
|
87
|
-
"""
|
88
|
-
asynchronously applies a function to each element in the input.
|
89
|
-
|
90
|
-
this async function is designed for operations where a given async function needs to
|
91
|
-
be applied to each element of an input list concurrently. it allows for the results
|
92
|
-
to be flattened and provides the flexibility to pass additional keyword arguments to
|
93
|
-
the function being applied. this is especially useful in scenarios where processing
|
94
|
-
each element of the input can be performed independently and concurrently, improving
|
95
|
-
efficiency and overall execution time.
|
96
|
-
|
97
|
-
Args:
|
98
|
-
input_ (Any, optional):
|
99
|
-
The input to process. defaults to None, which requires `func` to be capable of
|
100
|
-
handling the absence of explicit input.
|
101
|
-
func (Callable, optional):
|
102
|
-
The asynchronous function to apply. defaults to None.
|
103
|
-
flatten (bool, optional):
|
104
|
-
Whether to flatten the result. useful when `func` returns a list or iterable
|
105
|
-
that should be merged into a single list. defaults to False.
|
106
|
-
**kwargs:
|
107
|
-
Keyword arguments to pass to the function.
|
108
|
-
|
109
|
-
Returns:
|
110
|
-
list[Any]:
|
111
|
-
A list of results after asynchronously applying the function to each element
|
112
|
-
of the input, potentially flattened.
|
113
|
-
|
114
|
-
examples:
|
115
|
-
>>> async def square(x): return x * x
|
116
|
-
>>> await alcall([1, 2, 3], square)
|
117
|
-
[1, 4, 9]
|
118
|
-
"""
|
119
|
-
tasks = []
|
120
|
-
if input_ is not None:
|
121
|
-
lst = to_list(input_)
|
122
|
-
tasks = [call_handler(func, i, **kwargs) for i in lst]
|
123
|
-
|
124
|
-
else:
|
125
|
-
tasks = [call_handler(func, **kwargs)]
|
126
|
-
|
127
|
-
outs = await asyncio.gather(*tasks)
|
128
|
-
outs_ = []
|
129
|
-
for i in outs:
|
130
|
-
outs_.append(
|
131
|
-
await i if isinstance(i, (Coroutine, asyncio.Future)) else i
|
132
|
-
)
|
133
|
-
|
134
|
-
return to_list(outs_, flatten=flatten, dropna=dropna)
|
135
|
-
|
136
|
-
|
137
|
-
async def pcall(funcs):
|
138
|
-
task = [call_handler(func) for func in funcs]
|
139
|
-
return await asyncio.gather(*task)
|
140
|
-
|
141
|
-
|
142
|
-
async def mcall(
|
143
|
-
input_: Any, /, func: Any, *, explode: bool = False, **kwargs
|
144
|
-
) -> tuple[Any]:
|
145
|
-
"""
|
146
|
-
asynchronously map a function or functions over an input_ or inputs.
|
147
|
-
|
148
|
-
Args:
|
149
|
-
input_ (Any):
|
150
|
-
The input_ or inputs to process.
|
151
|
-
func (Any):
|
152
|
-
The function or functions to apply.
|
153
|
-
explode (bool, optional):
|
154
|
-
Whether to apply each function to each input_. default is False.
|
155
|
-
**kwargs:
|
156
|
-
Keyword arguments to pass to the function.
|
157
|
-
|
158
|
-
Returns:
|
159
|
-
list[Any]: A list of results after applying the function(s).
|
160
|
-
|
161
|
-
examples:
|
162
|
-
>>> async def add_one(x):
|
163
|
-
>>> return x + 1
|
164
|
-
>>> asyncio.run(mcall([1, 2, 3], add_one))
|
165
|
-
[2, 3, 4]
|
166
|
-
|
167
|
-
"""
|
168
|
-
inputs_ = to_list(input_, dropna=True)
|
169
|
-
funcs_ = to_list(func, dropna=True)
|
170
|
-
|
171
|
-
if explode:
|
172
|
-
tasks = [_alcall(inputs_, f, flatten=True, **kwargs) for f in funcs_]
|
173
|
-
elif len(inputs_) == len(funcs_):
|
174
|
-
tasks = [
|
175
|
-
AsyncUtil.handle_async_sync(func, inp, **kwargs)
|
176
|
-
for inp, func in zip(inputs_, funcs_)
|
177
|
-
]
|
178
|
-
else:
|
179
|
-
raise ValueError(
|
180
|
-
"Inputs and functions must be the same length for map calling."
|
181
|
-
)
|
182
|
-
return await AsyncUtil.execute_tasks(*tasks)
|
183
|
-
|
184
|
-
|
185
|
-
async def bcall(
|
186
|
-
input_: Any, /, func: Callable, *, batch_size: int, **kwargs
|
187
|
-
) -> list[Any]:
|
188
|
-
"""
|
189
|
-
asynchronously call a function on batches of inputs.
|
190
|
-
|
191
|
-
Args:
|
192
|
-
input_ (Any): The input_ to process.
|
193
|
-
func (Callable): The function to apply.
|
194
|
-
batch_size (int): The size of each batch.
|
195
|
-
**kwargs: Keyword arguments to pass to the function.
|
196
|
-
|
197
|
-
Returns:
|
198
|
-
list[Any]: A list of results after applying the function in batches.
|
199
|
-
|
200
|
-
examples:
|
201
|
-
>>> async def sum_batch(batch_): return sum(batch_)
|
202
|
-
>>> asyncio.run(bcall([1, 2, 3, 4], sum_batch, batch_size=2))
|
203
|
-
[3, 7]
|
204
|
-
"""
|
205
|
-
results = []
|
206
|
-
input_ = to_list(input_)
|
207
|
-
for i in range(0, len(input_), batch_size):
|
208
|
-
batch = input_[i : i + batch_size]
|
209
|
-
batch_results = await alcall(batch, func, **kwargs)
|
210
|
-
results.extend(batch_results)
|
211
|
-
|
212
|
-
return results
|
213
|
-
|
214
|
-
|
215
|
-
async def tcall(
|
216
|
-
func: Callable,
|
217
|
-
*args,
|
218
|
-
delay: float = 0,
|
219
|
-
err_msg: str | None = None,
|
220
|
-
ignore_err: bool = False,
|
221
|
-
timing: bool = False,
|
222
|
-
timeout: float | None = None,
|
223
|
-
**kwargs,
|
224
|
-
) -> Any:
|
225
|
-
"""
|
226
|
-
asynchronously executes a function with an optional delay, error handling, and timing.
|
227
|
-
|
228
|
-
this utility allows for the asynchronous invocation of a Callable with added controls
|
229
|
-
for execution delay, customizable timeout, and optional error suppression. it can
|
230
|
-
also measure the execution time if required. this function is useful in scenarios
|
231
|
-
where operations need to be scheduled with a delay, executed within a certain time
|
232
|
-
frame, or when monitoring execution duration.
|
233
|
-
|
234
|
-
Args:
|
235
|
-
func (Callable):
|
236
|
-
The asynchronous function to be called.
|
237
|
-
*args:
|
238
|
-
Positional arguments to pass to the function.
|
239
|
-
delay (float, optional):
|
240
|
-
Time in seconds to wait before executing the function. default to 0.
|
241
|
-
err_msg (str | None, optional):
|
242
|
-
Custom error message to display if an error occurs. defaults to None.
|
243
|
-
ignore_err (bool, optional):
|
244
|
-
If True, suppresses any errors that occur during function execution,
|
245
|
-
optionally returning a default value. defaults to False.
|
246
|
-
timing (bool, optional):
|
247
|
-
If True, returns a tuple containing the result of the function and the
|
248
|
-
execution duration in seconds. defaults to False.
|
249
|
-
timeout (float | None, optional):
|
250
|
-
Maximum time in seconds allowed for the function execution. if the execution
|
251
|
-
exceeds this time, a timeout error is raised. defaults to None.
|
252
|
-
**kwargs:
|
253
|
-
Keyword arguments to pass to the function.
|
254
|
-
|
255
|
-
Returns:
|
256
|
-
Any:
|
257
|
-
The result of the function call. if `timing` is True, returns a tuple of
|
258
|
-
(result, execution duration).
|
259
|
-
|
260
|
-
examples:
|
261
|
-
>>> async def sample_function(x):
|
262
|
-
... return x * x
|
263
|
-
>>> await tcall(sample_function, 3, delay=1, timing=True)
|
264
|
-
(9, execution_duration)
|
265
|
-
"""
|
266
|
-
|
267
|
-
async def async_call() -> tuple[Any, float]:
|
268
|
-
start_time = SysUtil.get_now(datetime_=False)
|
269
|
-
if timeout is not None:
|
270
|
-
result = await AsyncUtil.execute_timeout(
|
271
|
-
func(*args, **kwargs), timeout
|
272
|
-
)
|
273
|
-
duration = SysUtil.get_now(datetime_=False) - start_time
|
274
|
-
return (result, duration) if timing else result
|
275
|
-
try:
|
276
|
-
await AsyncUtil.sleep(delay)
|
277
|
-
result = await func(*args, **kwargs)
|
278
|
-
duration = SysUtil.get_now(datetime_=False) - start_time
|
279
|
-
return (result, duration) if timing else result
|
280
|
-
except Exception as e:
|
281
|
-
handle_error(e)
|
282
|
-
|
283
|
-
def sync_call() -> tuple[Any, float]:
|
284
|
-
start_time = SysUtil.get_now(datetime_=False)
|
285
|
-
try:
|
286
|
-
SysUtil.sleep(delay)
|
287
|
-
result = func(*args, **kwargs)
|
288
|
-
duration = SysUtil.get_now(datetime_=False) - start_time
|
289
|
-
return (result, duration) if timing else result
|
290
|
-
except Exception as e:
|
291
|
-
handle_error(e)
|
292
|
-
|
293
|
-
def handle_error(e: Exception):
|
294
|
-
_msg = (
|
295
|
-
f"{err_msg} Error: {e}" if err_msg else f"An error occurred: {e}"
|
296
|
-
)
|
297
|
-
print(_msg)
|
298
|
-
if not ignore_err:
|
299
|
-
raise
|
300
|
-
|
301
|
-
return (
|
302
|
-
await async_call()
|
303
|
-
if AsyncUtil.is_coroutine_func(func)
|
304
|
-
else sync_call()
|
305
|
-
)
|
306
|
-
|
307
|
-
|
308
|
-
async def rcall(
|
309
|
-
func: Callable,
|
310
|
-
*args,
|
311
|
-
retries: int = 0,
|
312
|
-
delay: float = 0.1,
|
313
|
-
backoff_factor: float = 2,
|
314
|
-
default: Any = None,
|
315
|
-
timeout: float | None = None,
|
316
|
-
timing: bool = False,
|
317
|
-
verbose: bool = True,
|
318
|
-
**kwargs,
|
319
|
-
) -> Any:
|
320
|
-
"""
|
321
|
-
asynchronously retries a function call with exponential backoff.
|
322
|
-
|
323
|
-
designed for resilience, this function attempts to call an asynchronous function
|
324
|
-
multiple times with increasing delays between attempts. this is particularly
|
325
|
-
beneficial for operations that may fail due to transient issues. the function
|
326
|
-
supports specifying a timeout for each attempt, a default return value in case of
|
327
|
-
persistent failures, and a backoff factor to control the delay increase.
|
328
|
-
|
329
|
-
Args:
|
330
|
-
func (Callable):
|
331
|
-
The asynchronous function to retry.
|
332
|
-
*args:
|
333
|
-
Positional arguments for the function.
|
334
|
-
retries (int, optional):
|
335
|
-
The number of retry attempts before giving up. default to 0.
|
336
|
-
delay (float, optional):
|
337
|
-
Initial delay between retries in seconds. default to 1.0.
|
338
|
-
backoff_factor (float, optional):
|
339
|
-
Multiplier for the delay between retries, for exponential backoff.
|
340
|
-
default to 2.0.
|
341
|
-
default (Any, optional):
|
342
|
-
A value to return if all retries fail. defaults to None.
|
343
|
-
timeout (float | None, optional):
|
344
|
-
Maximum duration in seconds for each attempt. defaults to None.
|
345
|
-
**kwargs:
|
346
|
-
Keyword arguments for the function.
|
347
|
-
|
348
|
-
Returns:
|
349
|
-
Any:
|
350
|
-
The result of the function call if successful within the retry attempts,
|
351
|
-
otherwise the `default` value if specified.
|
352
|
-
|
353
|
-
examples:
|
354
|
-
>>> async def fetch_data():
|
355
|
-
... # Simulate a fetch operation that might fail
|
356
|
-
... raise Exception("temporary error")
|
357
|
-
>>> await rcall(fetch_data, retries=3, delay=2, default="default value")
|
358
|
-
'default value'
|
359
|
-
"""
|
360
|
-
last_exception = None
|
361
|
-
result = None
|
362
|
-
|
363
|
-
start = SysUtil.get_now(datetime_=False)
|
364
|
-
for attempt in range(retries + 1) if retries == 0 else range(retries):
|
365
|
-
try:
|
366
|
-
err_msg = (
|
367
|
-
f"Attempt {attempt + 1}/{retries}: " if retries > 0 else None
|
368
|
-
)
|
369
|
-
if timing:
|
370
|
-
return (
|
371
|
-
await _tcall(
|
372
|
-
func, *args, err_msg=err_msg, timeout=timeout, **kwargs
|
373
|
-
),
|
374
|
-
SysUtil.get_now(datetime_=False) - start,
|
375
|
-
)
|
376
|
-
|
377
|
-
return await _tcall(func, *args, timeout=timeout, **kwargs)
|
378
|
-
except Exception as e:
|
379
|
-
last_exception = e
|
380
|
-
if attempt < retries:
|
381
|
-
if verbose:
|
382
|
-
print(
|
383
|
-
f"Attempt {attempt + 1}/{retries} failed: {e}, retrying..."
|
384
|
-
)
|
385
|
-
await asyncio.sleep(delay)
|
386
|
-
delay *= backoff_factor
|
387
|
-
else:
|
388
|
-
break
|
389
|
-
if result is None and default is not None:
|
390
|
-
return default
|
391
|
-
elif last_exception is not None:
|
392
|
-
raise RuntimeError(
|
393
|
-
f"Operation failed after {retries+1} attempts: {last_exception}"
|
394
|
-
) from last_exception
|
395
|
-
else:
|
396
|
-
raise RuntimeError("rcall failed without catching an exception")
|
397
|
-
|
398
|
-
|
399
|
-
def _dropna(lst_: list[Any]) -> list[Any]:
|
400
|
-
"""
|
401
|
-
Remove None values from a list.
|
402
|
-
|
403
|
-
Args:
|
404
|
-
lst_ (list[Any]): A list potentially containing None values.
|
405
|
-
|
406
|
-
Returns:
|
407
|
-
list[Any]: A list with None values removed.
|
408
|
-
|
409
|
-
Examples:
|
410
|
-
>>> _dropna([1, None, 3, None])
|
411
|
-
[1, 3]
|
412
|
-
"""
|
413
|
-
return [item for item in lst_ if item is not None]
|
414
|
-
|
415
|
-
|
416
|
-
async def _alcall(
|
417
|
-
input_: Any, func: Callable, flatten: bool = False, **kwargs
|
418
|
-
) -> list[Any]:
|
419
|
-
"""
|
420
|
-
asynchronously apply a function to each element in the input_.
|
421
|
-
|
422
|
-
Args:
|
423
|
-
input (Any): The input_ to process.
|
424
|
-
func (Callable): The function to apply.
|
425
|
-
flatten (bool, optional): Whether to flatten the result. default is False.
|
426
|
-
**kwargs: Keyword arguments to pass to the function.
|
427
|
-
|
428
|
-
Returns:
|
429
|
-
list[Any]: A list of results after asynchronously applying the function.
|
430
|
-
|
431
|
-
examples:
|
432
|
-
>>> async def square(x): return x * x
|
433
|
-
>>> asyncio.run(alcall([1, 2, 3], square))
|
434
|
-
[1, 4, 9]
|
435
|
-
"""
|
436
|
-
lst = to_list(input_)
|
437
|
-
tasks = [call_handler(func, i, **kwargs) for i in lst]
|
438
|
-
outs = await asyncio.gather(*tasks)
|
439
|
-
return to_list(outs, flatten=flatten)
|
440
|
-
|
441
|
-
|
442
|
-
async def _tcall(
|
443
|
-
func: Callable,
|
444
|
-
*args,
|
445
|
-
delay: float = 0,
|
446
|
-
err_msg: str | None = None,
|
447
|
-
ignore_err: bool = False,
|
448
|
-
timing: bool = False,
|
449
|
-
default: Any = None,
|
450
|
-
timeout: float | None = None,
|
451
|
-
**kwargs,
|
452
|
-
) -> Any:
|
453
|
-
"""
|
454
|
-
asynchronously call a function with optional delay, timeout, and error handling.
|
455
|
-
|
456
|
-
Args:
|
457
|
-
func (Callable): The function to call.
|
458
|
-
*args: Positional arguments to pass to the function.
|
459
|
-
delay (float): Delay before calling the function, in seconds.
|
460
|
-
err_msg (str | None): Custom error message.
|
461
|
-
ignore_err (bool): If True, ignore errors and return default.
|
462
|
-
timing (bool): If True, return a tuple (result, duration).
|
463
|
-
default (Any): Default value to return on error.
|
464
|
-
timeout (float | None): Timeout for the function call, in seconds.
|
465
|
-
**kwargs: Keyword arguments to pass to the function.
|
466
|
-
|
467
|
-
Returns:
|
468
|
-
Any: The result of the function call, or (result, duration) if timing is True.
|
469
|
-
|
470
|
-
examples:
|
471
|
-
>>> async def example_func(x): return x
|
472
|
-
>>> asyncio.run(tcall(example_func, 5, timing=True))
|
473
|
-
(5, duration)
|
474
|
-
"""
|
475
|
-
start_time = SysUtil.get_now(datetime_=False)
|
476
|
-
try:
|
477
|
-
await asyncio.sleep(delay)
|
478
|
-
# Apply timeout to the function call
|
479
|
-
if timeout is not None:
|
480
|
-
coro = ""
|
481
|
-
if is_coroutine_func(func):
|
482
|
-
coro = func(*args, **kwargs)
|
483
|
-
else:
|
484
|
-
|
485
|
-
async def coro_():
|
486
|
-
return func(*args, **kwargs)
|
487
|
-
|
488
|
-
coro = coro_()
|
489
|
-
|
490
|
-
result = await asyncio.wait_for(coro, timeout)
|
491
|
-
|
492
|
-
else:
|
493
|
-
if is_coroutine_func(func):
|
494
|
-
return await func(*args, **kwargs)
|
495
|
-
return func(*args, **kwargs)
|
496
|
-
duration = SysUtil.get_now(datetime_=False) - start_time
|
497
|
-
return (result, duration) if timing else result
|
498
|
-
except asyncio.TimeoutError as e:
|
499
|
-
err_msg = f"{err_msg or ''}Timeout {timeout} seconds exceeded"
|
500
|
-
if ignore_err:
|
501
|
-
return (
|
502
|
-
(default, SysUtil.get_now(datetime_=False) - start_time)
|
503
|
-
if timing
|
504
|
-
else default
|
505
|
-
)
|
506
|
-
else:
|
507
|
-
raise asyncio.TimeoutError(
|
508
|
-
err_msg
|
509
|
-
) # Re-raise the timeout exception
|
510
|
-
except Exception as e:
|
511
|
-
err_msg = (
|
512
|
-
f"{err_msg} Error: {e}" if err_msg else f"An error occurred: {e}"
|
513
|
-
)
|
514
|
-
if ignore_err:
|
515
|
-
return (
|
516
|
-
(default, SysUtil.get_now(datetime_=False) - start_time)
|
517
|
-
if timing
|
518
|
-
else default
|
519
|
-
)
|
520
|
-
else:
|
521
|
-
raise e
|
522
|
-
|
523
|
-
|
524
|
-
class CallDecorator:
|
525
|
-
"""
|
526
|
-
Provides a collection of decorators to enhance asynchronous function calls with
|
527
|
-
additional behaviors such as timeouts, retries, throttling, and more. These
|
528
|
-
decorators are designed to support both synchronous and asynchronous functions,
|
529
|
-
allowing for flexible and efficient execution patterns in a variety of contexts.
|
530
|
-
|
531
|
-
The decorators include functionality for applying timeouts, retrying with
|
532
|
-
exponential backoff, limiting concurrency, caching results, and preprocessing or
|
533
|
-
postprocessing inputs and outputs. They can be applied directly to functions or
|
534
|
-
methods to modify their behavior without altering the original logic.
|
535
|
-
|
536
|
-
Usage of these decorators simplifies the management of asynchronous operations,
|
537
|
-
making it easier to implement robust error handling, rate limiting, and result
|
538
|
-
caching. This is particularly beneficial in environments where operations are
|
539
|
-
I/O-bound and can greatly benefit from asynchronous execution.
|
540
|
-
"""
|
541
|
-
|
542
|
-
@staticmethod
|
543
|
-
def timeout(timeout: int) -> Callable:
|
544
|
-
"""
|
545
|
-
Applies a timeout to an asynchronous function call, ensuring that the function
|
546
|
-
execution completes within the specified duration.
|
547
|
-
|
548
|
-
This decorator is crucial for operations where a strict execution time limit is
|
549
|
-
required. It prevents the function from running indefinitely by raising an
|
550
|
-
asyncio.TimeoutError if the execution time exceeds the specified timeout.
|
551
|
-
|
552
|
-
Args:
|
553
|
-
timeout (int):
|
554
|
-
The maximum duration, in seconds, that the function is allowed to execute.
|
555
|
-
|
556
|
-
Returns:
|
557
|
-
Callable:
|
558
|
-
A decorated function that enforces the specified execution timeout.
|
559
|
-
|
560
|
-
Examples:
|
561
|
-
>>> @CallDecorator.timeout(5)
|
562
|
-
... async def long_running_task():
|
563
|
-
... # Implementation that may exceed the timeout duration
|
564
|
-
... await asyncio.sleep(10)
|
565
|
-
... return "Completed"
|
566
|
-
... # Executing `long_running_task` will raise an asyncio.TimeoutError after 5
|
567
|
-
... # seconds
|
568
|
-
"""
|
569
|
-
|
570
|
-
def decorator(func: Callable[..., Any]) -> Callable:
|
571
|
-
@functools.wraps(func)
|
572
|
-
async def wrapper(*args, **kwargs) -> Any:
|
573
|
-
return await rcall(func, *args, timeout=timeout, **kwargs)
|
574
|
-
|
575
|
-
return wrapper
|
576
|
-
|
577
|
-
return decorator
|
578
|
-
|
579
|
-
@staticmethod
|
580
|
-
def retry(
|
581
|
-
retries: int = 3,
|
582
|
-
delay: float = 2.0,
|
583
|
-
backoff_factor: float = 2.0,
|
584
|
-
default=...,
|
585
|
-
verbose=True,
|
586
|
-
) -> Callable:
|
587
|
-
"""
|
588
|
-
Decorates an asynchronous function to automatically retry on failure,
|
589
|
-
with configurable retries, delay, and exponential backoff.
|
590
|
-
|
591
|
-
This decorator is useful for handling operations that may fail due to transient
|
592
|
-
issues, such as network connectivity problems or temporary provider
|
593
|
-
unavailability. By automatically retrying the function call, it increases the
|
594
|
-
robustness of the application without complicating the core logic with retry
|
595
|
-
mechanisms.
|
596
|
-
|
597
|
-
Args:
|
598
|
-
retries (int, optional):
|
599
|
-
The number of retry attempts before giving up. Defaults to 3.
|
600
|
-
delay (float, optional):
|
601
|
-
The initial delay between retries, in seconds. Defaults to 2.0.
|
602
|
-
backoff_factor (float, optional):
|
603
|
-
The multiplier applied to the delay for each subsequent retry, for
|
604
|
-
exponential backoff. Default to 2.0.
|
605
|
-
|
606
|
-
Returns:
|
607
|
-
Callable:
|
608
|
-
A decorated asynchronous function with retry logic based on the specified
|
609
|
-
parameters.
|
610
|
-
|
611
|
-
Examples:
|
612
|
-
>>> @CallDecorator.retry(retries=2, delay=1, backoff_factor=2)
|
613
|
-
... async def fetch_data():
|
614
|
-
... # Implementation that might fail transiently
|
615
|
-
... raise ConnectionError("Temporary failure")
|
616
|
-
... # `fetch_data` will automatically retry on ConnectionError, up to 2 times,
|
617
|
-
... # with delays of 1s and 2s.
|
618
|
-
"""
|
619
|
-
|
620
|
-
def decorator(func: Callable[..., Any]) -> Callable:
|
621
|
-
@functools.wraps(func)
|
622
|
-
async def wrapper(*args, **kwargs) -> Any:
|
623
|
-
return await rcall(
|
624
|
-
func,
|
625
|
-
*args,
|
626
|
-
retries=retries,
|
627
|
-
delay=delay,
|
628
|
-
backoff_factor=backoff_factor,
|
629
|
-
default=default,
|
630
|
-
verbose=verbose,
|
631
|
-
**kwargs,
|
632
|
-
)
|
633
|
-
|
634
|
-
return wrapper
|
635
|
-
|
636
|
-
return decorator
|
637
|
-
|
638
|
-
@staticmethod
|
639
|
-
def default(default_value: Any) -> Callable:
|
640
|
-
"""
|
641
|
-
Decorates an asynchronous function to return a default value in case of an
|
642
|
-
exception, allowing the function to gracefully handle errors without
|
643
|
-
interrupting the application flow.
|
644
|
-
|
645
|
-
This decorator simplifies error handling by encapsulating the try-except logic
|
646
|
-
within the decoration process, providing a default result when an operation
|
647
|
-
fails. This is particularly useful for non-critical operations where a fallback
|
648
|
-
value can prevent the application from crashing or halting due to minor errors.
|
649
|
-
|
650
|
-
Args:
|
651
|
-
default_value (Any):
|
652
|
-
The value to return if the decorated function raises an exception.
|
653
|
-
|
654
|
-
Returns:
|
655
|
-
Callable:
|
656
|
-
A decorated asynchronous function that returns `default_value` in case of
|
657
|
-
error.
|
658
|
-
|
659
|
-
Examples:
|
660
|
-
>>> @CallDecorator.default(default_value="Fetch failed")
|
661
|
-
... async def get_resource():
|
662
|
-
... # Implementation that might raise an exception
|
663
|
-
... raise RuntimeError("Resource not available")
|
664
|
-
... # Executing `get_resource` will return "Fetch failed" instead of raising
|
665
|
-
... # an error
|
666
|
-
"""
|
667
|
-
|
668
|
-
def decorator(func: Callable[..., Any]) -> Callable:
|
669
|
-
@functools.wraps(func)
|
670
|
-
async def wrapper(*args, **kwargs) -> Any:
|
671
|
-
return await rcall(
|
672
|
-
func, *args, default=default_value, **kwargs
|
673
|
-
)
|
674
|
-
|
675
|
-
return wrapper
|
676
|
-
|
677
|
-
return decorator
|
678
|
-
|
679
|
-
@staticmethod
|
680
|
-
def throttle(period: int) -> Callable:
|
681
|
-
"""
|
682
|
-
Decorates an asynchronous function to limit its execution frequency to not
|
683
|
-
exceed one call per specified period. This is useful for rate-limiting calls to
|
684
|
-
external services, APIs, or any operation where maintaining a maximum call
|
685
|
-
frequency is required to avoid overloading resources or hitting API rate limits.
|
686
|
-
|
687
|
-
The throttling is achieved by introducing a delay if the time elapsed since the
|
688
|
-
last call is less than the specified period. This ensures that the decorated
|
689
|
-
function does not execute more frequently than the allowed rate.
|
690
|
-
|
691
|
-
Args:
|
692
|
-
period (int):
|
693
|
-
The minimum time interval, in seconds, between consecutive calls to the
|
694
|
-
decorated function.
|
695
|
-
|
696
|
-
Returns:
|
697
|
-
Callable:
|
698
|
-
A decorated asynchronous function that adheres to the specified call
|
699
|
-
frequency limit.
|
700
|
-
|
701
|
-
Examples:
|
702
|
-
>>> @CallDecorator.throttle(2)
|
703
|
-
... async def fetch_data():
|
704
|
-
... # Implementation that fetches data from an external source
|
705
|
-
... pass
|
706
|
-
... # `fetch_data` will not be called more often than once every 2 seconds.
|
707
|
-
"""
|
708
|
-
return Throttle(period)
|
709
|
-
|
710
|
-
@staticmethod
|
711
|
-
def map(function: Callable[[Any], Any]) -> Callable:
|
712
|
-
"""
|
713
|
-
Decorates an asynchronous function to apply a specified mapping function to
|
714
|
-
each element in the list returned by the decorated function. This is
|
715
|
-
particularly useful for post-processing the results of asynchronous operations,
|
716
|
-
such as transforming data fetched from an API or processing items in a
|
717
|
-
collection concurrently.
|
718
|
-
|
719
|
-
The mapping function is applied to each input_ in the output list of the
|
720
|
-
decorated function, enabling patterned modifications or transformations to be
|
721
|
-
succinctly applied to a collection of asynchronous results.
|
722
|
-
|
723
|
-
Args:
|
724
|
-
function (Callable[[Any], Any]):
|
725
|
-
A mapping function to apply to each element of the list returned by the
|
726
|
-
decorated function.
|
727
|
-
|
728
|
-
Returns:
|
729
|
-
Callable:
|
730
|
-
A decorated asynchronous function whose results are transformed by the
|
731
|
-
specified mapping function.
|
732
|
-
|
733
|
-
Examples:
|
734
|
-
>>> @CallDecorator.map(lambda x: x.upper())
|
735
|
-
... async def get_names():
|
736
|
-
... # Asynchronously fetches a list of names
|
737
|
-
... return ["alice", "bob", "charlie"]
|
738
|
-
... # `get_names` now returns ["ALICE", "BOB", "CHARLIE"]
|
739
|
-
"""
|
740
|
-
|
741
|
-
def decorator(func: Callable[..., list[Any]]) -> Callable:
|
742
|
-
if is_coroutine_func(func):
|
743
|
-
|
744
|
-
@functools.wraps(func)
|
745
|
-
async def async_wrapper(*args, **kwargs) -> list[Any]:
|
746
|
-
values = await func(*args, **kwargs)
|
747
|
-
return [function(value) for value in values]
|
748
|
-
|
749
|
-
return async_wrapper
|
750
|
-
else:
|
751
|
-
|
752
|
-
@functools.wraps(func)
|
753
|
-
def sync_wrapper(*args, **kwargs) -> list[Any]:
|
754
|
-
values = func(*args, **kwargs)
|
755
|
-
return [function(value) for value in values]
|
756
|
-
|
757
|
-
return sync_wrapper
|
758
|
-
|
759
|
-
return decorator
|
760
|
-
|
761
|
-
@staticmethod
|
762
|
-
def compose(*functions: Callable[[Any], Any]) -> Callable:
|
763
|
-
"""
|
764
|
-
Creates a decorator to sequentially apply multiple functions, where the output
|
765
|
-
of one function becomes the input to the next. This enables function
|
766
|
-
composition, allowing for elegant and flexible chaining of operations.
|
767
|
-
|
768
|
-
This decorator is particularly useful when you need to perform a series of
|
769
|
-
transformations or operations on data, especially when those operations need to
|
770
|
-
be applied in a specific order. It supports both synchronous and asynchronous
|
771
|
-
functions but requires all functions to be of the same type (all synchronous or
|
772
|
-
all asynchronous).
|
773
|
-
|
774
|
-
Args:
|
775
|
-
*functions (Callable[[Any], Any]):
|
776
|
-
A variable number of functions that are to be composed together. Each
|
777
|
-
function must accept a single argument and return a value.
|
778
|
-
|
779
|
-
Returns:
|
780
|
-
Callable:
|
781
|
-
A decorator that, when applied to a function, composes it with the
|
782
|
-
specified functions, creating a pipeline of function calls.
|
783
|
-
|
784
|
-
Raises:
|
785
|
-
ValueError:
|
786
|
-
If the provided functions mix synchronous and asynchronous types, as they
|
787
|
-
cannot be composed together.
|
788
|
-
|
789
|
-
Examples:
|
790
|
-
>>> def double(x): return x * 2
|
791
|
-
>>> def increment(x): return x + 1
|
792
|
-
>>> @CallDecorator.compose(increment, double)
|
793
|
-
... def start_value(x):
|
794
|
-
... return x
|
795
|
-
>>> start_value(3)
|
796
|
-
7 # The value is doubled to 6, then incremented to 7
|
797
|
-
"""
|
798
|
-
|
799
|
-
def decorator(func: Callable) -> Callable:
|
800
|
-
if not any(is_coroutine_func(f) for f in functions):
|
801
|
-
|
802
|
-
@functools.wraps(func)
|
803
|
-
def sync_wrapper(*args, **kwargs):
|
804
|
-
value = func(*args, **kwargs)
|
805
|
-
for function in functions:
|
806
|
-
try:
|
807
|
-
value = function(value)
|
808
|
-
except Exception as e:
|
809
|
-
raise ValueError(
|
810
|
-
f"Error in function {function.__name__}: {e}"
|
811
|
-
)
|
812
|
-
return value
|
813
|
-
|
814
|
-
return sync_wrapper
|
815
|
-
elif all(is_coroutine_func(f) for f in functions):
|
816
|
-
|
817
|
-
@functools.wraps(func)
|
818
|
-
async def async_wrapper(*args, **kwargs):
|
819
|
-
value = func(*args, **kwargs)
|
820
|
-
for function in functions:
|
821
|
-
try:
|
822
|
-
value = await function(value)
|
823
|
-
except Exception as e:
|
824
|
-
raise ValueError(
|
825
|
-
f"Error in function {function.__name__}: {e}"
|
826
|
-
)
|
827
|
-
return value
|
828
|
-
|
829
|
-
return async_wrapper
|
830
|
-
else:
|
831
|
-
raise ValueError(
|
832
|
-
"Cannot compose both synchronous and asynchronous functions."
|
833
|
-
)
|
834
|
-
|
835
|
-
return decorator
|
836
|
-
|
837
|
-
@staticmethod
|
838
|
-
def pre_post_process(
|
839
|
-
preprocess: Callable[..., Any] = None,
|
840
|
-
postprocess: Callable[..., Any] = None,
|
841
|
-
preprocess_args=[],
|
842
|
-
preprocess_kwargs={},
|
843
|
-
postprocess_args=[],
|
844
|
-
postprocess_kwargs={},
|
845
|
-
) -> Callable:
|
846
|
-
"""
|
847
|
-
Decorates a function with preprocessing and postprocessing steps, allowing for
|
848
|
-
modifications to the arguments before the function call and to the result after
|
849
|
-
the function call. This decorator is versatile, supporting both synchronous and
|
850
|
-
asynchronous functions, and enhances the modularity and reusability of code by
|
851
|
-
abstracting common preprocessing and postprocessing patterns into decorator form.
|
852
|
-
|
853
|
-
Preprocessing can include any modifications or checks to the arguments, such as
|
854
|
-
validation or transformation, while postprocessing allows for adjustments to the
|
855
|
-
function's output, such as formatting results or applying additional computations.
|
856
|
-
|
857
|
-
Args:
|
858
|
-
preprocess (Callable[..., Any]):
|
859
|
-
A function to preprocess the arguments passed to the decorated function.
|
860
|
-
It must accept the same arguments as the decorated function.
|
861
|
-
postprocess (Callable[..., Any]):
|
862
|
-
A function to postprocess the result of the decorated function. It must
|
863
|
-
accept a single argument, which is the output of the decorated function.
|
864
|
-
|
865
|
-
Returns:
|
866
|
-
Callable:
|
867
|
-
A decorated function that applies the specified preprocessing and
|
868
|
-
postprocessing steps to its execution.
|
869
|
-
|
870
|
-
Examples:
|
871
|
-
>>> @CallDecorator.pre_post_process(lambda x: x - 1, lambda x: x * 2)
|
872
|
-
... async def process_value(x):
|
873
|
-
... return x + 2
|
874
|
-
>>> asyncio.run(process_value(5))
|
875
|
-
12 # Input 5 is preprocessed to 4, processed to 6, and postprocessed to 12
|
876
|
-
"""
|
877
|
-
|
878
|
-
def decorator(func: Callable[..., Any]) -> Callable[..., Any]:
|
879
|
-
if is_coroutine_func(func):
|
880
|
-
|
881
|
-
@functools.wraps(func)
|
882
|
-
async def async_wrapper(*args, **kwargs) -> Any:
|
883
|
-
preprocessed_args = [
|
884
|
-
preprocess(arg, *preprocess_args, **preprocess_kwargs)
|
885
|
-
for arg in args
|
886
|
-
]
|
887
|
-
preprocessed_kwargs = {
|
888
|
-
k: preprocess(v, *preprocess_args, **preprocess_kwargs)
|
889
|
-
for k, v in kwargs.items()
|
890
|
-
}
|
891
|
-
result = await func(
|
892
|
-
*preprocessed_args, **preprocessed_kwargs
|
893
|
-
)
|
894
|
-
return postprocess(
|
895
|
-
result, *postprocess_args, **postprocess_kwargs
|
896
|
-
)
|
897
|
-
|
898
|
-
return async_wrapper
|
899
|
-
else:
|
900
|
-
|
901
|
-
@functools.wraps(func)
|
902
|
-
def sync_wrapper(*args, **kwargs) -> Any:
|
903
|
-
preprocessed_args = [
|
904
|
-
preprocess(arg, *preprocess_args, **preprocess_kwargs)
|
905
|
-
for arg in args
|
906
|
-
]
|
907
|
-
preprocessed_kwargs = {
|
908
|
-
k: preprocess(v, *preprocess_args, **preprocess_kwargs)
|
909
|
-
for k, v in kwargs.items()
|
910
|
-
}
|
911
|
-
result = func(*preprocessed_args, **preprocessed_kwargs)
|
912
|
-
return postprocess(
|
913
|
-
result, *postprocess_args, **postprocess_kwargs
|
914
|
-
)
|
915
|
-
|
916
|
-
return sync_wrapper
|
917
|
-
|
918
|
-
return decorator
|
919
|
-
|
920
|
-
@staticmethod
|
921
|
-
def cache(func: Callable, ttl=600, maxsize=None) -> Callable:
|
922
|
-
"""
|
923
|
-
Decorates a function (synchronous or asynchronous) to cache its results for a
|
924
|
-
specified time-to-live (TTL). This caching mechanism prevents re-execution of
|
925
|
-
the function with the same arguments, improving efficiency, especially for I/O
|
926
|
-
bound or computationally intensive operations.
|
927
|
-
|
928
|
-
Args: func (Callable): The target function to cache. Can be either synchronous
|
929
|
-
or asynchronous. ttl (int, optional): The time-to-live of the cache entries in
|
930
|
-
seconds. Defaults to 600 seconds. maxsize (Optional[int], optional): The
|
931
|
-
maximum size of the cache. If None, the cache is unbounded. Applies only to
|
932
|
-
synchronous functions.
|
933
|
-
|
934
|
-
Returns:
|
935
|
-
Callable: A decorated version of the function with caching applied. Subsequent
|
936
|
-
calls with the same arguments within the TTL will return the cached result.
|
937
|
-
|
938
|
-
Examples:
|
939
|
-
>>> @CallDecorator.cache(ttl=10)
|
940
|
-
... async def fetch_data(key):
|
941
|
-
... # Simulate a database fetch
|
942
|
-
... return "data for " + key
|
943
|
-
... # Subsequent calls to `fetch_data` with the same `key` within 10 seconds
|
944
|
-
... # will return the cached result without re-executing the function body.
|
945
|
-
"""
|
946
|
-
|
947
|
-
if is_coroutine_func(func):
|
948
|
-
# Asynchronous function handling
|
949
|
-
@AsyncUtil.cached(ttl=ttl)
|
950
|
-
async def cached_async(*args, **kwargs) -> Any:
|
951
|
-
return await func(*args, **kwargs)
|
952
|
-
|
953
|
-
@functools.wraps(func)
|
954
|
-
async def async_wrapper(*args, **kwargs) -> Any:
|
955
|
-
return await cached_async(*args, **kwargs)
|
956
|
-
|
957
|
-
return async_wrapper
|
958
|
-
|
959
|
-
else:
|
960
|
-
# Synchronous function handling
|
961
|
-
@functools.lru_cache(maxsize=maxsize)
|
962
|
-
def cached_sync(*args, **kwargs) -> Any:
|
963
|
-
return func(*args, **kwargs)
|
964
|
-
|
965
|
-
@functools.wraps(func)
|
966
|
-
def sync_wrapper(*args, **kwargs) -> Any:
|
967
|
-
return cached_sync(*args, **kwargs)
|
968
|
-
|
969
|
-
return sync_wrapper
|
970
|
-
|
971
|
-
@staticmethod
|
972
|
-
def filter(predicate: Callable[[Any], bool]) -> Callable:
|
973
|
-
"""
|
974
|
-
Decorates a function to filter its list result based on a given predicate. The
|
975
|
-
predicate determines which items in the list should be included in the final
|
976
|
-
result. This decorator can be applied to both synchronous and asynchronous
|
977
|
-
functions returning lists.
|
978
|
-
|
979
|
-
Args:
|
980
|
-
predicate (Callable[[Any], bool]):
|
981
|
-
A function that evaluates each input_ in the list. Items for which the
|
982
|
-
predicate returns True are included in the final result.
|
983
|
-
|
984
|
-
Returns:
|
985
|
-
Callable:
|
986
|
-
A decorated function that filters its list result according to the predicate.
|
987
|
-
|
988
|
-
Examples:
|
989
|
-
>>> @CallDecorator.filter(lambda x: x % 2 == 0)
|
990
|
-
... async def get_even_numbers():
|
991
|
-
... return [1, 2, 3, 4, 5]
|
992
|
-
>>> asyncio.run(get_even_numbers())
|
993
|
-
[2, 4]
|
994
|
-
... # The result list is filtered to include only even numbers.
|
995
|
-
"""
|
996
|
-
|
997
|
-
def decorator(func: Callable[..., list[Any]]) -> Callable:
|
998
|
-
if AsyncUtil.is_coroutine_func(func):
|
999
|
-
|
1000
|
-
@functools.wraps(func)
|
1001
|
-
async def wrapper(*args, **kwargs) -> list[Any]:
|
1002
|
-
values = await func(*args, **kwargs)
|
1003
|
-
return [value for value in values if predicate(value)]
|
1004
|
-
|
1005
|
-
return wrapper
|
1006
|
-
else:
|
1007
|
-
|
1008
|
-
@functools.wraps(func)
|
1009
|
-
def wrapper(*args, **kwargs) -> list[Any]:
|
1010
|
-
values = func(*args, **kwargs)
|
1011
|
-
return [value for value in values if predicate(value)]
|
1012
|
-
|
1013
|
-
return wrapper
|
1014
|
-
|
1015
|
-
return decorator
|
1016
|
-
|
1017
|
-
@staticmethod
|
1018
|
-
def reduce(function: Callable[[Any, Any], Any], initial: Any) -> Callable:
|
1019
|
-
"""
|
1020
|
-
Decorates a function to apply a reduction to its list result, combining all
|
1021
|
-
items in the list into a single value using the specified reduction function.
|
1022
|
-
This is useful for aggregating results or performing cumulative operations on
|
1023
|
-
the list returned by the decorated function. The decorator supports both
|
1024
|
-
synchronous and asynchronous functions.
|
1025
|
-
|
1026
|
-
Args:
|
1027
|
-
function (Callable[[Any, Any], Any]):
|
1028
|
-
The reduction function to apply to the list. It should take two arguments
|
1029
|
-
and return a single value that is the result of combining them.
|
1030
|
-
initial (Any):
|
1031
|
-
The initial value for the reduction process. This value is used as the
|
1032
|
-
starting point for the reduction and should be an identity value for the
|
1033
|
-
reduction operation.
|
1034
|
-
|
1035
|
-
Returns:
|
1036
|
-
Callable:
|
1037
|
-
A decorated function that applies the specified reduction to its list
|
1038
|
-
result,
|
1039
|
-
producing a single aggregated value.
|
1040
|
-
|
1041
|
-
Examples:
|
1042
|
-
>>> @CallDecorator.reduce(lambda x, y: x + y, 0)
|
1043
|
-
... async def sum_numbers():
|
1044
|
-
... return [1, 2, 3, 4]
|
1045
|
-
>>> asyncio.run(sum_numbers())
|
1046
|
-
10
|
1047
|
-
... # The numbers in the list are summed, resulting in a single value.
|
1048
|
-
"""
|
1049
|
-
|
1050
|
-
def decorator(func: Callable[..., list[Any]]) -> Callable:
|
1051
|
-
if is_coroutine_func(func):
|
1052
|
-
|
1053
|
-
@functools.wraps(func)
|
1054
|
-
async def async_wrapper(*args, **kwargs) -> Any:
|
1055
|
-
values = await func(*args, **kwargs)
|
1056
|
-
return functools.reduce(function, values, initial)
|
1057
|
-
|
1058
|
-
return async_wrapper
|
1059
|
-
else:
|
1060
|
-
|
1061
|
-
@functools.wraps(func)
|
1062
|
-
def sync_wrapper(*args, **kwargs) -> Any:
|
1063
|
-
values = func(*args, **kwargs)
|
1064
|
-
return functools.reduce(function, values, initial)
|
1065
|
-
|
1066
|
-
return sync_wrapper
|
1067
|
-
|
1068
|
-
return decorator
|
1069
|
-
|
1070
|
-
@staticmethod
|
1071
|
-
def max_concurrency(limit: int = 5) -> Callable:
|
1072
|
-
"""
|
1073
|
-
Limits the number of concurrent executions for an asynchronous function to
|
1074
|
-
ensure that no more than a specified number of instances of the function run
|
1075
|
-
simultaneously. This is particularly useful for controlling resource usage and
|
1076
|
-
preventing overload when dealing with IO-bound operations or external services
|
1077
|
-
that can only handle a limited amount of concurrent requests.
|
1078
|
-
|
1079
|
-
Args:
|
1080
|
-
limit (int):
|
1081
|
-
The maximum number of concurrent executions allowed for the decorated
|
1082
|
-
function.
|
1083
|
-
|
1084
|
-
Returns:
|
1085
|
-
Callable:
|
1086
|
-
An asynchronous function wrapper that enforces the concurrency limit.
|
1087
|
-
|
1088
|
-
Examples:
|
1089
|
-
>>> @CallDecorator.max_concurrency(3)
|
1090
|
-
... async def process_data(input_):
|
1091
|
-
... # Asynchronous processing logic here
|
1092
|
-
... pass
|
1093
|
-
... # No more than 3 instances of `process_data` will run concurrently.
|
1094
|
-
"""
|
1095
|
-
|
1096
|
-
def decorator(func: Callable) -> Callable:
|
1097
|
-
if not is_coroutine_func(func):
|
1098
|
-
raise TypeError(
|
1099
|
-
"max_concurrency decorator can only be used with async functions."
|
1100
|
-
)
|
1101
|
-
semaphore = asyncio.Semaphore(limit)
|
1102
|
-
|
1103
|
-
@functools.wraps(func)
|
1104
|
-
async def wrapper(*args, **kwargs):
|
1105
|
-
async with semaphore:
|
1106
|
-
return await func(*args, **kwargs)
|
1107
|
-
|
1108
|
-
return wrapper
|
1109
|
-
|
1110
|
-
return decorator
|
1111
|
-
|
1112
|
-
# # noinspection PyRedeclaration
|
1113
|
-
# @staticmethod
|
1114
|
-
# def throttle(period: int) -> Callable:
|
1115
|
-
# """
|
1116
|
-
# A static method to create a throttling decorator. This method utilizes the
|
1117
|
-
# _Throttle class to enforce a minimum time period between successive calls of the
|
1118
|
-
# decorated function.
|
1119
|
-
|
1120
|
-
# Args:
|
1121
|
-
# period (int):
|
1122
|
-
# The minimum time period, in seconds, that must elapse between successive
|
1123
|
-
# calls to the decorated function.
|
1124
|
-
|
1125
|
-
# Returns:
|
1126
|
-
# Callable:
|
1127
|
-
# A decorator that applies a throttling mechanism to the decorated function,
|
1128
|
-
# ensuring that the function is not called more frequently than the
|
1129
|
-
# specified period.
|
1130
|
-
|
1131
|
-
# Examples:
|
1132
|
-
# >>> @CallDecorator.throttle(2) # Ensures at least 2 seconds between calls
|
1133
|
-
# ... async def fetch_data(): pass
|
1134
|
-
|
1135
|
-
# This decorator is particularly useful in scenarios like rate-limiting API
|
1136
|
-
# calls or reducing the frequency of resource-intensive operations.
|
1137
|
-
# """
|
1138
|
-
# return Throttle(period)
|
1139
|
-
|
1140
|
-
@staticmethod
|
1141
|
-
def force_async(fn):
|
1142
|
-
pool = ThreadPoolExecutor()
|
1143
|
-
|
1144
|
-
@functools.wraps(fn)
|
1145
|
-
def wrapper(*args, **kwargs):
|
1146
|
-
future = pool.submit(fn, *args, **kwargs)
|
1147
|
-
return asyncio.wrap_future(future) # make it awaitable
|
1148
|
-
|
1149
|
-
return wrapper
|
1150
|
-
|
1151
|
-
|
1152
|
-
class Throttle:
|
1153
|
-
"""
|
1154
|
-
A class that provides a throttling mechanism for function calls.
|
1155
|
-
|
1156
|
-
When used as a decorator, it ensures that the decorated function can only be called
|
1157
|
-
once per specified period. Subsequent calls within this period are delayed to enforce
|
1158
|
-
this constraint.
|
1159
|
-
|
1160
|
-
Attributes:
|
1161
|
-
period (int): The minimum time period (in seconds) between successive calls.
|
1162
|
-
|
1163
|
-
Methods:
|
1164
|
-
__call__: Decorates a synchronous function with throttling.
|
1165
|
-
__call_async__: Decorates an asynchronous function with throttling.
|
1166
|
-
"""
|
1167
|
-
|
1168
|
-
def __init__(self, period: int) -> None:
|
1169
|
-
"""
|
1170
|
-
Initializes a new instance of _Throttle.
|
1171
|
-
|
1172
|
-
Args:
|
1173
|
-
period (int): The minimum time period (in seconds) between successive calls.
|
1174
|
-
"""
|
1175
|
-
self.period = period
|
1176
|
-
self.last_called = 0
|
1177
|
-
|
1178
|
-
def __call__(self, func: Callable[..., Any]) -> Callable[..., Any]:
|
1179
|
-
"""
|
1180
|
-
Decorates a synchronous function with the throttling mechanism.
|
1181
|
-
|
1182
|
-
Args:
|
1183
|
-
func (Callable[..., Any]): The synchronous function to be throttled.
|
1184
|
-
|
1185
|
-
Returns:
|
1186
|
-
Callable[..., Any]: The throttled synchronous function.
|
1187
|
-
"""
|
1188
|
-
|
1189
|
-
@functools.wraps(func)
|
1190
|
-
def wrapper(*args, **kwargs) -> Any:
|
1191
|
-
elapsed = SysUtil.get_now(datetime_=False) - self.last_called
|
1192
|
-
if elapsed < self.period:
|
1193
|
-
SysUtil.sleep(self.period - elapsed)
|
1194
|
-
self.last_called = SysUtil.get_now(datetime_=False)
|
1195
|
-
return func(*args, **kwargs)
|
1196
|
-
|
1197
|
-
return wrapper
|
1198
|
-
|
1199
|
-
async def __call_async__(
|
1200
|
-
self, func: Callable[..., Any]
|
1201
|
-
) -> Callable[..., Any]:
|
1202
|
-
"""
|
1203
|
-
Decorates an asynchronous function with the throttling mechanism.
|
1204
|
-
|
1205
|
-
Args:
|
1206
|
-
func (Callable[..., Any]): The asynchronous function to be throttled.
|
1207
|
-
|
1208
|
-
Returns:
|
1209
|
-
Callable[..., Any]: The throttled asynchronous function.
|
1210
|
-
"""
|
1211
|
-
|
1212
|
-
@functools.wraps(func)
|
1213
|
-
async def wrapper(*args, **kwargs) -> Any:
|
1214
|
-
elapsed = SysUtil.get_now(datetime_=False) - self.last_called
|
1215
|
-
if elapsed < self.period:
|
1216
|
-
await AsyncUtil.sleep(self.period - elapsed)
|
1217
|
-
self.last_called = SysUtil.get_now(datetime_=False)
|
1218
|
-
return await func(*args, **kwargs)
|
1219
|
-
|
1220
|
-
return wrapper
|
1221
|
-
|
1222
|
-
|
1223
|
-
def _custom_error_handler(
|
1224
|
-
error: Exception, error_map: dict[type, Callable]
|
1225
|
-
) -> None:
|
1226
|
-
# noinspection PyUnresolvedReferences
|
1227
|
-
"""
|
1228
|
-
handle errors based on a given error mapping.
|
1229
|
-
|
1230
|
-
Args:
|
1231
|
-
error (Exception):
|
1232
|
-
The error to handle.
|
1233
|
-
error_map (Dict[type, Callable]):
|
1234
|
-
A dictionary mapping error types to handler functions.
|
1235
|
-
|
1236
|
-
examples:
|
1237
|
-
>>> def handle_value_error(e): print("ValueError occurred")
|
1238
|
-
>>> custom_error_handler(ValueError(), {ValueError: handle_value_error})
|
1239
|
-
ValueError occurred
|
1240
|
-
"""
|
1241
|
-
if handler := error_map.get(type(error)):
|
1242
|
-
handler(error)
|
1243
|
-
else:
|
1244
|
-
logging.error(f"Unhandled error: {error}")
|
1245
|
-
|
1246
|
-
|
1247
|
-
async def call_handler(
|
1248
|
-
func: Callable, *args, error_map: dict[type, Callable] = None, **kwargs
|
1249
|
-
) -> Any:
|
1250
|
-
"""
|
1251
|
-
call a function with error handling, supporting both synchronous and asynchronous
|
1252
|
-
functions.
|
1253
|
-
|
1254
|
-
Args:
|
1255
|
-
func (Callable):
|
1256
|
-
The function to call.
|
1257
|
-
*args:
|
1258
|
-
Positional arguments to pass to the function.
|
1259
|
-
error_map (Dict[type, Callable], optional):
|
1260
|
-
A dictionary mapping error types to handler functions.
|
1261
|
-
**kwargs:
|
1262
|
-
Keyword arguments to pass to the function.
|
1263
|
-
|
1264
|
-
Returns:
|
1265
|
-
Any: The result of the function call.
|
1266
|
-
|
1267
|
-
Raises:
|
1268
|
-
Exception: Propagates any exceptions not handled by the error_map.
|
1269
|
-
|
1270
|
-
examples:
|
1271
|
-
>>> async def async_add(x, y): return x + y
|
1272
|
-
>>> asyncio.run(_call_handler(async_add, 1, 2))
|
1273
|
-
3
|
1274
|
-
"""
|
1275
|
-
try:
|
1276
|
-
if not is_coroutine_func(func):
|
1277
|
-
return func(*args, **kwargs)
|
1278
|
-
|
1279
|
-
# Checking for a running event loop
|
1280
|
-
try:
|
1281
|
-
loop = asyncio.get_running_loop()
|
1282
|
-
except RuntimeError: # No running event loop
|
1283
|
-
loop = asyncio.new_event_loop()
|
1284
|
-
result = loop.run_until_complete(func(*args, **kwargs))
|
1285
|
-
|
1286
|
-
loop.close()
|
1287
|
-
return result
|
1288
|
-
|
1289
|
-
if loop.is_running():
|
1290
|
-
return await func(*args, **kwargs)
|
1291
|
-
|
1292
|
-
except Exception as e:
|
1293
|
-
if error_map:
|
1294
|
-
_custom_error_handler(e, error_map)
|
1295
|
-
# else:
|
1296
|
-
# logging.error(f"Error in call_handler: {e}")
|
1297
|
-
raise
|
1298
|
-
|
1299
|
-
|
1300
|
-
@functools.cache
|
1301
|
-
def is_coroutine_func(func: Callable) -> bool:
|
1302
|
-
"""
|
1303
|
-
checks if the specified function is an asyncio coroutine function.
|
1304
|
-
|
1305
|
-
this utility function is critical for asynchronous programming in Python, allowing
|
1306
|
-
developers to distinguish between synchronous and asynchronous functions.
|
1307
|
-
understanding whether a function is coroutine-enabled is essential for making
|
1308
|
-
correct asynchronous calls and for integrating synchronous functions into
|
1309
|
-
asynchronous codebases correctly.
|
1310
|
-
|
1311
|
-
Args:
|
1312
|
-
func (Callable):
|
1313
|
-
The function to check for coroutine compatibility.
|
1314
|
-
|
1315
|
-
Returns:
|
1316
|
-
bool:
|
1317
|
-
True if `func` is an asyncio coroutine function, False otherwise. this
|
1318
|
-
determination is based on whether the function is defined with `async def`.
|
1319
|
-
|
1320
|
-
examples:
|
1321
|
-
>>> async def async_func(): pass
|
1322
|
-
>>> def sync_func(): pass
|
1323
|
-
>>> is_coroutine_func(async_func)
|
1324
|
-
True
|
1325
|
-
>>> is_coroutine_func(sync_func)
|
1326
|
-
False
|
1327
|
-
"""
|
1328
|
-
return asyncio.iscoroutinefunction(func)
|