{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Tutorial: Sessions and variables\n", "In this tutorial we will explain how objects and the variables assigned to them behave with different sessions. This tutorial is not available on binder since `wrapper_x` and `wrapper_y` are fictional implementations for educational purposes.\n", "\n", "## Background\n", "Some use cases of OSP-core, like coupling and linking, require interaction between multiple sessions. Even using just one wrapper usually means working with the default CoreSession initially, and then the wrapper session.\n", "\n", "When an object from one session is added to a different one, a deepcopy of the object is carried out. This means that both objects are technically the same at that point (same uid, oclass, relationships...), but reside in different memory areas and can theoretically differ in the future. The purpose of this behaviour is to allow selective synching and enable different sessions to have different states of an instance.\n", "\n", "Variables that point to an object in the origin session will keep pointing to it (and not to the added session) unless explicitedly updated.\n", "\n", "Here we will try to explain said behaviour with simple illustrative examples.\n", "\n", "**Note:** This tutorial is not meant to be run like the others. The session classes and ontology entities are not real implementations. However, the behaviour shown will be the same as that of a real setup." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Let's show an example\n", "We start by importing the necessary components, namely the session and a sample namespace:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from osp.core.namespaces import namespace\n", "from osp.wrappers.wrapper_x import SessionX\n", "from osp.wrappers.wrapper_y import SessionY" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can now instantiate the Session objects and bind them to wrapper instances:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sess_x = SessionX()\n", "sess_y = SessionY()\n", "\n", "wrapper_x = namespace.WrapperX(session=sess_x)\n", "wrapper_y = namespace.WrapperY(session=sess_y)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Next, we will add some entities to the wrappers. For simplicity, we will use the `session` parameter available to all entities in their initialisation. This is just to explicitly work with `SessionX` and `SessionY`, without worrying about the default `CoreSession`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "a = namespace.A(session=sess_x, name=\"a\")\n", "wrapper_x.add(a)\n", "\n", "b = namespace.B(session=sess_y, name=\"b\")\n", "wrapper_y.add(b)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's add `a` to `wrapper_y`:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "wrapper_y.add(a)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "After the previous statement, both wrappers have an identical version of a. However, they are not linked together. This means one can be changed and the other one won't be. Also, the variable `a` points to the instance inside `wrapper_x`, and there is no reference to the one inside `wrapper_y`. We can test that:" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "output_type": "stream", "name": "stdout", "text": "Name of a: a updated\nName of a_in_y: a\n" } ], "source": [ "a.name = \"a updated\"\n", "\n", "a_in_y = wrapper_y.get(a.uid)\n", "\n", "print(\"Name of a: \", a.name)\n", "print(\"Name of a_in_y: \", a_in_y.name)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As you can see, changing the name through `a`, which points to the object in `SessionX`, only changed one version.\n", "Also bear in mind that `SessionX` and `SessionY` represent two arbitrary sessions, so this situation could arise when adding objects to a wrapper from the normal instantiation (remember that objects reside in the `CoreSession` by default)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In order to get a reference to the added object, you can assign the return of the call to `add` to a variable. For example:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "b = wrapper_x.add(b)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "That way we can modify the name of `b` in `wrapper_x` more easily:" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "output_type": "stream", "name": "stdout", "text": "Name of b: b updated\nName of b_in_y: b\n" } ], "source": [ "b.name = \"b updated\"\n", "\n", "b_in_y = wrapper_y.get(b.uid)\n", "\n", "print(\"Name of b: \", b.name)\n", "print(\"Name of b_in_y: \", b_in_y.name)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Summary\n", "In short, these are the things you should be aware of:\n", "\n", "- Adding an object to a different session creates a deepcopy.\n", "- Two versions of the same instance (same uid and type) in different sessions are not automatically synced.\n", "- Variables point to an object in a session, and will not change when the objects are added somewhere else.\n", "- If you want to quickly assign a variable to an object in a new session, you can capture the return of the `add` call." ] } ], "metadata": {}, "nbformat": 4, "nbformat_minor": 2 }