synapse-sdk 1.0.0a11__py3-none-any.whl → 2026.1.1b2__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 +9 -8
- synapse_sdk/cli/agent/__init__.py +25 -0
- synapse_sdk/cli/agent/config.py +104 -0
- synapse_sdk/cli/agent/select.py +197 -0
- synapse_sdk/cli/auth.py +104 -0
- synapse_sdk/cli/main.py +1025 -0
- synapse_sdk/cli/plugin/__init__.py +58 -0
- synapse_sdk/cli/plugin/create.py +566 -0
- synapse_sdk/cli/plugin/job.py +196 -0
- synapse_sdk/cli/plugin/publish.py +322 -0
- synapse_sdk/cli/plugin/run.py +131 -0
- synapse_sdk/cli/plugin/test.py +200 -0
- synapse_sdk/clients/README.md +239 -0
- synapse_sdk/clients/__init__.py +5 -0
- synapse_sdk/clients/_template.py +266 -0
- synapse_sdk/clients/agent/__init__.py +84 -29
- synapse_sdk/clients/agent/async_ray.py +289 -0
- synapse_sdk/clients/agent/container.py +83 -0
- synapse_sdk/clients/agent/plugin.py +101 -0
- synapse_sdk/clients/agent/ray.py +296 -39
- synapse_sdk/clients/backend/__init__.py +152 -12
- synapse_sdk/clients/backend/annotation.py +164 -22
- synapse_sdk/clients/backend/core.py +101 -0
- synapse_sdk/clients/backend/data_collection.py +292 -0
- synapse_sdk/clients/backend/hitl.py +87 -0
- synapse_sdk/clients/backend/integration.py +374 -46
- synapse_sdk/clients/backend/ml.py +134 -22
- synapse_sdk/clients/backend/models.py +247 -0
- synapse_sdk/clients/base.py +538 -59
- synapse_sdk/clients/exceptions.py +35 -7
- synapse_sdk/clients/pipeline/__init__.py +5 -0
- synapse_sdk/clients/pipeline/client.py +636 -0
- synapse_sdk/clients/protocols.py +178 -0
- synapse_sdk/clients/utils.py +86 -8
- synapse_sdk/clients/validation.py +58 -0
- synapse_sdk/enums.py +76 -0
- synapse_sdk/exceptions.py +168 -0
- synapse_sdk/integrations/__init__.py +74 -0
- synapse_sdk/integrations/_base.py +119 -0
- synapse_sdk/integrations/_context.py +53 -0
- synapse_sdk/integrations/ultralytics/__init__.py +78 -0
- synapse_sdk/integrations/ultralytics/_callbacks.py +126 -0
- synapse_sdk/integrations/ultralytics/_patches.py +124 -0
- synapse_sdk/loggers.py +476 -95
- synapse_sdk/mcp/MCP.md +69 -0
- synapse_sdk/mcp/__init__.py +48 -0
- synapse_sdk/mcp/__main__.py +6 -0
- synapse_sdk/mcp/config.py +349 -0
- synapse_sdk/mcp/prompts/__init__.py +4 -0
- synapse_sdk/mcp/resources/__init__.py +4 -0
- synapse_sdk/mcp/server.py +1352 -0
- synapse_sdk/mcp/tools/__init__.py +6 -0
- synapse_sdk/plugins/__init__.py +133 -9
- synapse_sdk/plugins/action.py +229 -0
- synapse_sdk/plugins/actions/__init__.py +82 -0
- synapse_sdk/plugins/actions/dataset/__init__.py +37 -0
- synapse_sdk/plugins/actions/dataset/action.py +471 -0
- synapse_sdk/plugins/actions/export/__init__.py +55 -0
- synapse_sdk/plugins/actions/export/action.py +183 -0
- synapse_sdk/plugins/actions/export/context.py +59 -0
- synapse_sdk/plugins/actions/inference/__init__.py +84 -0
- synapse_sdk/plugins/actions/inference/action.py +285 -0
- synapse_sdk/plugins/actions/inference/context.py +81 -0
- synapse_sdk/plugins/actions/inference/deployment.py +322 -0
- synapse_sdk/plugins/actions/inference/serve.py +252 -0
- synapse_sdk/plugins/actions/train/__init__.py +54 -0
- synapse_sdk/plugins/actions/train/action.py +326 -0
- synapse_sdk/plugins/actions/train/context.py +57 -0
- synapse_sdk/plugins/actions/upload/__init__.py +49 -0
- synapse_sdk/plugins/actions/upload/action.py +165 -0
- synapse_sdk/plugins/actions/upload/context.py +61 -0
- synapse_sdk/plugins/config.py +98 -0
- synapse_sdk/plugins/context/__init__.py +109 -0
- synapse_sdk/plugins/context/env.py +113 -0
- synapse_sdk/plugins/datasets/__init__.py +113 -0
- synapse_sdk/plugins/datasets/converters/__init__.py +76 -0
- synapse_sdk/plugins/datasets/converters/base.py +347 -0
- synapse_sdk/plugins/datasets/converters/yolo/__init__.py +9 -0
- synapse_sdk/plugins/datasets/converters/yolo/from_dm.py +468 -0
- synapse_sdk/plugins/datasets/converters/yolo/to_dm.py +381 -0
- synapse_sdk/plugins/datasets/formats/__init__.py +82 -0
- synapse_sdk/plugins/datasets/formats/dm.py +351 -0
- synapse_sdk/plugins/datasets/formats/yolo.py +240 -0
- synapse_sdk/plugins/decorators.py +83 -0
- synapse_sdk/plugins/discovery.py +790 -0
- synapse_sdk/plugins/docs/ACTION_DEV_GUIDE.md +933 -0
- synapse_sdk/plugins/docs/ARCHITECTURE.md +1225 -0
- synapse_sdk/plugins/docs/LOGGING_SYSTEM.md +683 -0
- synapse_sdk/plugins/docs/OVERVIEW.md +531 -0
- synapse_sdk/plugins/docs/PIPELINE_GUIDE.md +145 -0
- synapse_sdk/plugins/docs/README.md +513 -0
- synapse_sdk/plugins/docs/STEP.md +656 -0
- synapse_sdk/plugins/enums.py +70 -10
- synapse_sdk/plugins/errors.py +92 -0
- synapse_sdk/plugins/executors/__init__.py +43 -0
- synapse_sdk/plugins/executors/local.py +99 -0
- synapse_sdk/plugins/executors/ray/__init__.py +18 -0
- synapse_sdk/plugins/executors/ray/base.py +282 -0
- synapse_sdk/plugins/executors/ray/job.py +298 -0
- synapse_sdk/plugins/executors/ray/jobs_api.py +511 -0
- synapse_sdk/plugins/executors/ray/packaging.py +137 -0
- synapse_sdk/plugins/executors/ray/pipeline.py +792 -0
- synapse_sdk/plugins/executors/ray/task.py +257 -0
- synapse_sdk/plugins/models/__init__.py +26 -0
- synapse_sdk/plugins/models/logger.py +173 -0
- synapse_sdk/plugins/models/pipeline.py +25 -0
- synapse_sdk/plugins/pipelines/__init__.py +81 -0
- synapse_sdk/plugins/pipelines/action_pipeline.py +417 -0
- synapse_sdk/plugins/pipelines/context.py +107 -0
- synapse_sdk/plugins/pipelines/display.py +311 -0
- synapse_sdk/plugins/runner.py +114 -0
- synapse_sdk/plugins/schemas/__init__.py +19 -0
- synapse_sdk/plugins/schemas/results.py +152 -0
- synapse_sdk/plugins/steps/__init__.py +63 -0
- synapse_sdk/plugins/steps/base.py +128 -0
- synapse_sdk/plugins/steps/context.py +90 -0
- synapse_sdk/plugins/steps/orchestrator.py +128 -0
- synapse_sdk/plugins/steps/registry.py +103 -0
- synapse_sdk/plugins/steps/utils/__init__.py +20 -0
- synapse_sdk/plugins/steps/utils/logging.py +85 -0
- synapse_sdk/plugins/steps/utils/timing.py +71 -0
- synapse_sdk/plugins/steps/utils/validation.py +68 -0
- synapse_sdk/plugins/templates/__init__.py +50 -0
- synapse_sdk/plugins/templates/base/.gitignore.j2 +26 -0
- synapse_sdk/plugins/templates/base/.synapseignore.j2 +11 -0
- synapse_sdk/plugins/templates/base/README.md.j2 +26 -0
- synapse_sdk/plugins/templates/base/plugin/__init__.py.j2 +1 -0
- synapse_sdk/plugins/templates/base/pyproject.toml.j2 +14 -0
- synapse_sdk/plugins/templates/base/requirements.txt.j2 +1 -0
- synapse_sdk/plugins/templates/custom/plugin/main.py.j2 +18 -0
- synapse_sdk/plugins/templates/data_validation/plugin/validate.py.j2 +32 -0
- synapse_sdk/plugins/templates/export/plugin/export.py.j2 +36 -0
- synapse_sdk/plugins/templates/neural_net/plugin/inference.py.j2 +36 -0
- synapse_sdk/plugins/templates/neural_net/plugin/train.py.j2 +33 -0
- synapse_sdk/plugins/templates/post_annotation/plugin/post_annotate.py.j2 +32 -0
- synapse_sdk/plugins/templates/pre_annotation/plugin/pre_annotate.py.j2 +32 -0
- synapse_sdk/plugins/templates/smart_tool/plugin/auto_label.py.j2 +44 -0
- synapse_sdk/plugins/templates/upload/plugin/upload.py.j2 +35 -0
- synapse_sdk/plugins/testing/__init__.py +25 -0
- synapse_sdk/plugins/testing/sample_actions.py +98 -0
- synapse_sdk/plugins/types.py +206 -0
- synapse_sdk/plugins/upload.py +595 -64
- synapse_sdk/plugins/utils.py +325 -37
- synapse_sdk/shared/__init__.py +25 -0
- synapse_sdk/utils/__init__.py +1 -0
- synapse_sdk/utils/auth.py +74 -0
- synapse_sdk/utils/file/__init__.py +58 -0
- synapse_sdk/utils/file/archive.py +449 -0
- synapse_sdk/utils/file/checksum.py +167 -0
- synapse_sdk/utils/file/download.py +286 -0
- synapse_sdk/utils/file/io.py +129 -0
- synapse_sdk/utils/file/requirements.py +36 -0
- synapse_sdk/utils/network.py +168 -0
- synapse_sdk/utils/storage/__init__.py +238 -0
- synapse_sdk/utils/storage/config.py +188 -0
- synapse_sdk/utils/storage/errors.py +52 -0
- synapse_sdk/utils/storage/providers/__init__.py +13 -0
- synapse_sdk/utils/storage/providers/base.py +76 -0
- synapse_sdk/utils/storage/providers/gcs.py +168 -0
- synapse_sdk/utils/storage/providers/http.py +250 -0
- synapse_sdk/utils/storage/providers/local.py +126 -0
- synapse_sdk/utils/storage/providers/s3.py +177 -0
- synapse_sdk/utils/storage/providers/sftp.py +208 -0
- synapse_sdk/utils/storage/registry.py +125 -0
- synapse_sdk/utils/websocket.py +99 -0
- synapse_sdk-2026.1.1b2.dist-info/METADATA +715 -0
- synapse_sdk-2026.1.1b2.dist-info/RECORD +172 -0
- {synapse_sdk-1.0.0a11.dist-info → synapse_sdk-2026.1.1b2.dist-info}/WHEEL +1 -1
- synapse_sdk-2026.1.1b2.dist-info/licenses/LICENSE +201 -0
- locale/en/LC_MESSAGES/messages.mo +0 -0
- locale/en/LC_MESSAGES/messages.po +0 -39
- locale/ko/LC_MESSAGES/messages.mo +0 -0
- locale/ko/LC_MESSAGES/messages.po +0 -34
- synapse_sdk/cli/create_plugin.py +0 -10
- synapse_sdk/clients/agent/core.py +0 -7
- synapse_sdk/clients/agent/service.py +0 -15
- synapse_sdk/clients/backend/dataset.py +0 -51
- synapse_sdk/clients/ray/__init__.py +0 -6
- synapse_sdk/clients/ray/core.py +0 -22
- synapse_sdk/clients/ray/serve.py +0 -20
- synapse_sdk/i18n.py +0 -35
- synapse_sdk/plugins/categories/__init__.py +0 -0
- synapse_sdk/plugins/categories/base.py +0 -235
- synapse_sdk/plugins/categories/data_validation/__init__.py +0 -0
- synapse_sdk/plugins/categories/data_validation/actions/__init__.py +0 -0
- synapse_sdk/plugins/categories/data_validation/actions/validation.py +0 -10
- synapse_sdk/plugins/categories/data_validation/templates/config.yaml +0 -3
- synapse_sdk/plugins/categories/data_validation/templates/plugin/__init__.py +0 -0
- synapse_sdk/plugins/categories/data_validation/templates/plugin/validation.py +0 -5
- synapse_sdk/plugins/categories/decorators.py +0 -13
- synapse_sdk/plugins/categories/export/__init__.py +0 -0
- synapse_sdk/plugins/categories/export/actions/__init__.py +0 -0
- synapse_sdk/plugins/categories/export/actions/export.py +0 -10
- synapse_sdk/plugins/categories/import/__init__.py +0 -0
- synapse_sdk/plugins/categories/import/actions/__init__.py +0 -0
- synapse_sdk/plugins/categories/import/actions/import.py +0 -10
- synapse_sdk/plugins/categories/neural_net/__init__.py +0 -0
- synapse_sdk/plugins/categories/neural_net/actions/__init__.py +0 -0
- synapse_sdk/plugins/categories/neural_net/actions/deployment.py +0 -45
- synapse_sdk/plugins/categories/neural_net/actions/inference.py +0 -18
- synapse_sdk/plugins/categories/neural_net/actions/test.py +0 -10
- synapse_sdk/plugins/categories/neural_net/actions/train.py +0 -143
- synapse_sdk/plugins/categories/neural_net/templates/config.yaml +0 -12
- synapse_sdk/plugins/categories/neural_net/templates/plugin/__init__.py +0 -0
- synapse_sdk/plugins/categories/neural_net/templates/plugin/inference.py +0 -4
- synapse_sdk/plugins/categories/neural_net/templates/plugin/test.py +0 -2
- synapse_sdk/plugins/categories/neural_net/templates/plugin/train.py +0 -14
- synapse_sdk/plugins/categories/post_annotation/__init__.py +0 -0
- synapse_sdk/plugins/categories/post_annotation/actions/__init__.py +0 -0
- synapse_sdk/plugins/categories/post_annotation/actions/post_annotation.py +0 -10
- synapse_sdk/plugins/categories/post_annotation/templates/config.yaml +0 -3
- synapse_sdk/plugins/categories/post_annotation/templates/plugin/__init__.py +0 -0
- synapse_sdk/plugins/categories/post_annotation/templates/plugin/post_annotation.py +0 -3
- synapse_sdk/plugins/categories/pre_annotation/__init__.py +0 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/__init__.py +0 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/pre_annotation.py +0 -10
- synapse_sdk/plugins/categories/pre_annotation/templates/config.yaml +0 -3
- synapse_sdk/plugins/categories/pre_annotation/templates/plugin/__init__.py +0 -0
- synapse_sdk/plugins/categories/pre_annotation/templates/plugin/pre_annotation.py +0 -3
- synapse_sdk/plugins/categories/registry.py +0 -16
- synapse_sdk/plugins/categories/smart_tool/__init__.py +0 -0
- synapse_sdk/plugins/categories/smart_tool/actions/__init__.py +0 -0
- synapse_sdk/plugins/categories/smart_tool/actions/auto_label.py +0 -37
- synapse_sdk/plugins/categories/smart_tool/templates/config.yaml +0 -7
- synapse_sdk/plugins/categories/smart_tool/templates/plugin/__init__.py +0 -0
- synapse_sdk/plugins/categories/smart_tool/templates/plugin/auto_label.py +0 -11
- synapse_sdk/plugins/categories/templates.py +0 -32
- synapse_sdk/plugins/cli/__init__.py +0 -21
- synapse_sdk/plugins/cli/publish.py +0 -37
- synapse_sdk/plugins/cli/run.py +0 -67
- synapse_sdk/plugins/exceptions.py +0 -22
- synapse_sdk/plugins/models.py +0 -121
- synapse_sdk/plugins/templates/cookiecutter.json +0 -11
- synapse_sdk/plugins/templates/hooks/post_gen_project.py +0 -3
- synapse_sdk/plugins/templates/hooks/pre_prompt.py +0 -21
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/.env +0 -24
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/.env.dist +0 -24
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/.gitignore +0 -27
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/.pre-commit-config.yaml +0 -7
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/README.md +0 -5
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/config.yaml +0 -6
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/main.py +0 -4
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/plugin/__init__.py +0 -0
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/pyproject.toml +0 -13
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/requirements.txt +0 -1
- synapse_sdk/shared/enums.py +0 -8
- synapse_sdk/utils/debug.py +0 -5
- synapse_sdk/utils/file.py +0 -87
- synapse_sdk/utils/module_loading.py +0 -29
- synapse_sdk/utils/pydantic/__init__.py +0 -0
- synapse_sdk/utils/pydantic/config.py +0 -4
- synapse_sdk/utils/pydantic/errors.py +0 -33
- synapse_sdk/utils/pydantic/validators.py +0 -7
- synapse_sdk/utils/storage.py +0 -91
- synapse_sdk/utils/string.py +0 -11
- synapse_sdk-1.0.0a11.dist-info/LICENSE +0 -21
- synapse_sdk-1.0.0a11.dist-info/METADATA +0 -43
- synapse_sdk-1.0.0a11.dist-info/RECORD +0 -111
- {synapse_sdk-1.0.0a11.dist-info → synapse_sdk-2026.1.1b2.dist-info}/entry_points.txt +0 -0
- {synapse_sdk-1.0.0a11.dist-info → synapse_sdk-2026.1.1b2.dist-info}/top_level.txt +0 -0
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
from synapse_sdk.plugins.categories.base import Action
|
|
2
|
-
from synapse_sdk.plugins.categories.decorators import register_action
|
|
3
|
-
from synapse_sdk.plugins.enums import PluginCategory, RunMethod
|
|
4
|
-
from synapse_sdk.plugins.exceptions import ActionError
|
|
5
|
-
from synapse_sdk.plugins.utils import get_action
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
@register_action
|
|
9
|
-
class AutoLabelAction(Action):
|
|
10
|
-
name = 'auto_label'
|
|
11
|
-
category = PluginCategory.SMART_TOOL
|
|
12
|
-
method = RunMethod.TASK
|
|
13
|
-
|
|
14
|
-
def get_auto_label(self):
|
|
15
|
-
return self.entrypoint(**self.params)
|
|
16
|
-
|
|
17
|
-
def run_model(self, input_data):
|
|
18
|
-
try:
|
|
19
|
-
action = get_action(
|
|
20
|
-
'inference',
|
|
21
|
-
input_data,
|
|
22
|
-
config={
|
|
23
|
-
'category': 'neural_net',
|
|
24
|
-
'code': self.params['plugin'],
|
|
25
|
-
'version': self.params['version'],
|
|
26
|
-
'actions': {'inference': {'method': input_data['method']}},
|
|
27
|
-
},
|
|
28
|
-
)
|
|
29
|
-
return action.run_action()
|
|
30
|
-
except ActionError as e:
|
|
31
|
-
raise Exception(e.errors)
|
|
32
|
-
|
|
33
|
-
def start(self):
|
|
34
|
-
auto_label = self.get_auto_label()
|
|
35
|
-
input_data = auto_label.handle_input(self.params)
|
|
36
|
-
output_data = self.run_model(input_data)
|
|
37
|
-
return auto_label.handle_output(output_data)
|
|
File without changes
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import shutil
|
|
2
|
-
from pathlib import Path
|
|
3
|
-
|
|
4
|
-
import yaml
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
def copy_project_category_template(category):
|
|
8
|
-
copy_plugin(category)
|
|
9
|
-
merge_config(category)
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
def copy_plugin(category):
|
|
13
|
-
template_path = Path(__file__).parent / category / 'templates' / 'plugin'
|
|
14
|
-
if not template_path.exists():
|
|
15
|
-
return
|
|
16
|
-
|
|
17
|
-
output_path = Path('plugin')
|
|
18
|
-
shutil.copytree(template_path, output_path, dirs_exist_ok=True)
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
def merge_config(category):
|
|
22
|
-
config_path = Path(__file__).parent / category / 'templates' / 'config.yaml'
|
|
23
|
-
if not config_path.exists():
|
|
24
|
-
return
|
|
25
|
-
|
|
26
|
-
config_base_path = Path('config.yaml')
|
|
27
|
-
|
|
28
|
-
config_base = yaml.safe_load(config_base_path.read_text(encoding='utf-8'))
|
|
29
|
-
config = yaml.safe_load(config_path.read_text(encoding='utf-8'))
|
|
30
|
-
|
|
31
|
-
config_base.update(config)
|
|
32
|
-
config_base_path.write_text(yaml.dump(config_base, sort_keys=False), encoding='utf-8')
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import click
|
|
2
|
-
|
|
3
|
-
from synapse_sdk.i18n import gettext as _
|
|
4
|
-
|
|
5
|
-
from .publish import publish
|
|
6
|
-
from .run import run
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
@click.group()
|
|
10
|
-
@click.option('--debug/--no-debug', default=False)
|
|
11
|
-
@click.pass_context
|
|
12
|
-
def cli(ctx, debug):
|
|
13
|
-
ctx.ensure_object(dict)
|
|
14
|
-
ctx.obj['DEBUG'] = debug
|
|
15
|
-
|
|
16
|
-
if debug:
|
|
17
|
-
click.echo(_('Debug mode is "on"'))
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
cli.add_command(run)
|
|
21
|
-
cli.add_command(publish)
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import json
|
|
2
|
-
from pathlib import Path
|
|
3
|
-
|
|
4
|
-
import click
|
|
5
|
-
|
|
6
|
-
from synapse_sdk.clients.backend import BackendClient
|
|
7
|
-
from synapse_sdk.i18n import gettext as _
|
|
8
|
-
from synapse_sdk.plugins.models import PluginRelease
|
|
9
|
-
from synapse_sdk.plugins.upload import archive
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
@click.command()
|
|
13
|
-
@click.option('--host', required=True)
|
|
14
|
-
@click.option('--user_token', required=True)
|
|
15
|
-
@click.option('--tenant', required=True)
|
|
16
|
-
@click.option('--debug_modules', default='', envvar='SYNAPSE_DEBUG_MODULES')
|
|
17
|
-
@click.pass_context
|
|
18
|
-
def publish(ctx, host, user_token, tenant, debug_modules):
|
|
19
|
-
debug = ctx.obj['DEBUG']
|
|
20
|
-
|
|
21
|
-
plugin_release = PluginRelease()
|
|
22
|
-
|
|
23
|
-
source_path = Path('./')
|
|
24
|
-
archive_path = source_path / 'dist' / 'archive.zip'
|
|
25
|
-
archive(source_path, archive_path)
|
|
26
|
-
|
|
27
|
-
data = {'plugin': plugin_release.plugin, 'file': str(archive_path), 'debug': debug}
|
|
28
|
-
if debug:
|
|
29
|
-
data['debug_meta'] = json.dumps({'modules': debug_modules.split(',')})
|
|
30
|
-
|
|
31
|
-
client = BackendClient(host, user_token, tenant=tenant)
|
|
32
|
-
client.create_plugin_release(data)
|
|
33
|
-
click.secho(
|
|
34
|
-
_('Successfully published "{}" ({}) to synapse backend!').format(plugin_release.name, plugin_release.code),
|
|
35
|
-
fg='green',
|
|
36
|
-
bold=True,
|
|
37
|
-
)
|
synapse_sdk/plugins/cli/run.py
DELETED
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
|
|
3
|
-
import click
|
|
4
|
-
|
|
5
|
-
from synapse_sdk.clients.agent import AgentClient
|
|
6
|
-
from synapse_sdk.clients.backend import BackendClient
|
|
7
|
-
from synapse_sdk.plugins.models import PluginRelease
|
|
8
|
-
from synapse_sdk.plugins.utils import get_action
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
@click.command()
|
|
12
|
-
@click.argument('action')
|
|
13
|
-
@click.argument('params')
|
|
14
|
-
@click.option('--job-id')
|
|
15
|
-
@click.option('--direct/--no-direct', default=False)
|
|
16
|
-
@click.option('--run-by', type=click.Choice(['script', 'agent', 'backend']), default='script')
|
|
17
|
-
@click.option('--agent-host')
|
|
18
|
-
@click.option('--agent-token')
|
|
19
|
-
@click.option('--host')
|
|
20
|
-
@click.option('--agent')
|
|
21
|
-
@click.option('--user-token')
|
|
22
|
-
@click.option('--tenant')
|
|
23
|
-
@click.pass_context
|
|
24
|
-
def run(ctx, action, params, job_id, direct, run_by, agent_host, agent_token, host, agent, user_token, tenant):
|
|
25
|
-
debug = ctx.obj['DEBUG']
|
|
26
|
-
|
|
27
|
-
if run_by == 'script':
|
|
28
|
-
run_by_script(action, params, job_id, direct, debug)
|
|
29
|
-
elif run_by == 'agent':
|
|
30
|
-
run_by_agent(action, params, job_id, agent_host, agent_token, user_token, tenant, debug)
|
|
31
|
-
elif run_by == 'backend':
|
|
32
|
-
run_by_backend(action, params, agent, host, user_token, tenant)
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
def run_by_script(action, params, job_id, direct, debug):
|
|
36
|
-
action = get_action(action, params, job_id=job_id, direct=direct, debug=debug)
|
|
37
|
-
result = action.run_action()
|
|
38
|
-
|
|
39
|
-
if debug:
|
|
40
|
-
click.echo(result)
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
def run_by_agent(action, params, job_id, agent_host, agent_token, user_token, tenant, debug):
|
|
44
|
-
client = AgentClient(agent_host, agent_token, user_token, tenant)
|
|
45
|
-
data = {'action': action, 'params': params}
|
|
46
|
-
if job_id:
|
|
47
|
-
data['job_id'] = job_id
|
|
48
|
-
if debug:
|
|
49
|
-
data.update({
|
|
50
|
-
'plugin_path': os.getcwd(),
|
|
51
|
-
'modules': os.getenv('SYNAPSE_DEBUG_MODULES', '').split(','),
|
|
52
|
-
})
|
|
53
|
-
result = client.run_debug_plugin_release(data=data)
|
|
54
|
-
else:
|
|
55
|
-
plugin_release = PluginRelease()
|
|
56
|
-
result = client.run_plugin_release(code=plugin_release.code, data=data)
|
|
57
|
-
|
|
58
|
-
click.echo(result)
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
def run_by_backend(action, params, agent, host, user_token, tenant):
|
|
62
|
-
client = BackendClient(host, user_token, tenant=tenant)
|
|
63
|
-
plugin_release = PluginRelease()
|
|
64
|
-
data = {'agent': agent, 'version': plugin_release.version, 'action': action, 'params': params}
|
|
65
|
-
result = client.run_plugin(plugin_release.plugin, data=data)
|
|
66
|
-
|
|
67
|
-
click.echo(result)
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
class ActionError(Exception):
|
|
2
|
-
errors = None
|
|
3
|
-
|
|
4
|
-
def __init__(self, errors, *args):
|
|
5
|
-
if isinstance(errors, (str, dict)):
|
|
6
|
-
self.errors = errors
|
|
7
|
-
elif isinstance(errors, Exception) and len(errors.args) == 1:
|
|
8
|
-
self.errors = ActionError(errors.args[0]).errors
|
|
9
|
-
else:
|
|
10
|
-
self.errors = str(errors)
|
|
11
|
-
super().__init__(errors, *args)
|
|
12
|
-
|
|
13
|
-
def as_drf_error(self, data=None):
|
|
14
|
-
if data is None:
|
|
15
|
-
data = self.errors
|
|
16
|
-
|
|
17
|
-
if isinstance(data, list):
|
|
18
|
-
return data
|
|
19
|
-
elif isinstance(data, dict):
|
|
20
|
-
return {key: self.as_drf_error(value) for key, value in data.items()}
|
|
21
|
-
|
|
22
|
-
return [str(data)]
|
synapse_sdk/plugins/models.py
DELETED
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
from functools import cached_property
|
|
3
|
-
from typing import Any, Dict
|
|
4
|
-
|
|
5
|
-
from synapse_sdk.clients.backend import BackendClient
|
|
6
|
-
from synapse_sdk.loggers import BackendLogger, ConsoleLogger
|
|
7
|
-
from synapse_sdk.plugins.utils import read_plugin_config
|
|
8
|
-
from synapse_sdk.shared.enums import Context
|
|
9
|
-
from synapse_sdk.utils.storage import get_storage
|
|
10
|
-
from synapse_sdk.utils.string import hash_text
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
class PluginRelease:
|
|
14
|
-
config: Dict[str, Any]
|
|
15
|
-
envs = None
|
|
16
|
-
|
|
17
|
-
def __init__(self, config=None, plugin_path=None, envs=None):
|
|
18
|
-
if config:
|
|
19
|
-
self.config = config
|
|
20
|
-
else:
|
|
21
|
-
self.config = read_plugin_config(plugin_path=plugin_path)
|
|
22
|
-
self.envs = envs
|
|
23
|
-
|
|
24
|
-
@cached_property
|
|
25
|
-
def plugin(self):
|
|
26
|
-
return self.config['code']
|
|
27
|
-
|
|
28
|
-
@cached_property
|
|
29
|
-
def version(self):
|
|
30
|
-
return self.config['version']
|
|
31
|
-
|
|
32
|
-
@cached_property
|
|
33
|
-
def code(self):
|
|
34
|
-
return f'{self.plugin}@{self.version}'
|
|
35
|
-
|
|
36
|
-
@cached_property
|
|
37
|
-
def category(self):
|
|
38
|
-
return self.config['category']
|
|
39
|
-
|
|
40
|
-
@cached_property
|
|
41
|
-
def name(self):
|
|
42
|
-
return self.config['name']
|
|
43
|
-
|
|
44
|
-
@cached_property
|
|
45
|
-
def checksum(self):
|
|
46
|
-
return hash_text(self.code)
|
|
47
|
-
|
|
48
|
-
@cached_property
|
|
49
|
-
def actions(self):
|
|
50
|
-
return list(self.config['actions'].keys())
|
|
51
|
-
|
|
52
|
-
def setup_runtime_env(self):
|
|
53
|
-
import ray
|
|
54
|
-
from ray.util.scheduling_strategies import NodeAffinitySchedulingStrategy
|
|
55
|
-
from ray.util.state import list_nodes
|
|
56
|
-
|
|
57
|
-
@ray.remote
|
|
58
|
-
def warm_up():
|
|
59
|
-
pass
|
|
60
|
-
|
|
61
|
-
nodes = list_nodes(address=self.envs['RAY_DASHBOARD_URL'])
|
|
62
|
-
node_ids = [n['node_id'] for n in nodes]
|
|
63
|
-
for node_id in node_ids:
|
|
64
|
-
strategy = NodeAffinitySchedulingStrategy(node_id=node_id, soft=False)
|
|
65
|
-
|
|
66
|
-
warm_up.options(
|
|
67
|
-
runtime_env={
|
|
68
|
-
'pip': ['-r ${RAY_RUNTIME_ENV_CREATE_WORKING_DIR}/requirements.txt'],
|
|
69
|
-
'working_dir': self.get_url(self.envs['SYNAPSE_PLUGIN_STORAGE']),
|
|
70
|
-
},
|
|
71
|
-
scheduling_strategy=strategy,
|
|
72
|
-
).remote()
|
|
73
|
-
|
|
74
|
-
def get_action_config(self, action):
|
|
75
|
-
return self.config['actions'][action]
|
|
76
|
-
|
|
77
|
-
def get_url(self, storage_url):
|
|
78
|
-
storage = get_storage(storage_url)
|
|
79
|
-
return storage.get_url(f'{self.checksum}.zip')
|
|
80
|
-
|
|
81
|
-
def get_serve_url(self, serve_address, path):
|
|
82
|
-
return os.path.join(serve_address, self.checksum, path)
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
class Run:
|
|
86
|
-
logger = None
|
|
87
|
-
job_id = None
|
|
88
|
-
context = None
|
|
89
|
-
|
|
90
|
-
def __init__(self, job_id, context):
|
|
91
|
-
self.job_id = job_id
|
|
92
|
-
self.context = context
|
|
93
|
-
self.set_logger()
|
|
94
|
-
|
|
95
|
-
def set_logger(self):
|
|
96
|
-
kwargs = {'progress_categories': self.context['progress_categories']}
|
|
97
|
-
if self.job_id:
|
|
98
|
-
client = BackendClient(
|
|
99
|
-
self.context['envs']['SYNAPSE_PLUGIN_RUN_HOST'],
|
|
100
|
-
self.context['envs']['SYNAPSE_PLUGIN_RUN_USER_TOKEN'],
|
|
101
|
-
self.context['envs']['SYNAPSE_PLUGIN_RUN_TENANT'],
|
|
102
|
-
)
|
|
103
|
-
self.logger = BackendLogger(client, self.job_id, **kwargs)
|
|
104
|
-
else:
|
|
105
|
-
self.logger = ConsoleLogger(**kwargs)
|
|
106
|
-
|
|
107
|
-
@property
|
|
108
|
-
def client(self):
|
|
109
|
-
return getattr(self.logger, 'client', None)
|
|
110
|
-
|
|
111
|
-
def set_progress(self, current, total, category=''):
|
|
112
|
-
self.logger.set_progress(current, total, category)
|
|
113
|
-
|
|
114
|
-
def log(self, event, data):
|
|
115
|
-
self.logger.log(event, data)
|
|
116
|
-
|
|
117
|
-
def log_message(self, message, context=Context.INFO.value):
|
|
118
|
-
self.logger.log('message', {'context': context, 'content': message})
|
|
119
|
-
|
|
120
|
-
def end_log(self):
|
|
121
|
-
self.log_message('Plugin run is complete.')
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"category": null,
|
|
3
|
-
"plugin_name": null,
|
|
4
|
-
"plugin_code": "{{ cookiecutter.plugin_name.lower().replace(' ', '-') }}",
|
|
5
|
-
"version": "0.1.0",
|
|
6
|
-
"description": "This is {{ cookiecutter.plugin_name }} plugin",
|
|
7
|
-
"__prompts__": {
|
|
8
|
-
"project_name": "Plugin name",
|
|
9
|
-
"project_slug": "Unique identifier of your plugin. Lowercase letters, numbers, and hyphens only (e.g., 'my-plugin-name')"
|
|
10
|
-
}
|
|
11
|
-
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import json
|
|
2
|
-
from pathlib import Path
|
|
3
|
-
|
|
4
|
-
from synapse_sdk.plugins.utils import get_plugin_categories
|
|
5
|
-
from synapse_sdk.utils.file import get_dict_from_file
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
def update_config(config):
|
|
9
|
-
config['category'] = get_plugin_categories()
|
|
10
|
-
return config
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
def main():
|
|
14
|
-
cookiecutter_path = Path('cookiecutter.json')
|
|
15
|
-
config = get_dict_from_file(cookiecutter_path)
|
|
16
|
-
config = update_config(config)
|
|
17
|
-
cookiecutter_path.write_text(json.dumps(config, indent=4, ensure_ascii=False), encoding='utf-8')
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
if __name__ == '__main__':
|
|
21
|
-
main()
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
# RAY 관련
|
|
2
|
-
RAY_ADDRESS=
|
|
3
|
-
RAY_DASHBOARD_ADDRESS=
|
|
4
|
-
RAY_SERVE_ADDRESS=
|
|
5
|
-
|
|
6
|
-
# 플러그인 관련
|
|
7
|
-
SYNAPSE_PLUGIN_STORAGE=
|
|
8
|
-
|
|
9
|
-
# 플러그인 디버그 관련
|
|
10
|
-
SYNAPSE_DEBUG_PLUGIN_PATH=
|
|
11
|
-
SYNAPSE_DEBUG_MODULES=
|
|
12
|
-
|
|
13
|
-
# 플러그인 PUBLISH 관련
|
|
14
|
-
SYNAPSE_PLUGIN_PUBLISH_HOST=https://synapse.datamaker.io
|
|
15
|
-
SYNAPSE_PLUGIN_PUBLISH_USER_TOKEN=
|
|
16
|
-
SYNAPSE_PLUGIN_PUBLISH_TENANT=
|
|
17
|
-
|
|
18
|
-
# 플러그인 RUN 관련
|
|
19
|
-
SYNAPSE_PLUGIN_RUN_HOST=
|
|
20
|
-
SYNAPSE_PLUGIN_RUN_AGENT=
|
|
21
|
-
SYNAPSE_PLUGIN_RUN_AGENT_HOST=
|
|
22
|
-
SYNAPSE_PLUGIN_RUN_AGENT_TOKEN=
|
|
23
|
-
SYNAPSE_PLUGIN_RUN_USER_TOKEN=
|
|
24
|
-
SYNAPSE_PLUGIN_RUN_TENANT=
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
# RAY 관련
|
|
2
|
-
RAY_ADDRESS=
|
|
3
|
-
RAY_DASHBOARD_ADDRESS=
|
|
4
|
-
RAY_SERVE_ADDRESS=
|
|
5
|
-
|
|
6
|
-
# 플러그인 관련
|
|
7
|
-
SYNAPSE_PLUGIN_STORAGE=
|
|
8
|
-
|
|
9
|
-
# 플러그인 디버그 관련
|
|
10
|
-
SYNAPSE_DEBUG_PLUGIN_PATH=
|
|
11
|
-
SYNAPSE_DEBUG_MODULES=
|
|
12
|
-
|
|
13
|
-
# 플러그인 PUBLISH 관련
|
|
14
|
-
SYNAPSE_PLUGIN_PUBLISH_HOST=https://synapse.datamaker.io
|
|
15
|
-
SYNAPSE_PLUGIN_PUBLISH_USER_TOKEN=
|
|
16
|
-
SYNAPSE_PLUGIN_PUBLISH_TENANT=
|
|
17
|
-
|
|
18
|
-
# 플러그인 RUN 관련
|
|
19
|
-
SYNAPSE_PLUGIN_RUN_HOST=
|
|
20
|
-
SYNAPSE_PLUGIN_RUN_AGENT=
|
|
21
|
-
SYNAPSE_PLUGIN_RUN_AGENT_HOST=
|
|
22
|
-
SYNAPSE_PLUGIN_RUN_AGENT_TOKEN=
|
|
23
|
-
SYNAPSE_PLUGIN_RUN_USER_TOKEN=
|
|
24
|
-
SYNAPSE_PLUGIN_RUN_TENANT=
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
### Example user template template
|
|
2
|
-
# virtualenv
|
|
3
|
-
.venv
|
|
4
|
-
|
|
5
|
-
# idea
|
|
6
|
-
.DS_Store
|
|
7
|
-
*.pyc
|
|
8
|
-
.idea
|
|
9
|
-
*.iml
|
|
10
|
-
|
|
11
|
-
# http
|
|
12
|
-
/http/
|
|
13
|
-
|
|
14
|
-
# general things to ignore
|
|
15
|
-
build/
|
|
16
|
-
dist/
|
|
17
|
-
*.egg-info/
|
|
18
|
-
*.egg
|
|
19
|
-
*.py[cod]
|
|
20
|
-
__pycache__/
|
|
21
|
-
*.so
|
|
22
|
-
*~
|
|
23
|
-
.env
|
|
24
|
-
|
|
25
|
-
# due to using tox and pytest
|
|
26
|
-
.tox
|
|
27
|
-
.cache
|
synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/plugin/__init__.py
DELETED
|
File without changes
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
[tool.ruff]
|
|
2
|
-
line-length = 120
|
|
3
|
-
|
|
4
|
-
[tool.ruff.lint]
|
|
5
|
-
select = ['E', 'F', 'I', 'Q']
|
|
6
|
-
ignore = ['W191', 'E111', 'E114', 'E117', 'D206', 'D300', 'Q000', 'Q001', 'Q002', 'Q003', 'COM812', 'COM819', 'ISC001', 'ISC002']
|
|
7
|
-
|
|
8
|
-
[tool.ruff.lint.pydocstyle]
|
|
9
|
-
convention = 'google'
|
|
10
|
-
|
|
11
|
-
[tool.ruff.format]
|
|
12
|
-
quote-style = 'single'
|
|
13
|
-
preview = true
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
synapse-sdk[all]
|
synapse_sdk/shared/enums.py
DELETED
synapse_sdk/utils/debug.py
DELETED
synapse_sdk/utils/file.py
DELETED
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
import hashlib
|
|
2
|
-
import json
|
|
3
|
-
import operator
|
|
4
|
-
import zipfile
|
|
5
|
-
from functools import reduce
|
|
6
|
-
from pathlib import Path
|
|
7
|
-
from urllib.parse import urlparse
|
|
8
|
-
|
|
9
|
-
import requests
|
|
10
|
-
import yaml
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
def download_file(url, path_download, name=None, coerce=None):
|
|
14
|
-
if name:
|
|
15
|
-
name += Path(url).suffix
|
|
16
|
-
else:
|
|
17
|
-
name = Path(url).name
|
|
18
|
-
|
|
19
|
-
name = urlparse(name).path
|
|
20
|
-
path = Path(path_download) / name
|
|
21
|
-
if not path.is_file():
|
|
22
|
-
r = requests.get(url, allow_redirects=True)
|
|
23
|
-
open(str(path), 'wb').write(r.content)
|
|
24
|
-
|
|
25
|
-
if coerce:
|
|
26
|
-
path = coerce(path)
|
|
27
|
-
|
|
28
|
-
return path
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
def files_url_to_path(files, coerce=None):
|
|
32
|
-
path_download = Path('/tmp/datamaker') / 'media'
|
|
33
|
-
path_download.mkdir(parents=True, exist_ok=True)
|
|
34
|
-
for file_name in files:
|
|
35
|
-
if isinstance(files[file_name], str):
|
|
36
|
-
files[file_name] = download_file(files[file_name], path_download, coerce=coerce)
|
|
37
|
-
else:
|
|
38
|
-
files[file_name]['path'] = download_file(files[file_name].pop('url'), path_download, coerce=coerce)
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
def files_url_to_path_from_objs(objs, files_fields, coerce=None, is_list=False):
|
|
42
|
-
if not is_list:
|
|
43
|
-
objs = [objs]
|
|
44
|
-
|
|
45
|
-
for obj in objs:
|
|
46
|
-
for files_field in files_fields:
|
|
47
|
-
try:
|
|
48
|
-
files = reduce(operator.getitem, files_field.split('.'), obj)
|
|
49
|
-
files_url_to_path(files, coerce=coerce)
|
|
50
|
-
except KeyError:
|
|
51
|
-
pass
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
def get_dict_from_file(file_path):
|
|
55
|
-
if isinstance(file_path, str):
|
|
56
|
-
file_path = Path(file_path)
|
|
57
|
-
|
|
58
|
-
with open(file_path) as f:
|
|
59
|
-
if file_path.suffix == '.yaml':
|
|
60
|
-
return yaml.safe_load(f)
|
|
61
|
-
else:
|
|
62
|
-
return json.load(f)
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
def calculate_checksum(file_path, prefix=''):
|
|
66
|
-
md5_hash = hashlib.md5()
|
|
67
|
-
with open(file_path, 'rb') as f:
|
|
68
|
-
for byte_block in iter(lambda: f.read(4096), b''):
|
|
69
|
-
md5_hash.update(byte_block)
|
|
70
|
-
checksum = md5_hash.hexdigest()
|
|
71
|
-
if prefix:
|
|
72
|
-
return f'dev-{checksum}'
|
|
73
|
-
return checksum
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
def archive(input_path, output_path):
|
|
77
|
-
input_path = Path(input_path)
|
|
78
|
-
output_path = Path(output_path)
|
|
79
|
-
|
|
80
|
-
with zipfile.ZipFile(output_path, mode='w', compression=zipfile.ZIP_DEFLATED) as zipf:
|
|
81
|
-
if input_path.is_file():
|
|
82
|
-
zipf.write(input_path, input_path.name)
|
|
83
|
-
else:
|
|
84
|
-
for file_path in input_path.rglob('*'):
|
|
85
|
-
if file_path.is_file(): # Only add files, skip directories
|
|
86
|
-
arcname = file_path.relative_to(input_path.parent)
|
|
87
|
-
zipf.write(file_path, arcname)
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import sys
|
|
2
|
-
from importlib import import_module
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
def cached_import(module_path, class_name):
|
|
6
|
-
# Check whether module is loaded and fully initialized.
|
|
7
|
-
if not (
|
|
8
|
-
(module := sys.modules.get(module_path))
|
|
9
|
-
and (spec := getattr(module, '__spec__', None))
|
|
10
|
-
and getattr(spec, '_initializing', False) is False
|
|
11
|
-
):
|
|
12
|
-
module = import_module(module_path)
|
|
13
|
-
return getattr(module, class_name)
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
def import_string(dotted_path):
|
|
17
|
-
"""
|
|
18
|
-
Import a dotted module path and return the attribute/class designated by the
|
|
19
|
-
last name in the path. Raise ImportError if the import failed.
|
|
20
|
-
"""
|
|
21
|
-
try:
|
|
22
|
-
module_path, class_name = dotted_path.rsplit('.', 1)
|
|
23
|
-
except ValueError as err:
|
|
24
|
-
raise ImportError("%s doesn't look like a module path" % dotted_path) from err
|
|
25
|
-
|
|
26
|
-
try:
|
|
27
|
-
return cached_import(module_path, class_name)
|
|
28
|
-
except AttributeError as err:
|
|
29
|
-
raise ImportError('Module "%s" does not define a "%s" attribute/class' % (module_path, class_name)) from err
|
|
File without changes
|