Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support "Operations" in SimPhoNy #783

Merged
merged 8 commits into from
Jun 3, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@
"Dataspace = simphony_osp.interfaces.dataspace:Dataspace",
"Remote = simphony_osp.interfaces.remote:Remote",
},
"simphony_osp.ontology.operations": {
"File = simphony_osp.ontology.operations.file:File",
"Container = simphony_osp.ontology.operations.container:Container",
},
"console_scripts": {
"pico = simphony_osp.tools.pico:terminal",
"semantic2dot = simphony_osp.tools.semantic2dot"
Expand Down
3 changes: 2 additions & 1 deletion simphony_osp/develop.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""Developer tools."""

from simphony_osp.interfaces.interface import Interface as Wrapper
from simphony_osp.ontology.operations import Operations, find_operations

__all__ = ["Wrapper"]
__all__ = ["Operations", "Wrapper", "find_operations"]
8 changes: 5 additions & 3 deletions simphony_osp/ontology/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@
from simphony_osp.utils.datatypes import UID, Triple

if TYPE_CHECKING:
from simphony_osp.ontology.interactive.container import Container
from simphony_osp.ontology.namespace import OntologyNamespace
from simphony_osp.ontology.operations.container import Container
from simphony_osp.session.session import Session
from simphony_osp.session.wrapper import Wrapper

Expand Down Expand Up @@ -445,12 +445,14 @@ def __init__(
from simphony_osp.session.wrapper import Wrapper

if session is None:
from simphony_osp.ontology.interactive.container import Container
from simphony_osp.ontology.operations.container import (
ContainerEnvironment,
)
from simphony_osp.session.session import Environment, Session

environment = Environment.get_default_environment()
session = Session.get_default_session()
if isinstance(environment, Container):
if isinstance(environment, ContainerEnvironment):
environment.connect(self.identifier)
elif isinstance(session, Wrapper):
session = session.session
Expand Down
78 changes: 78 additions & 0 deletions simphony_osp/ontology/individual.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
from simphony_osp.ontology.attribute import OntologyAttribute
from simphony_osp.ontology.entity import OntologyEntity
from simphony_osp.ontology.oclass import OntologyClass
from simphony_osp.ontology.operations.operations import OperationsNamespace
from simphony_osp.ontology.relationship import OntologyRelationship
from simphony_osp.ontology.utils import DataStructureSet
from simphony_osp.utils.datatypes import (
Expand All @@ -41,8 +42,10 @@
RelationshipValue,
Triple,
)
from simphony_osp.utils.simphony_namespace import simphony_namespace

if TYPE_CHECKING:
from simphony_osp.ontology.operations.container import ContainerEnvironment
from simphony_osp.session.session import Session

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -1659,9 +1662,84 @@ def iterator() -> Iterator[
iterator = iterator()
return iterator

@property
def operations(self) -> OperationsNamespace:
"""Access operations specific this individual's class."""
if self._operations_namespace is None:
self._operations_namespace = OperationsNamespace(individual=self)
return self._operations_namespace

def __enter__(self) -> ContainerEnvironment:
"""Use an ontology individual as a context manager.

At the moment, individuals cannot be used as a context manager,
except for containers.

Returns:
A `ContainerEnvironment` object when a container is used as a
context manager. `ContainerEnvironment` objects have the same
functionality as the operations defined for a container,
but those can be called directly.

Raises:
AttributeError: When an ontology individual which does not
belong to the `Container` subclass is used as a context
manager.
"""
classes = set(class_.identifier for class_ in self.superclasses)

if simphony_namespace.Container in classes:
# Triggers the creation of the operations instance and thus the
# environment.
environment = self.operations.environment
self._operations_context = environment
return environment.__enter__()

raise AttributeError("__enter__")

def __exit__(self, *args):
"""Leave the individual's context manager when used as such.

At the moment, individuals cannot be used as a context manager,
except for containers.

Raises:
AttributeError: When the method `__enter__` has not been called
on the individual before.
"""
if self._operations_context is not None:
context_return = self._operations_context.__exit__(*args)
self._operations_context = None
return context_return

raise AttributeError("__exit__")

# ↑ ------ ↑
# Public API

_operations_namespace: Optional[OperationsNamespace] = None
"""Holds the operations namespace instance for this ontology individual.

The namespace in turns holds the instances of any subclasses of
`Operation` that were defined and compatible with the classes of the
individual.
"""

_operations_context: Optional[ContainerEnvironment] = None
"""Stores the current context object.

Some individuals (currently only containers) can be used as context
managers through the operations API. The way this works is the
following: when the `with` statement is used, an operation on the
individual is called that retrieves the actual context manager object
(which is not an individual) and calls the `__enter__` method from it.

This context manager object needs to be stored somewhere so that when
the individual context manager is exited, the actual context manager
object's `__exit__` method is also called. This is the purpose of this
attribute.
"""

def _attribute_autocompletion(self) -> Iterable[str]:
"""Compute individual's attributes as autocompletion suggestions."""
result = iter(())
Expand Down
6 changes: 0 additions & 6 deletions simphony_osp/ontology/interactive/__init__.py

This file was deleted.

64 changes: 0 additions & 64 deletions simphony_osp/ontology/interactive/file.py

This file was deleted.

25 changes: 0 additions & 25 deletions simphony_osp/ontology/oclass.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,33 +142,8 @@ def __call__(
or UID()
)

from simphony_osp.namespaces import simphony
from simphony_osp.ontology.individual import OntologyIndividual

if self.is_subclass_of(simphony.Container):
from simphony_osp.ontology.interactive.container import Container

result = Container(
uid=uid,
session=session,
attributes=self._kwargs_to_attributes(
kwargs, _skip_checks=_force
),
)
return result
elif self.is_subclass_of(simphony.File):
from simphony_osp.ontology.interactive.file import File

result = File(
uid=uid,
session=session,
attributes=self._kwargs_to_attributes(
kwargs, _skip_checks=_force
),
)
return result
# TODO: Multiclass individuals.

# build attributes dictionary by combining
# kwargs and defaults
return OntologyIndividual(
Expand Down
13 changes: 13 additions & 0 deletions simphony_osp/ontology/operations/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
"""Module defining operations for ontology individuals.

Interactive ontology individuals add additional functionality to individuals of
a specific class. For example, SimPhoNY Files have a method to download or
upload an associated file object.
"""

from simphony_osp.ontology.operations.operations import (
Operations,
find_operations,
)

__all__ = ["Operations", "find_operations"]
Loading