Skip to content

Commit

Permalink
Extend entrypoint parameter (#77)
Browse files Browse the repository at this point in the history
* Add Entrypoint class and update CodeContent model to reference Entrypoint.

Allows distinction between different types of plain code executions and more freedom in defining how it should be run.

* Update `entrypoint` field to also accept strings for backwards compatibility.

* Use Union for Python 3.9 compatibility

* Remove Entrypoint model and integrate some fields into CodeContent

* Extend parsing of `ProgramMessage` to account for default values

Default values in `ProgramContent` could make `item_content` verification fail, as they get added upon parsing. Creating `item_content` now triggers a parsing of `ProgramContent` and dumps its resulting struct.

* Fix formatting

* Fix typing

* Format with isort & black

* Fix rebase

* Fix typing for mypy

* Change all new fields to have None default values to not break backward compatibility
  • Loading branch information
MHHukiewitz authored Jan 2, 2024
1 parent 1f5e910 commit 92ad3e4
Show file tree
Hide file tree
Showing 5 changed files with 41 additions and 28 deletions.
20 changes: 7 additions & 13 deletions aleph_message/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,7 @@
from hashlib import sha256
from json import JSONDecodeError
from pathlib import Path
from typing import (
Any,
Dict,
List,
Literal,
Optional,
Type,
Union, TypeVar, cast,
)
from typing import Any, Dict, List, Literal, Optional, Type, TypeVar, Union, cast

from pydantic import BaseModel, Extra, Field, validator
from typing_extensions import TypeAlias
Expand Down Expand Up @@ -174,7 +166,9 @@ class BaseMessage(BaseModel):
size: Optional[int] = Field(
default=None, description="Size of the content"
) # Almost always present
time: datetime.datetime = Field(description="Unix timestamp or datetime when the message was published")
time: datetime.datetime = Field(
description="Unix timestamp or datetime when the message was published"
)
item_type: ItemType = Field(description="Storage method used for the content")
item_content: Optional[str] = Field(
default=None,
Expand All @@ -189,7 +183,7 @@ class BaseMessage(BaseModel):
forgotten_by: Optional[List[str]]

@validator("item_content")
def check_item_content(cls, v: Optional[str], values):
def check_item_content(cls, v: Optional[str], values) -> Optional[str]:
item_type = values["item_type"]
if v is None:
return None
Expand All @@ -207,7 +201,7 @@ def check_item_content(cls, v: Optional[str], values):
return v

@validator("item_hash")
def check_item_hash(cls, v, values):
def check_item_hash(cls, v: ItemHash, values) -> ItemHash:
item_type = values["item_type"]
if item_type == ItemType.inline:
item_content: str = values["item_content"]
Expand Down Expand Up @@ -313,7 +307,7 @@ class InstanceMessage(BaseMessage):
]


T = TypeVar('T', bound=AlephMessage)
T = TypeVar("T", bound=AlephMessage)

AlephMessageType: TypeAlias = Type[T]

Expand Down
8 changes: 8 additions & 0 deletions aleph_message/models/execution/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,11 @@ class Payment(HashableModel):
@property
def is_stream(self):
return self.type == PaymentType.superfluid


class Interface(str, Enum):
"""Two types of program interfaces supported:
Running plain binary and ASGI apps."""

asgi = "asgi"
binary = "binary"
8 changes: 3 additions & 5 deletions aleph_message/models/execution/instance.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
from __future__ import annotations

from typing import Any, Dict

from pydantic import Field

from aleph_message.models.abstract import HashableModel
from aleph_message.models.item_hash import ItemHash

from .abstract import BaseExecutableContent
from .volume import VolumePersistence, PersistentVolumeSizeMib, ParentVolume
from .volume import ParentVolume, PersistentVolumeSizeMib, VolumePersistence


class RootfsVolume(HashableModel):
Expand All @@ -17,6 +15,7 @@ class RootfsVolume(HashableModel):
The root file system of an instance is built as a copy of a reference image, named parent
image. The user determines a custom size and persistence model.
"""

parent: ParentVolume
persistence: VolumePersistence
# Use the same size constraint as persistent volumes for now
Expand All @@ -29,4 +28,3 @@ class InstanceContent(BaseExecutableContent):
rootfs: RootfsVolume = Field(
description="Root filesystem of the system, will be booted by the kernel"
)

20 changes: 16 additions & 4 deletions aleph_message/models/execution/program.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
from __future__ import annotations

from typing import Literal, Optional
from typing import Literal, Optional, Union

from pydantic import Field
from pydantic import Field, validator

from .environment import FunctionTriggers
from ..abstract import HashableModel
from ..item_hash import ItemHash
from .abstract import BaseExecutableContent
from .base import Encoding, MachineType
from .base import Encoding, Interface, MachineType
from .environment import FunctionTriggers


class FunctionRuntime(HashableModel):
Expand All @@ -23,8 +23,20 @@ class CodeContent(HashableModel):
encoding: Encoding
entrypoint: str
ref: ItemHash # Must reference a StoreMessage
interface: Optional[Interface] = None
args: Optional[list[str]] = None
use_latest: bool = False

@property
def inferred_interface(self) -> Interface:
"""The initial behaviour is to use asgi, if there is a semicolon in the entrypoint. Else, assume its a binary on port 8000."""
if self.interface:
return self.interface
elif ":" in self.entrypoint:
return Interface.asgi
else:
return Interface.binary


class DataContent(HashableModel):
"""Reference to the StoreMessage that contains the input data of a program."""
Expand Down
13 changes: 7 additions & 6 deletions aleph_message/tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,13 @@
MessageType,
PostContent,
PostMessage,
ProgramContent,
ProgramMessage,
add_item_content_and_hash,
create_message_from_file,
create_message_from_json,
create_new_message,
parse_message, AlephMessage,
parse_message,
)
from aleph_message.tests.download_messages import MESSAGES_STORAGE_PATH

Expand Down Expand Up @@ -87,7 +88,7 @@ def test_messages_last_page():
if message_dict["item_hash"] in HASHES_TO_IGNORE:
continue

message: AlephMessage = parse_message(message_dict)
message = parse_message(message_dict)
assert message


Expand Down Expand Up @@ -285,14 +286,14 @@ def test_create_new_message():
"signature": "0x123456789", # Signature validation requires using aleph-client
}

new_message_1: PostMessage = create_new_message(message_dict, factory=PostMessage)
new_message_1 = create_new_message(message_dict, factory=PostMessage)
assert new_message_1
assert new_message_1.type == MessageType.post
# Check that the time was converted to a datetime
assert new_message_1.time.isoformat() == '2021-07-07T10:04:47.017000+00:00'
assert new_message_1.time.isoformat() == "2021-07-07T10:04:47.017000+00:00"

# The time field can be either a float or a datetime as string
message_dict["time"] = '2021-07-07T10:04:47.017000+00:00'
message_dict["time"] = "2021-07-07T10:04:47.017000+00:00"
new_message_2 = create_message_from_json(
json.dumps(message_dict), factory=PostMessage
)
Expand All @@ -308,7 +309,7 @@ def test_messages_from_disk():
data_dict = json.load(page_fd)
for message_dict in data_dict["messages"]:
try:
message: AlephMessage = parse_message(message_dict)
message = parse_message(message_dict)
assert message
except ValidationError as e:
console.print("-" * 79)
Expand Down

0 comments on commit 92ad3e4

Please sign in to comment.