diff --git a/Makefile b/Makefile index 229e0e71..43dc8bfe 100644 --- a/Makefile +++ b/Makefile @@ -5,11 +5,11 @@ gha-setup: .PHONY: init init: gha-setup - docker compose -f workflows/docker-compose.yml up -d $(MAKE) -C entities local-init + $(MAKE) -C workflows local-init .PHONY: clean clean: - docker compose -f workflows/docker-compose.yml down $(MAKE) -C entities local-clean + $(MAKE) -C workflows local-clean docker compose down diff --git a/entities/docker-compose.yml b/entities/docker-compose.yml index 2989ddb9..076ea3f4 100644 --- a/entities/docker-compose.yml +++ b/entities/docker-compose.yml @@ -2,7 +2,6 @@ version: "3.8" services: entities-db: - platform: linux/arm64 image: postgres:15 restart: always environment: @@ -26,7 +25,6 @@ services: ["server", "--config", "/entities/config/config.yaml"] entities: image: "platformics-entities" - platform: linux/arm64 build: context: "." args: diff --git a/workflows/Makefile b/workflows/Makefile index 3471b82c..a15f1e11 100644 --- a/workflows/Makefile +++ b/workflows/Makefile @@ -31,7 +31,7 @@ rm-pycache: ## remove all __pycache__ files (run if encountering issues with pyc local-init: $(docker_compose) up -d while [ -z "$$($(docker_compose) exec -T postgres psql $(LOCAL_DB_CONN_STRING) -c 'select 1')" ]; do echo "waiting for db to start..."; sleep 1; done; - $(docker_compose) run $(FOLDER) make alembic-upgrade-head + $(docker_compose) run $(FOLDER) alembic upgrade head $(MAKE) local-seed .PHONY: debugger @@ -111,13 +111,13 @@ gha-setup: ### ALEMBIC ############################################# alembic-upgrade-head: - alembic upgrade head ## Run alembic migrations locally + $(docker_compose) exec workflows alembic upgrade head ## Run alembic migrations locally alembic-undo-migration: ## Downgrade the latest alembic migration - alembic downgrade -1 + $(docker_compose) exec workflows alembic downgrade -1 alembic-autogenerate: ## Create new alembic migrations files based on SA schema changes. - alembic revision --autogenerate -m "$(MESSAGE)" --rev-id $$(date +%Y%m%d_%H%M%S) + $(docker_compose) exec workflows alembic revision --autogenerate -m "$(MESSAGE)" --rev-id $$(date +%Y%m%d_%H%M%S) ### SWIPE Plugin Tests diff --git a/workflows/config.py b/workflows/config.py index 8b8d1dd8..61e823b2 100644 --- a/workflows/config.py +++ b/workflows/config.py @@ -1,6 +1,6 @@ from importlib.metadata import entry_points from typing import Dict -from plugin_types import WorkflowRunner +from plugin_types import EventBus, WorkflowRunner def load_workflow_runners() -> Dict[str, WorkflowRunner]: @@ -10,3 +10,11 @@ def load_workflow_runners() -> Dict[str, WorkflowRunner]: assert isinstance(workflow_runner, WorkflowRunner) workflow_runners_by_name[plugin.name] = workflow_runner return workflow_runners_by_name + +def load_event_buses() -> Dict[str, EventBus]: + event_buses_by_name: Dict[str, EventBus] = {} + for plugin in entry_points(group="czid.plugin.event_bus"): + event_bus = plugin.load()() + assert isinstance(event_bus, EventBus) + event_buses_by_name[plugin.name] = event_bus + return event_buses_by_name diff --git a/workflows/database/models/__init__.py b/workflows/database/models/__init__.py index 33be2ee2..06354482 100644 --- a/workflows/database/models/__init__.py +++ b/workflows/database/models/__init__.py @@ -5,7 +5,5 @@ RunStatus, Run, RunStep, - WorkflowVersionInput, - WorkflowVersionOutput, RunEntityInput, ) # noqa: F401 diff --git a/workflows/database/models/workflow.py b/workflows/database/models/workflow.py index 9b14c2a1..0bfe83c8 100644 --- a/workflows/database/models/workflow.py +++ b/workflows/database/models/workflow.py @@ -15,27 +15,17 @@ class Workflow(Base): name = Column(String, nullable=False) default_version = Column(String, nullable=False) minimum_supported_version = Column(String, nullable=False) - versions = relationship("WorkflowVersion", back_populates="workflow") + versions = relationship("WorkflowVersion", back_populates="workflow", foreign_keys="WorkflowVersion.workflow_id") class WorkflowVersion(Base): __tablename__ = "workflow_version" # TODO: replace with uuid7 id = Column(Integer, primary_key=True, autoincrement=True) - version = Column(String, nullable=False) - type = Column(String, nullable=False) - package_uri = Column(String, nullable=False) - beta = Column(Boolean, default=False, nullable=False) - deprecated = Column(Boolean, default=False, nullable=False) - # TODO: add this back in when we add JSONB to strawberry-sqlalchemy-mapper - # graph_json = Column(JSONB) - graph_json = Column(String) - workflow_id = Column(Integer, ForeignKey("workflow.id"), nullable=False) - workflow = relationship("Workflow", back_populates="versions") - runs = relationship("Run", back_populates="workflow_version") - workflow_version_inputs = relationship("WorkflowVersionInput", back_populates="workflow_version") - workflow_version_outputs = relationship("WorkflowVersionOutput", back_populates="workflow_version") - + workflow_id = Column(Integer, ForeignKey('workflow.id'), nullable=False) + workflow = relationship('Workflow', back_populates='versions', foreign_keys=[workflow_id]) + runs = relationship('Run', back_populates='workflow_version', foreign_keys='Run.workflow_version_id') + manifest = Column(String, nullable=False) @strawberry.enum class RunStatus(enum.Enum): @@ -60,12 +50,12 @@ class Run(Base): inputs_json = Column(String, nullable=False) # TODO: add this back in when we add JSONB to strawberry-sqlalchemy-mapper # outputs_json = Column(JSONB) - outputs_json = Column(String) + outputs_json = Column(String, nullable=True) status = Column(Enum(RunStatus), nullable=False, default=RunStatus.STARTED, name="status") workflow_version_id = Column(Integer, ForeignKey("workflow_version.id"), nullable=False) - workflow_version = relationship("WorkflowVersion", back_populates="runs") - run_steps = relationship("RunStep", back_populates="run") - run_entity_inputs = relationship("RunEntityInput", back_populates="run") + workflow_version = relationship("WorkflowVersion", back_populates="runs", foreign_keys=[workflow_version_id]) + run_steps = relationship("RunStep", back_populates="run", foreign_keys="RunStep.run_id") + run_entity_inputs = relationship("RunEntityInput", back_populates="run", foreign_keys="RunEntityInput.run_id") @strawberry.enum @@ -79,40 +69,18 @@ class RunStep(Base): # TODO: replace with uuid7 id = Column(Integer, primary_key=True, autoincrement=True) run_id = Column(Integer, ForeignKey("run.id"), nullable=False) - run = relationship("Run", back_populates="run_steps") + run = relationship("Run", back_populates="run_steps", foreign_keys=[run_id]) started_at = Column(DateTime, nullable=False, server_default=func.now()) ended_at = Column(DateTime) status = Column(Enum(RunStepStatus), name="status") - -class WorkflowVersionInput(Base): - __tablename__ = "workflow_version_input" - # TODO: replace with uuid7 - id = Column(Integer, primary_key=True, autoincrement=True) - workflow_version_id = Column(Integer, ForeignKey("workflow_version.id"), nullable=False) - workflow_version = relationship("WorkflowVersion", back_populates="workflow_version_inputs") - name = Column(String, nullable=False) - entity_type = Column(String, nullable=False) - run_entity_inputs = relationship("RunEntityInput", back_populates="workflow_version_input") - - -class WorkflowVersionOutput(Base): - __tablename__ = "workflow_version_output" - # TODO: replace with uuid7 - id = Column(Integer, primary_key=True, autoincrement=True) - workflow_version_id = Column(Integer, ForeignKey("workflow_version.id"), nullable=False) - workflow_version = relationship("WorkflowVersion", back_populates="workflow_version_outputs") - name = Column(String, nullable=False) - output_type = Column(String, nullable=False) - output_type_version = Column(String, nullable=False) - - class RunEntityInput(Base): __tablename__ = "run_entity_input" # TODO: replace with uuid7 id = Column(Integer, primary_key=True, autoincrement=True) - run_id = Column(Integer, ForeignKey("run.id"), nullable=False) - run = relationship("Run", back_populates="run_entity_inputs") - workflow_version_input_id = Column(Integer, ForeignKey("workflow_version_input.id"), nullable=False) - workflow_version_input = relationship("WorkflowVersionInput", back_populates="run_entity_inputs") + run_id = Column(Integer, ForeignKey('run.id'), nullable=False) + run = relationship('Run', back_populates='run_entity_inputs', foreign_keys=[run_id]) + # workflow_version_input_id = Column(Integer, ForeignKey('workflow_version_input.id'), nullable=False) + # workflow_version_input = relationship('WorkflowVersionInput', back_populates='run_entity_inputs') entity_id = Column(Integer, nullable=False) + field_name = Column(String, nullable=False) diff --git a/workflows/database_migrations/versions/20230724_215129_initial_commit.py b/workflows/database_migrations/versions/20230724_215129_initial_commit.py deleted file mode 100644 index 51c5cf97..00000000 --- a/workflows/database_migrations/versions/20230724_215129_initial_commit.py +++ /dev/null @@ -1,34 +0,0 @@ -"""initial_commit - -Revision ID: 20230724_215129 -Revises: -Create Date: 2023-07-24 21:51:29.973699 - -""" -import sqlalchemy as sa -from alembic import op - -# revision identifiers, used by Alembic. -revision = "20230724_215129" -down_revision = None -branch_labels = None -depends_on = None - - -def upgrade() -> None: - # ### commands auto generated by Alembic - please adjust! ### - op.create_table( - "workflow", - sa.Column("id", sa.Integer(), autoincrement=True, nullable=False), - sa.Column("name", sa.String(), nullable=False), - sa.Column("version", sa.String(), nullable=False), - sa.Column("minimum_supported_version", sa.String(), nullable=False), - sa.PrimaryKeyConstraint("id", name=op.f("pk_workflow")), - ) - # ### end Alembic commands ### - - -def downgrade() -> None: - # ### commands auto generated by Alembic - please adjust! ### - op.drop_table("workflow") - # ### end Alembic commands ### diff --git a/workflows/database_migrations/versions/20230810_235656_add_tables_to_workflow_schema.py b/workflows/database_migrations/versions/20230810_235656_add_tables_to_workflow_schema.py deleted file mode 100644 index 8c68887d..00000000 --- a/workflows/database_migrations/versions/20230810_235656_add_tables_to_workflow_schema.py +++ /dev/null @@ -1,120 +0,0 @@ -"""add tables to workflow schema - -Revision ID: 20230810_235656 -Revises: 20230724_215129 -Create Date: 2023-08-10 23:57:31.583905 - -""" -import sqlalchemy as sa -from alembic import op - -# revision identifiers, used by Alembic. -revision = "20230810_235656" -down_revision = "20230724_215129" -branch_labels = None -depends_on = None - - -def upgrade() -> None: - # ### commands auto generated by Alembic - please adjust! ### - op.create_table( - "workflow_version", - sa.Column("id", sa.Integer(), autoincrement=True, nullable=False), - sa.Column("version", sa.String(), nullable=False), - sa.Column("type", sa.String(), nullable=False), - sa.Column("package_uri", sa.String(), nullable=False), - sa.Column("beta", sa.Boolean(), nullable=False), - sa.Column("deprecated", sa.Boolean(), nullable=False), - sa.Column("graph_json", sa.String(), nullable=True), - sa.Column("workflow_id", sa.Integer(), nullable=False), - sa.ForeignKeyConstraint( - ["workflow_id"], ["workflow.id"], name=op.f("fk_workflow_version_workflow_id_workflow") - ), - sa.PrimaryKeyConstraint("id", name=op.f("pk_workflow_version")), - ) - op.create_table( - "run", - sa.Column("id", sa.Integer(), autoincrement=True, nullable=False), - sa.Column("user_id", sa.Integer(), nullable=False), - sa.Column("project_id", sa.Integer(), nullable=False), - sa.Column("started_at", sa.DateTime(), server_default=sa.text("now()"), nullable=False), - sa.Column("ended_at", sa.DateTime(), nullable=True), - sa.Column("execution_id", sa.String(), nullable=False), - sa.Column("inputs_json", sa.String(), nullable=False), - sa.Column("outputs_json", sa.String(), nullable=True), - sa.Column( - "status", sa.Enum("PENDING", "STARTED", "RUNNING", "SUCCEEDED", "FAILED", name="runstatus"), nullable=False - ), - sa.Column("workflow_version_id", sa.Integer(), nullable=False), - sa.ForeignKeyConstraint( - ["workflow_version_id"], ["workflow_version.id"], name=op.f("fk_run_workflow_version_id_workflow_version") - ), - sa.PrimaryKeyConstraint("id", name=op.f("pk_run")), - ) - op.create_table( - "workflow_version_input", - sa.Column("id", sa.Integer(), autoincrement=True, nullable=False), - sa.Column("workflow_version_id", sa.Integer(), nullable=False), - sa.Column("name", sa.String(), nullable=False), - sa.Column("entity_type", sa.String(), nullable=False), - sa.ForeignKeyConstraint( - ["workflow_version_id"], - ["workflow_version.id"], - name=op.f("fk_workflow_version_input_workflow_version_id_workflow_version"), - ), - sa.PrimaryKeyConstraint("id", name=op.f("pk_workflow_version_input")), - ) - op.create_table( - "workflow_version_output", - sa.Column("id", sa.Integer(), autoincrement=True, nullable=False), - sa.Column("workflow_version_id", sa.Integer(), nullable=False), - sa.Column("name", sa.String(), nullable=False), - sa.Column("output_type", sa.String(), nullable=False), - sa.Column("output_type_version", sa.String(), nullable=False), - sa.ForeignKeyConstraint( - ["workflow_version_id"], - ["workflow_version.id"], - name=op.f("fk_workflow_version_output_workflow_version_id_workflow_version"), - ), - sa.PrimaryKeyConstraint("id", name=op.f("pk_workflow_version_output")), - ) - op.create_table( - "run_entity_input", - sa.Column("id", sa.Integer(), autoincrement=True, nullable=False), - sa.Column("run_id", sa.Integer(), nullable=False), - sa.Column("workflow_version_input_id", sa.Integer(), nullable=False), - sa.Column("entity_id", sa.Integer(), nullable=False), - sa.ForeignKeyConstraint(["run_id"], ["run.id"], name=op.f("fk_run_entity_input_run_id_run")), - sa.ForeignKeyConstraint( - ["workflow_version_input_id"], - ["workflow_version_input.id"], - name=op.f("fk_run_entity_input_workflow_version_input_id_workflow_version_input"), - ), - sa.PrimaryKeyConstraint("id", name=op.f("pk_run_entity_input")), - ) - op.create_table( - "run_step", - sa.Column("id", sa.Integer(), autoincrement=True, nullable=False), - sa.Column("run_id", sa.Integer(), nullable=False), - sa.Column("started_at", sa.DateTime(), server_default=sa.text("now()"), nullable=False), - sa.Column("ended_at", sa.DateTime(), nullable=True), - sa.Column("status", sa.Enum("SUCCEEDED", "FAILED", name="runstepstatus"), nullable=True), - sa.ForeignKeyConstraint(["run_id"], ["run.id"], name=op.f("fk_run_step_run_id_run")), - sa.PrimaryKeyConstraint("id", name=op.f("pk_run_step")), - ) - op.add_column("workflow", sa.Column("default_version", sa.String(), nullable=False)) - op.drop_column("workflow", "version") - # ### end Alembic commands ### - - -def downgrade() -> None: - # ### commands auto generated by Alembic - please adjust! ### - op.add_column("workflow", sa.Column("version", sa.VARCHAR(), autoincrement=False, nullable=False)) - op.drop_column("workflow", "default_version") - op.drop_table("run_step") - op.drop_table("run_entity_input") - op.drop_table("workflow_version_output") - op.drop_table("workflow_version_input") - op.drop_table("run") - op.drop_table("workflow_version") - # ### end Alembic commands ### diff --git a/workflows/database_migrations/versions/20230913_105240_create_db_schema.py b/workflows/database_migrations/versions/20230913_105240_create_db_schema.py new file mode 100644 index 00000000..fe710a05 --- /dev/null +++ b/workflows/database_migrations/versions/20230913_105240_create_db_schema.py @@ -0,0 +1,74 @@ +"""create db schema + +Revision ID: 20230913_105240 +Revises: +Create Date: 2023-09-13 17:52:41.279976 + +""" +import sqlalchemy as sa +from alembic import op + +# revision identifiers, used by Alembic. +revision = '20230913_105240' +down_revision = None +branch_labels = None +depends_on = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('workflow', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('name', sa.String(), nullable=False), + sa.Column('default_version', sa.String(), nullable=False), + sa.Column('minimum_supported_version', sa.String(), nullable=False), + sa.PrimaryKeyConstraint('id', name=op.f('pk_workflow')) + ) + op.create_table('workflow_version', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('workflow_id', sa.Integer(), nullable=False), + sa.ForeignKeyConstraint(['workflow_id'], ['workflow.id'], name=op.f('fk_workflow_version_workflow_id_workflow')), + sa.PrimaryKeyConstraint('id', name=op.f('pk_workflow_version')) + ) + op.create_table('run', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('user_id', sa.Integer(), nullable=False), + sa.Column('project_id', sa.Integer(), nullable=False), + sa.Column('started_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('ended_at', sa.DateTime(), nullable=True), + sa.Column('execution_id', sa.String(), nullable=False), + sa.Column('inputs_json', sa.String(), nullable=False), + sa.Column('outputs_json', sa.String(), nullable=True), + sa.Column('status', sa.Enum('PENDING', 'STARTED', 'RUNNING', 'SUCCEEDED', 'FAILED', name='runstatus'), nullable=False), + sa.Column('workflow_version_id', sa.Integer(), nullable=False), + sa.ForeignKeyConstraint(['workflow_version_id'], ['workflow_version.id'], name=op.f('fk_run_workflow_version_id_workflow_version')), + sa.PrimaryKeyConstraint('id', name=op.f('pk_run')) + ) + op.create_table('run_entity_input', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('run_id', sa.Integer(), nullable=False), + sa.Column('entity_id', sa.Integer(), nullable=False), + sa.Column('field_name', sa.String(), nullable=False), + sa.ForeignKeyConstraint(['run_id'], ['run.id'], name=op.f('fk_run_entity_input_run_id_run')), + sa.PrimaryKeyConstraint('id', name=op.f('pk_run_entity_input')) + ) + op.create_table('run_step', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('run_id', sa.Integer(), nullable=False), + sa.Column('started_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('ended_at', sa.DateTime(), nullable=True), + sa.Column('status', sa.Enum('SUCCEEDED', 'FAILED', name='runstepstatus'), nullable=True), + sa.ForeignKeyConstraint(['run_id'], ['run.id'], name=op.f('fk_run_step_run_id_run')), + sa.PrimaryKeyConstraint('id', name=op.f('pk_run_step')) + ) + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('run_step') + op.drop_table('run_entity_input') + op.drop_table('run') + op.drop_table('workflow_version') + op.drop_table('workflow') + # ### end Alembic commands ### diff --git a/workflows/database_migrations/versions/20230913_105933_add_workflow_manifest_field.py b/workflows/database_migrations/versions/20230913_105933_add_workflow_manifest_field.py new file mode 100644 index 00000000..e9983683 --- /dev/null +++ b/workflows/database_migrations/versions/20230913_105933_add_workflow_manifest_field.py @@ -0,0 +1,27 @@ +"""add workflow manifest field + +Revision ID: 20230913_105933 +Revises: 20230913_105240 +Create Date: 2023-09-13 17:59:34.580392 + +""" +import sqlalchemy as sa +from alembic import op + +# revision identifiers, used by Alembic. +revision = '20230913_105933' +down_revision = '20230913_105240' +branch_labels = None +depends_on = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('workflow_version', sa.Column('manifest', sa.String(), nullable=False)) + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column('workflow_version', 'manifest') + # ### end Alembic commands ### diff --git a/workflows/database_migrations/versions/20230914_095805_make_outputs_json_nullable.py b/workflows/database_migrations/versions/20230914_095805_make_outputs_json_nullable.py new file mode 100644 index 00000000..f65d54df --- /dev/null +++ b/workflows/database_migrations/versions/20230914_095805_make_outputs_json_nullable.py @@ -0,0 +1,31 @@ +"""make outputs_json nullable + +Revision ID: 20230914_095805 +Revises: 20230913_105933 +Create Date: 2023-09-14 16:58:08.065217 + +""" +import sqlalchemy as sa +from alembic import op + +# revision identifiers, used by Alembic. +revision = '20230914_095805' +down_revision = '20230913_105933' +branch_labels = None +depends_on = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.alter_column('run', 'outputs_json', + existing_type=sa.VARCHAR(), + nullable=False) + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.alter_column('run', 'outputs_json', + existing_type=sa.VARCHAR(), + nullable=True) + # ### end Alembic commands ### diff --git a/workflows/database_migrations/versions/20230914_095959_make_outputs_json_nullable_for_real.py b/workflows/database_migrations/versions/20230914_095959_make_outputs_json_nullable_for_real.py new file mode 100644 index 00000000..9d4e4276 --- /dev/null +++ b/workflows/database_migrations/versions/20230914_095959_make_outputs_json_nullable_for_real.py @@ -0,0 +1,31 @@ +"""make outputs_json nullable for real + +Revision ID: 20230914_095959 +Revises: 20230914_095805 +Create Date: 2023-09-14 17:00:01.820926 + +""" +import sqlalchemy as sa +from alembic import op + +# revision identifiers, used by Alembic. +revision = '20230914_095959' +down_revision = '20230914_095805' +branch_labels = None +depends_on = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.alter_column('run', 'outputs_json', + existing_type=sa.VARCHAR(), + nullable=True) + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.alter_column('run', 'outputs_json', + existing_type=sa.VARCHAR(), + nullable=False) + # ### end Alembic commands ### diff --git a/workflows/docker-compose.yml b/workflows/docker-compose.yml index 52a48e7c..848af537 100644 --- a/workflows/docker-compose.yml +++ b/workflows/docker-compose.yml @@ -39,11 +39,11 @@ services: args: - BUILDKIT_INLINE_CACHE=1 restart: always - platform: linux/amd64 volumes: - .:/workflows + # mount /tmp for local runner which uses miniwdl + - /tmp:/tmp # enable docker in docker - - $PWD/test_workflows:$PWD/test_workflows - /var/run/docker.sock:/var/run/docker.sock ports: - 8042:8042 @@ -52,7 +52,6 @@ services: command: uvicorn main:app --reload --host 0.0.0.0 --port 8042 environment: <<: [*aws-variables, *db-variables, *workflow-variables] - LOCAL_RUNNER_FOLDER: ${PWD}/test_workflows ENTITY_SERVICE_URL: http://entities.czidnet:8008/graphql ENTITY_SERVICE_AUTH_TOKEN: "eyJhbGciOiJFQ0RILUVTIiwiZW5jIjoiQTI1NkNCQy1IUzUxMiIsImVwayI6eyJjcnYiOiJQLTM4NCIsImt0eSI6IkVDIiwieCI6Ik5Nc3ZKbXVuYnBXY0VsdVlJTmRVeVVIcUkzbjZCR2VQd2V3ajRXS0pVdEt0QXhmUUtrVE81M2kzQ2dSZkZYVEQiLCJ5IjoiYmp6TkJuZjExWjRIV3dBVm95UVpSOGRWSERicW9wTjhvQkJZYnIxQlBiU1llZHdaWkVuYzJqS21rY0xxcloxTiJ9LCJraWQiOiItQmx2bF9wVk5LU2JRQ2N5dGV4UzNfMk5MaHBia2J6LVk5VFFjbkY5S1drIiwidHlwIjoiSldFIn0..Ymjmtj6nXp8r8AFe8AgI1g.e_39w7OXGJaOVKL_QkP38rvlcEeSgFQsxT0rTdCgI5E-b328zlVHagLSFZ_Iqvqiy6Z8KcU4pLJ3NTaW3Ys_YQsnUn6yUEvwqOY2KESB0mT0Bp3qpNYRBZJVA8PW43YAbOnO7h7ZTwQZJfwMzwhcaaYeZW8pN9rvcNtQX3rgBubSuR-LHKL6k4uAMPh9A8ZxXKZgpI6tpJPxE-uspvYi-foW8VyjZtwOUMvMp3lfZPyL1oQIv_rEUhOGNO_lfi339QcT6F7DwBjXK6C_7U65F-dFZScnReLnVczPfHhJ7z3NnVt46sFcddgZpLIpQyzA6puCcDoRm5ZZCVvm8h-LHVy-9dGWLVxBRhGRdBwBhbiVu2O_CNeVabXl8JhAs3oeV2zDgYfOj_-kkHWsbgHZ0y-tc-HtgoXzsUqaRP1IXQ3g3VDES7UjsaKsfxgURH5EIsrdWwFrWHGoLNfGwwPSwTBI5Mul7LT10-Pg_uBBCiHfQIDqerRQeADRFhV_07GYatBDt-RxwNL4bO59V8ewCzhpdCYRpL363HGldT1Pic-SpTk2NsY2t8MA6__FhJU9JSKYwJpeKMaGLUHA_40PEQ.gb5q-WZTU-ZKpV7WYFbMGMEF2CZIBrFlCUeaZ5ffPDU" motoserver: diff --git a/workflows/entity_interface.py b/workflows/entity_interface.py index 62618cd3..68862196 100644 --- a/workflows/entity_interface.py +++ b/workflows/entity_interface.py @@ -1,27 +1,145 @@ -from typing import List +from abc import ABC +import asyncio +from dataclasses import dataclass, field, fields +import os +from typing import Generic, Optional +import typing +from uuid import UUID +from semver import Version +from gql import gql, Client +from gql.transport.aiohttp import AIOHTTPTransport -class Entity: - pass +ENTITY_SERVICE_URL = os.environ["ENTITY_SERVICE_URL"] +ENTITY_SERVICE_AUTH_TOKEN = os.environ["ENTITY_SERVICE_AUTH_TOKEN"] -class String(Entity): - value: str - def __init__(self, value: str): - self.value = value +def _snake_to_camel(s: str): + title = "".join(word.title() for word in s.split("_")) + return title[0].lower() + title[1:] -class Sequence(Entity): - value: str +_type_name_to_graphql_type = { + "str": "String", + "int": "Int", + "float": "Float", + "bool": "Boolean", +} - def __init__(self, value: str): - self.value = value +@dataclass +class Entity(ABC): + entity_id: Optional[UUID] = field(default_factory=lambda: None, init=False) + version: Optional[Version] = field(default_factory=lambda: Version(0), init=False) + def _mutation_name(self): + return f"create{self.__class__.__name__}" -async def create_entities(entities: List[Entity]): + def _fields(self): + for field in fields(self): + if field.name in ["entity_id", "version"]: + continue + yield field + + def gql_create_mutation(self): + field_name_types = [] + for field in self._fields(): + if field.type.__name__ == "EntityReference": + field_name_types.append((field.metadata["id_name"], "UUID")) + continue + field_name_types.append((_snake_to_camel(field.name), _type_name_to_graphql_type[field.type.__name__])) + + # field_name_types.append(("userId", "Int")) + field_name_types.append(("collectionId", "Int")) + + type_signature = ", ".join([f"${field_name}: {field_type}!" for field_name, field_type in field_name_types]) + variable_signature = ", ".join([f"{field_name}: ${field_name}" for field_name, _ in field_name_types]) + return f""" + mutation Create{self.__class__.__name__}({type_signature}) {'{'} + {self._mutation_name()}({variable_signature}) {'{'} + id + {'}'} + {'}'} + """ + + def gql_variables(self): + variables = {} + for field in self._fields(): + if field.type.__name__ == "EntityReference": + variables[field.metadata["id_name"]] = getattr(self, field.name).entity_id + continue + variables[_snake_to_camel(field.name)] = getattr(self, field.name) + + return variables + + def get_dependent_entities(self): + for field in fields(self): + if field.type.__name__ == "EntityReference": + entity_ref: EntityReference = getattr(self, field.name) + yield entity_ref + + async def create_if_not_exists(self, user_id: int, collection_id: int, client: Client): + if self.entity_id: + return + + dependent_entity_futures = [] + for entity_ref in self.get_dependent_entities(): + dependent_entity_futures.append(entity_ref.create_if_not_exists(user_id, collection_id, client)) + await asyncio.gather(*dependent_entity_futures) + + variables = self.gql_variables() + # variables["userId"] = user_id + variables["collectionId"] = collection_id + response = await client.execute_async(gql(self.gql_create_mutation()), variable_values=variables) + entity_id = response.get(self._mutation_name(), {}).get("id") + self.entity_id = entity_id + + +@dataclass +class Sample(Entity): + name: str + location: str + +T = typing.TypeVar("T", bound=Entity) + +@dataclass +class EntityReference(Generic[T]): + entity_id: Optional[UUID] = field(default_factory=lambda: None) + entity: Optional[T] = field(default_factory=lambda: None) + + async def create_if_not_exists(self, user_id: int, collection_id: int, client: Client): + if self.entity_id: + return + if self.entity is None: + raise ValueError("EntityReference has no entity") + await self.entity.create_if_not_exists(user_id, collection_id, client) + self.entity_id = self.entity.entity_id + + def exists(self): + return self.entity_id is not None + + async def load(self): + pass + + +@dataclass +class SequencingRead(Entity): + nucleotide: str + sequence: str + protocol: str + sample: EntityReference[Sample] = field(metadata={"id_name": "sampleId"}) + +@dataclass +class Contig(Entity): + sequence: str + sequencing_read: Optional[EntityReference[SequencingRead]] = field(metadata={"id_name": "sequencingReadId"}) + + +async def create_entities(user_id: int, collection_id: int, entities: list[Entity]): + headers = {"Authorization": f"Bearer {ENTITY_SERVICE_AUTH_TOKEN}"} + transport = AIOHTTPTransport(url=ENTITY_SERVICE_URL, headers=headers) + client = Client(transport=transport, fetch_schema_from_transport=True) + futures = [] for entity in entities: - if isinstance(entity, String): - print(f"create string: {entity.value}") - elif isinstance(entity, Sequence): - print(f"create sequence: {entity.value}") + futures.append(entity.create_if_not_exists(user_id, collection_id, client)) + await asyncio.gather(*futures) diff --git a/workflows/factoryboy/workflow_factory.py b/workflows/factoryboy/workflow_factory.py index 0513f1c4..9af4d2df 100644 --- a/workflows/factoryboy/workflow_factory.py +++ b/workflows/factoryboy/workflow_factory.py @@ -43,13 +43,14 @@ class Meta: model = WorkflowVersion sqlalchemy_session = None - version = factory.Sequence(lambda n: "v1.0.%s" % n) - type = factory.fuzzy.FuzzyChoice(["WDL", "SMK", "NF"]) - package_uri = factory.LazyAttribute(lambda n: f"s3://path/to/workflow-{n.version}/run.{n.type.lower()}") - beta = Faker("pybool") - deprecated = Faker("pybool") - graph_json = fuzzy.FuzzyChoice(["{}"]) + # version = factory.Sequence(lambda n: "v1.0.%s" % n) + # type = factory.fuzzy.FuzzyChoice(["WDL", "SMK", "NF"]) + # package_uri = factory.LazyAttribute(lambda n: f"s3://path/to/workflow-{n.version}/run.{n.type.lower()}") + # beta = Faker("pybool") + # deprecated = Faker("pybool") + # graph_json = fuzzy.FuzzyChoice(["{}"]) workflow = factory.SubFactory(WorkflowFactory) + manifest = factory.LazyAttribute(lambda n: open("first_workflow_manifest.json").read()) class RunFactory(CommonFactory): diff --git a/workflows/first_workflow_manifest.json b/workflows/first_workflow_manifest.json new file mode 100644 index 00000000..f97b3d74 --- /dev/null +++ b/workflows/first_workflow_manifest.json @@ -0,0 +1,68 @@ +{ + "name":"first_sequence", + "version": "1.0.0", + "type":"WDL", + "deprecated": false, + "description":"A workflow that takes a sequence and returns the first base", + "entity_inputs":[ + { + "name": "fasta", + "entity_type":"fasta", + "description":"A fasta file" + } + ], + "workflow_inputs":[ + { "name":"sequences", + "workflow_data_type":"File", + "description":"A fasta file", + "version":"1.0.0", + "type_annotations": [{ + "name":"fasta", + "version":"1.0.0" + }] + } + ], + "input_loaders":[{ + "name":"fasta", + "version":"1.0.0", + "workflow_input":"workflow_inputs.sequences", + "entity_inputs":[{ + "name": "fasta", + "value": "inputs.entities.fasta" + }] + } + ], + "workflow_outputs":[ + { + "name": "id", + "workflow_data_type":"String", + "description":"The id of the first sequence" + }, + { + "name": "sequence", + "workflow_data_type":"String", + "description":"The first sequence" + } + ], + "entity_outputs":[ + { + "name": "sequence", + "entity_type":"Sequence", + "version":"1.0.0" + } + ], + "output_loaders":[ + { + "name":"sequence", + "version":"1.0.0", + "entity_output":"sequence", + "fields":[{ + "name": "id", + "reference": "outputs.id" + },{ + "name": "sequence", + "reference": "outputs.sequence" + }] + } + ] +} \ No newline at end of file diff --git a/workflows/loader.py b/workflows/loader.py index 553c5a81..5f5b36ee 100644 --- a/workflows/loader.py +++ b/workflows/loader.py @@ -1,72 +1,103 @@ import asyncio -from typing import Any, Dict, List +import sys +from typing import Dict, List, Literal, Tuple, Type, TypeVar from importlib.metadata import entry_points from sqlalchemy import select -from workflows.database.models.workflow import WorkflowVersion, Run +from database.models.workflow import Run from semver import Version from sqlalchemy.ext.asyncio import AsyncSession -from workflows.entity_interface import create_entities - -from workflows.plugin_types import EventListener, Loader, LoaderInput, WorkflowSucceededMessage - - -def load_output_loaders() -> List[Loader]: - loaders: List[Loader] = [] - for plugin in entry_points(group="czid.plugin.loader"): +from entity_interface import create_entities + +from plugin_types import EventBus, EntityInputLoader, EntityOutputLoader, WorkflowSucceededMessage +from manifest import Manifest, load_manifest + +T = TypeVar('T', bound=Type[EntityInputLoader] | Type[EntityOutputLoader]) +def load_loader_plugins(input_or_output: Literal["input", "output"], cls: T) -> Dict[str, List[Tuple[Version, T]]]: + loaders: Dict[str, List[Tuple[Version, T]]] = {} + for plugin in entry_points(group=f"czid.plugin.entity_{input_or_output}_loader"): + assert plugin.dist, "Plugin distribution not found" + name = plugin.name + vesion = Version.parse(plugin.dist.version) loader = plugin.load()() - assert isinstance(loader, Loader) - loaders.append(loader) + assert isinstance(loader, cls) + if name not in loaders: + loaders[name] = [] + loaders[name].append((vesion, loader)) return loaders - -loaders = load_output_loaders() -listener: EventListener - - -class WorkflowOutput: - name: str - output_type_version: Version - - -class EntityOutput: - workflow_outputs: List[str] - entity_type: str - loader_inputs: List[LoaderInput] +input_loaders = load_loader_plugins("input", EntityInputLoader) +def resolve_entity_input_loaders(workflow_manifest: Manifest) -> List[EntityInputLoader]: + resolved_loaders = [] + for loader_config in workflow_manifest.input_loaders: + name = loader_config.name + try: + versions = input_loaders[loader_config.name] + except KeyError: + raise Exception(f"Could not find loader named '{name}'") + # TODO: version constrants, for now pick latest version + _, latest = max(versions, key=lambda x: x[0]) + resolved_loaders.append(latest) + return resolved_loaders + +output_loaders = load_loader_plugins("output", EntityOutputLoader) +# TODO: DRY with above but make the types work poperly +def resolve_entity_output_loaders(workflow_manifest: Manifest) -> List[EntityOutputLoader]: + resolved_loaders = [] + for loader_config in workflow_manifest.output_loaders: + name = loader_config.name + try: + versions = output_loaders[name] + except KeyError: + raise Exception(f"Could not find loader named '{name}'") + # TODO: version constrants, for now pick latest version + _, latest = max(versions, key=lambda x: x[0]) + resolved_loaders.append(latest) + return resolved_loaders class LoaderDriver: session: AsyncSession - loader_cache: Dict[Any, List[Loader]] = {} - - def resolve_loaders(self, workflow_version: WorkflowVersion) -> List[Loader]: - if workflow_version.id in self.loader_cache: - return self.loader_cache[workflow_version.id] - - loaders = [] - for entity in workflow_version.entity_outputs: - for loader in loaders: - if loader.satisfies(entity.loader_inputs): - loaders.append(loader) - break - else: - raise Exception(f"Could not find loader for {entity.loader_inputs}") - self.loader_cache[workflow_version.id] = loaders - return loaders - - async def process_workflow_completed(self, workflow_version: WorkflowVersion): - loaders = self.resolve_loaders(workflow_version) - entities_lists = await asyncio.gather(*[loader.load() for loader in loaders]) + bus: EventBus + + def __init__(self, session: AsyncSession, bus: EventBus) -> None: + self.session = session + self.bus = bus + + async def process_workflow_completed(self, user_id: int, collection_id: int, workflow_manifest: Manifest, outputs: Dict[str, str]): + loaders = resolve_entity_output_loaders(workflow_manifest) + loader_futures = [] + for loader_config, loader in zip(workflow_manifest.output_loaders, loaders): + args = {} + print("loader_config", loader_config.fields, file=sys.stderr) + for field in loader_config.fields: + source, field_name = field.reference.split(".") + if source == "outputs": + args[field.name] = outputs[f"{workflow_manifest.name}.{field_name}"] + elif source == "inputs": + raise Exception("TODO!") + + # field = loader_config.fields[0] + # outputs = {item.name: outputs[f'{workflow_manifest.name}.{item.name}'] for item in loader_config.fields} + print("args", args, file=sys.stderr) + print("outputs", outputs, file=sys.stderr) + loader_futures.append(loader.load(args)) + entities_lists = await asyncio.gather(*loader_futures) for entities in entities_lists: - await create_entities(entities) + await create_entities(user_id, collection_id, entities) async def main(self): while True: - for event in await listener.poll(): + for event in await self.bus.poll(): if isinstance(event, WorkflowSucceededMessage): + + manifest = load_manifest(open("sequence_manifest.json").read()) _event: WorkflowSucceededMessage = event - run = ( - await self.session.execute(select(Run).where(Run.runner_assigned_id == _event.runner_id)) - ).scalar_one() - await self.process_workflow_completed(run.workflow_version) + # run = (await self.session.execute( + # select(Run).where(Run.runner_assigned_id == _event.runner_id) + # )).scalar_one() + user_id = 111 + collection_id = 444 + await self.process_workflow_completed(user_id, collection_id, manifest, _event.outputs) + await asyncio.sleep(1) diff --git a/workflows/main.py b/workflows/main.py index 7bb85822..3becfd54 100644 --- a/workflows/main.py +++ b/workflows/main.py @@ -1,7 +1,9 @@ +import asyncio import configparser import json import os import typing +import asyncio import sqlalchemy as sa import strawberry @@ -14,9 +16,14 @@ import database.models as db import entity_gql_schema as entity_schema -from config import load_workflow_runners -from database.connect import init_async_db, init_sync_db -from database.models.base import Base +from database.connect import init_async_db +from config import load_event_buses, load_workflow_runners + +from strawberry_sqlalchemy_mapper import ( + StrawberrySQLAlchemyMapper, StrawberrySQLAlchemyLoader) + +from loader import LoaderDriver + ############ # Database # @@ -33,6 +40,7 @@ default_workflow_runner_name = config.get("plugins", "default_workflow_runner") workflow_runners = load_workflow_runners() +event_buses = load_event_buses() ###################### # Strawberry-GraphQL # @@ -60,17 +68,6 @@ class Run: class RunStep: pass - -@strawberry_sqlalchemy_mapper.type(db.WorkflowVersionInput) -class WorkflowVersionInput: - pass - - -@strawberry_sqlalchemy_mapper.type(db.WorkflowVersionOutput) -class WorkflowVersionOutput: - pass - - @strawberry_sqlalchemy_mapper.type(db.RunEntityInput) class RunEntityInput: pass @@ -91,9 +88,9 @@ async def get_workflow(self, id: int) -> Workflow: return result.scalars().one() @strawberry.field - async def get_workflows(self) -> typing.List[Workflow]: + async def get_workflows(self) -> typing.Sequence[Workflow]: result = await session.execute(sa.select(db.Workflow)) - return result.scalars() + return result.scalars().all() @strawberry.field async def get_workflow_version(self) -> WorkflowVersion: @@ -101,50 +98,30 @@ async def get_workflow_version(self) -> WorkflowVersion: return result.scalars().one() @strawberry.field - async def get_workflow_versions(self) -> typing.List[WorkflowVersion]: + async def get_workflow_versions(self) -> typing.Sequence[WorkflowVersion]: result = await session.execute(sa.select(db.WorkflowVersion)) - return result.scalars() - + return result.scalars().all() + @strawberry.field async def get_run(self) -> Run: result = await session.execute(sa.select(db.Run).where(db.Run.id == id)) return result.scalars().one() @strawberry.field - async def get_runs(self) -> typing.List[Run]: + async def get_runs(self) -> typing.Sequence[Run]: result = await session.execute(sa.select(db.Run)) - return result.scalars() - + return result.scalars().all() + @strawberry.field async def get_run_step(self) -> RunStep: result = await session.execute(sa.select(db.RunStep).where(db.RunStep.id == id)) return result.scalars().one() @strawberry.field - async def get_run_steps(self) -> typing.List[RunStep]: + async def get_run_steps(self) -> typing.Sequence[RunStep]: result = await session.execute(sa.select(db.RunStep)) - return result.scalars() - - @strawberry.field - async def get_workflow_version_input(self) -> WorkflowVersionInput: - result = await session.execute(sa.select(db.WorkflowVersionInput).where(db.WorkflowVersionInput.id == id)) - return result.scalars().one() - - @strawberry.field - async def get_workflow_version_inputs(self) -> typing.List[WorkflowVersionInput]: - result = await session.execute(sa.select(db.WorkflowVersionInput)) - return result.scalars() - - @strawberry.field - async def get_workflow_version_output(self) -> WorkflowVersionOutput: - result = await session.execute(sa.select(db.WorkflowVersionOutput).where(db.WorkflowVersionOutput.id == id)) - return result.scalars().one() - - @strawberry.field - async def get_workflow_version_outputs(self) -> typing.List[WorkflowVersionOutput]: - result = await session.execute(sa.select(db.WorkflowVersionOutput)) - return result.scalars() - + return result.scalars().all() + @strawberry.field async def get_run_entity_input(self) -> RunEntityInput: result = await session.execute(sa.select(db.RunEntityInput).where(db.RunEntityInput.id == id)) @@ -220,54 +197,51 @@ async def add_workflow_version( return db_workflow_version @strawberry.mutation - async def add_run( + async def submit_workflow( self, - user_id: int, - project_id: int, - execution_id: str, - inputs_json: str, - outputs_json: str, - status: str, - workflow_version_id: int, + # user_id: int, + # project_id: int, + # execution_id: str, + # inputs_json: str, + # outputs_json: str, this one matters! + # status: str, + # workflow_version_id: int, + workflow_inputs: str, + workflow_runner: str = default_workflow_runner_name ) -> Run: - db_run = db.Run( - user_id=user_id, - project_id=project_id, - execution_id=execution_id, - inputs_json=inputs_json, - outputs_json=outputs_json, - status=status, - workflow_version_id=workflow_version_id, - ) - session.add(db_run) - await session.commit() - return db_run - - @strawberry.mutation - async def submit_workflow(self, workflow_inputs: str, workflow_runner: str = default_workflow_runner_name) -> str: - # TODO: create a workflow run # TODO: how do we determine the docker_image_id? Argument to miniwdl, may not be defined, other devs may want to submit custom containers # inputs_json = { # "query_0": "s3://idseq-samples-development/rlim-test/test-upload/valid_input1.fastq", # "db_chunk": "s3://czid-public-references/ncbi-indexes-prod/2021-01-22/index-generation-2/nt_k14_w8_20/nt.part_001.idx", # "docker_image_id": "732052188396.dkr.ecr.us-west-2.amazonaws.com/minimap2:latest" # } - inputs_json = { - "sequences": "s3://idseq-samples-development/rlim-test/test-upload/valid_input1.fastq", - } + # inputs_json = { + # "sequences": "/home/todd/czid-platformics/workflows/test_workflows/foo.fa", + # } assert workflow_runner in workflow_runners, f"Workflow runner {workflow_runner} not found" - workflow_runner = workflow_runners[workflow_runner] - assert ( - "WDL" in workflow_runner.supported_workflow_types() - ), f"Workflow runner {workflow_runner} does not support WDL" - response = workflow_runner.run_workflow( - on_complete=lambda x: print(x), # TODO: add the listener service here - workflow_run_id=1, # TODO: When we create the workflow run add the uuid here - workflow_path="s3://idseq-workflows/minimap2-v1.0.0/minimap2.wdl", # TODO: should come from the WorkflowVersion model - inputs=inputs_json, + _workflow_runner = workflow_runners[workflow_runner] + assert "WDL" in _workflow_runner.supported_workflow_types(), f"Workflow runner {workflow_runner} does not support WDL" + response = await _workflow_runner.run_workflow( + event_bus=event_buses["local"], + workflow_run_id='1', # TODO: When we create the workflow run add the uuid here + workflow_path="/workflows/test_workflows/static_sample/static_sample.wdl", # TODO: should come from the WorkflowVersion model + inputs={} + ) + + # creates a workflow run in the db + # TODO: remove hardcoding + db_run = db.Run( + user_id=111, + project_id=444, + execution_id=response, + inputs_json="{}", + status="STARTED", + workflow_version_id=1, ) + session.add(db_run) + await session.commit() - return response + return db_run @strawberry.mutation async def add_run_step(self, run_id: int, step_name: str, status: str, start_time: str, end_time: str) -> RunStep: @@ -278,28 +252,6 @@ async def add_run_step(self, run_id: int, step_name: str, status: str, start_tim await session.commit() return db_run_step - @strawberry.mutation - async def add_workflow_version_input( - self, workflow_version_id: int, name: str, type: str, description: str - ) -> WorkflowVersionInput: - db_workflow_version_input = db.WorkflowVersionInput( - workflow_version_id=workflow_version_id, name=name, type=type, description=description - ) - session.add(db_workflow_version_input) - await session.commit() - return db_workflow_version_input - - @strawberry.mutation - async def add_workflow_version_output( - self, workflow_version_id: int, name: str, type: str, description: str - ) -> WorkflowVersionOutput: - db_workflow_version_output = db.WorkflowVersionOutput( - workflow_version_id=workflow_version_id, name=name, type=type, description=description - ) - session.add(db_workflow_version_output) - await session.commit() - return db_workflow_version_output - @strawberry.mutation async def add_run_entity_input(self, run_id: int, workflow_version_input_id: int, entity_id: int) -> RunEntityInput: db_run_entity_input = db.RunEntityInput( @@ -337,6 +289,11 @@ def get_context(): app = FastAPI() app.include_router(graphql_app, prefix="/graphql") +loader = LoaderDriver(session, event_buses["local"]) + +# call main in it's own thread +loop = asyncio.get_event_loop() +loop.create_task(loader.main()) @app.get("/") async def root(): diff --git a/workflows/manifest.py b/workflows/manifest.py new file mode 100644 index 00000000..eb62e331 --- /dev/null +++ b/workflows/manifest.py @@ -0,0 +1,86 @@ +import json +from pydantic import BaseModel, conlist +import semver + + +class PydanticVersion(semver.Version): + @classmethod + def _parse(cls, version, *args): + return cls.parse(version) + + @classmethod + def __get_validators__(cls): + """Return a list of validator methods for pydantic models.""" + yield cls._parse + +def convert_semver(v: PydanticVersion): + return str(v) + +class ManifestModel(BaseModel): + class Config: + json_encoders = {PydanticVersion: convert_semver} + +class WorkflowTypeAnnotation(ManifestModel): + name: str + version: PydanticVersion + +class WorkflowInput(ManifestModel): + name: str + workflow_data_type: str + description: str + version: PydanticVersion + type_annotations: list[WorkflowTypeAnnotation] + +class EntityInput(ManifestModel): + name: str + entity_type: str + description: str + +class EntityInputReference(ManifestModel): + name: str + value: str + +class InputLoader(ManifestModel): + name: str + version: PydanticVersion + workflow_input: str + entity_inputs: list[EntityInputReference] + +class EntityOutput(ManifestModel): + name: str + entity_type: str + version: PydanticVersion + +class WorkflowOutput(ManifestModel): + name: str + workflow_data_type: str + description: str + +class OutputFieldMap(ManifestModel): + name: str + reference: str + +class OutputLoader(ManifestModel): + name: str + version: PydanticVersion + entity_output: str + fields: list[OutputFieldMap] + +class Manifest(ManifestModel): + name: str + version: PydanticVersion + type: str = "WDL" + deprecated: bool = False + description: str + entity_inputs: list[EntityInput] + workflow_inputs: list[WorkflowInput] + input_loaders: list[InputLoader] + workflow_outputs: list[WorkflowOutput] + entity_outputs: list[EntityOutput] + output_loaders: list[OutputLoader] + + +def load_manifest(manifest_json: str) -> Manifest: + manifest_dict = json.loads(manifest_json) + manifest = Manifest.model_validate(manifest_dict) + return manifest \ No newline at end of file diff --git a/workflows/plugin_types.py b/workflows/plugin_types.py index 5c81d737..7bbde0c8 100644 --- a/workflows/plugin_types.py +++ b/workflows/plugin_types.py @@ -1,27 +1,41 @@ from abc import ABC, abstractmethod -from typing import Callable, List, NamedTuple, TypedDict, Literal, Optional, Coroutine, Any - -from semver import Version +from dataclasses import dataclass +from typing import Callable, Dict, List, Literal, Coroutine, Any from entity_interface import Entity +from version import WorkflowInput, WorkflowOutput - -class WorkflowStartedMessage(TypedDict): +@dataclass +class WorkflowStatusMessage: runner_id: str + status: Literal["WORKFLOW_STARTED", "WORKFLOW_SUCCESS", "WORKFLOW_FAILURE"] + +@dataclass +class WorkflowStartedMessage(WorkflowStatusMessage): status: Literal["WORKFLOW_STARTED"] +class WorkflowSucceededMessage(WorkflowStatusMessage): + status: Literal["WORKFLOW_SUCCESS"] = "WORKFLOW_SUCCESS" + outputs: Dict[str, str] -class WorkflowSucceededMessage(TypedDict): - runner_id: str - status: Literal["WORKFLOW_SUCCESS"] + def __init__(self, runner_id: str, outputs: Dict[str, str]): + self.runner_id = runner_id + self.outputs = outputs - -class WorkflowFailedMessage(TypedDict): - runner_id: str +@dataclass +class WorkflowFailedMessage(WorkflowStatusMessage): status: Literal["WORKFLOW_FAILURE"] -WorkflowStatusMessage = WorkflowStartedMessage | WorkflowSucceededMessage | WorkflowFailedMessage +class EventBus(ABC): + @abstractmethod + async def send(self, message: WorkflowStatusMessage) -> None: + pass + + @abstractmethod + async def poll() -> List[WorkflowStatusMessage]: + pass + class WorkflowRunner(ABC): @@ -36,61 +50,20 @@ def description(self) -> str: raise NotImplementedError() @abstractmethod - async def run_workflow( - self, - on_complete: Callable[[WorkflowStatusMessage], Coroutine[Any, Any, Any]], - workflow_run_id: str, - workflow_path: str, - inputs: dict, - ) -> str: + async def run_workflow(self, event_bus: EventBus, workflow_run_id: str, workflow_path: str, inputs: dict) -> str: raise NotImplementedError() -class EventListener: - @abstractmethod - async def send(message: WorkflowStatusMessage) -> None: - pass - - @abstractmethod - async def poll() -> List[WorkflowStatusMessage]: - pass - - -class LoaderInput(NamedTuple): - output_or_type: Literal["output", "type"] - name: str - version: Version - - -class LoaderInputConstraint(NamedTuple): - output_or_type: Literal["output", "type"] - name: str - min_version: Optional[Version] - max_version: Optional[Version] - - -class Loader(ABC): +class EntityInputLoader(ABC): @abstractmethod - def constraints(self) -> List[LoaderInputConstraint]: + async def load(self, **kwargs: Dict[str, Entity]) -> WorkflowInput: + """Processes workflow output specified by the type constraints in worrkflow_output_types and returns a list of lists of entities. The outer list represents the order the entities must be created in, while the inner lists can be created in parallel.""" raise NotImplementedError() - def satisfies(self, inputs: List[LoaderInput]) -> bool: - if len(inputs) != len(self.constraints()): - return False - - for input, constraint in zip(inputs, self.constraints()): - if input.output_or_type != constraint.output_or_type: - return False - if input.name != constraint.name: - return False - if constraint.min_version is not None and input.version < constraint.min_version: - return False - if constraint.max_version is not None and input.version > constraint.max_version: - return False - - return True +class EntityOutputLoader(ABC): @abstractmethod - async def load(self, *args) -> List[List[Entity]]: + # TODO: type specificity on workflow_outputs, convert values from str to a representation of workflow outputs + async def load(self, workflow_outputs: Dict[str, str]) -> List[Entity]: """Processes workflow output specified by the type constraints in worrkflow_output_types and returns a list of lists of entities. The outer list represents the order the entities must be created in, while the inner lists can be created in parallel.""" raise NotImplementedError() diff --git a/workflows/plugins/entity_input_loaders/fasta/loader_fasta.py b/workflows/plugins/entity_input_loaders/fasta/loader_fasta.py new file mode 100644 index 00000000..72aa505e --- /dev/null +++ b/workflows/plugins/entity_input_loaders/fasta/loader_fasta.py @@ -0,0 +1,11 @@ +from typing import List, TypedDict +from plugin_types import EntityInputLoader +from entity_interface import SequencingRead + +class Input(TypedDict): + name: str + sequencing_read: str + +class FastaLoader(EntityInputLoader): + async def load(self, args: Input) -> List[List[SequencingRead]]: + return [[SequencingRead(args["name"], args["sequencing_read"])]] \ No newline at end of file diff --git a/workflows/plugins/entity_input_loaders/fasta/setup.py b/workflows/plugins/entity_input_loaders/fasta/setup.py new file mode 100644 index 00000000..25af59d5 --- /dev/null +++ b/workflows/plugins/entity_input_loaders/fasta/setup.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python3 +from setuptools import setup + +setup( + name="entity-input-loader-fasta", + version="0.0.1", + description="Loads a fasta entity", + url="", + project_urls={ + "Documentation": "", + "Source Code": "", + "Issue Tracker": "" + }, + long_description="", + long_description_content_type="text/markdown", + author="Todd Morse", + py_modules=["loader_fasta"], + python_requires=">=3.6", + setup_requires=[], + install_requires=["miniwdl"], + reentry_register=True, + entry_points={ + 'czid.plugin.entity_input_loader': [ + 'fasta = loader_fasta:FastaLoader', + ], + } +) + diff --git a/workflows/plugins/entity_output_loaders/sample/loader_sample.py b/workflows/plugins/entity_output_loaders/sample/loader_sample.py new file mode 100644 index 00000000..2442f587 --- /dev/null +++ b/workflows/plugins/entity_output_loaders/sample/loader_sample.py @@ -0,0 +1,11 @@ +from typing import List, TypedDict +from plugin_types import EntityOutputLoader +from entity_interface import Sample + +class Input(TypedDict): + name: str + location: str + +class SampleLoader(EntityOutputLoader): + async def load(self, args: Input) -> List[Sample]: + return [Sample(args["name"], args["location"])] \ No newline at end of file diff --git a/workflows/plugins/entity_output_loaders/sample/setup.py b/workflows/plugins/entity_output_loaders/sample/setup.py new file mode 100644 index 00000000..9b593d23 --- /dev/null +++ b/workflows/plugins/entity_output_loaders/sample/setup.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python3 +from setuptools import setup + +setup( + name="entity-output-loader-sample", + version="0.0.1", + description="Loads a sample entity", + url="", + project_urls={"Documentation": "", "Source Code": "", "Issue Tracker": ""}, + long_description="", + long_description_content_type="text/markdown", + author="Todd Morse", + py_modules=["loader_sample"], + python_requires=">=3.6", + setup_requires=[], + install_requires=["miniwdl"], + reentry_register=True, + entry_points={ + 'czid.plugin.entity_output_loader': [ + 'sample = loader_sample:SampleLoader', + ], + }, +) diff --git a/workflows/plugins/entity_output_loaders/sequence/loader_sequence.py b/workflows/plugins/entity_output_loaders/sequence/loader_sequence.py new file mode 100644 index 00000000..72cdc806 --- /dev/null +++ b/workflows/plugins/entity_output_loaders/sequence/loader_sequence.py @@ -0,0 +1,14 @@ +from typing import List, TypedDict +from plugin_types import EntityOutputLoader +from entity_interface import EntityReference, Sample, SequencingRead + +class Input(TypedDict): + nucleotide: str + sequence: str + protocol: str + +class SequenceLoader(EntityOutputLoader): + async def load(self, args: Input) -> List[SequencingRead]: + # TODO don't hardcode me :( + sample = Sample("dummy", "Venus") + return [SequencingRead(args["protocol"], args["sequence"], args["nucleotide"], EntityReference(entity=sample))] \ No newline at end of file diff --git a/workflows/plugins/loaders/sequence/setup.py b/workflows/plugins/entity_output_loaders/sequence/setup.py similarity index 71% rename from workflows/plugins/loaders/sequence/setup.py rename to workflows/plugins/entity_output_loaders/sequence/setup.py index 02393f21..c148a3ef 100644 --- a/workflows/plugins/loaders/sequence/setup.py +++ b/workflows/plugins/entity_output_loaders/sequence/setup.py @@ -2,9 +2,9 @@ from setuptools import setup setup( - name="loader-sequence", + name="entity-output-loader-sequence", version="0.0.1", - description="Loads a sequence", + description="Loads a sequence entity", url="", project_urls={"Documentation": "", "Source Code": "", "Issue Tracker": ""}, long_description="", @@ -16,8 +16,8 @@ install_requires=["miniwdl"], reentry_register=True, entry_points={ - "czid.plugin.loader": [ - "sequence = loader_sequence:Sequence", + 'czid.plugin.entity_output_loader': [ + 'sequence = loader_sequence:SequenceLoader', ], }, ) diff --git a/workflows/plugins/event_bus/local/event_bus_local.py b/workflows/plugins/event_bus/local/event_bus_local.py new file mode 100644 index 00000000..93f3261e --- /dev/null +++ b/workflows/plugins/event_bus/local/event_bus_local.py @@ -0,0 +1,19 @@ +from typing import List +from threading import RLock +from plugin_types import EventBus, WorkflowStatusMessage + +# TODO: don't lock +class EventBusLocal(EventBus): + def __init__(self): + self._queue: List[WorkflowStatusMessage] = [] + self._lock = RLock() + + async def send(self, message: WorkflowStatusMessage): + with self._lock: + self._queue.append(message) + + async def poll(self) -> List[WorkflowStatusMessage]: + with self._lock: + batch = self._queue + self._queue = [] + return batch \ No newline at end of file diff --git a/workflows/plugins/event_bus/local/setup.py b/workflows/plugins/event_bus/local/setup.py new file mode 100644 index 00000000..f37fd8af --- /dev/null +++ b/workflows/plugins/event_bus/local/setup.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python3 +from setuptools import setup + +setup( + name="event-bus-local", + version="0.0.1", + description="A local event bus", + url="", + project_urls={ + "Documentation": "", + "Source Code": "", + "Issue Tracker": "" + }, + long_description="", + long_description_content_type="text/markdown", + author="Todd Morse", + py_modules=["event_bus_local"], + python_requires=">=3.6", + setup_requires=[], + install_requires=["miniwdl"], + reentry_register=True, + entry_points={ + 'czid.plugin.event_bus': [ + 'local = event_bus_local:EventBusLocal', + ], + } +) + + diff --git a/workflows/plugins/loaders/sequence/loader_sequence.py b/workflows/plugins/loaders/sequence/loader_sequence.py deleted file mode 100644 index bcb3e80a..00000000 --- a/workflows/plugins/loaders/sequence/loader_sequence.py +++ /dev/null @@ -1,12 +0,0 @@ -from typing import List -from plugin_types import Loader -from workflows.entity_interface import Sequence -from workflows.plugin_types import LoaderInputConstraint - - -class SequenceLoader(Loader): - def constraints(self) -> List[LoaderInputConstraint]: - return [LoaderInputConstraint("type", "sequence", None, None)] - - def load(self, sequence: str) -> List[List[Sequence]]: - return [[Sequence(sequence)]] diff --git a/workflows/plugins/workflow_runners/local/workflow_runner_local.py b/workflows/plugins/workflow_runners/local/workflow_runner_local.py index b7c09fa0..06de5520 100644 --- a/workflows/plugins/workflow_runners/local/workflow_runner_local.py +++ b/workflows/plugins/workflow_runners/local/workflow_runner_local.py @@ -3,15 +3,19 @@ import sys import tempfile import os -import asyncio -from typing import Callable, Coroutine, Any, List +from typing import List from uuid import uuid4 import re -from plugin_types import EventListener, WorkflowRunner, WorkflowStatusMessage, WorkflowSucceededMessage +from plugin_types import EventBus, WorkflowFailedMessage, WorkflowRunner, WorkflowStartedMessage, WorkflowStatusMessage, WorkflowSucceededMessage -local_runner_folder = os.environ["LOCAL_RUNNER_FOLDER"] +def _search_group(pattern: str | re.Pattern[str], string: str, n: int) -> str: + match = re.search(pattern, string) + assert match + group = match.group(n) + assert isinstance(group, str) + return group class LocalWorkflowRunner(WorkflowRunner): def supported_workflow_types(self) -> List[str]: @@ -22,71 +26,26 @@ def description(self) -> str: """Returns a description of the workflow runner""" return "Runs WDL workflows locally using miniWDL" - def _run_workflow_blocking( - self, - on_complete: Callable[[WorkflowStatusMessage], Coroutine[Any, Any, Any]], - workflow_run_id: str, - workflow_path: str, - inputs: dict, - workflow_runner_id: str, - listener: EventListener, - ): - local_runner_folder = os.environ["LOCAL_RUNNER_FOLDER"] - with tempfile.TemporaryDirectory(dir=local_runner_folder) as tmpdir: - try: - p = subprocess.run( - ["miniwdl", "run", "--verbose", os.path.abspath(workflow_path)] - + [f"{k}={v}" for k, v in inputs.items()], - check=True, - cwd=tmpdir, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - ) - outputs = json.loads(p.stdout.decode())["outputs"] - asyncio.run( - on_complete( - { - "runner_id": workflow_runner_id, - "status": "SUCCESS", - "outputs": outputs, - "error": None, - } - ) - ) - listener.send(WorkflowSucceededMessage(runner_id=workflow_runner_id)) - except subprocess.CalledProcessError as e: - print(e) - asyncio.run( - on_complete( - { - "runner_id": workflow_runner_id, - "status": "FAILURE", - "outputs": None, - "error": e.stderr.decode(), - } - ) - ) - def detect_task_output(self, line): if "INFO output :: job:" in line: - task = re.search(r"job: (.*),", line).group(1) - outputs = json.loads(re.search(r"values: (\{.*\})", line).group(1)) - breakpoint() + task = _search_group(r"job: (.*),", line, 1) + outputs = json.loads(_search_group(r"values: (\{.*\})", line, 1)) print(f"task complete: {task}") for key, output in outputs.items(): print(f"{key}: {output}") - def run_workflow( + + async def run_workflow( self, - on_complete: Callable[[WorkflowStatusMessage], Coroutine[Any, Any, Any]], + event_bus: EventBus, workflow_run_id: str, workflow_path: str, inputs: dict, - listener: EventListener, ) -> str: runner_id = str(uuid4()) - # Running docker-in-docker requires the paths to files and outputs to be the same between - with tempfile.TemporaryDirectory(dir=local_runner_folder) as tmpdir: + await event_bus.send(WorkflowStartedMessage(runner_id, "WORKFLOW_STARTED")) + # Running docker-in-docker requires the paths to files and outputs to be the same between + with tempfile.TemporaryDirectory(dir='/tmp') as tmpdir: try: p = subprocess.Popen( ["miniwdl", "run", "--verbose", os.path.abspath(workflow_path)] @@ -96,14 +55,17 @@ def run_workflow( stderr=subprocess.PIPE, ) while True: + assert p.stderr line = p.stderr.readline().decode() self.detect_task_output(line) print(line, file=sys.stderr) - if not line: - break + if not line: break + + assert p.stdout + outputs = json.loads(p.stdout.read().decode())["outputs"] + await event_bus.send(WorkflowSucceededMessage(runner_id, outputs)) except subprocess.CalledProcessError as e: print(e.output) - breakpoint() - print("hello world") + await event_bus.send(WorkflowFailedMessage(runner_id, "WORKFLOW_FAILURE")) return runner_id diff --git a/workflows/poetry.lock b/workflows/poetry.lock index ccab6967..becd9463 100644 --- a/workflows/poetry.lock +++ b/workflows/poetry.lock @@ -1,5 +1,127 @@ # This file is automatically @generated by Poetry 1.5.0 and should not be changed by hand. +[[package]] +name = "aiohttp" +version = "3.8.5" +description = "Async http client/server framework (asyncio)" +optional = false +python-versions = ">=3.6" +files = [ + {file = "aiohttp-3.8.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a94159871304770da4dd371f4291b20cac04e8c94f11bdea1c3478e557fbe0d8"}, + {file = "aiohttp-3.8.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:13bf85afc99ce6f9ee3567b04501f18f9f8dbbb2ea11ed1a2e079670403a7c84"}, + {file = "aiohttp-3.8.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2ce2ac5708501afc4847221a521f7e4b245abf5178cf5ddae9d5b3856ddb2f3a"}, + {file = "aiohttp-3.8.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:96943e5dcc37a6529d18766597c491798b7eb7a61d48878611298afc1fca946c"}, + {file = "aiohttp-3.8.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2ad5c3c4590bb3cc28b4382f031f3783f25ec223557124c68754a2231d989e2b"}, + {file = "aiohttp-3.8.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0c413c633d0512df4dc7fd2373ec06cc6a815b7b6d6c2f208ada7e9e93a5061d"}, + {file = "aiohttp-3.8.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df72ac063b97837a80d80dec8d54c241af059cc9bb42c4de68bd5b61ceb37caa"}, + {file = "aiohttp-3.8.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c48c5c0271149cfe467c0ff8eb941279fd6e3f65c9a388c984e0e6cf57538e14"}, + {file = "aiohttp-3.8.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:368a42363c4d70ab52c2c6420a57f190ed3dfaca6a1b19afda8165ee16416a82"}, + {file = "aiohttp-3.8.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7607ec3ce4993464368505888af5beb446845a014bc676d349efec0e05085905"}, + {file = "aiohttp-3.8.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:0d21c684808288a98914e5aaf2a7c6a3179d4df11d249799c32d1808e79503b5"}, + {file = "aiohttp-3.8.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:312fcfbacc7880a8da0ae8b6abc6cc7d752e9caa0051a53d217a650b25e9a691"}, + {file = "aiohttp-3.8.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ad093e823df03bb3fd37e7dec9d4670c34f9e24aeace76808fc20a507cace825"}, + {file = "aiohttp-3.8.5-cp310-cp310-win32.whl", hash = "sha256:33279701c04351a2914e1100b62b2a7fdb9a25995c4a104259f9a5ead7ed4802"}, + {file = "aiohttp-3.8.5-cp310-cp310-win_amd64.whl", hash = "sha256:6e4a280e4b975a2e7745573e3fc9c9ba0d1194a3738ce1cbaa80626cc9b4f4df"}, + {file = "aiohttp-3.8.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ae871a964e1987a943d83d6709d20ec6103ca1eaf52f7e0d36ee1b5bebb8b9b9"}, + {file = "aiohttp-3.8.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:461908b2578955045efde733719d62f2b649c404189a09a632d245b445c9c975"}, + {file = "aiohttp-3.8.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:72a860c215e26192379f57cae5ab12b168b75db8271f111019509a1196dfc780"}, + {file = "aiohttp-3.8.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc14be025665dba6202b6a71cfcdb53210cc498e50068bc088076624471f8bb9"}, + {file = "aiohttp-3.8.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8af740fc2711ad85f1a5c034a435782fbd5b5f8314c9a3ef071424a8158d7f6b"}, + {file = "aiohttp-3.8.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:841cd8233cbd2111a0ef0a522ce016357c5e3aff8a8ce92bcfa14cef890d698f"}, + {file = "aiohttp-3.8.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ed1c46fb119f1b59304b5ec89f834f07124cd23ae5b74288e364477641060ff"}, + {file = "aiohttp-3.8.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:84f8ae3e09a34f35c18fa57f015cc394bd1389bce02503fb30c394d04ee6b938"}, + {file = "aiohttp-3.8.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:62360cb771707cb70a6fd114b9871d20d7dd2163a0feafe43fd115cfe4fe845e"}, + {file = "aiohttp-3.8.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:23fb25a9f0a1ca1f24c0a371523546366bb642397c94ab45ad3aedf2941cec6a"}, + {file = "aiohttp-3.8.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:b0ba0d15164eae3d878260d4c4df859bbdc6466e9e6689c344a13334f988bb53"}, + {file = "aiohttp-3.8.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:5d20003b635fc6ae3f96d7260281dfaf1894fc3aa24d1888a9b2628e97c241e5"}, + {file = "aiohttp-3.8.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0175d745d9e85c40dcc51c8f88c74bfbaef9e7afeeeb9d03c37977270303064c"}, + {file = "aiohttp-3.8.5-cp311-cp311-win32.whl", hash = "sha256:2e1b1e51b0774408f091d268648e3d57f7260c1682e7d3a63cb00d22d71bb945"}, + {file = "aiohttp-3.8.5-cp311-cp311-win_amd64.whl", hash = "sha256:043d2299f6dfdc92f0ac5e995dfc56668e1587cea7f9aa9d8a78a1b6554e5755"}, + {file = "aiohttp-3.8.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:cae533195e8122584ec87531d6df000ad07737eaa3c81209e85c928854d2195c"}, + {file = "aiohttp-3.8.5-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f21e83f355643c345177a5d1d8079f9f28b5133bcd154193b799d380331d5d3"}, + {file = "aiohttp-3.8.5-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a7a75ef35f2df54ad55dbf4b73fe1da96f370e51b10c91f08b19603c64004acc"}, + {file = "aiohttp-3.8.5-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2e2e9839e14dd5308ee773c97115f1e0a1cb1d75cbeeee9f33824fa5144c7634"}, + {file = "aiohttp-3.8.5-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c44e65da1de4403d0576473e2344828ef9c4c6244d65cf4b75549bb46d40b8dd"}, + {file = "aiohttp-3.8.5-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:78d847e4cde6ecc19125ccbc9bfac4a7ab37c234dd88fbb3c5c524e8e14da543"}, + {file = "aiohttp-3.8.5-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:c7a815258e5895d8900aec4454f38dca9aed71085f227537208057853f9d13f2"}, + {file = "aiohttp-3.8.5-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:8b929b9bd7cd7c3939f8bcfffa92fae7480bd1aa425279d51a89327d600c704d"}, + {file = "aiohttp-3.8.5-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:5db3a5b833764280ed7618393832e0853e40f3d3e9aa128ac0ba0f8278d08649"}, + {file = "aiohttp-3.8.5-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:a0215ce6041d501f3155dc219712bc41252d0ab76474615b9700d63d4d9292af"}, + {file = "aiohttp-3.8.5-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:fd1ed388ea7fbed22c4968dd64bab0198de60750a25fe8c0c9d4bef5abe13824"}, + {file = "aiohttp-3.8.5-cp36-cp36m-win32.whl", hash = "sha256:6e6783bcc45f397fdebc118d772103d751b54cddf5b60fbcc958382d7dd64f3e"}, + {file = "aiohttp-3.8.5-cp36-cp36m-win_amd64.whl", hash = "sha256:b5411d82cddd212644cf9360879eb5080f0d5f7d809d03262c50dad02f01421a"}, + {file = "aiohttp-3.8.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:01d4c0c874aa4ddfb8098e85d10b5e875a70adc63db91f1ae65a4b04d3344cda"}, + {file = "aiohttp-3.8.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e5980a746d547a6ba173fd5ee85ce9077e72d118758db05d229044b469d9029a"}, + {file = "aiohttp-3.8.5-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2a482e6da906d5e6e653be079b29bc173a48e381600161c9932d89dfae5942ef"}, + {file = "aiohttp-3.8.5-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80bd372b8d0715c66c974cf57fe363621a02f359f1ec81cba97366948c7fc873"}, + {file = "aiohttp-3.8.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c1161b345c0a444ebcf46bf0a740ba5dcf50612fd3d0528883fdc0eff578006a"}, + {file = "aiohttp-3.8.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cd56db019015b6acfaaf92e1ac40eb8434847d9bf88b4be4efe5bfd260aee692"}, + {file = "aiohttp-3.8.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:153c2549f6c004d2754cc60603d4668899c9895b8a89397444a9c4efa282aaf4"}, + {file = "aiohttp-3.8.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:4a01951fabc4ce26ab791da5f3f24dca6d9a6f24121746eb19756416ff2d881b"}, + {file = "aiohttp-3.8.5-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bfb9162dcf01f615462b995a516ba03e769de0789de1cadc0f916265c257e5d8"}, + {file = "aiohttp-3.8.5-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:7dde0009408969a43b04c16cbbe252c4f5ef4574ac226bc8815cd7342d2028b6"}, + {file = "aiohttp-3.8.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:4149d34c32f9638f38f544b3977a4c24052042affa895352d3636fa8bffd030a"}, + {file = "aiohttp-3.8.5-cp37-cp37m-win32.whl", hash = "sha256:68c5a82c8779bdfc6367c967a4a1b2aa52cd3595388bf5961a62158ee8a59e22"}, + {file = "aiohttp-3.8.5-cp37-cp37m-win_amd64.whl", hash = "sha256:2cf57fb50be5f52bda004b8893e63b48530ed9f0d6c96c84620dc92fe3cd9b9d"}, + {file = "aiohttp-3.8.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:eca4bf3734c541dc4f374ad6010a68ff6c6748f00451707f39857f429ca36ced"}, + {file = "aiohttp-3.8.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1274477e4c71ce8cfe6c1ec2f806d57c015ebf84d83373676036e256bc55d690"}, + {file = "aiohttp-3.8.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:28c543e54710d6158fc6f439296c7865b29e0b616629767e685a7185fab4a6b9"}, + {file = "aiohttp-3.8.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:910bec0c49637d213f5d9877105d26e0c4a4de2f8b1b29405ff37e9fc0ad52b8"}, + {file = "aiohttp-3.8.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5443910d662db951b2e58eb70b0fbe6b6e2ae613477129a5805d0b66c54b6cb7"}, + {file = "aiohttp-3.8.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2e460be6978fc24e3df83193dc0cc4de46c9909ed92dd47d349a452ef49325b7"}, + {file = "aiohttp-3.8.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fb1558def481d84f03b45888473fc5a1f35747b5f334ef4e7a571bc0dfcb11f8"}, + {file = "aiohttp-3.8.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:34dd0c107799dcbbf7d48b53be761a013c0adf5571bf50c4ecad5643fe9cfcd0"}, + {file = "aiohttp-3.8.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:aa1990247f02a54185dc0dff92a6904521172a22664c863a03ff64c42f9b5410"}, + {file = "aiohttp-3.8.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:0e584a10f204a617d71d359fe383406305a4b595b333721fa50b867b4a0a1548"}, + {file = "aiohttp-3.8.5-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:a3cf433f127efa43fee6b90ea4c6edf6c4a17109d1d037d1a52abec84d8f2e42"}, + {file = "aiohttp-3.8.5-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:c11f5b099adafb18e65c2c997d57108b5bbeaa9eeee64a84302c0978b1ec948b"}, + {file = "aiohttp-3.8.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:84de26ddf621d7ac4c975dbea4c945860e08cccde492269db4e1538a6a6f3c35"}, + {file = "aiohttp-3.8.5-cp38-cp38-win32.whl", hash = "sha256:ab88bafedc57dd0aab55fa728ea10c1911f7e4d8b43e1d838a1739f33712921c"}, + {file = "aiohttp-3.8.5-cp38-cp38-win_amd64.whl", hash = "sha256:5798a9aad1879f626589f3df0f8b79b3608a92e9beab10e5fda02c8a2c60db2e"}, + {file = "aiohttp-3.8.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a6ce61195c6a19c785df04e71a4537e29eaa2c50fe745b732aa937c0c77169f3"}, + {file = "aiohttp-3.8.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:773dd01706d4db536335fcfae6ea2440a70ceb03dd3e7378f3e815b03c97ab51"}, + {file = "aiohttp-3.8.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f83a552443a526ea38d064588613aca983d0ee0038801bc93c0c916428310c28"}, + {file = "aiohttp-3.8.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f7372f7341fcc16f57b2caded43e81ddd18df53320b6f9f042acad41f8e049a"}, + {file = "aiohttp-3.8.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ea353162f249c8097ea63c2169dd1aa55de1e8fecbe63412a9bc50816e87b761"}, + {file = "aiohttp-3.8.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5d47ae48db0b2dcf70bc8a3bc72b3de86e2a590fc299fdbbb15af320d2659de"}, + {file = "aiohttp-3.8.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d827176898a2b0b09694fbd1088c7a31836d1a505c243811c87ae53a3f6273c1"}, + {file = "aiohttp-3.8.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3562b06567c06439d8b447037bb655ef69786c590b1de86c7ab81efe1c9c15d8"}, + {file = "aiohttp-3.8.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4e874cbf8caf8959d2adf572a78bba17cb0e9d7e51bb83d86a3697b686a0ab4d"}, + {file = "aiohttp-3.8.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:6809a00deaf3810e38c628e9a33271892f815b853605a936e2e9e5129762356c"}, + {file = "aiohttp-3.8.5-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:33776e945d89b29251b33a7e7d006ce86447b2cfd66db5e5ded4e5cd0340585c"}, + {file = "aiohttp-3.8.5-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:eaeed7abfb5d64c539e2db173f63631455f1196c37d9d8d873fc316470dfbacd"}, + {file = "aiohttp-3.8.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e91d635961bec2d8f19dfeb41a539eb94bd073f075ca6dae6c8dc0ee89ad6f91"}, + {file = "aiohttp-3.8.5-cp39-cp39-win32.whl", hash = "sha256:00ad4b6f185ec67f3e6562e8a1d2b69660be43070bd0ef6fcec5211154c7df67"}, + {file = "aiohttp-3.8.5-cp39-cp39-win_amd64.whl", hash = "sha256:c0a9034379a37ae42dea7ac1e048352d96286626251862e448933c0f59cbd79c"}, + {file = "aiohttp-3.8.5.tar.gz", hash = "sha256:b9552ec52cc147dbf1944ac7ac98af7602e51ea2dcd076ed194ca3c0d1c7d0bc"}, +] + +[package.dependencies] +aiosignal = ">=1.1.2" +async-timeout = ">=4.0.0a3,<5.0" +attrs = ">=17.3.0" +charset-normalizer = ">=2.0,<4.0" +frozenlist = ">=1.1.1" +multidict = ">=4.5,<7.0" +yarl = ">=1.0,<2.0" + +[package.extras] +speedups = ["Brotli", "aiodns", "cchardet"] + +[[package]] +name = "aiosignal" +version = "1.3.1" +description = "aiosignal: a list of registered asynchronous callbacks" +optional = false +python-versions = ">=3.7" +files = [ + {file = "aiosignal-1.3.1-py3-none-any.whl", hash = "sha256:f8376fb07dd1e86a584e4fcdec80b36b7f81aac666ebc724e2c090300dd83b17"}, + {file = "aiosignal-1.3.1.tar.gz", hash = "sha256:54cd96e15e1649b75d6c87526a6ff0b6c1b0dd3459f43d9ca11d48c339b68cfc"}, +] + +[package.dependencies] +frozenlist = ">=1.1.0" + [[package]] name = "alembic" version = "1.12.0" @@ -50,6 +172,17 @@ doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)"] test = ["anyio[trio]", "coverage[toml] (>=7)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"] trio = ["trio (>=0.22)"] +[[package]] +name = "async-timeout" +version = "4.0.3" +description = "Timeout context manager for asyncio programs" +optional = false +python-versions = ">=3.7" +files = [ + {file = "async-timeout-4.0.3.tar.gz", hash = "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f"}, + {file = "async_timeout-4.0.3-py3-none-any.whl", hash = "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028"}, +] + [[package]] name = "asyncpg" version = "0.28.0" @@ -103,6 +236,35 @@ files = [ docs = ["Sphinx (>=5.3.0,<5.4.0)", "sphinx-rtd-theme (>=1.2.2)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)"] test = ["flake8 (>=5.0,<6.0)", "uvloop (>=0.15.3)"] +[[package]] +name = "attrs" +version = "23.1.0" +description = "Classes Without Boilerplate" +optional = false +python-versions = ">=3.7" +files = [ + {file = "attrs-23.1.0-py3-none-any.whl", hash = "sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04"}, + {file = "attrs-23.1.0.tar.gz", hash = "sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015"}, +] + +[package.extras] +cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] +dev = ["attrs[docs,tests]", "pre-commit"] +docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] +tests = ["attrs[tests-no-zope]", "zope-interface"] +tests-no-zope = ["cloudpickle", "hypothesis", "mypy (>=1.1.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] + +[[package]] +name = "backoff" +version = "2.2.1" +description = "Function decoration for backoff and retry" +optional = false +python-versions = ">=3.7,<4.0" +files = [ + {file = "backoff-2.2.1-py3-none-any.whl", hash = "sha256:63579f9a0628e06278f7e47b7d7d5b6ce20dc65c5e96a6f3ca99a6adca0396e8"}, + {file = "backoff-2.2.1.tar.gz", hash = "sha256:03f829f5bb1923180821643f8753b0502c3b682293992485b0eef2807afa5cba"}, +] + [[package]] name = "boto3" version = "1.28.42" @@ -141,6 +303,101 @@ urllib3 = ">=1.25.4,<1.27" [package.extras] crt = ["awscrt (==0.16.26)"] +[[package]] +name = "certifi" +version = "2023.7.22" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.6" +files = [ + {file = "certifi-2023.7.22-py3-none-any.whl", hash = "sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9"}, + {file = "certifi-2023.7.22.tar.gz", hash = "sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082"}, +] + +[[package]] +name = "charset-normalizer" +version = "3.2.0" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "charset-normalizer-3.2.0.tar.gz", hash = "sha256:3bb3d25a8e6c0aedd251753a79ae98a093c7e7b471faa3aa9a93a81431987ace"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b87549028f680ca955556e3bd57013ab47474c3124dc069faa0b6545b6c9710"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7c70087bfee18a42b4040bb9ec1ca15a08242cf5867c58726530bdf3945672ed"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a103b3a7069b62f5d4890ae1b8f0597618f628b286b03d4bc9195230b154bfa9"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94aea8eff76ee6d1cdacb07dd2123a68283cb5569e0250feab1240058f53b623"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:db901e2ac34c931d73054d9797383d0f8009991e723dab15109740a63e7f902a"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b0dac0ff919ba34d4df1b6131f59ce95b08b9065233446be7e459f95554c0dc8"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:193cbc708ea3aca45e7221ae58f0fd63f933753a9bfb498a3b474878f12caaad"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09393e1b2a9461950b1c9a45d5fd251dc7c6f228acab64da1c9c0165d9c7765c"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:baacc6aee0b2ef6f3d308e197b5d7a81c0e70b06beae1f1fcacffdbd124fe0e3"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:bf420121d4c8dce6b889f0e8e4ec0ca34b7f40186203f06a946fa0276ba54029"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:c04a46716adde8d927adb9457bbe39cf473e1e2c2f5d0a16ceb837e5d841ad4f"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:aaf63899c94de41fe3cf934601b0f7ccb6b428c6e4eeb80da72c58eab077b19a"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d62e51710986674142526ab9f78663ca2b0726066ae26b78b22e0f5e571238dd"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-win32.whl", hash = "sha256:04e57ab9fbf9607b77f7d057974694b4f6b142da9ed4a199859d9d4d5c63fe96"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:48021783bdf96e3d6de03a6e39a1171ed5bd7e8bb93fc84cc649d11490f87cea"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4957669ef390f0e6719db3613ab3a7631e68424604a7b448f079bee145da6e09"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:46fb8c61d794b78ec7134a715a3e564aafc8f6b5e338417cb19fe9f57a5a9bf2"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f779d3ad205f108d14e99bb3859aa7dd8e9c68874617c72354d7ecaec2a054ac"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f25c229a6ba38a35ae6e25ca1264621cc25d4d38dca2942a7fce0b67a4efe918"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2efb1bd13885392adfda4614c33d3b68dee4921fd0ac1d3988f8cbb7d589e72a"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f30b48dd7fa1474554b0b0f3fdfdd4c13b5c737a3c6284d3cdc424ec0ffff3a"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:246de67b99b6851627d945db38147d1b209a899311b1305dd84916f2b88526c6"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bd9b3b31adcb054116447ea22caa61a285d92e94d710aa5ec97992ff5eb7cf3"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:8c2f5e83493748286002f9369f3e6607c565a6a90425a3a1fef5ae32a36d749d"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:3170c9399da12c9dc66366e9d14da8bf7147e1e9d9ea566067bbce7bb74bd9c2"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:7a4826ad2bd6b07ca615c74ab91f32f6c96d08f6fcc3902ceeedaec8cdc3bcd6"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:3b1613dd5aee995ec6d4c69f00378bbd07614702a315a2cf6c1d21461fe17c23"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9e608aafdb55eb9f255034709e20d5a83b6d60c054df0802fa9c9883d0a937aa"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-win32.whl", hash = "sha256:f2a1d0fd4242bd8643ce6f98927cf9c04540af6efa92323e9d3124f57727bfc1"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:681eb3d7e02e3c3655d1b16059fbfb605ac464c834a0c629048a30fad2b27489"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c57921cda3a80d0f2b8aec7e25c8aa14479ea92b5b51b6876d975d925a2ea346"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41b25eaa7d15909cf3ac4c96088c1f266a9a93ec44f87f1d13d4a0e86c81b982"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f058f6963fd82eb143c692cecdc89e075fa0828db2e5b291070485390b2f1c9c"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a7647ebdfb9682b7bb97e2a5e7cb6ae735b1c25008a70b906aecca294ee96cf4"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eef9df1eefada2c09a5e7a40991b9fc6ac6ef20b1372abd48d2794a316dc0449"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e03b8895a6990c9ab2cdcd0f2fe44088ca1c65ae592b8f795c3294af00a461c3"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:ee4006268ed33370957f55bf2e6f4d263eaf4dc3cfc473d1d90baff6ed36ce4a"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c4983bf937209c57240cff65906b18bb35e64ae872da6a0db937d7b4af845dd7"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:3bb7fda7260735efe66d5107fb7e6af6a7c04c7fce9b2514e04b7a74b06bf5dd"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:72814c01533f51d68702802d74f77ea026b5ec52793c791e2da806a3844a46c3"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:70c610f6cbe4b9fce272c407dd9d07e33e6bf7b4aa1b7ffb6f6ded8e634e3592"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-win32.whl", hash = "sha256:a401b4598e5d3f4a9a811f3daf42ee2291790c7f9d74b18d75d6e21dda98a1a1"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-win_amd64.whl", hash = "sha256:c0b21078a4b56965e2b12f247467b234734491897e99c1d51cee628da9786959"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:95eb302ff792e12aba9a8b8f8474ab229a83c103d74a750ec0bd1c1eea32e669"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1a100c6d595a7f316f1b6f01d20815d916e75ff98c27a01ae817439ea7726329"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6339d047dab2780cc6220f46306628e04d9750f02f983ddb37439ca47ced7149"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4b749b9cc6ee664a3300bb3a273c1ca8068c46be705b6c31cf5d276f8628a94"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a38856a971c602f98472050165cea2cdc97709240373041b69030be15047691f"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f87f746ee241d30d6ed93969de31e5ffd09a2961a051e60ae6bddde9ec3583aa"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89f1b185a01fe560bc8ae5f619e924407efca2191b56ce749ec84982fc59a32a"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e1c8a2f4c69e08e89632defbfabec2feb8a8d99edc9f89ce33c4b9e36ab63037"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2f4ac36d8e2b4cc1aa71df3dd84ff8efbe3bfb97ac41242fbcfc053c67434f46"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a386ebe437176aab38c041de1260cd3ea459c6ce5263594399880bbc398225b2"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:ccd16eb18a849fd8dcb23e23380e2f0a354e8daa0c984b8a732d9cfaba3a776d"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:e6a5bf2cba5ae1bb80b154ed68a3cfa2fa00fde979a7f50d6598d3e17d9ac20c"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:45de3f87179c1823e6d9e32156fb14c1927fcc9aba21433f088fdfb555b77c10"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-win32.whl", hash = "sha256:1000fba1057b92a65daec275aec30586c3de2401ccdcd41f8a5c1e2c87078706"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:8b2c760cfc7042b27ebdb4a43a4453bd829a5742503599144d54a032c5dc7e9e"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:855eafa5d5a2034b4621c74925d89c5efef61418570e5ef9b37717d9c796419c"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:203f0c8871d5a7987be20c72442488a0b8cfd0f43b7973771640fc593f56321f"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e857a2232ba53ae940d3456f7533ce6ca98b81917d47adc3c7fd55dad8fab858"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e86d77b090dbddbe78867a0275cb4df08ea195e660f1f7f13435a4649e954e5"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4fb39a81950ec280984b3a44f5bd12819953dc5fa3a7e6fa7a80db5ee853952"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2dee8e57f052ef5353cf608e0b4c871aee320dd1b87d351c28764fc0ca55f9f4"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8700f06d0ce6f128de3ccdbc1acaea1ee264d2caa9ca05daaf492fde7c2a7200"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1920d4ff15ce893210c1f0c0e9d19bfbecb7983c76b33f046c13a8ffbd570252"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:c1c76a1743432b4b60ab3358c937a3fe1341c828ae6194108a94c69028247f22"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f7560358a6811e52e9c4d142d497f1a6e10103d3a6881f18d04dbce3729c0e2c"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:c8063cf17b19661471ecbdb3df1c84f24ad2e389e326ccaf89e3fb2484d8dd7e"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:cd6dbe0238f7743d0efe563ab46294f54f9bc8f4b9bcf57c3c666cc5bc9d1299"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:1249cbbf3d3b04902ff081ffbb33ce3377fa6e4c7356f759f3cd076cc138d020"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-win32.whl", hash = "sha256:6c409c0deba34f147f77efaa67b8e4bb83d2f11c8806405f76397ae5b8c0d1c9"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:7095f6fbfaa55defb6b733cfeb14efaae7a29f0b59d8cf213be4e7ca0b857b80"}, + {file = "charset_normalizer-3.2.0-py3-none-any.whl", hash = "sha256:8e098148dd37b4ce3baca71fb394c81dc5d9c7728c95df695d2dca218edf40e6"}, +] + [[package]] name = "click" version = "8.1.7" @@ -243,6 +500,108 @@ typing-extensions = ">=4.5.0" [package.extras] all = ["email-validator (>=2.0.0)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.5)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"] +[[package]] +name = "frozenlist" +version = "1.4.0" +description = "A list-like structure which implements collections.abc.MutableSequence" +optional = false +python-versions = ">=3.8" +files = [ + {file = "frozenlist-1.4.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:764226ceef3125e53ea2cb275000e309c0aa5464d43bd72abd661e27fffc26ab"}, + {file = "frozenlist-1.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d6484756b12f40003c6128bfcc3fa9f0d49a687e171186c2d85ec82e3758c559"}, + {file = "frozenlist-1.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9ac08e601308e41eb533f232dbf6b7e4cea762f9f84f6357136eed926c15d12c"}, + {file = "frozenlist-1.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d081f13b095d74b67d550de04df1c756831f3b83dc9881c38985834387487f1b"}, + {file = "frozenlist-1.4.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:71932b597f9895f011f47f17d6428252fc728ba2ae6024e13c3398a087c2cdea"}, + {file = "frozenlist-1.4.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:981b9ab5a0a3178ff413bca62526bb784249421c24ad7381e39d67981be2c326"}, + {file = "frozenlist-1.4.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e41f3de4df3e80de75845d3e743b3f1c4c8613c3997a912dbf0229fc61a8b963"}, + {file = "frozenlist-1.4.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6918d49b1f90821e93069682c06ffde41829c346c66b721e65a5c62b4bab0300"}, + {file = "frozenlist-1.4.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0e5c8764c7829343d919cc2dfc587a8db01c4f70a4ebbc49abde5d4b158b007b"}, + {file = "frozenlist-1.4.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:8d0edd6b1c7fb94922bf569c9b092ee187a83f03fb1a63076e7774b60f9481a8"}, + {file = "frozenlist-1.4.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e29cda763f752553fa14c68fb2195150bfab22b352572cb36c43c47bedba70eb"}, + {file = "frozenlist-1.4.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:0c7c1b47859ee2cac3846fde1c1dc0f15da6cec5a0e5c72d101e0f83dcb67ff9"}, + {file = "frozenlist-1.4.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:901289d524fdd571be1c7be054f48b1f88ce8dddcbdf1ec698b27d4b8b9e5d62"}, + {file = "frozenlist-1.4.0-cp310-cp310-win32.whl", hash = "sha256:1a0848b52815006ea6596c395f87449f693dc419061cc21e970f139d466dc0a0"}, + {file = "frozenlist-1.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:b206646d176a007466358aa21d85cd8600a415c67c9bd15403336c331a10d956"}, + {file = "frozenlist-1.4.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:de343e75f40e972bae1ef6090267f8260c1446a1695e77096db6cfa25e759a95"}, + {file = "frozenlist-1.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ad2a9eb6d9839ae241701d0918f54c51365a51407fd80f6b8289e2dfca977cc3"}, + {file = "frozenlist-1.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bd7bd3b3830247580de99c99ea2a01416dfc3c34471ca1298bccabf86d0ff4dc"}, + {file = "frozenlist-1.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bdf1847068c362f16b353163391210269e4f0569a3c166bc6a9f74ccbfc7e839"}, + {file = "frozenlist-1.4.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:38461d02d66de17455072c9ba981d35f1d2a73024bee7790ac2f9e361ef1cd0c"}, + {file = "frozenlist-1.4.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5a32087d720c608f42caed0ef36d2b3ea61a9d09ee59a5142d6070da9041b8f"}, + {file = "frozenlist-1.4.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dd65632acaf0d47608190a71bfe46b209719bf2beb59507db08ccdbe712f969b"}, + {file = "frozenlist-1.4.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:261b9f5d17cac914531331ff1b1d452125bf5daa05faf73b71d935485b0c510b"}, + {file = "frozenlist-1.4.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b89ac9768b82205936771f8d2eb3ce88503b1556324c9f903e7156669f521472"}, + {file = "frozenlist-1.4.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:008eb8b31b3ea6896da16c38c1b136cb9fec9e249e77f6211d479db79a4eaf01"}, + {file = "frozenlist-1.4.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:e74b0506fa5aa5598ac6a975a12aa8928cbb58e1f5ac8360792ef15de1aa848f"}, + {file = "frozenlist-1.4.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:490132667476f6781b4c9458298b0c1cddf237488abd228b0b3650e5ecba7467"}, + {file = "frozenlist-1.4.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:76d4711f6f6d08551a7e9ef28c722f4a50dd0fc204c56b4bcd95c6cc05ce6fbb"}, + {file = "frozenlist-1.4.0-cp311-cp311-win32.whl", hash = "sha256:a02eb8ab2b8f200179b5f62b59757685ae9987996ae549ccf30f983f40602431"}, + {file = "frozenlist-1.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:515e1abc578dd3b275d6a5114030b1330ba044ffba03f94091842852f806f1c1"}, + {file = "frozenlist-1.4.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:f0ed05f5079c708fe74bf9027e95125334b6978bf07fd5ab923e9e55e5fbb9d3"}, + {file = "frozenlist-1.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ca265542ca427bf97aed183c1676e2a9c66942e822b14dc6e5f42e038f92a503"}, + {file = "frozenlist-1.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:491e014f5c43656da08958808588cc6c016847b4360e327a62cb308c791bd2d9"}, + {file = "frozenlist-1.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:17ae5cd0f333f94f2e03aaf140bb762c64783935cc764ff9c82dff626089bebf"}, + {file = "frozenlist-1.4.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1e78fb68cf9c1a6aa4a9a12e960a5c9dfbdb89b3695197aa7064705662515de2"}, + {file = "frozenlist-1.4.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5655a942f5f5d2c9ed93d72148226d75369b4f6952680211972a33e59b1dfdc"}, + {file = "frozenlist-1.4.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c11b0746f5d946fecf750428a95f3e9ebe792c1ee3b1e96eeba145dc631a9672"}, + {file = "frozenlist-1.4.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e66d2a64d44d50d2543405fb183a21f76b3b5fd16f130f5c99187c3fb4e64919"}, + {file = "frozenlist-1.4.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:88f7bc0fcca81f985f78dd0fa68d2c75abf8272b1f5c323ea4a01a4d7a614efc"}, + {file = "frozenlist-1.4.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:5833593c25ac59ede40ed4de6d67eb42928cca97f26feea219f21d0ed0959b79"}, + {file = "frozenlist-1.4.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:fec520865f42e5c7f050c2a79038897b1c7d1595e907a9e08e3353293ffc948e"}, + {file = "frozenlist-1.4.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:b826d97e4276750beca7c8f0f1a4938892697a6bcd8ec8217b3312dad6982781"}, + {file = "frozenlist-1.4.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ceb6ec0a10c65540421e20ebd29083c50e6d1143278746a4ef6bcf6153171eb8"}, + {file = "frozenlist-1.4.0-cp38-cp38-win32.whl", hash = "sha256:2b8bcf994563466db019fab287ff390fffbfdb4f905fc77bc1c1d604b1c689cc"}, + {file = "frozenlist-1.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:a6c8097e01886188e5be3e6b14e94ab365f384736aa1fca6a0b9e35bd4a30bc7"}, + {file = "frozenlist-1.4.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:6c38721585f285203e4b4132a352eb3daa19121a035f3182e08e437cface44bf"}, + {file = "frozenlist-1.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a0c6da9aee33ff0b1a451e867da0c1f47408112b3391dd43133838339e410963"}, + {file = "frozenlist-1.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:93ea75c050c5bb3d98016b4ba2497851eadf0ac154d88a67d7a6816206f6fa7f"}, + {file = "frozenlist-1.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f61e2dc5ad442c52b4887f1fdc112f97caeff4d9e6ebe78879364ac59f1663e1"}, + {file = "frozenlist-1.4.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aa384489fefeb62321b238e64c07ef48398fe80f9e1e6afeff22e140e0850eef"}, + {file = "frozenlist-1.4.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:10ff5faaa22786315ef57097a279b833ecab1a0bfb07d604c9cbb1c4cdc2ed87"}, + {file = "frozenlist-1.4.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:007df07a6e3eb3e33e9a1fe6a9db7af152bbd8a185f9aaa6ece10a3529e3e1c6"}, + {file = "frozenlist-1.4.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f4f399d28478d1f604c2ff9119907af9726aed73680e5ed1ca634d377abb087"}, + {file = "frozenlist-1.4.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:c5374b80521d3d3f2ec5572e05adc94601985cc526fb276d0c8574a6d749f1b3"}, + {file = "frozenlist-1.4.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:ce31ae3e19f3c902de379cf1323d90c649425b86de7bbdf82871b8a2a0615f3d"}, + {file = "frozenlist-1.4.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7211ef110a9194b6042449431e08c4d80c0481e5891e58d429df5899690511c2"}, + {file = "frozenlist-1.4.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:556de4430ce324c836789fa4560ca62d1591d2538b8ceb0b4f68fb7b2384a27a"}, + {file = "frozenlist-1.4.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7645a8e814a3ee34a89c4a372011dcd817964ce8cb273c8ed6119d706e9613e3"}, + {file = "frozenlist-1.4.0-cp39-cp39-win32.whl", hash = "sha256:19488c57c12d4e8095a922f328df3f179c820c212940a498623ed39160bc3c2f"}, + {file = "frozenlist-1.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:6221d84d463fb110bdd7619b69cb43878a11d51cbb9394ae3105d082d5199167"}, + {file = "frozenlist-1.4.0.tar.gz", hash = "sha256:09163bdf0b2907454042edb19f887c6d33806adc71fbd54afc14908bfdc22251"}, +] + +[[package]] +name = "gql" +version = "3.4.1" +description = "GraphQL client for Python" +optional = false +python-versions = "*" +files = [ + {file = "gql-3.4.1-py2.py3-none-any.whl", hash = "sha256:315624ca0f4d571ef149d455033ebd35e45c1a13f18a059596aeddcea99135cf"}, + {file = "gql-3.4.1.tar.gz", hash = "sha256:11dc5d8715a827f2c2899593439a4f36449db4f0eafa5b1ea63948f8a2f8c545"}, +] + +[package.dependencies] +aiohttp = {version = ">=3.7.1,<3.9.0", optional = true, markers = "extra == \"all\""} +backoff = ">=1.11.1,<3.0" +botocore = {version = ">=1.21,<2", optional = true, markers = "extra == \"all\""} +graphql-core = ">=3.2,<3.3" +requests = {version = ">=2.26,<3", optional = true, markers = "extra == \"all\""} +requests-toolbelt = {version = ">=0.9.1,<1", optional = true, markers = "extra == \"all\""} +urllib3 = {version = ">=1.26,<2", optional = true, markers = "extra == \"all\""} +websockets = {version = ">=10,<11", optional = true, markers = "python_version > \"3.6\" and extra == \"all\""} +yarl = ">=1.6,<2.0" + +[package.extras] +aiohttp = ["aiohttp (>=3.7.1,<3.9.0)"] +all = ["aiohttp (>=3.7.1,<3.9.0)", "botocore (>=1.21,<2)", "requests (>=2.26,<3)", "requests-toolbelt (>=0.9.1,<1)", "urllib3 (>=1.26,<2)", "websockets (>=10,<11)", "websockets (>=9,<10)"] +botocore = ["botocore (>=1.21,<2)"] +dev = ["aiofiles", "aiohttp (>=3.7.1,<3.9.0)", "black (==22.3.0)", "botocore (>=1.21,<2)", "check-manifest (>=0.42,<1)", "flake8 (==3.8.1)", "isort (==4.3.21)", "mock (==4.0.2)", "mypy (==0.910)", "parse (==1.15.0)", "pytest (==6.2.5)", "pytest-asyncio (==0.16.0)", "pytest-console-scripts (==1.3.1)", "pytest-cov (==3.0.0)", "requests (>=2.26,<3)", "requests-toolbelt (>=0.9.1,<1)", "sphinx (>=3.0.0,<4)", "sphinx-argparse (==0.2.5)", "sphinx-rtd-theme (>=0.4,<1)", "types-aiofiles", "types-mock", "types-requests", "urllib3 (>=1.26,<2)", "vcrpy (==4.0.2)", "websockets (>=10,<11)", "websockets (>=9,<10)"] +requests = ["requests (>=2.26,<3)", "requests-toolbelt (>=0.9.1,<1)", "urllib3 (>=1.26,<2)"] +test = ["aiofiles", "aiohttp (>=3.7.1,<3.9.0)", "botocore (>=1.21,<2)", "mock (==4.0.2)", "parse (==1.15.0)", "pytest (==6.2.5)", "pytest-asyncio (==0.16.0)", "pytest-console-scripts (==1.3.1)", "pytest-cov (==3.0.0)", "requests (>=2.26,<3)", "requests-toolbelt (>=0.9.1,<1)", "urllib3 (>=1.26,<2)", "vcrpy (==4.0.2)", "websockets (>=10,<11)", "websockets (>=9,<10)"] +test-no-transport = ["aiofiles", "mock (==4.0.2)", "parse (==1.15.0)", "pytest (==6.2.5)", "pytest-asyncio (==0.16.0)", "pytest-console-scripts (==1.3.1)", "pytest-cov (==3.0.0)", "vcrpy (==4.0.2)"] +websockets = ["websockets (>=10,<11)", "websockets (>=9,<10)"] + [[package]] name = "graphql-core" version = "3.2.3" @@ -548,6 +907,16 @@ files = [ {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"}, {file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"}, {file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-win32.whl", hash = "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707"}, @@ -605,6 +974,89 @@ files = [ [package.dependencies] psutil = {version = ">=4.0.0", markers = "sys_platform != \"cygwin\""} +[[package]] +name = "multidict" +version = "6.0.4" +description = "multidict implementation" +optional = false +python-versions = ">=3.7" +files = [ + {file = "multidict-6.0.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b1a97283e0c85772d613878028fec909f003993e1007eafa715b24b377cb9b8"}, + {file = "multidict-6.0.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:eeb6dcc05e911516ae3d1f207d4b0520d07f54484c49dfc294d6e7d63b734171"}, + {file = "multidict-6.0.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d6d635d5209b82a3492508cf5b365f3446afb65ae7ebd755e70e18f287b0adf7"}, + {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c048099e4c9e9d615545e2001d3d8a4380bd403e1a0578734e0d31703d1b0c0b"}, + {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ea20853c6dbbb53ed34cb4d080382169b6f4554d394015f1bef35e881bf83547"}, + {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:16d232d4e5396c2efbbf4f6d4df89bfa905eb0d4dc5b3549d872ab898451f569"}, + {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36c63aaa167f6c6b04ef2c85704e93af16c11d20de1d133e39de6a0e84582a93"}, + {file = "multidict-6.0.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:64bdf1086b6043bf519869678f5f2757f473dee970d7abf6da91ec00acb9cb98"}, + {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:43644e38f42e3af682690876cff722d301ac585c5b9e1eacc013b7a3f7b696a0"}, + {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7582a1d1030e15422262de9f58711774e02fa80df0d1578995c76214f6954988"}, + {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:ddff9c4e225a63a5afab9dd15590432c22e8057e1a9a13d28ed128ecf047bbdc"}, + {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:ee2a1ece51b9b9e7752e742cfb661d2a29e7bcdba2d27e66e28a99f1890e4fa0"}, + {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a2e4369eb3d47d2034032a26c7a80fcb21a2cb22e1173d761a162f11e562caa5"}, + {file = "multidict-6.0.4-cp310-cp310-win32.whl", hash = "sha256:574b7eae1ab267e5f8285f0fe881f17efe4b98c39a40858247720935b893bba8"}, + {file = "multidict-6.0.4-cp310-cp310-win_amd64.whl", hash = "sha256:4dcbb0906e38440fa3e325df2359ac6cb043df8e58c965bb45f4e406ecb162cc"}, + {file = "multidict-6.0.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0dfad7a5a1e39c53ed00d2dd0c2e36aed4650936dc18fd9a1826a5ae1cad6f03"}, + {file = "multidict-6.0.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:64da238a09d6039e3bd39bb3aee9c21a5e34f28bfa5aa22518581f910ff94af3"}, + {file = "multidict-6.0.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ff959bee35038c4624250473988b24f846cbeb2c6639de3602c073f10410ceba"}, + {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:01a3a55bd90018c9c080fbb0b9f4891db37d148a0a18722b42f94694f8b6d4c9"}, + {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c5cb09abb18c1ea940fb99360ea0396f34d46566f157122c92dfa069d3e0e982"}, + {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:666daae833559deb2d609afa4490b85830ab0dfca811a98b70a205621a6109fe"}, + {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11bdf3f5e1518b24530b8241529d2050014c884cf18b6fc69c0c2b30ca248710"}, + {file = "multidict-6.0.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7d18748f2d30f94f498e852c67d61261c643b349b9d2a581131725595c45ec6c"}, + {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:458f37be2d9e4c95e2d8866a851663cbc76e865b78395090786f6cd9b3bbf4f4"}, + {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:b1a2eeedcead3a41694130495593a559a668f382eee0727352b9a41e1c45759a"}, + {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:7d6ae9d593ef8641544d6263c7fa6408cc90370c8cb2bbb65f8d43e5b0351d9c"}, + {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:5979b5632c3e3534e42ca6ff856bb24b2e3071b37861c2c727ce220d80eee9ed"}, + {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:dcfe792765fab89c365123c81046ad4103fcabbc4f56d1c1997e6715e8015461"}, + {file = "multidict-6.0.4-cp311-cp311-win32.whl", hash = "sha256:3601a3cece3819534b11d4efc1eb76047488fddd0c85a3948099d5da4d504636"}, + {file = "multidict-6.0.4-cp311-cp311-win_amd64.whl", hash = "sha256:81a4f0b34bd92df3da93315c6a59034df95866014ac08535fc819f043bfd51f0"}, + {file = "multidict-6.0.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:67040058f37a2a51ed8ea8f6b0e6ee5bd78ca67f169ce6122f3e2ec80dfe9b78"}, + {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:853888594621e6604c978ce2a0444a1e6e70c8d253ab65ba11657659dcc9100f"}, + {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:39ff62e7d0f26c248b15e364517a72932a611a9b75f35b45be078d81bdb86603"}, + {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:af048912e045a2dc732847d33821a9d84ba553f5c5f028adbd364dd4765092ac"}, + {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1e8b901e607795ec06c9e42530788c45ac21ef3aaa11dbd0c69de543bfb79a9"}, + {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62501642008a8b9871ddfccbf83e4222cf8ac0d5aeedf73da36153ef2ec222d2"}, + {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:99b76c052e9f1bc0721f7541e5e8c05db3941eb9ebe7b8553c625ef88d6eefde"}, + {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:509eac6cf09c794aa27bcacfd4d62c885cce62bef7b2c3e8b2e49d365b5003fe"}, + {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:21a12c4eb6ddc9952c415f24eef97e3e55ba3af61f67c7bc388dcdec1404a067"}, + {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:5cad9430ab3e2e4fa4a2ef4450f548768400a2ac635841bc2a56a2052cdbeb87"}, + {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ab55edc2e84460694295f401215f4a58597f8f7c9466faec545093045476327d"}, + {file = "multidict-6.0.4-cp37-cp37m-win32.whl", hash = "sha256:5a4dcf02b908c3b8b17a45fb0f15b695bf117a67b76b7ad18b73cf8e92608775"}, + {file = "multidict-6.0.4-cp37-cp37m-win_amd64.whl", hash = "sha256:6ed5f161328b7df384d71b07317f4d8656434e34591f20552c7bcef27b0ab88e"}, + {file = "multidict-6.0.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5fc1b16f586f049820c5c5b17bb4ee7583092fa0d1c4e28b5239181ff9532e0c"}, + {file = "multidict-6.0.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1502e24330eb681bdaa3eb70d6358e818e8e8f908a22a1851dfd4e15bc2f8161"}, + {file = "multidict-6.0.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b692f419760c0e65d060959df05f2a531945af31fda0c8a3b3195d4efd06de11"}, + {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45e1ecb0379bfaab5eef059f50115b54571acfbe422a14f668fc8c27ba410e7e"}, + {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ddd3915998d93fbcd2566ddf9cf62cdb35c9e093075f862935573d265cf8f65d"}, + {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:59d43b61c59d82f2effb39a93c48b845efe23a3852d201ed2d24ba830d0b4cf2"}, + {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc8e1d0c705233c5dd0c5e6460fbad7827d5d36f310a0fadfd45cc3029762258"}, + {file = "multidict-6.0.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6aa0418fcc838522256761b3415822626f866758ee0bc6632c9486b179d0b52"}, + {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6748717bb10339c4760c1e63da040f5f29f5ed6e59d76daee30305894069a660"}, + {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4d1a3d7ef5e96b1c9e92f973e43aa5e5b96c659c9bc3124acbbd81b0b9c8a951"}, + {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4372381634485bec7e46718edc71528024fcdc6f835baefe517b34a33c731d60"}, + {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:fc35cb4676846ef752816d5be2193a1e8367b4c1397b74a565a9d0389c433a1d"}, + {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:4b9d9e4e2b37daddb5c23ea33a3417901fa7c7b3dee2d855f63ee67a0b21e5b1"}, + {file = "multidict-6.0.4-cp38-cp38-win32.whl", hash = "sha256:e41b7e2b59679edfa309e8db64fdf22399eec4b0b24694e1b2104fb789207779"}, + {file = "multidict-6.0.4-cp38-cp38-win_amd64.whl", hash = "sha256:d6c254ba6e45d8e72739281ebc46ea5eb5f101234f3ce171f0e9f5cc86991480"}, + {file = "multidict-6.0.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:16ab77bbeb596e14212e7bab8429f24c1579234a3a462105cda4a66904998664"}, + {file = "multidict-6.0.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bc779e9e6f7fda81b3f9aa58e3a6091d49ad528b11ed19f6621408806204ad35"}, + {file = "multidict-6.0.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4ceef517eca3e03c1cceb22030a3e39cb399ac86bff4e426d4fc6ae49052cc60"}, + {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:281af09f488903fde97923c7744bb001a9b23b039a909460d0f14edc7bf59706"}, + {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:52f2dffc8acaba9a2f27174c41c9e57f60b907bb9f096b36b1a1f3be71c6284d"}, + {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b41156839806aecb3641f3208c0dafd3ac7775b9c4c422d82ee2a45c34ba81ca"}, + {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d5e3fc56f88cc98ef8139255cf8cd63eb2c586531e43310ff859d6bb3a6b51f1"}, + {file = "multidict-6.0.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8316a77808c501004802f9beebde51c9f857054a0c871bd6da8280e718444449"}, + {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f70b98cd94886b49d91170ef23ec5c0e8ebb6f242d734ed7ed677b24d50c82cf"}, + {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bf6774e60d67a9efe02b3616fee22441d86fab4c6d335f9d2051d19d90a40063"}, + {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:e69924bfcdda39b722ef4d9aa762b2dd38e4632b3641b1d9a57ca9cd18f2f83a"}, + {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:6b181d8c23da913d4ff585afd1155a0e1194c0b50c54fcfe286f70cdaf2b7176"}, + {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:52509b5be062d9eafc8170e53026fbc54cf3b32759a23d07fd935fb04fc22d95"}, + {file = "multidict-6.0.4-cp39-cp39-win32.whl", hash = "sha256:27c523fbfbdfd19c6867af7346332b62b586eed663887392cff78d614f9ec313"}, + {file = "multidict-6.0.4-cp39-cp39-win_amd64.whl", hash = "sha256:33029f5734336aa0d4c0384525da0387ef89148dc7191aae00ca5fb23d7aafc2"}, + {file = "multidict-6.0.4.tar.gz", hash = "sha256:3666906492efb76453c0e7b97f2cf459b0682e7402c0489a95484965dbc1da49"}, +] + [[package]] name = "mypy-extensions" version = "1.0.0" @@ -1096,6 +1548,41 @@ files = [ {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, ] +[[package]] +name = "requests" +version = "2.31.0" +description = "Python HTTP for Humans." +optional = false +python-versions = ">=3.7" +files = [ + {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, + {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, +] + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = ">=2,<4" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<3" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] + +[[package]] +name = "requests-toolbelt" +version = "0.10.1" +description = "A utility belt for advanced users of python-requests" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "requests-toolbelt-0.10.1.tar.gz", hash = "sha256:62e09f7ff5ccbda92772a29f394a49c3ad6cb181d568b1337626b2abb628a63d"}, + {file = "requests_toolbelt-0.10.1-py2.py3-none-any.whl", hash = "sha256:18565aa58116d9951ac39baa288d3adb5b3ff975c4f25eee78555d89e8f247f7"}, +] + +[package.dependencies] +requests = ">=2.0.1,<3.0.0" + [[package]] name = "rich" version = "13.5.2" @@ -1478,6 +1965,171 @@ h11 = ">=0.8" [package.extras] standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.4)"] +[[package]] +name = "websockets" +version = "10.4" +description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" +optional = false +python-versions = ">=3.7" +files = [ + {file = "websockets-10.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d58804e996d7d2307173d56c297cf7bc132c52df27a3efaac5e8d43e36c21c48"}, + {file = "websockets-10.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bc0b82d728fe21a0d03e65f81980abbbcb13b5387f733a1a870672c5be26edab"}, + {file = "websockets-10.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ba089c499e1f4155d2a3c2a05d2878a3428cf321c848f2b5a45ce55f0d7d310c"}, + {file = "websockets-10.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:33d69ca7612f0ddff3316b0c7b33ca180d464ecac2d115805c044bf0a3b0d032"}, + {file = "websockets-10.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62e627f6b6d4aed919a2052efc408da7a545c606268d5ab5bfab4432734b82b4"}, + {file = "websockets-10.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38ea7b82bfcae927eeffc55d2ffa31665dc7fec7b8dc654506b8e5a518eb4d50"}, + {file = "websockets-10.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e0cb5cc6ece6ffa75baccfd5c02cffe776f3f5c8bf486811f9d3ea3453676ce8"}, + {file = "websockets-10.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:ae5e95cfb53ab1da62185e23b3130e11d64431179debac6dc3c6acf08760e9b1"}, + {file = "websockets-10.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7c584f366f46ba667cfa66020344886cf47088e79c9b9d39c84ce9ea98aaa331"}, + {file = "websockets-10.4-cp310-cp310-win32.whl", hash = "sha256:b029fb2032ae4724d8ae8d4f6b363f2cc39e4c7b12454df8df7f0f563ed3e61a"}, + {file = "websockets-10.4-cp310-cp310-win_amd64.whl", hash = "sha256:8dc96f64ae43dde92530775e9cb169979f414dcf5cff670455d81a6823b42089"}, + {file = "websockets-10.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:47a2964021f2110116cc1125b3e6d87ab5ad16dea161949e7244ec583b905bb4"}, + {file = "websockets-10.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e789376b52c295c4946403bd0efecf27ab98f05319df4583d3c48e43c7342c2f"}, + {file = "websockets-10.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7d3f0b61c45c3fa9a349cf484962c559a8a1d80dae6977276df8fd1fa5e3cb8c"}, + {file = "websockets-10.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f55b5905705725af31ccef50e55391621532cd64fbf0bc6f4bac935f0fccec46"}, + {file = "websockets-10.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:00c870522cdb69cd625b93f002961ffb0c095394f06ba8c48f17eef7c1541f96"}, + {file = "websockets-10.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f38706e0b15d3c20ef6259fd4bc1700cd133b06c3c1bb108ffe3f8947be15fa"}, + {file = "websockets-10.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f2c38d588887a609191d30e902df2a32711f708abfd85d318ca9b367258cfd0c"}, + {file = "websockets-10.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:fe10ddc59b304cb19a1bdf5bd0a7719cbbc9fbdd57ac80ed436b709fcf889106"}, + {file = "websockets-10.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:90fcf8929836d4a0e964d799a58823547df5a5e9afa83081761630553be731f9"}, + {file = "websockets-10.4-cp311-cp311-win32.whl", hash = "sha256:b9968694c5f467bf67ef97ae7ad4d56d14be2751000c1207d31bf3bb8860bae8"}, + {file = "websockets-10.4-cp311-cp311-win_amd64.whl", hash = "sha256:a7a240d7a74bf8d5cb3bfe6be7f21697a28ec4b1a437607bae08ac7acf5b4882"}, + {file = "websockets-10.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:74de2b894b47f1d21cbd0b37a5e2b2392ad95d17ae983e64727e18eb281fe7cb"}, + {file = "websockets-10.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e3a686ecb4aa0d64ae60c9c9f1a7d5d46cab9bfb5d91a2d303d00e2cd4c4c5cc"}, + {file = "websockets-10.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b0d15c968ea7a65211e084f523151dbf8ae44634de03c801b8bd070b74e85033"}, + {file = "websockets-10.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00213676a2e46b6ebf6045bc11d0f529d9120baa6f58d122b4021ad92adabd41"}, + {file = "websockets-10.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:e23173580d740bf8822fd0379e4bf30aa1d5a92a4f252d34e893070c081050df"}, + {file = "websockets-10.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:dd500e0a5e11969cdd3320935ca2ff1e936f2358f9c2e61f100a1660933320ea"}, + {file = "websockets-10.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:4239b6027e3d66a89446908ff3027d2737afc1a375f8fd3eea630a4842ec9a0c"}, + {file = "websockets-10.4-cp37-cp37m-win32.whl", hash = "sha256:8a5cc00546e0a701da4639aa0bbcb0ae2bb678c87f46da01ac2d789e1f2d2038"}, + {file = "websockets-10.4-cp37-cp37m-win_amd64.whl", hash = "sha256:a9f9a735deaf9a0cadc2d8c50d1a5bcdbae8b6e539c6e08237bc4082d7c13f28"}, + {file = "websockets-10.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5c1289596042fad2cdceb05e1ebf7aadf9995c928e0da2b7a4e99494953b1b94"}, + {file = "websockets-10.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0cff816f51fb33c26d6e2b16b5c7d48eaa31dae5488ace6aae468b361f422b63"}, + {file = "websockets-10.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:dd9becd5fe29773d140d68d607d66a38f60e31b86df75332703757ee645b6faf"}, + {file = "websockets-10.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45ec8e75b7dbc9539cbfafa570742fe4f676eb8b0d3694b67dabe2f2ceed8aa6"}, + {file = "websockets-10.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f72e5cd0f18f262f5da20efa9e241699e0cf3a766317a17392550c9ad7b37d8"}, + {file = "websockets-10.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:185929b4808b36a79c65b7865783b87b6841e852ef5407a2fb0c03381092fa3b"}, + {file = "websockets-10.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:7d27a7e34c313b3a7f91adcd05134315002aaf8540d7b4f90336beafaea6217c"}, + {file = "websockets-10.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:884be66c76a444c59f801ac13f40c76f176f1bfa815ef5b8ed44321e74f1600b"}, + {file = "websockets-10.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:931c039af54fc195fe6ad536fde4b0de04da9d5916e78e55405436348cfb0e56"}, + {file = "websockets-10.4-cp38-cp38-win32.whl", hash = "sha256:db3c336f9eda2532ec0fd8ea49fef7a8df8f6c804cdf4f39e5c5c0d4a4ad9a7a"}, + {file = "websockets-10.4-cp38-cp38-win_amd64.whl", hash = "sha256:48c08473563323f9c9debac781ecf66f94ad5a3680a38fe84dee5388cf5acaf6"}, + {file = "websockets-10.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:40e826de3085721dabc7cf9bfd41682dadc02286d8cf149b3ad05bff89311e4f"}, + {file = "websockets-10.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:56029457f219ade1f2fc12a6504ea61e14ee227a815531f9738e41203a429112"}, + {file = "websockets-10.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f5fc088b7a32f244c519a048c170f14cf2251b849ef0e20cbbb0fdf0fdaf556f"}, + {file = "websockets-10.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2fc8709c00704194213d45e455adc106ff9e87658297f72d544220e32029cd3d"}, + {file = "websockets-10.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0154f7691e4fe6c2b2bc275b5701e8b158dae92a1ab229e2b940efe11905dff4"}, + {file = "websockets-10.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c6d2264f485f0b53adf22697ac11e261ce84805c232ed5dbe6b1bcb84b00ff0"}, + {file = "websockets-10.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9bc42e8402dc5e9905fb8b9649f57efcb2056693b7e88faa8fb029256ba9c68c"}, + {file = "websockets-10.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:edc344de4dac1d89300a053ac973299e82d3db56330f3494905643bb68801269"}, + {file = "websockets-10.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:84bc2a7d075f32f6ed98652db3a680a17a4edb21ca7f80fe42e38753a58ee02b"}, + {file = "websockets-10.4-cp39-cp39-win32.whl", hash = "sha256:c94ae4faf2d09f7c81847c63843f84fe47bf6253c9d60b20f25edfd30fb12588"}, + {file = "websockets-10.4-cp39-cp39-win_amd64.whl", hash = "sha256:bbccd847aa0c3a69b5f691a84d2341a4f8a629c6922558f2a70611305f902d74"}, + {file = "websockets-10.4-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:82ff5e1cae4e855147fd57a2863376ed7454134c2bf49ec604dfe71e446e2193"}, + {file = "websockets-10.4-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d210abe51b5da0ffdbf7b43eed0cfdff8a55a1ab17abbec4301c9ff077dd0342"}, + {file = "websockets-10.4-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:942de28af58f352a6f588bc72490ae0f4ccd6dfc2bd3de5945b882a078e4e179"}, + {file = "websockets-10.4-pp37-pypy37_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9b27d6c1c6cd53dc93614967e9ce00ae7f864a2d9f99fe5ed86706e1ecbf485"}, + {file = "websockets-10.4-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:3d3cac3e32b2c8414f4f87c1b2ab686fa6284a980ba283617404377cd448f631"}, + {file = "websockets-10.4-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:da39dd03d130162deb63da51f6e66ed73032ae62e74aaccc4236e30edccddbb0"}, + {file = "websockets-10.4-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:389f8dbb5c489e305fb113ca1b6bdcdaa130923f77485db5b189de343a179393"}, + {file = "websockets-10.4-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09a1814bb15eff7069e51fed0826df0bc0702652b5cb8f87697d469d79c23576"}, + {file = "websockets-10.4-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff64a1d38d156d429404aaa84b27305e957fd10c30e5880d1765c9480bea490f"}, + {file = "websockets-10.4-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:b343f521b047493dc4022dd338fc6db9d9282658862756b4f6fd0e996c1380e1"}, + {file = "websockets-10.4-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:932af322458da7e4e35df32f050389e13d3d96b09d274b22a7aa1808f292fee4"}, + {file = "websockets-10.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d6a4162139374a49eb18ef5b2f4da1dd95c994588f5033d64e0bbfda4b6b6fcf"}, + {file = "websockets-10.4-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c57e4c1349fbe0e446c9fa7b19ed2f8a4417233b6984277cce392819123142d3"}, + {file = "websockets-10.4-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b627c266f295de9dea86bd1112ed3d5fafb69a348af30a2422e16590a8ecba13"}, + {file = "websockets-10.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:05a7233089f8bd355e8cbe127c2e8ca0b4ea55467861906b80d2ebc7db4d6b72"}, + {file = "websockets-10.4.tar.gz", hash = "sha256:eef610b23933c54d5d921c92578ae5f89813438fded840c2e9809d378dc765d3"}, +] + +[[package]] +name = "yarl" +version = "1.9.2" +description = "Yet another URL library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "yarl-1.9.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8c2ad583743d16ddbdf6bb14b5cd76bf43b0d0006e918809d5d4ddf7bde8dd82"}, + {file = "yarl-1.9.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:82aa6264b36c50acfb2424ad5ca537a2060ab6de158a5bd2a72a032cc75b9eb8"}, + {file = "yarl-1.9.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c0c77533b5ed4bcc38e943178ccae29b9bcf48ffd1063f5821192f23a1bd27b9"}, + {file = "yarl-1.9.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ee4afac41415d52d53a9833ebae7e32b344be72835bbb589018c9e938045a560"}, + {file = "yarl-1.9.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9bf345c3a4f5ba7f766430f97f9cc1320786f19584acc7086491f45524a551ac"}, + {file = "yarl-1.9.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2a96c19c52ff442a808c105901d0bdfd2e28575b3d5f82e2f5fd67e20dc5f4ea"}, + {file = "yarl-1.9.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:891c0e3ec5ec881541f6c5113d8df0315ce5440e244a716b95f2525b7b9f3608"}, + {file = "yarl-1.9.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c3a53ba34a636a256d767c086ceb111358876e1fb6b50dfc4d3f4951d40133d5"}, + {file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:566185e8ebc0898b11f8026447eacd02e46226716229cea8db37496c8cdd26e0"}, + {file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:2b0738fb871812722a0ac2154be1f049c6223b9f6f22eec352996b69775b36d4"}, + {file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:32f1d071b3f362c80f1a7d322bfd7b2d11e33d2adf395cc1dd4df36c9c243095"}, + {file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:e9fdc7ac0d42bc3ea78818557fab03af6181e076a2944f43c38684b4b6bed8e3"}, + {file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:56ff08ab5df8429901ebdc5d15941b59f6253393cb5da07b4170beefcf1b2528"}, + {file = "yarl-1.9.2-cp310-cp310-win32.whl", hash = "sha256:8ea48e0a2f931064469bdabca50c2f578b565fc446f302a79ba6cc0ee7f384d3"}, + {file = "yarl-1.9.2-cp310-cp310-win_amd64.whl", hash = "sha256:50f33040f3836e912ed16d212f6cc1efb3231a8a60526a407aeb66c1c1956dde"}, + {file = "yarl-1.9.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:646d663eb2232d7909e6601f1a9107e66f9791f290a1b3dc7057818fe44fc2b6"}, + {file = "yarl-1.9.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:aff634b15beff8902d1f918012fc2a42e0dbae6f469fce134c8a0dc51ca423bb"}, + {file = "yarl-1.9.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a83503934c6273806aed765035716216cc9ab4e0364f7f066227e1aaea90b8d0"}, + {file = "yarl-1.9.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b25322201585c69abc7b0e89e72790469f7dad90d26754717f3310bfe30331c2"}, + {file = "yarl-1.9.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:22a94666751778629f1ec4280b08eb11815783c63f52092a5953faf73be24191"}, + {file = "yarl-1.9.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ec53a0ea2a80c5cd1ab397925f94bff59222aa3cf9c6da938ce05c9ec20428d"}, + {file = "yarl-1.9.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:159d81f22d7a43e6eabc36d7194cb53f2f15f498dbbfa8edc8a3239350f59fe7"}, + {file = "yarl-1.9.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:832b7e711027c114d79dffb92576acd1bd2decc467dec60e1cac96912602d0e6"}, + {file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:95d2ecefbcf4e744ea952d073c6922e72ee650ffc79028eb1e320e732898d7e8"}, + {file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:d4e2c6d555e77b37288eaf45b8f60f0737c9efa3452c6c44626a5455aeb250b9"}, + {file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:783185c75c12a017cc345015ea359cc801c3b29a2966c2655cd12b233bf5a2be"}, + {file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:b8cc1863402472f16c600e3e93d542b7e7542a540f95c30afd472e8e549fc3f7"}, + {file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:822b30a0f22e588b32d3120f6d41e4ed021806418b4c9f0bc3048b8c8cb3f92a"}, + {file = "yarl-1.9.2-cp311-cp311-win32.whl", hash = "sha256:a60347f234c2212a9f0361955007fcf4033a75bf600a33c88a0a8e91af77c0e8"}, + {file = "yarl-1.9.2-cp311-cp311-win_amd64.whl", hash = "sha256:be6b3fdec5c62f2a67cb3f8c6dbf56bbf3f61c0f046f84645cd1ca73532ea051"}, + {file = "yarl-1.9.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:38a3928ae37558bc1b559f67410df446d1fbfa87318b124bf5032c31e3447b74"}, + {file = "yarl-1.9.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac9bb4c5ce3975aeac288cfcb5061ce60e0d14d92209e780c93954076c7c4367"}, + {file = "yarl-1.9.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3da8a678ca8b96c8606bbb8bfacd99a12ad5dd288bc6f7979baddd62f71c63ef"}, + {file = "yarl-1.9.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:13414591ff516e04fcdee8dc051c13fd3db13b673c7a4cb1350e6b2ad9639ad3"}, + {file = "yarl-1.9.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf74d08542c3a9ea97bb8f343d4fcbd4d8f91bba5ec9d5d7f792dbe727f88938"}, + {file = "yarl-1.9.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e7221580dc1db478464cfeef9b03b95c5852cc22894e418562997df0d074ccc"}, + {file = "yarl-1.9.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:494053246b119b041960ddcd20fd76224149cfea8ed8777b687358727911dd33"}, + {file = "yarl-1.9.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:52a25809fcbecfc63ac9ba0c0fb586f90837f5425edfd1ec9f3372b119585e45"}, + {file = "yarl-1.9.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:e65610c5792870d45d7b68c677681376fcf9cc1c289f23e8e8b39c1485384185"}, + {file = "yarl-1.9.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:1b1bba902cba32cdec51fca038fd53f8beee88b77efc373968d1ed021024cc04"}, + {file = "yarl-1.9.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:662e6016409828ee910f5d9602a2729a8a57d74b163c89a837de3fea050c7582"}, + {file = "yarl-1.9.2-cp37-cp37m-win32.whl", hash = "sha256:f364d3480bffd3aa566e886587eaca7c8c04d74f6e8933f3f2c996b7f09bee1b"}, + {file = "yarl-1.9.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6a5883464143ab3ae9ba68daae8e7c5c95b969462bbe42e2464d60e7e2698368"}, + {file = "yarl-1.9.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5610f80cf43b6202e2c33ba3ec2ee0a2884f8f423c8f4f62906731d876ef4fac"}, + {file = "yarl-1.9.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b9a4e67ad7b646cd6f0938c7ebfd60e481b7410f574c560e455e938d2da8e0f4"}, + {file = "yarl-1.9.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:83fcc480d7549ccebe9415d96d9263e2d4226798c37ebd18c930fce43dfb9574"}, + {file = "yarl-1.9.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5fcd436ea16fee7d4207c045b1e340020e58a2597301cfbcfdbe5abd2356c2fb"}, + {file = "yarl-1.9.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84e0b1599334b1e1478db01b756e55937d4614f8654311eb26012091be109d59"}, + {file = "yarl-1.9.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3458a24e4ea3fd8930e934c129b676c27452e4ebda80fbe47b56d8c6c7a63a9e"}, + {file = "yarl-1.9.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:838162460b3a08987546e881a2bfa573960bb559dfa739e7800ceeec92e64417"}, + {file = "yarl-1.9.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f4e2d08f07a3d7d3e12549052eb5ad3eab1c349c53ac51c209a0e5991bbada78"}, + {file = "yarl-1.9.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:de119f56f3c5f0e2fb4dee508531a32b069a5f2c6e827b272d1e0ff5ac040333"}, + {file = "yarl-1.9.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:149ddea5abf329752ea5051b61bd6c1d979e13fbf122d3a1f9f0c8be6cb6f63c"}, + {file = "yarl-1.9.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:674ca19cbee4a82c9f54e0d1eee28116e63bc6fd1e96c43031d11cbab8b2afd5"}, + {file = "yarl-1.9.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:9b3152f2f5677b997ae6c804b73da05a39daa6a9e85a512e0e6823d81cdad7cc"}, + {file = "yarl-1.9.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5415d5a4b080dc9612b1b63cba008db84e908b95848369aa1da3686ae27b6d2b"}, + {file = "yarl-1.9.2-cp38-cp38-win32.whl", hash = "sha256:f7a3d8146575e08c29ed1cd287068e6d02f1c7bdff8970db96683b9591b86ee7"}, + {file = "yarl-1.9.2-cp38-cp38-win_amd64.whl", hash = "sha256:63c48f6cef34e6319a74c727376e95626f84ea091f92c0250a98e53e62c77c72"}, + {file = "yarl-1.9.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:75df5ef94c3fdc393c6b19d80e6ef1ecc9ae2f4263c09cacb178d871c02a5ba9"}, + {file = "yarl-1.9.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c027a6e96ef77d401d8d5a5c8d6bc478e8042f1e448272e8d9752cb0aff8b5c8"}, + {file = "yarl-1.9.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f3b078dbe227f79be488ffcfc7a9edb3409d018e0952cf13f15fd6512847f3f7"}, + {file = "yarl-1.9.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59723a029760079b7d991a401386390c4be5bfec1e7dd83e25a6a0881859e716"}, + {file = "yarl-1.9.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b03917871bf859a81ccb180c9a2e6c1e04d2f6a51d953e6a5cdd70c93d4e5a2a"}, + {file = "yarl-1.9.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c1012fa63eb6c032f3ce5d2171c267992ae0c00b9e164efe4d73db818465fac3"}, + {file = "yarl-1.9.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a74dcbfe780e62f4b5a062714576f16c2f3493a0394e555ab141bf0d746bb955"}, + {file = "yarl-1.9.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8c56986609b057b4839968ba901944af91b8e92f1725d1a2d77cbac6972b9ed1"}, + {file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:2c315df3293cd521033533d242d15eab26583360b58f7ee5d9565f15fee1bef4"}, + {file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:b7232f8dfbd225d57340e441d8caf8652a6acd06b389ea2d3222b8bc89cbfca6"}, + {file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:53338749febd28935d55b41bf0bcc79d634881195a39f6b2f767870b72514caf"}, + {file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:066c163aec9d3d073dc9ffe5dd3ad05069bcb03fcaab8d221290ba99f9f69ee3"}, + {file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8288d7cd28f8119b07dd49b7230d6b4562f9b61ee9a4ab02221060d21136be80"}, + {file = "yarl-1.9.2-cp39-cp39-win32.whl", hash = "sha256:b124e2a6d223b65ba8768d5706d103280914d61f5cae3afbc50fc3dfcc016623"}, + {file = "yarl-1.9.2-cp39-cp39-win_amd64.whl", hash = "sha256:61016e7d582bc46a5378ffdd02cd0314fb8ba52f40f9cf4d9a5e7dbef88dee18"}, + {file = "yarl-1.9.2.tar.gz", hash = "sha256:04ab9d4b9f587c06d801c2abfe9317b77cdf996c65a90d5e84ecc45010823571"}, +] + +[package.dependencies] +idna = ">=2.0" +multidict = ">=4.0" + [[package]] name = "zipp" version = "3.16.2" @@ -1496,4 +2148,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "e6622977ab14b1baaf4d74f0b926c2df5dea4819e6b5f9d041d22006c6577df5" +content-hash = "25464669e127095f85549e8e14c539d64e02922838c766de7c24f8dfd71744a4" diff --git a/workflows/pyproject.toml b/workflows/pyproject.toml index 4981f10d..17bbf288 100644 --- a/workflows/pyproject.toml +++ b/workflows/pyproject.toml @@ -28,6 +28,8 @@ boto3 = "^1.28.12" semver = "^3.0.1" sgqlc = "^16.3" psycopg-binary = "^3.1.10" +gql = {extras = ["all"], version = "^3.4.1"} +pydantic = "^2.3.0" [build-system] diff --git a/workflows/sequence_manifest.json b/workflows/sequence_manifest.json new file mode 100644 index 00000000..c03f93e4 --- /dev/null +++ b/workflows/sequence_manifest.json @@ -0,0 +1,44 @@ +{ + "name": "static_sample", + "version": "1.0.0", + "type": "WDL", + "deprecated": false, + "description": "A workflow that takes a sequence and returns the first base", + "entity_inputs": [], + "workflow_inputs": [], + "input_loaders": [], + "workflow_outputs": [ + { + "name": "name", + "workflow_data_type": "String", + "description": "The name of the sample" + }, + { + "name": "location", + "workflow_data_type": "String", + "description": "The location of the sample" + } + ], + "entity_outputs": [{ + "name": "sample", + "entity_type": "sample", + "version": "1.0.0" + }], + "output_loaders": [ + { + "name": "sequence", + "version": "1.0.0", + "entity_output": "sample", + "fields": [{ + "name": "nucleotide", + "reference": "outputs.name" + }, { + "name": "sequence", + "reference": "outputs.name" + }, { + "name": "protocol", + "reference": "outputs.location" + }] + } + ] +} \ No newline at end of file diff --git a/workflows/test_workflows/contigs_squared/contigs_squared.wdl b/workflows/test_workflows/contigs_squared/contigs_squared.wdl new file mode 100644 index 00000000..2cd6b885 --- /dev/null +++ b/workflows/test_workflows/contigs_squared/contigs_squared.wdl @@ -0,0 +1,42 @@ +version 1.1 + +workflow contig_squared { + input { + String sequence + Int n_contigs = 10 + } + + scatter(i in range(n_contigs)) { + call repeat_sequence_contig { + input: + sequence=sequence, + n = n_contigs, + } + } + + output { + String contigs = repeat_sequence_contig.contig[0] + } +} + +task repeat_sequence_contig { + input { + String sequence + Int n + } + + command <<< + for i in {1..~{n}}; do + echo -n ~{sequence} >> contig.fa + done + + >>> + + output { + String contig = read_string("contig.fa") + } + + runtime { + docker: "ubuntu" + } +} diff --git a/workflows/test_workflows/first_sequence/manifest.yml b/workflows/test_workflows/first_sequence/manifest.yml index bc90dd65..f51d0084 100644 --- a/workflows/test_workflows/first_sequence/manifest.yml +++ b/workflows/test_workflows/first_sequence/manifest.yml @@ -1,20 +1,21 @@ name: first_sequence version: 0.0.1 type: WDL -beta: false deprecated: false description: Get the first id and sequence in a single-line fasta file -inputs: +workflow_inputs: - name: sequences - entity_type: fasta - required: true - description: "fastq file to ge the stats of" -outputs: -- name: id version: 0.0.1 +input_entities: +- input_name: sequences + entities: + - name: 'First Sequence' + entity_type: sequence + description: "fastq file to ge the stats of" +workflow_outputs: - name: sequence version: 0.0.1 -entities: +output_entities: - name: 'First Sequence' type: sequence outputs: diff --git a/workflows/test_workflows/foo.fa b/workflows/test_workflows/foo.fa new file mode 100644 index 00000000..ebc328c2 --- /dev/null +++ b/workflows/test_workflows/foo.fa @@ -0,0 +1,2 @@ +>my_id +ACTG \ No newline at end of file diff --git a/workflows/test_workflows/static_sample/static_sample.wdl b/workflows/test_workflows/static_sample/static_sample.wdl new file mode 100644 index 00000000..f2094e29 --- /dev/null +++ b/workflows/test_workflows/static_sample/static_sample.wdl @@ -0,0 +1,38 @@ +version 1.1 + +workflow static_sample { + input {} + + call echo as echo_name { + input: + str = "My Sample", + } + + call echo as echo_locaton { + input: + str = "Mars", + } + + output { + String name = echo_name.out + String location = echo_locaton.out + } +} + +task echo { + input { + String str + } + + command <<< + echo ~{str} + >>> + + output { + String out = read_string(stdout()) + } + + runtime { + docker: "ubuntu" + } +} diff --git a/workflows/version.py b/workflows/version.py new file mode 100644 index 00000000..72335702 --- /dev/null +++ b/workflows/version.py @@ -0,0 +1,224 @@ +from typing import Dict, List, Literal, Optional +from semver import Version +from dataclasses import asdict, dataclass, field + +from entity_interface import Entity + +WorkflowDataType = str +EntityType = str + +@dataclass +class EntityTypeConstraint: + entity_type: EntityType + description: Optional[str] = None + min_version: Optional[Version] = None + max_version: Optional[Version] = None + # TODO: add metadata + + def to_dict(self): + _dict = asdict(self) + if _dict["min_version"]: + _dict["min_version"] = str(_dict["min_version"]) + if _dict["max_version"]: + _dict["max_version"] = str(_dict["max_version"]) + return _dict + + def satisfies(self, entity: Entity) -> bool: + if entity.entity_type != self.entity_type: + return False + if entity.version is None: + return True + if self.min_version is not None and entity.version < self.min_version: + return False + if self.max_version is not None and entity.version > self.max_version: + return False + return True + +@dataclass +class WorkflowTypeAnnotation: + name: str + version: Version + metadata: Dict[str, str] = field(default_factory=dict) + +@dataclass +class WorkflowData: + workflow_data_type: WorkflowDataType + description: Optional[str] = None + version: Version = field(default_factory=lambda: Version(0)) + type_annotation: Optional[WorkflowTypeAnnotation] = None + + def to_dict(self): + _dict = asdict(self) + _dict["version"] = str(_dict["version"]) + return _dict + +@dataclass +class WorkflowInput: + data: WorkflowData + required: bool = False + +@dataclass +class InputLoader: + name: str + version: Version + workflow_input: WorkflowInput + entity_inputs: Dict[str, EntityTypeConstraint] = field(default_factory=dict) + + def to_dict(self): + _dict = asdict(self) + _dict["version"] = str(_dict["version"]) + return _dict + +@dataclass +class EntityOutput: + entity_type: EntityType + version: Version = field(default_factory=lambda: Version(0)) + +@dataclass +class WorkflowOutput(WorkflowData): + pass + +@dataclass +class OutputLoader: + name: str + version: Version + entity_output: EntityOutput + workflow_outputs: Dict[str, WorkflowOutput] = field(default_factory=dict) + + def to_dict(self): + _dict = asdict(self) + _dict["version"] = str(_dict["version"]) + return _dict + +@dataclass +class WorkflowVersion: + name: str + version: Version + type: Literal["WDL"] + deprecated: bool + description: str + entity_inputs: Dict[str, EntityTypeConstraint] = field(default_factory=dict) + workflow_inputs: Dict[str, WorkflowInput] = field(default_factory=dict) + input_loaders: List[InputLoader] = field(default_factory=list) + workflow_outputs: Dict[str, WorkflowOutput] = field(default_factory=dict) + entity_outputs: Dict[str, EntityOutput] = field(default_factory=dict) + output_loaders: List[OutputLoader] = field(default_factory=list) + +fasta_entity = EntityTypeConstraint( + entity_type="fasta", + description="A fasta file", +) + +fasta_workflow_input = WorkflowInput( + data=WorkflowData( + workflow_data_type="File", + description="A fasta file", + version=Version(1, 0, 0), + type_annotation=WorkflowTypeAnnotation( + name="fasta", + version=Version(1, 0, 0), + ), + ), + required=True, +) + +sequence_id_workflow_output = WorkflowOutput( + workflow_data_type="String", + description="The id of the first sequence", +) + +sequence_workflow_output = WorkflowOutput( + workflow_data_type="String", + description="The first sequence", +) + +sequence_entity_output = EntityOutput( + entity_type="Sequence", + version=Version(1, 0, 0), +) + +first_sequence = WorkflowVersion( + name="first_sequence", + version=Version(1, 0, 0), + type="WDL", + deprecated=False, + description="A workflow that takes a sequence and returns the first base", + entity_inputs={ + "fasta": fasta_entity, + }, + workflow_inputs={ + "sequences": fasta_workflow_input, + }, + input_loaders=[ + InputLoader( + name="fasta", + version=Version(1, 0, 0), + workflow_input=fasta_workflow_input, + entity_inputs={ + "fasta": fasta_entity, + }, + ), + ], + workflow_outputs={ + "id": sequence_id_workflow_output, + "sequence": sequence_workflow_output, + }, + entity_outputs={ + "sequence": sequence_entity_output, + }, + output_loaders=[ + OutputLoader( + name="sequence", + version=Version(1, 0, 0), + entity_output=sequence_entity_output, + workflow_outputs={ + "id": sequence_id_workflow_output, + "sequence": sequence_workflow_output, + }, + ), + ], +) + +name_workflow_output = WorkflowOutput( + workflow_data_type="String", + description="The name of the sample", +) + +location_workflow_output = WorkflowOutput( + workflow_data_type="String", + description="The location of the sample", +) + +sample_entity_output = EntityOutput( + entity_type="sample", + version=Version(0, 0, 0), +) + +static_sample = WorkflowVersion( + name="static_sample", + version=Version(1, 0, 0), + type="WDL", + deprecated=False, + description="A workflow that takes a sequence and returns the first base", + entity_inputs={}, + workflow_inputs={}, + input_loaders=[], + workflow_outputs={ + "name": name_workflow_output, + "location": location_workflow_output, + }, + entity_outputs={ + "sample": sample_entity_output, + }, + output_loaders=[ + OutputLoader( + name="sample", + version=Version(1, 0, 0), + entity_output=sample_entity_output, + workflow_outputs={ + "name": name_workflow_output, + "location": location_workflow_output, + }, + ) + ], +) \ No newline at end of file