Skip to content

Commit b014be7

Browse files
authored
✨ Enhancement/allow node port to get set value (#2605)
* allow getting/setting value link without downloading/uploading directly * allow for using codestyle
1 parent 50b5eb2 commit b014be7

File tree

18 files changed

+320
-107
lines changed

18 files changed

+320
-107
lines changed

packages/models-library/src/models_library/settings/rabbit.py

+11-18
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import warnings
2-
from typing import Dict, Optional
2+
from typing import Dict
33

4-
from pydantic import BaseSettings, Extra, validator
4+
from pydantic import BaseSettings, Extra
55
from pydantic.networks import AnyUrl
66
from pydantic.types import PositiveInt, SecretStr
77

@@ -25,28 +25,21 @@ class RabbitConfig(BaseSettings):
2525
user: str = "simcore"
2626
password: SecretStr = SecretStr("simcore")
2727

28-
dsn: Optional[RabbitDsn] = None
29-
3028
# channels
3129
channels: Dict[str, str] = {
3230
"log": "comp.backend.channels.log",
3331
"instrumentation": "comp.backend.channels.instrumentation",
3432
}
3533

36-
@validator("dsn", pre=True)
37-
@classmethod
38-
def autofill_dsn(cls, v, values):
39-
if not v and all(
40-
key in values for key in cls.__fields__ if key not in ["dsn", "channels"]
41-
):
42-
return RabbitDsn.build(
43-
scheme="amqp",
44-
user=values["user"],
45-
password=values["password"].get_secret_value(),
46-
host=values["host"],
47-
port=f"{values['port']}",
48-
)
49-
return v
34+
@property
35+
def dsn(self) -> str:
36+
return RabbitDsn.build(
37+
scheme="amqp",
38+
user=self.user,
39+
password=self.password.get_secret_value(),
40+
host=self.host,
41+
port=f"{self.port}",
42+
)
5043

5144
class Config:
5245
env_prefix = "RABBIT_"

packages/pytest-simcore/src/pytest_simcore/rabbit_service.py

+10-11
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,15 @@
66
import json
77
import logging
88
import socket
9-
from typing import Any, Dict, Optional, Tuple
9+
from typing import Any, Dict, Iterable, Optional, Tuple
1010

1111
import aio_pika
1212
import pytest
1313
import tenacity
1414
from models_library.settings.rabbit import RabbitConfig
15+
from tenacity.before_sleep import before_sleep_log
16+
from tenacity.stop import stop_after_attempt
17+
from tenacity.wait import wait_fixed
1518

1619
from .helpers.utils_docker import get_service_published_port
1720

@@ -31,16 +34,12 @@ async def rabbit_config(
3134
) -> RabbitConfig:
3235
prefix = testing_environ_vars["SWARM_STACK_NAME"]
3336
assert f"{prefix}_rabbit" in docker_stack["services"]
34-
3537
rabbit_config = RabbitConfig(
3638
user=testing_environ_vars["RABBIT_USER"],
3739
password=testing_environ_vars["RABBIT_PASSWORD"],
3840
host="127.0.0.1",
3941
port=get_service_published_port("rabbit", testing_environ_vars["RABBIT_PORT"]),
40-
channels={
41-
"log": "logs_channel",
42-
"instrumentation": "instrumentation_channel",
43-
},
42+
channels=json.loads(testing_environ_vars["RABBIT_CHANNELS"]),
4443
)
4544

4645
url = rabbit_config.dsn
@@ -57,7 +56,7 @@ async def rabbit_service(rabbit_config: RabbitConfig, monkeypatch) -> RabbitConf
5756
monkeypatch.setenv("RABBIT_PASSWORD", rabbit_config.password.get_secret_value())
5857
monkeypatch.setenv("RABBIT_CHANNELS", json.dumps(rabbit_config.channels))
5958

60-
return RabbitConfig
59+
return rabbit_config
6160

6261

6362
@pytest.fixture(scope="function")
@@ -126,7 +125,7 @@ async def rabbit_exchange(
126125
async def rabbit_queue(
127126
rabbit_channel: aio_pika.Channel,
128127
rabbit_exchange: Tuple[aio_pika.Exchange, aio_pika.Exchange],
129-
) -> aio_pika.Queue:
128+
) -> Iterable[aio_pika.Queue]:
130129
(logs_exchange, instrumentation_exchange) = rabbit_exchange
131130
# declare queue
132131
queue = await rabbit_channel.declare_queue(exclusive=True)
@@ -141,9 +140,9 @@ async def rabbit_queue(
141140

142141

143142
@tenacity.retry(
144-
wait=tenacity.wait_fixed(5),
145-
stop=tenacity.stop_after_attempt(60),
146-
before_sleep=tenacity.before_sleep_log(log, logging.INFO),
143+
wait=wait_fixed(5),
144+
stop=stop_after_attempt(60),
145+
before_sleep=before_sleep_log(log, logging.INFO),
147146
reraise=True,
148147
)
149148
async def wait_till_rabbit_responsive(url: str) -> None:

packages/pytest-simcore/src/pytest_simcore/simcore_storage_service.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
# pylint:disable=redefined-outer-name
44
import os
55
from copy import deepcopy
6-
from typing import Dict
6+
from typing import Dict, Iterable
77

88
import aiohttp
99
import pytest
@@ -16,7 +16,7 @@
1616

1717

1818
@pytest.fixture(scope="module")
19-
def storage_endpoint(docker_stack: Dict, testing_environ_vars: Dict) -> URL:
19+
def storage_endpoint(docker_stack: Dict, testing_environ_vars: Dict) -> Iterable[URL]:
2020
prefix = testing_environ_vars["SWARM_STACK_NAME"]
2121
assert f"{prefix}_storage" in docker_stack["services"]
2222

@@ -39,7 +39,7 @@ async def storage_service(
3939
) -> URL:
4040
await wait_till_storage_responsive(storage_endpoint)
4141

42-
yield storage_endpoint
42+
return storage_endpoint
4343

4444

4545
# HELPERS --

packages/pytest-simcore/src/pytest_simcore/websocket_client.py

+9-4
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
# pylint:disable=unused-argument
33
# pylint:disable=redefined-outer-name
44

5-
from typing import Callable, Optional
5+
from typing import AsyncIterable, Awaitable, Callable, Iterable, Optional
66
from uuid import uuid4
77

88
import pytest
@@ -23,7 +23,7 @@ def create() -> str:
2323

2424

2525
@pytest.fixture()
26-
def socketio_url_factory(client) -> Callable:
26+
def socketio_url_factory(client) -> Iterable[Callable[[Optional[TestClient]], str]]:
2727
def create_url(client_override: Optional[TestClient] = None) -> str:
2828
SOCKET_IO_PATH = "/socket.io/"
2929
return str((client_override or client).make_url(SOCKET_IO_PATH))
@@ -32,7 +32,9 @@ def create_url(client_override: Optional[TestClient] = None) -> str:
3232

3333

3434
@pytest.fixture()
35-
async def security_cookie_factory(client: TestClient) -> Callable:
35+
async def security_cookie_factory(
36+
client: TestClient,
37+
) -> AsyncIterable[Callable[[Optional[TestClient]], Awaitable[str]]]:
3638
async def creator(client_override: Optional[TestClient] = None) -> str:
3739
# get the cookie by calling the root entrypoint
3840
resp = await (client_override or client).get("/v0/")
@@ -55,7 +57,9 @@ async def socketio_client_factory(
5557
socketio_url_factory: Callable,
5658
security_cookie_factory: Callable,
5759
client_session_id_factory: Callable,
58-
) -> Callable[[str, Optional[TestClient]], socketio.AsyncClient]:
60+
) -> AsyncIterable[
61+
Callable[[Optional[str], Optional[TestClient]], Awaitable[socketio.AsyncClient]]
62+
]:
5963
clients = []
6064

6165
async def connect(
@@ -67,6 +71,7 @@ async def connect(
6771

6872
sio = socketio.AsyncClient(ssl_verify=False)
6973
# enginio 3.10.0 introduced ssl verification
74+
assert client_session_id
7075
url = str(
7176
URL(socketio_url_factory(client)).with_query(
7277
{"client_session_id": client_session_id}

packages/service-library/src/servicelib/archiving_utils.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ def _full_file_path_from_dir_and_subdirs(dir_path: Path) -> Iterator[Path]:
2222

2323

2424
def _strip_directory_from_path(input_path: Path, to_strip: Path) -> Path:
25-
to_strip = f"{str(to_strip)}/"
26-
return Path(str(input_path).replace(to_strip, ""))
25+
_to_strip = f"{str(to_strip)}/"
26+
return Path(str(input_path).replace(_to_strip, ""))
2727

2828

2929
def _read_in_chunks(file_object, chunk_size=1024 * 8):

packages/settings-library/src/settings_library/logging_utils.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import logging
22
from functools import cached_property
3+
from typing import Any
34

45

56
class MixinLoggingSettings:
67
@classmethod
7-
def validate_log_level(cls, value) -> str:
8+
def validate_log_level(cls, value: Any) -> str:
89
try:
910
getattr(logging, value.upper())
1011
except AttributeError as err:

packages/simcore-sdk/Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
# by sanderegg, pcrespov
99
#
1010
include ../../scripts/common.Makefile
11+
include ../../scripts/common-package.Makefile
1112

1213

1314
.PHONY: install-dev install-prod install-ci

packages/simcore-sdk/src/simcore_sdk/node_ports_v2/links.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,15 @@
33

44
from models_library.projects_nodes_io import UUID_REGEX, BaseFileLink, DownloadLink
55
from models_library.projects_nodes_io import PortLink as BasePortLink
6-
from pydantic import Extra, Field, StrictBool, StrictFloat, StrictInt, StrictStr
6+
from pydantic import AnyUrl, Extra, Field, StrictBool, StrictFloat, StrictInt, StrictStr
77

88

99
class PortLink(BasePortLink):
1010
node_uuid: str = Field(..., regex=UUID_REGEX, alias="nodeUuid")
1111

1212

1313
class FileLink(BaseFileLink):
14-
""" allow all kind of file links """
14+
"""allow all kind of file links"""
1515

1616
class Config:
1717
extra = Extra.allow
@@ -22,5 +22,6 @@ class Config:
2222
]
2323

2424
ItemConcreteValue = Union[int, float, bool, str, Path]
25+
ItemValue = Union[int, float, bool, str, AnyUrl]
2526

2627
__all__ = ["FileLink", "DownloadLink", "PortLink", "DataItemValue", "ItemConcreteValue"]

packages/simcore-sdk/src/simcore_sdk/node_ports_v2/nodeports_v2.py

+12-3
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
import logging
22
from collections import deque
33
from pathlib import Path
4-
from typing import Any, Callable, Coroutine, Dict, Type
4+
from typing import Any, Callable, Coroutine, Dict, Optional, Type
55

66
from pydantic import BaseModel, Field
77
from servicelib.utils import logged_gather
88

99
from ..node_ports_common.dbmanager import DBManager
1010
from ..node_ports_common.exceptions import PortNotFound, UnboundPortError
11-
from .links import ItemConcreteValue
11+
from .links import ItemConcreteValue, ItemValue
1212
from .port_utils import is_file_type
1313
from .ports_mapping import InputsList, OutputsList
1414

@@ -55,7 +55,16 @@ async def outputs(self) -> OutputsList:
5555
await self._auto_update_from_db()
5656
return self.internal_outputs
5757

58-
async def get(self, item_key: str) -> ItemConcreteValue:
58+
async def get_value_link(self, item_key: str) -> Optional[ItemValue]:
59+
try:
60+
return await (await self.inputs)[item_key].get_value()
61+
except UnboundPortError:
62+
# not available try outputs
63+
pass
64+
# if this fails it will raise an exception
65+
return await (await self.outputs)[item_key].get_value()
66+
67+
async def get(self, item_key: str) -> Optional[ItemConcreteValue]:
5968
try:
6069
return await (await self.inputs)[item_key].get()
6170
except UnboundPortError:

0 commit comments

Comments
 (0)