synapse-sdk 1.0.0a35__py3-none-any.whl → 2025.11.7__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.
Potentially problematic release.
This version of synapse-sdk might be problematic. Click here for more details.
- synapse_sdk/__init__.py +24 -0
- synapse_sdk/cli/__init__.py +308 -5
- synapse_sdk/cli/alias/utils.py +1 -1
- synapse_sdk/cli/code_server.py +687 -0
- synapse_sdk/cli/config.py +440 -0
- synapse_sdk/cli/devtools.py +90 -0
- synapse_sdk/cli/plugin/publish.py +23 -15
- synapse_sdk/clients/agent/__init__.py +9 -3
- synapse_sdk/clients/agent/container.py +133 -0
- synapse_sdk/clients/agent/core.py +19 -0
- synapse_sdk/clients/agent/ray.py +298 -9
- synapse_sdk/clients/backend/__init__.py +28 -12
- synapse_sdk/clients/backend/annotation.py +9 -1
- synapse_sdk/clients/backend/core.py +31 -4
- synapse_sdk/clients/backend/data_collection.py +186 -0
- synapse_sdk/clients/backend/hitl.py +1 -1
- synapse_sdk/clients/backend/integration.py +4 -3
- synapse_sdk/clients/backend/ml.py +1 -1
- synapse_sdk/clients/backend/models.py +35 -1
- synapse_sdk/clients/base.py +309 -36
- synapse_sdk/clients/ray/serve.py +2 -0
- synapse_sdk/devtools/__init__.py +0 -0
- synapse_sdk/devtools/config.py +94 -0
- synapse_sdk/devtools/docs/.gitignore +20 -0
- synapse_sdk/devtools/docs/README.md +41 -0
- synapse_sdk/devtools/docs/blog/2019-05-28-first-blog-post.md +12 -0
- synapse_sdk/devtools/docs/blog/2019-05-29-long-blog-post.md +44 -0
- synapse_sdk/devtools/docs/blog/2021-08-01-mdx-blog-post.mdx +24 -0
- synapse_sdk/devtools/docs/blog/2021-08-26-welcome/docusaurus-plushie-banner.jpeg +0 -0
- synapse_sdk/devtools/docs/blog/2021-08-26-welcome/index.md +29 -0
- synapse_sdk/devtools/docs/blog/authors.yml +25 -0
- synapse_sdk/devtools/docs/blog/tags.yml +19 -0
- synapse_sdk/devtools/docs/docs/api/clients/agent.md +43 -0
- synapse_sdk/devtools/docs/docs/api/clients/annotation-mixin.md +378 -0
- synapse_sdk/devtools/docs/docs/api/clients/backend.md +420 -0
- synapse_sdk/devtools/docs/docs/api/clients/base.md +257 -0
- synapse_sdk/devtools/docs/docs/api/clients/core-mixin.md +477 -0
- synapse_sdk/devtools/docs/docs/api/clients/data-collection-mixin.md +422 -0
- synapse_sdk/devtools/docs/docs/api/clients/hitl-mixin.md +554 -0
- synapse_sdk/devtools/docs/docs/api/clients/index.md +391 -0
- synapse_sdk/devtools/docs/docs/api/clients/integration-mixin.md +571 -0
- synapse_sdk/devtools/docs/docs/api/clients/ml-mixin.md +578 -0
- synapse_sdk/devtools/docs/docs/api/clients/ray.md +342 -0
- synapse_sdk/devtools/docs/docs/api/index.md +52 -0
- synapse_sdk/devtools/docs/docs/api/plugins/categories.md +43 -0
- synapse_sdk/devtools/docs/docs/api/plugins/models.md +114 -0
- synapse_sdk/devtools/docs/docs/api/plugins/utils.md +328 -0
- synapse_sdk/devtools/docs/docs/categories.md +0 -0
- synapse_sdk/devtools/docs/docs/cli-usage.md +280 -0
- synapse_sdk/devtools/docs/docs/concepts/index.md +38 -0
- synapse_sdk/devtools/docs/docs/configuration.md +83 -0
- synapse_sdk/devtools/docs/docs/contributing.md +306 -0
- synapse_sdk/devtools/docs/docs/examples/index.md +29 -0
- synapse_sdk/devtools/docs/docs/faq.md +179 -0
- synapse_sdk/devtools/docs/docs/features/converters/index.md +455 -0
- synapse_sdk/devtools/docs/docs/features/index.md +24 -0
- synapse_sdk/devtools/docs/docs/features/utils/file.md +415 -0
- synapse_sdk/devtools/docs/docs/features/utils/network.md +378 -0
- synapse_sdk/devtools/docs/docs/features/utils/storage.md +57 -0
- synapse_sdk/devtools/docs/docs/features/utils/types.md +51 -0
- synapse_sdk/devtools/docs/docs/installation.md +94 -0
- synapse_sdk/devtools/docs/docs/introduction.md +47 -0
- synapse_sdk/devtools/docs/docs/plugins/categories/neural-net-plugins/train-action-overview.md +814 -0
- synapse_sdk/devtools/docs/docs/plugins/categories/pre-annotation-plugins/pre-annotation-plugin-overview.md +198 -0
- synapse_sdk/devtools/docs/docs/plugins/categories/pre-annotation-plugins/to-task-action-development.md +1645 -0
- synapse_sdk/devtools/docs/docs/plugins/categories/pre-annotation-plugins/to-task-overview.md +717 -0
- synapse_sdk/devtools/docs/docs/plugins/categories/pre-annotation-plugins/to-task-template-development.md +1380 -0
- synapse_sdk/devtools/docs/docs/plugins/categories/upload-plugins/upload-plugin-action.md +948 -0
- synapse_sdk/devtools/docs/docs/plugins/categories/upload-plugins/upload-plugin-overview.md +544 -0
- synapse_sdk/devtools/docs/docs/plugins/categories/upload-plugins/upload-plugin-template.md +766 -0
- synapse_sdk/devtools/docs/docs/plugins/export-plugins.md +1092 -0
- synapse_sdk/devtools/docs/docs/plugins/plugins.md +852 -0
- synapse_sdk/devtools/docs/docs/quickstart.md +78 -0
- synapse_sdk/devtools/docs/docs/troubleshooting.md +519 -0
- synapse_sdk/devtools/docs/docs/tutorial-basics/_category_.json +8 -0
- synapse_sdk/devtools/docs/docs/tutorial-basics/congratulations.md +23 -0
- synapse_sdk/devtools/docs/docs/tutorial-basics/create-a-blog-post.md +34 -0
- synapse_sdk/devtools/docs/docs/tutorial-basics/create-a-document.md +57 -0
- synapse_sdk/devtools/docs/docs/tutorial-basics/create-a-page.md +43 -0
- synapse_sdk/devtools/docs/docs/tutorial-basics/deploy-your-site.md +31 -0
- synapse_sdk/devtools/docs/docs/tutorial-basics/markdown-features.mdx +152 -0
- synapse_sdk/devtools/docs/docs/tutorial-extras/_category_.json +7 -0
- synapse_sdk/devtools/docs/docs/tutorial-extras/img/docsVersionDropdown.png +0 -0
- synapse_sdk/devtools/docs/docs/tutorial-extras/img/localeDropdown.png +0 -0
- synapse_sdk/devtools/docs/docs/tutorial-extras/manage-docs-versions.md +55 -0
- synapse_sdk/devtools/docs/docs/tutorial-extras/translate-your-site.md +88 -0
- synapse_sdk/devtools/docs/docusaurus.config.ts +148 -0
- synapse_sdk/devtools/docs/i18n/ko/code.json +325 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/clients/agent.md +43 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/clients/annotation-mixin.md +289 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/clients/backend.md +420 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/clients/base.md +257 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/clients/core-mixin.md +417 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/clients/data-collection-mixin.md +356 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/clients/hitl-mixin.md +192 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/clients/index.md +391 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/clients/integration-mixin.md +479 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/clients/ml-mixin.md +284 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/clients/ray.md +342 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/index.md +52 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/plugins/models.md +114 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/categories.md +0 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/cli-usage.md +280 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/concepts/index.md +38 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/configuration.md +83 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/contributing.md +306 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/examples/index.md +29 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/faq.md +179 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/features/converters/index.md +30 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/features/index.md +24 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/features/utils/file.md +415 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/features/utils/network.md +378 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/features/utils/storage.md +60 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/features/utils/types.md +51 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/installation.md +94 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/introduction.md +47 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/plugins/categories/neural-net-plugins/train-action-overview.md +815 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/plugins/categories/pre-annotation-plugins/pre-annotation-plugin-overview.md +198 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/plugins/categories/pre-annotation-plugins/to-task-action-development.md +1645 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/plugins/categories/pre-annotation-plugins/to-task-overview.md +717 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/plugins/categories/pre-annotation-plugins/to-task-template-development.md +1380 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/plugins/categories/upload-plugins/upload-plugin-action.md +948 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/plugins/categories/upload-plugins/upload-plugin-overview.md +544 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/plugins/categories/upload-plugins/upload-plugin-template.md +766 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/plugins/export-plugins.md +1092 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/plugins/plugins.md +117 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/quickstart.md +78 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/troubleshooting.md +519 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current.json +34 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-theme-classic/footer.json +42 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-theme-classic/navbar.json +18 -0
- synapse_sdk/devtools/docs/package-lock.json +18784 -0
- synapse_sdk/devtools/docs/package.json +48 -0
- synapse_sdk/devtools/docs/sidebars.ts +122 -0
- synapse_sdk/devtools/docs/src/components/HomepageFeatures/index.tsx +71 -0
- synapse_sdk/devtools/docs/src/components/HomepageFeatures/styles.module.css +11 -0
- synapse_sdk/devtools/docs/src/css/custom.css +30 -0
- synapse_sdk/devtools/docs/src/pages/index.module.css +23 -0
- synapse_sdk/devtools/docs/src/pages/index.tsx +21 -0
- synapse_sdk/devtools/docs/src/pages/markdown-page.md +7 -0
- synapse_sdk/devtools/docs/static/.nojekyll +0 -0
- synapse_sdk/devtools/docs/static/img/docusaurus-social-card.jpg +0 -0
- synapse_sdk/devtools/docs/static/img/docusaurus.png +0 -0
- synapse_sdk/devtools/docs/static/img/favicon.ico +0 -0
- synapse_sdk/devtools/docs/static/img/logo.png +0 -0
- synapse_sdk/devtools/docs/static/img/undraw_docusaurus_mountain.svg +171 -0
- synapse_sdk/devtools/docs/static/img/undraw_docusaurus_react.svg +170 -0
- synapse_sdk/devtools/docs/static/img/undraw_docusaurus_tree.svg +40 -0
- synapse_sdk/devtools/docs/tsconfig.json +8 -0
- synapse_sdk/devtools/server.py +41 -0
- synapse_sdk/devtools/streamlit_app/__init__.py +5 -0
- synapse_sdk/devtools/streamlit_app/app.py +128 -0
- synapse_sdk/devtools/streamlit_app/services/__init__.py +11 -0
- synapse_sdk/devtools/streamlit_app/services/job_service.py +233 -0
- synapse_sdk/devtools/streamlit_app/services/plugin_service.py +236 -0
- synapse_sdk/devtools/streamlit_app/services/serve_service.py +95 -0
- synapse_sdk/devtools/streamlit_app/ui/__init__.py +15 -0
- synapse_sdk/devtools/streamlit_app/ui/config_tab.py +76 -0
- synapse_sdk/devtools/streamlit_app/ui/deployment_tab.py +66 -0
- synapse_sdk/devtools/streamlit_app/ui/http_tab.py +125 -0
- synapse_sdk/devtools/streamlit_app/ui/jobs_tab.py +573 -0
- synapse_sdk/devtools/streamlit_app/ui/serve_tab.py +346 -0
- synapse_sdk/devtools/streamlit_app/ui/status_bar.py +118 -0
- synapse_sdk/devtools/streamlit_app/utils/__init__.py +40 -0
- synapse_sdk/devtools/streamlit_app/utils/json_viewer.py +197 -0
- synapse_sdk/devtools/streamlit_app/utils/log_formatter.py +38 -0
- synapse_sdk/devtools/streamlit_app/utils/styles.py +241 -0
- synapse_sdk/devtools/streamlit_app/utils/ui_components.py +289 -0
- synapse_sdk/devtools/streamlit_app.py +10 -0
- synapse_sdk/loggers.py +65 -7
- synapse_sdk/plugins/README.md +1340 -0
- synapse_sdk/plugins/categories/base.py +73 -11
- synapse_sdk/plugins/categories/data_validation/actions/validation.py +72 -0
- synapse_sdk/plugins/categories/data_validation/templates/plugin/validation.py +33 -5
- synapse_sdk/plugins/categories/export/actions/__init__.py +3 -0
- synapse_sdk/plugins/categories/export/actions/export/__init__.py +28 -0
- synapse_sdk/plugins/categories/export/actions/export/action.py +165 -0
- synapse_sdk/plugins/categories/export/actions/export/enums.py +113 -0
- synapse_sdk/plugins/categories/export/actions/export/exceptions.py +53 -0
- synapse_sdk/plugins/categories/export/actions/export/models.py +74 -0
- synapse_sdk/plugins/categories/export/actions/export/run.py +195 -0
- synapse_sdk/plugins/categories/export/actions/{export.py → export/utils.py} +47 -82
- synapse_sdk/plugins/categories/export/templates/config.yaml +19 -1
- synapse_sdk/plugins/categories/export/templates/plugin/__init__.py +390 -0
- synapse_sdk/plugins/categories/export/templates/plugin/export.py +153 -129
- synapse_sdk/plugins/categories/neural_net/actions/deployment.py +9 -62
- synapse_sdk/plugins/categories/neural_net/actions/train.py +1062 -32
- synapse_sdk/plugins/categories/neural_net/actions/tune.py +534 -0
- synapse_sdk/plugins/categories/neural_net/templates/config.yaml +27 -5
- synapse_sdk/plugins/categories/neural_net/templates/plugin/inference.py +26 -10
- synapse_sdk/plugins/categories/pre_annotation/actions/__init__.py +4 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/pre_annotation/__init__.py +3 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/pre_annotation/action.py +10 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/__init__.py +28 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/action.py +145 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/enums.py +269 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/exceptions.py +14 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/factory.py +76 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/models.py +97 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/orchestrator.py +250 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/run.py +64 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/__init__.py +17 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/annotation.py +287 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/base.py +170 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/extraction.py +83 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/metrics.py +87 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/preprocessor.py +127 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/validation.py +143 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task.py +966 -0
- synapse_sdk/plugins/categories/pre_annotation/templates/config.yaml +19 -0
- synapse_sdk/plugins/categories/pre_annotation/templates/plugin/to_task.py +40 -0
- synapse_sdk/plugins/categories/upload/actions/upload/__init__.py +19 -0
- synapse_sdk/plugins/categories/upload/actions/upload/action.py +232 -0
- synapse_sdk/plugins/categories/upload/actions/upload/context.py +185 -0
- synapse_sdk/plugins/categories/upload/actions/upload/enums.py +471 -0
- synapse_sdk/plugins/categories/upload/actions/upload/exceptions.py +36 -0
- synapse_sdk/plugins/categories/upload/actions/upload/factory.py +138 -0
- synapse_sdk/plugins/categories/upload/actions/upload/models.py +203 -0
- synapse_sdk/plugins/categories/upload/actions/upload/orchestrator.py +183 -0
- synapse_sdk/plugins/categories/upload/actions/upload/registry.py +113 -0
- synapse_sdk/plugins/categories/upload/actions/upload/run.py +179 -0
- synapse_sdk/plugins/categories/upload/actions/upload/steps/__init__.py +1 -0
- synapse_sdk/plugins/categories/upload/actions/upload/steps/base.py +107 -0
- synapse_sdk/plugins/categories/upload/actions/upload/steps/cleanup.py +62 -0
- synapse_sdk/plugins/categories/upload/actions/upload/steps/collection.py +63 -0
- synapse_sdk/plugins/categories/upload/actions/upload/steps/generate.py +84 -0
- synapse_sdk/plugins/categories/upload/actions/upload/steps/initialize.py +82 -0
- synapse_sdk/plugins/categories/upload/actions/upload/steps/metadata.py +235 -0
- synapse_sdk/plugins/categories/upload/actions/upload/steps/organize.py +203 -0
- synapse_sdk/plugins/categories/upload/actions/upload/steps/upload.py +97 -0
- synapse_sdk/plugins/categories/upload/actions/upload/steps/validate.py +71 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/__init__.py +1 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/base.py +82 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/data_unit/__init__.py +1 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/data_unit/batch.py +39 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/data_unit/single.py +29 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/file_discovery/__init__.py +1 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/file_discovery/flat.py +258 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/file_discovery/recursive.py +281 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/metadata/__init__.py +1 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/metadata/excel.py +174 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/metadata/none.py +16 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/upload/__init__.py +1 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/upload/sync.py +84 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/validation/__init__.py +1 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/validation/default.py +60 -0
- synapse_sdk/plugins/categories/upload/actions/upload/utils.py +250 -0
- synapse_sdk/plugins/categories/upload/templates/README.md +470 -0
- synapse_sdk/plugins/categories/upload/templates/config.yaml +29 -2
- synapse_sdk/plugins/categories/upload/templates/plugin/__init__.py +294 -0
- synapse_sdk/plugins/categories/upload/templates/plugin/upload.py +88 -30
- synapse_sdk/plugins/models.py +122 -16
- synapse_sdk/plugins/templates/plugin-config-schema.json +406 -0
- synapse_sdk/plugins/templates/schema.json +491 -0
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/requirements.txt +1 -1
- synapse_sdk/plugins/utils/__init__.py +46 -0
- synapse_sdk/plugins/utils/actions.py +119 -0
- synapse_sdk/plugins/utils/config.py +203 -0
- synapse_sdk/plugins/{utils.py → utils/legacy.py} +26 -46
- synapse_sdk/plugins/utils/ray_gcs.py +66 -0
- synapse_sdk/plugins/utils/registry.py +58 -0
- synapse_sdk/shared/__init__.py +25 -0
- synapse_sdk/shared/enums.py +93 -0
- synapse_sdk/utils/converters/__init__.py +240 -0
- synapse_sdk/utils/converters/coco/__init__.py +0 -0
- synapse_sdk/utils/converters/coco/from_dm.py +322 -0
- synapse_sdk/utils/converters/coco/to_dm.py +215 -0
- synapse_sdk/utils/converters/dm/__init__.py +56 -0
- synapse_sdk/utils/converters/dm/from_v1.py +627 -0
- synapse_sdk/utils/converters/dm/to_v1.py +367 -0
- synapse_sdk/utils/converters/pascal/__init__.py +0 -0
- synapse_sdk/utils/converters/pascal/from_dm.py +244 -0
- synapse_sdk/utils/converters/pascal/to_dm.py +214 -0
- synapse_sdk/utils/converters/yolo/__init__.py +0 -0
- synapse_sdk/utils/converters/yolo/from_dm.py +384 -0
- synapse_sdk/utils/converters/yolo/to_dm.py +267 -0
- synapse_sdk/utils/dataset.py +46 -0
- synapse_sdk/utils/encryption.py +158 -0
- synapse_sdk/utils/file/__init__.py +39 -0
- synapse_sdk/utils/file/archive.py +32 -0
- synapse_sdk/utils/file/checksum.py +56 -0
- synapse_sdk/utils/file/chunking.py +31 -0
- synapse_sdk/utils/file/download.py +385 -0
- synapse_sdk/utils/file/encoding.py +40 -0
- synapse_sdk/utils/file/io.py +22 -0
- synapse_sdk/utils/file/video/__init__.py +29 -0
- synapse_sdk/utils/file/video/transcode.py +307 -0
- synapse_sdk/utils/{file.py → file.py.backup} +84 -2
- synapse_sdk/utils/http.py +138 -0
- synapse_sdk/utils/network.py +293 -0
- synapse_sdk/utils/storage/__init__.py +36 -2
- synapse_sdk/utils/storage/providers/__init__.py +141 -0
- synapse_sdk/utils/storage/providers/file_system.py +134 -0
- synapse_sdk/utils/storage/providers/http.py +190 -0
- synapse_sdk/utils/storage/providers/s3.py +54 -6
- synapse_sdk/utils/storage/providers/sftp.py +31 -0
- synapse_sdk/utils/storage/registry.py +6 -0
- synapse_sdk-2025.11.7.dist-info/METADATA +122 -0
- synapse_sdk-2025.11.7.dist-info/RECORD +386 -0
- {synapse_sdk-1.0.0a35.dist-info → synapse_sdk-2025.11.7.dist-info}/WHEEL +1 -1
- synapse_sdk/clients/backend/dataset.py +0 -102
- synapse_sdk/plugins/categories/upload/actions/upload.py +0 -293
- synapse_sdk-1.0.0a35.dist-info/METADATA +0 -47
- synapse_sdk-1.0.0a35.dist-info/RECORD +0 -137
- {synapse_sdk-1.0.0a35.dist-info → synapse_sdk-2025.11.7.dist-info}/entry_points.txt +0 -0
- {synapse_sdk-1.0.0a35.dist-info → synapse_sdk-2025.11.7.dist-info}/licenses/LICENSE +0 -0
- {synapse_sdk-1.0.0a35.dist-info → synapse_sdk-2025.11.7.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from typing import IO, Any, Dict, List, Tuple
|
|
3
|
+
|
|
4
|
+
import yaml
|
|
5
|
+
from PIL import Image
|
|
6
|
+
|
|
7
|
+
from synapse_sdk.utils.converters import ToDMConverter
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class YOLOToDMConverter(ToDMConverter):
|
|
11
|
+
"""Convert YOLO formatted datasets to DM format."""
|
|
12
|
+
|
|
13
|
+
IMG_EXTENSIONS = ['.jpg', '.jpeg', '.png', '.bmp']
|
|
14
|
+
CLASS_NAMES = []
|
|
15
|
+
|
|
16
|
+
def __init__(
|
|
17
|
+
self,
|
|
18
|
+
root_dir: str = None,
|
|
19
|
+
is_categorized_dataset: bool = False,
|
|
20
|
+
is_single_conversion: bool = False,
|
|
21
|
+
class_names: List[str] = None,
|
|
22
|
+
):
|
|
23
|
+
super().__init__(root_dir, is_categorized_dataset, is_single_conversion)
|
|
24
|
+
|
|
25
|
+
# Set class names
|
|
26
|
+
self.CLASS_NAMES = class_names
|
|
27
|
+
|
|
28
|
+
# Get class names from dataset.yaml if not provided
|
|
29
|
+
if not class_names:
|
|
30
|
+
dataset_yaml_path = os.path.join(self.root_dir, 'dataset.yaml')
|
|
31
|
+
if not os.path.exists(dataset_yaml_path):
|
|
32
|
+
raise FileNotFoundError(f'No dataset.yaml file found in {self.root_dir}.')
|
|
33
|
+
with open(dataset_yaml_path, 'r', encoding='utf-8') as f:
|
|
34
|
+
dataset_yaml = yaml.safe_load(f)
|
|
35
|
+
self.CLASS_NAMES = dataset_yaml.get('names', [])
|
|
36
|
+
|
|
37
|
+
def convert(self):
|
|
38
|
+
"""Convert YOLO dataset to DM format."""
|
|
39
|
+
if self.is_categorized_dataset:
|
|
40
|
+
splits = self._validate_splits(['train', 'valid'], ['test'])
|
|
41
|
+
all_split_data = {}
|
|
42
|
+
for split, split_dir in splits.items():
|
|
43
|
+
split_data = self._convert_yolo_split_to_dm(split_dir)
|
|
44
|
+
all_split_data[split] = split_data
|
|
45
|
+
self.converted_data = all_split_data
|
|
46
|
+
return all_split_data
|
|
47
|
+
else:
|
|
48
|
+
split_data = self._convert_yolo_split_to_dm(self.root_dir)
|
|
49
|
+
self.converted_data = split_data
|
|
50
|
+
return split_data
|
|
51
|
+
|
|
52
|
+
def _find_image_path(self, images_dir, base):
|
|
53
|
+
"""Find the image file corresponding to the base name in the images directory."""
|
|
54
|
+
for ext in self.IMG_EXTENSIONS:
|
|
55
|
+
img_path = os.path.join(images_dir, base + ext)
|
|
56
|
+
if os.path.exists(img_path):
|
|
57
|
+
return img_path
|
|
58
|
+
return None
|
|
59
|
+
|
|
60
|
+
@staticmethod
|
|
61
|
+
def _get_image_size(image_path: str) -> Tuple[int, int]:
|
|
62
|
+
"""Get the size of the image at the given path."""
|
|
63
|
+
with Image.open(image_path) as img:
|
|
64
|
+
return img.size
|
|
65
|
+
|
|
66
|
+
def _parse_yolo_line(self, line: str, class_names: List[str], img_size: Tuple[int, int]):
|
|
67
|
+
"""Parse a single line from a YOLO label file."""
|
|
68
|
+
parts = line.strip().split()
|
|
69
|
+
if len(parts) < 5:
|
|
70
|
+
return None # skip malformed
|
|
71
|
+
|
|
72
|
+
class_idx = int(parts[0])
|
|
73
|
+
class_name = class_names[class_idx] if class_idx < len(class_names) else f'class_{class_idx}'
|
|
74
|
+
img_w, img_h = img_size
|
|
75
|
+
|
|
76
|
+
# Check if it's a polygon (more than 5 values and even number of coordinates after class_id)
|
|
77
|
+
if len(parts) > 5 and (len(parts) - 1) % 2 == 0:
|
|
78
|
+
# Polygon format: class_id x1 y1 x2 y2 x3 y3 ... (normalized coordinates)
|
|
79
|
+
coords = []
|
|
80
|
+
for i in range(1, len(parts), 2):
|
|
81
|
+
x_norm = float(parts[i])
|
|
82
|
+
y_norm = float(parts[i + 1])
|
|
83
|
+
# Convert normalized coordinates to absolute coordinates
|
|
84
|
+
x_abs = int(x_norm * img_w)
|
|
85
|
+
y_abs = int(y_norm * img_h)
|
|
86
|
+
coords.append([x_abs, y_abs])
|
|
87
|
+
|
|
88
|
+
return {'type': 'polygon', 'classification': class_name, 'data': coords}
|
|
89
|
+
|
|
90
|
+
# Standard bounding box format
|
|
91
|
+
elif len(parts) == 5:
|
|
92
|
+
x_center, y_center, width, height = map(float, parts[1:5])
|
|
93
|
+
|
|
94
|
+
# Denormalize YOLO (x_center, y_center, w, h) to (left, top, w, h)
|
|
95
|
+
left = int((x_center - width / 2) * img_w)
|
|
96
|
+
top = int((y_center - height / 2) * img_h)
|
|
97
|
+
abs_w = int(width * img_w)
|
|
98
|
+
abs_h = int(height * img_h)
|
|
99
|
+
|
|
100
|
+
return {'type': 'bounding_box', 'classification': class_name, 'data': [left, top, abs_w, abs_h]}
|
|
101
|
+
|
|
102
|
+
# Keypoint format: class_id x_center y_center w h x1 y1 v1 x2 y2 v2 ...
|
|
103
|
+
elif len(parts) > 5 and (len(parts) - 5) % 3 == 0:
|
|
104
|
+
x_center, y_center, width, height = map(float, parts[1:5])
|
|
105
|
+
|
|
106
|
+
# Denormalize bounding box
|
|
107
|
+
left = int((x_center - width / 2) * img_w)
|
|
108
|
+
top = int((y_center - height / 2) * img_h)
|
|
109
|
+
abs_w = int(width * img_w)
|
|
110
|
+
abs_h = int(height * img_h)
|
|
111
|
+
|
|
112
|
+
keypoints = []
|
|
113
|
+
for i in range(5, len(parts), 3):
|
|
114
|
+
xk = int(float(parts[i]) * img_w)
|
|
115
|
+
yk = int(float(parts[i + 1]) * img_h)
|
|
116
|
+
vk = int(parts[i + 2])
|
|
117
|
+
keypoints.append([xk, yk, vk])
|
|
118
|
+
|
|
119
|
+
return {
|
|
120
|
+
'type': 'keypoint',
|
|
121
|
+
'classification': class_name,
|
|
122
|
+
'data': keypoints,
|
|
123
|
+
'bounding_box': [left, top, abs_w, abs_h],
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return None
|
|
127
|
+
|
|
128
|
+
def _convert_yolo_split_to_dm(self, split_dir: str) -> Dict[str, Any]:
|
|
129
|
+
"""Convert a single YOLO split directory to DM format."""
|
|
130
|
+
# Find image and label directories
|
|
131
|
+
images_dir = None
|
|
132
|
+
for candidate in ['images', 'img', 'imgs']:
|
|
133
|
+
candidate_path = os.path.join(split_dir, candidate)
|
|
134
|
+
if os.path.isdir(candidate_path):
|
|
135
|
+
images_dir = candidate_path
|
|
136
|
+
break
|
|
137
|
+
if images_dir is None:
|
|
138
|
+
raise FileNotFoundError(f"No images directory found in {split_dir} (tried 'images', 'img', 'imgs').")
|
|
139
|
+
|
|
140
|
+
labels_dir = os.path.join(split_dir, 'labels')
|
|
141
|
+
if not os.path.isdir(labels_dir):
|
|
142
|
+
raise FileNotFoundError(f"No labels directory found in {split_dir} (expected 'labels').")
|
|
143
|
+
|
|
144
|
+
# Build DM data
|
|
145
|
+
result = {}
|
|
146
|
+
for label_filename in os.listdir(labels_dir):
|
|
147
|
+
if not label_filename.endswith('.txt'):
|
|
148
|
+
continue
|
|
149
|
+
base = os.path.splitext(label_filename)[0]
|
|
150
|
+
img_path = self._find_image_path(images_dir, base)
|
|
151
|
+
if img_path is None:
|
|
152
|
+
print(f'[WARNING] Image not found for label {label_filename}, skipping.')
|
|
153
|
+
continue
|
|
154
|
+
img_size = self._get_image_size(img_path)
|
|
155
|
+
label_path = os.path.join(labels_dir, label_filename)
|
|
156
|
+
with open(label_path, 'r', encoding='utf-8') as f:
|
|
157
|
+
label_lines = [line.strip() for line in f if line.strip()]
|
|
158
|
+
|
|
159
|
+
# Prepare DM annotation structure
|
|
160
|
+
dm_img = {
|
|
161
|
+
'bounding_box': [],
|
|
162
|
+
'polygon': [],
|
|
163
|
+
'keypoint': [],
|
|
164
|
+
'relation': [],
|
|
165
|
+
'group': [],
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
for line in label_lines:
|
|
169
|
+
ann = self._parse_yolo_line(line, self.CLASS_NAMES, img_size)
|
|
170
|
+
if ann is None:
|
|
171
|
+
continue
|
|
172
|
+
|
|
173
|
+
if ann['type'] == 'bounding_box':
|
|
174
|
+
dm_img['bounding_box'].append({
|
|
175
|
+
'id': self._generate_unique_id(),
|
|
176
|
+
'classification': ann['classification'],
|
|
177
|
+
'attrs': [],
|
|
178
|
+
'data': ann['data'],
|
|
179
|
+
})
|
|
180
|
+
elif ann['type'] == 'polygon':
|
|
181
|
+
dm_img['polygon'].append({
|
|
182
|
+
'id': self._generate_unique_id(),
|
|
183
|
+
'classification': ann['classification'],
|
|
184
|
+
'attrs': [],
|
|
185
|
+
'data': ann['data'],
|
|
186
|
+
})
|
|
187
|
+
elif ann['type'] == 'keypoint':
|
|
188
|
+
dm_img['keypoint'].append({
|
|
189
|
+
'id': self._generate_unique_id(),
|
|
190
|
+
'classification': ann['classification'],
|
|
191
|
+
'attrs': [],
|
|
192
|
+
'data': ann['data'],
|
|
193
|
+
'bounding_box': ann['bounding_box'],
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
dm_json = {'images': [dm_img]}
|
|
197
|
+
result[os.path.basename(img_path)] = (dm_json, img_path)
|
|
198
|
+
return result
|
|
199
|
+
|
|
200
|
+
def convert_single_file(self, data: List[str], original_file: IO) -> Dict[str, Any]:
|
|
201
|
+
"""Convert a single YOLO label data and corresponding image to DM format.
|
|
202
|
+
|
|
203
|
+
Args:
|
|
204
|
+
data: List of YOLO label lines (strings from .txt file content)
|
|
205
|
+
original_file: File object for the corresponding original image
|
|
206
|
+
|
|
207
|
+
Returns:
|
|
208
|
+
Dictionary containing DM format data for the single file
|
|
209
|
+
"""
|
|
210
|
+
if not self.is_single_conversion:
|
|
211
|
+
raise RuntimeError('convert_single_file is only available when is_single_conversion=True')
|
|
212
|
+
|
|
213
|
+
img_path = getattr(original_file, 'name', None)
|
|
214
|
+
if not img_path:
|
|
215
|
+
raise ValueError('original_file must have a "name" attribute representing its path or filename.')
|
|
216
|
+
|
|
217
|
+
if hasattr(self, '_get_image_size'):
|
|
218
|
+
img_size = self._get_image_size(img_path)
|
|
219
|
+
else:
|
|
220
|
+
raise AttributeError('Converter missing _get_image_size method for file objects.')
|
|
221
|
+
|
|
222
|
+
# data is already a list of label lines
|
|
223
|
+
label_lines = [line.strip() for line in data if line.strip()]
|
|
224
|
+
|
|
225
|
+
# Prepare DM annotation structure
|
|
226
|
+
dm_img = {
|
|
227
|
+
'bounding_box': [],
|
|
228
|
+
'polygon': [],
|
|
229
|
+
'keypoint': [],
|
|
230
|
+
'relation': [],
|
|
231
|
+
'group': [],
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
for line in label_lines:
|
|
235
|
+
ann = self._parse_yolo_line(line, self.CLASS_NAMES, img_size)
|
|
236
|
+
if ann is None:
|
|
237
|
+
continue
|
|
238
|
+
|
|
239
|
+
if ann['type'] == 'bounding_box':
|
|
240
|
+
dm_img['bounding_box'].append({
|
|
241
|
+
'id': self._generate_unique_id(),
|
|
242
|
+
'classification': ann['classification'],
|
|
243
|
+
'attrs': [],
|
|
244
|
+
'data': ann['data'],
|
|
245
|
+
})
|
|
246
|
+
elif ann['type'] == 'polygon':
|
|
247
|
+
dm_img['polygon'].append({
|
|
248
|
+
'id': self._generate_unique_id(),
|
|
249
|
+
'classification': ann['classification'],
|
|
250
|
+
'attrs': [],
|
|
251
|
+
'data': ann['data'],
|
|
252
|
+
})
|
|
253
|
+
elif ann['type'] == 'keypoint':
|
|
254
|
+
dm_img['keypoint'].append({
|
|
255
|
+
'id': self._generate_unique_id(),
|
|
256
|
+
'classification': ann['classification'],
|
|
257
|
+
'attrs': [],
|
|
258
|
+
'data': ann['data'],
|
|
259
|
+
'bounding_box': ann['bounding_box'],
|
|
260
|
+
})
|
|
261
|
+
|
|
262
|
+
dm_json = {'images': [dm_img]}
|
|
263
|
+
return {
|
|
264
|
+
'dm_json': dm_json,
|
|
265
|
+
'image_path': img_path,
|
|
266
|
+
'image_name': os.path.basename(img_path),
|
|
267
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def remap_class_labels(dataset, data_type, class_pattern, new_class):
|
|
5
|
+
"""
|
|
6
|
+
Remaps class labels in a dataset by replacing specific classification patterns.
|
|
7
|
+
|
|
8
|
+
This function finds items of a specified data type (e.g., 'pcd', 'image', 'video')
|
|
9
|
+
where the classification matches a given pattern (e.g., 'truck', 'car'), and
|
|
10
|
+
remaps them to a new class label (e.g., 'vehicle'). Useful for consolidating
|
|
11
|
+
or standardizing class labels in ground truth data.
|
|
12
|
+
|
|
13
|
+
Args:
|
|
14
|
+
dataset (dict): The dataset containing ground truth labels
|
|
15
|
+
data_type (str): Type of data to process (e.g., 'pcd', 'image', 'video')
|
|
16
|
+
class_pattern (dict): Pattern to identify target classifications (e.g., {'name': 'truck'})
|
|
17
|
+
new_class (dict): New classification to apply (e.g., {'name': 'vehicle'})
|
|
18
|
+
|
|
19
|
+
Returns:
|
|
20
|
+
dict: Dataset with remapped class labels
|
|
21
|
+
"""
|
|
22
|
+
updated_dataset = dataset.copy()
|
|
23
|
+
|
|
24
|
+
if data_type not in updated_dataset:
|
|
25
|
+
logging.log(logging.WARNING, f"Data type '{data_type}' not found in dataset.")
|
|
26
|
+
return updated_dataset
|
|
27
|
+
|
|
28
|
+
def matches_pattern(classification, pattern):
|
|
29
|
+
"""Check if a classification matches the specified pattern."""
|
|
30
|
+
for key, pattern_value in pattern.items():
|
|
31
|
+
if key not in classification:
|
|
32
|
+
return False
|
|
33
|
+
|
|
34
|
+
if isinstance(pattern_value, dict) and isinstance(classification[key], dict):
|
|
35
|
+
if not matches_pattern(classification[key], pattern_value):
|
|
36
|
+
return False
|
|
37
|
+
elif classification[key] != pattern_value:
|
|
38
|
+
return False
|
|
39
|
+
|
|
40
|
+
return True
|
|
41
|
+
|
|
42
|
+
for item in updated_dataset[data_type]:
|
|
43
|
+
if 'classification' in item and matches_pattern(item['classification'], class_pattern):
|
|
44
|
+
item['classification'] = new_class.copy()
|
|
45
|
+
|
|
46
|
+
return updated_dataset
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
"""Encryption utilities for plugin code security."""
|
|
2
|
+
|
|
3
|
+
import base64
|
|
4
|
+
import os
|
|
5
|
+
import zipfile
|
|
6
|
+
from io import BytesIO
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Dict, List, Optional, Tuple
|
|
9
|
+
|
|
10
|
+
from cryptography.fernet import Fernet
|
|
11
|
+
from cryptography.hazmat.primitives import hashes
|
|
12
|
+
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def generate_key(password: str, salt: bytes) -> bytes:
|
|
16
|
+
"""Generate encryption key from password.
|
|
17
|
+
|
|
18
|
+
* Argument iterations should be at least 500,000.
|
|
19
|
+
"""
|
|
20
|
+
kdf = PBKDF2HMAC(
|
|
21
|
+
algorithm=hashes.SHA256(),
|
|
22
|
+
length=32,
|
|
23
|
+
salt=salt,
|
|
24
|
+
iterations=700000,
|
|
25
|
+
)
|
|
26
|
+
return base64.urlsafe_b64encode(kdf.derive(password.encode()))
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def encrypt_data(data: bytes, password: str) -> Dict:
|
|
30
|
+
"""Encrypt data with password."""
|
|
31
|
+
salt = os.urandom(16)
|
|
32
|
+
key = generate_key(password, salt)
|
|
33
|
+
fernet = Fernet(key)
|
|
34
|
+
encrypted_data = fernet.encrypt(data)
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
'encrypted_data': base64.b64encode(encrypted_data).decode(),
|
|
38
|
+
'salt': base64.b64encode(salt).decode(),
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def decrypt_data(encrypted_package: Dict, password: str) -> bytes:
|
|
43
|
+
"""Decrypt data with password."""
|
|
44
|
+
salt = base64.b64decode(encrypted_package['salt'])
|
|
45
|
+
encrypted_data = base64.b64decode(encrypted_package['encrypted_data'])
|
|
46
|
+
|
|
47
|
+
key = generate_key(password, salt)
|
|
48
|
+
fernet = Fernet(key)
|
|
49
|
+
return fernet.decrypt(encrypted_data)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def is_plugin_directory(path: Path) -> bool:
|
|
53
|
+
"""Check if directory contains a Synapse plugin."""
|
|
54
|
+
config_file = path / 'config.yaml'
|
|
55
|
+
plugin_dir = path / 'plugin'
|
|
56
|
+
|
|
57
|
+
return config_file.exists() and plugin_dir.exists() and plugin_dir.is_dir()
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def get_plugin_files(plugin_path: Path) -> List[Tuple[Path, str]]:
|
|
61
|
+
"""Get all plugin files with their relative paths."""
|
|
62
|
+
plugin_files = []
|
|
63
|
+
|
|
64
|
+
# Essential plugin files
|
|
65
|
+
essential_patterns = [
|
|
66
|
+
'config.yaml',
|
|
67
|
+
'requirements.txt',
|
|
68
|
+
'README.md',
|
|
69
|
+
'pyproject.toml',
|
|
70
|
+
]
|
|
71
|
+
|
|
72
|
+
for pattern in essential_patterns:
|
|
73
|
+
file_path = plugin_path / pattern
|
|
74
|
+
if file_path.exists():
|
|
75
|
+
plugin_files.append((file_path, pattern))
|
|
76
|
+
|
|
77
|
+
# Plugin source code
|
|
78
|
+
plugin_dir = plugin_path / 'plugin'
|
|
79
|
+
if plugin_dir.exists():
|
|
80
|
+
for file_path in plugin_dir.rglob('*'):
|
|
81
|
+
if file_path.is_file() and not file_path.name.startswith('.'):
|
|
82
|
+
relative_path = f'plugin/{file_path.relative_to(plugin_dir)}'
|
|
83
|
+
plugin_files.append((file_path, relative_path))
|
|
84
|
+
|
|
85
|
+
# Additional common directories
|
|
86
|
+
for additional_dir in ['tests', 'docs', 'data']:
|
|
87
|
+
dir_path = plugin_path / additional_dir
|
|
88
|
+
if dir_path.exists() and dir_path.is_dir():
|
|
89
|
+
for file_path in dir_path.rglob('*'):
|
|
90
|
+
if file_path.is_file() and not file_path.name.startswith('.'):
|
|
91
|
+
relative_path = f'{additional_dir}/{file_path.relative_to(dir_path)}'
|
|
92
|
+
plugin_files.append((file_path, relative_path))
|
|
93
|
+
|
|
94
|
+
return plugin_files
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def create_plugin_archive(plugin_path: Path) -> bytes:
|
|
98
|
+
"""Create a zip archive of the plugin."""
|
|
99
|
+
plugin_files = get_plugin_files(plugin_path)
|
|
100
|
+
|
|
101
|
+
archive_buffer = BytesIO()
|
|
102
|
+
with zipfile.ZipFile(archive_buffer, 'w', zipfile.ZIP_DEFLATED) as zip_file:
|
|
103
|
+
for file_path, archive_path in plugin_files:
|
|
104
|
+
zip_file.write(file_path, archive_path)
|
|
105
|
+
|
|
106
|
+
return archive_buffer.getvalue()
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def encrypt_plugin(plugin_path: Path, password: Optional[str] = None) -> Dict:
|
|
110
|
+
"""Encrypt a plugin directory."""
|
|
111
|
+
if not is_plugin_directory(plugin_path):
|
|
112
|
+
raise ValueError(f'Directory {plugin_path} is not a valid plugin directory')
|
|
113
|
+
|
|
114
|
+
# Generate password if not provided
|
|
115
|
+
if password is None:
|
|
116
|
+
password = base64.urlsafe_b64encode(os.urandom(32)).decode()
|
|
117
|
+
|
|
118
|
+
# Create plugin archive
|
|
119
|
+
archive_data = create_plugin_archive(plugin_path)
|
|
120
|
+
|
|
121
|
+
# Encrypt the archive
|
|
122
|
+
encrypted_package = encrypt_data(archive_data, password)
|
|
123
|
+
|
|
124
|
+
# Add metadata
|
|
125
|
+
encrypted_package.update({
|
|
126
|
+
'plugin_name': plugin_path.name,
|
|
127
|
+
'plugin_path': str(plugin_path),
|
|
128
|
+
'encryption_method': 'fernet',
|
|
129
|
+
'archive_format': 'zip',
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
return encrypted_package, password
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def get_plugin_info(plugin_path: Path) -> Optional[Dict]:
|
|
136
|
+
"""Get basic plugin information."""
|
|
137
|
+
if not is_plugin_directory(plugin_path):
|
|
138
|
+
return None
|
|
139
|
+
|
|
140
|
+
info = {'name': plugin_path.name, 'path': str(plugin_path), 'is_plugin': True}
|
|
141
|
+
|
|
142
|
+
# Try to read config.yaml for additional info
|
|
143
|
+
config_file = plugin_path / 'config.yaml'
|
|
144
|
+
if config_file.exists():
|
|
145
|
+
try:
|
|
146
|
+
import yaml
|
|
147
|
+
|
|
148
|
+
with open(config_file, 'r', encoding='utf-8') as f:
|
|
149
|
+
config = yaml.safe_load(f)
|
|
150
|
+
info.update({
|
|
151
|
+
'version': config.get('version', 'unknown'),
|
|
152
|
+
'description': config.get('description', ''),
|
|
153
|
+
'category': config.get('category', 'unknown'),
|
|
154
|
+
})
|
|
155
|
+
except Exception:
|
|
156
|
+
pass # Continue without config details if parsing fails
|
|
157
|
+
|
|
158
|
+
return info
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# File utilities module
|
|
2
|
+
# Maintains backward compatibility by re-exporting all functions
|
|
3
|
+
|
|
4
|
+
from .archive import archive, unarchive
|
|
5
|
+
from .checksum import calculate_checksum, get_checksum_from_file
|
|
6
|
+
from .chunking import read_file_in_chunks
|
|
7
|
+
from .download import (
|
|
8
|
+
adownload_file,
|
|
9
|
+
afiles_url_to_path,
|
|
10
|
+
afiles_url_to_path_from_objs,
|
|
11
|
+
download_file,
|
|
12
|
+
files_url_to_path,
|
|
13
|
+
files_url_to_path_from_objs,
|
|
14
|
+
)
|
|
15
|
+
from .encoding import convert_file_to_base64
|
|
16
|
+
from .io import get_dict_from_file, get_temp_path
|
|
17
|
+
|
|
18
|
+
__all__ = [
|
|
19
|
+
# Chunking
|
|
20
|
+
'read_file_in_chunks',
|
|
21
|
+
# Download
|
|
22
|
+
'download_file',
|
|
23
|
+
'adownload_file',
|
|
24
|
+
'files_url_to_path',
|
|
25
|
+
'afiles_url_to_path',
|
|
26
|
+
'files_url_to_path_from_objs',
|
|
27
|
+
'afiles_url_to_path_from_objs',
|
|
28
|
+
# Checksum
|
|
29
|
+
'calculate_checksum',
|
|
30
|
+
'get_checksum_from_file',
|
|
31
|
+
# Archive
|
|
32
|
+
'archive',
|
|
33
|
+
'unarchive',
|
|
34
|
+
# Encoding
|
|
35
|
+
'convert_file_to_base64',
|
|
36
|
+
# I/O
|
|
37
|
+
'get_dict_from_file',
|
|
38
|
+
'get_temp_path',
|
|
39
|
+
]
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import zipfile
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def archive(input_path, output_path, append=False):
|
|
6
|
+
input_path = Path(input_path)
|
|
7
|
+
output_path = Path(output_path)
|
|
8
|
+
|
|
9
|
+
mode = 'a' if append and output_path.exists() else 'w'
|
|
10
|
+
with zipfile.ZipFile(output_path, mode=mode, compression=zipfile.ZIP_DEFLATED) as zipf:
|
|
11
|
+
if input_path.is_file():
|
|
12
|
+
zipf.write(input_path, input_path.name)
|
|
13
|
+
else:
|
|
14
|
+
for file_path in input_path.rglob('*'):
|
|
15
|
+
if file_path.is_file(): # Only add files, skip directories
|
|
16
|
+
arcname = file_path.relative_to(input_path.parent)
|
|
17
|
+
zipf.write(file_path, arcname)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def unarchive(file_path, output_path):
|
|
21
|
+
"""
|
|
22
|
+
Unarchives a ZIP file to a given directory.
|
|
23
|
+
|
|
24
|
+
Parameters:
|
|
25
|
+
file_path (str | Path): The path to the ZIP file.
|
|
26
|
+
output_path (str): The directory where the files will be extracted.
|
|
27
|
+
"""
|
|
28
|
+
output_path = Path(output_path)
|
|
29
|
+
output_path.mkdir(parents=True, exist_ok=True)
|
|
30
|
+
|
|
31
|
+
with zipfile.ZipFile(str(file_path), 'r') as zip_ref:
|
|
32
|
+
zip_ref.extractall(output_path)
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import hashlib
|
|
2
|
+
from typing import IO, Any, Callable
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def calculate_checksum(file_path, prefix=''):
|
|
6
|
+
md5_hash = hashlib.md5()
|
|
7
|
+
with open(file_path, 'rb') as f:
|
|
8
|
+
for byte_block in iter(lambda: f.read(4096), b''):
|
|
9
|
+
md5_hash.update(byte_block)
|
|
10
|
+
checksum = md5_hash.hexdigest()
|
|
11
|
+
if prefix:
|
|
12
|
+
return f'dev-{checksum}'
|
|
13
|
+
return checksum
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def get_checksum_from_file(file: IO[Any], digest_mod: Callable[[], Any] = hashlib.sha1) -> str:
|
|
17
|
+
"""
|
|
18
|
+
Calculate checksum for a file-like object.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
file (IO[Any]): File-like object with read() method that supports reading in chunks
|
|
22
|
+
digest_mod (Callable[[], Any]): Hash algorithm from hashlib (defaults to hashlib.sha1)
|
|
23
|
+
|
|
24
|
+
Returns:
|
|
25
|
+
str: Hexadecimal digest of the file contents
|
|
26
|
+
|
|
27
|
+
Example:
|
|
28
|
+
```python
|
|
29
|
+
import hashlib
|
|
30
|
+
from io import BytesIO
|
|
31
|
+
from synapse_sdk.utils.file import get_checksum_from_file
|
|
32
|
+
|
|
33
|
+
# With BytesIO
|
|
34
|
+
data = BytesIO(b'Hello, world!')
|
|
35
|
+
checksum = get_checksum_from_file(data)
|
|
36
|
+
|
|
37
|
+
# With different hash algorithm
|
|
38
|
+
checksum = get_checksum_from_file(data, digest_mod=hashlib.sha256)
|
|
39
|
+
```
|
|
40
|
+
"""
|
|
41
|
+
digest = digest_mod()
|
|
42
|
+
chunk_size = 4096
|
|
43
|
+
|
|
44
|
+
# Reset file pointer to beginning if possible
|
|
45
|
+
if hasattr(file, 'seek'):
|
|
46
|
+
file.seek(0)
|
|
47
|
+
|
|
48
|
+
while True:
|
|
49
|
+
chunk = file.read(chunk_size)
|
|
50
|
+
if not chunk:
|
|
51
|
+
break
|
|
52
|
+
if isinstance(chunk, str):
|
|
53
|
+
chunk = chunk.encode('utf-8')
|
|
54
|
+
digest.update(chunk)
|
|
55
|
+
|
|
56
|
+
return digest.hexdigest()
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
def read_file_in_chunks(file_path, chunk_size=1024 * 1024 * 50):
|
|
2
|
+
"""
|
|
3
|
+
Read a file in chunks for efficient memory usage during file processing.
|
|
4
|
+
|
|
5
|
+
This function is particularly useful for large files or when you need to process
|
|
6
|
+
files in chunks, such as for uploading or hashing.
|
|
7
|
+
|
|
8
|
+
Args:
|
|
9
|
+
file_path (str | Path): Path to the file to read
|
|
10
|
+
chunk_size (int, optional): Size of each chunk in bytes. Defaults to 50MB (1024 * 1024 * 50)
|
|
11
|
+
|
|
12
|
+
Yields:
|
|
13
|
+
bytes: File content chunks
|
|
14
|
+
|
|
15
|
+
Raises:
|
|
16
|
+
FileNotFoundError: If the file doesn't exist
|
|
17
|
+
PermissionError: If the file can't be read due to permissions
|
|
18
|
+
OSError: If there's an OS-level error reading the file
|
|
19
|
+
|
|
20
|
+
Example:
|
|
21
|
+
```python
|
|
22
|
+
from synapse_sdk.utils.file import read_file_in_chunks
|
|
23
|
+
|
|
24
|
+
# Read a file in 10MB chunks
|
|
25
|
+
for chunk in read_file_in_chunks('large_file.bin', chunk_size=1024*1024*10):
|
|
26
|
+
process_chunk(chunk)
|
|
27
|
+
```
|
|
28
|
+
"""
|
|
29
|
+
with open(file_path, 'rb') as file:
|
|
30
|
+
while chunk := file.read(chunk_size):
|
|
31
|
+
yield chunk
|