Skip to content

Commit

Permalink
move client instance out of service context
Browse files Browse the repository at this point in the history
  • Loading branch information
David Erb committed May 17, 2023
1 parent 07033f7 commit 3d8afdd
Show file tree
Hide file tree
Showing 3 changed files with 143 additions and 119 deletions.
66 changes: 45 additions & 21 deletions src/dls_servbase_lib/datafaces/context.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import logging

from dls_servbase_api.datafaces.context import Context as DatafaceContext
from typing import Dict

# Base class for an asyncio context
from dls_servbase_lib.contexts.base import Base as ContextBase
Expand All @@ -16,23 +15,34 @@

class Context(ContextBase):
"""
Asyncio context for a dls_servbase_dataface server object.
Asyncio context for a servbase object.
On entering, it creates the object according to the specification (a dict).
If configured, it starts the server as a coroutine, thread or process.
On exiting, it commands the server to shut down.
The enter and exit methods are exposed for use during testing.
If specified, it starts the server as a coroutine, thread or process.
If not a server, then it will instatiate a direct access to a servbase.
On exiting, it commands the server to shut down and/or releases the direct access resources.
"""

# ----------------------------------------------------------------------------------------
def __init__(self, specification):
def __init__(self, specification: Dict):
"""
Constructor.
Args:
specification (Dict): specification of the servbase object to be constructed within the context.
The only key in the specification that relates to the context is "start_as", which can be "coro", "thread", "process" or None.
All other keys in the specification relate to creating the servbase object.
"""
ContextBase.__init__(self, thing_type, specification)

self.__api_context = None

# ----------------------------------------------------------------------------------------
async def aenter(self):
""" """
async def aenter(self) -> None:
"""
Asyncio context entry.
Starts and activates service as specified.
Establishes the global (singleton-like) default servbase.
"""

# Build the object according to the specification.
self.server = Datafaces().build_object(self.specification())
Expand All @@ -46,16 +56,30 @@ async def aenter(self):
elif self.context_specification.get("start_as") == "process":
await self.server.start_process()

self.__api_context = DatafaceContext(self.specification())
await self.__api_context.aenter()
# Not running as a service?
elif self.context_specification.get("start_as") == "direct":
# We need to activate the tick() task.
await self.server.activate()

# ----------------------------------------------------------------------------------------
async def aexit(self):
""" """
async def aexit(self) -> None:
"""
Asyncio context exit.
if self.server is not None:
# Put in request to shutdown the server.
await self.server.client_shutdown()
Stop service if one was started and releases any client resources.
"""

if self.__api_context is not None:
await self.__api_context.aexit()
if self.server is not None:
if self.context_specification.get("start_as") == "process":
logger.info(
"[DISSHU] in context exit, sending shutdown to client process"
)
# Put in request to shutdown the server.
await self.server.client_shutdown()
logger.info("[DISSHU] in context exit, sent shutdown to client process")

if self.context_specification.get("start_as") == "coro":
await self.server.direct_shutdown()

if self.context_specification.get("start_as") == "direct":
await self.server.deactivate()
138 changes: 71 additions & 67 deletions tests/test_dataface.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
import logging

from dls_servbase_api.databases.constants import CookieFieldnames, Tablenames

# Object managing datafaces.
from dls_servbase_api.datafaces.context import Context as ClientContext
from dls_servbase_api.datafaces.datafaces import dls_servbase_datafaces_get_default

# Context creator.
from dls_servbase_lib.contexts.contexts import Contexts
from dls_servbase_lib.datafaces.context import Context as ServerContext

# Base class for the tester.
from tests.base_context_tester import BaseContextTester
Expand Down Expand Up @@ -40,65 +37,72 @@ async def _main_coroutine(self, constants, output_directory):

context_configuration = await dls_servbase_multiconf.load()

dls_servbase_context = Contexts().build_object(context_configuration)

async with dls_servbase_context:
dataface = dls_servbase_datafaces_get_default()

# Write one record.
await dataface.insert(
Tablenames.COOKIES,
[
{
CookieFieldnames.UUID: "f0",
CookieFieldnames.CONTENTS: "{'a': 'f000'}",
}
],
)

all_sql = f"SELECT * FROM {Tablenames.COOKIES}"
records = await dataface.query(all_sql)

assert len(records) == 1
assert records[0][CookieFieldnames.UUID] == "f0"
assert records[0][CookieFieldnames.CONTENTS] == "{'a': 'f000'}"

# ----------------------------------------------------------------
# Now try a direct update.
record = {
CookieFieldnames.CONTENTS: "{'b': 'f1111'}",
}

subs = ["f0"]
result = await dataface.update(
Tablenames.COOKIES,
record,
f"{CookieFieldnames.UUID} = ?",
subs=subs,
why="test update",
)

assert result["count"] == 1
records = await dataface.query(all_sql)

assert len(records) == 1
assert records[0][CookieFieldnames.UUID] == "f0"
assert records[0][CookieFieldnames.CONTENTS] == "{'b': 'f1111'}"

# ----------------------------------------------------------------
# Now try a high level API update.
record = {
CookieFieldnames.UUID: "f0",
CookieFieldnames.CONTENTS: "{'c': 'f2222'}",
}

result = await dataface.update_cookie(
record,
)

assert result["count"] == 1
records = await dataface.query(all_sql)

assert len(records) == 1
assert records[0][CookieFieldnames.UUID] == "f0"
assert records[0][CookieFieldnames.CONTENTS] == "{'c': 'f2222'}"
servbase_specification = context_configuration[
"dls_servbase_dataface_specification"
]

dls_servbase_client_context = ClientContext(servbase_specification)

dls_servbase_server_context = ServerContext(servbase_specification)

async with dls_servbase_client_context:
async with dls_servbase_server_context:
dataface = dls_servbase_datafaces_get_default()

# Write one record.
await dataface.insert(
Tablenames.COOKIES,
[
{
CookieFieldnames.UUID: "f0",
CookieFieldnames.CONTENTS: "{'a': 'f000'}",
}
],
)

all_sql = f"SELECT * FROM {Tablenames.COOKIES}"
records = await dataface.query(all_sql)

assert len(records) == 1
assert records[0][CookieFieldnames.UUID] == "f0"
assert records[0][CookieFieldnames.CONTENTS] == "{'a': 'f000'}"

# ----------------------------------------------------------------
# Now try a direct update.
record = {
CookieFieldnames.CONTENTS: "{'b': 'f1111'}",
}

subs = ["f0"]
result = await dataface.update(
Tablenames.COOKIES,
record,
f"{CookieFieldnames.UUID} = ?",
subs=subs,
why="test update",
)

assert result["count"] == 1
records = await dataface.query(all_sql)

assert len(records) == 1
assert records[0][CookieFieldnames.UUID] == "f0"
assert records[0][CookieFieldnames.CONTENTS] == "{'b': 'f1111'}"

# ----------------------------------------------------------------
# Now try a high level API update.
record = {
CookieFieldnames.UUID: "f0",
CookieFieldnames.CONTENTS: "{'c': 'f2222'}",
}

result = await dataface.update_cookie(
record,
)

assert result["count"] == 1
records = await dataface.query(all_sql)

assert len(records) == 1
assert records[0][CookieFieldnames.UUID] == "f0"
assert records[0][CookieFieldnames.CONTENTS] == "{'c': 'f2222'}"
58 changes: 27 additions & 31 deletions tests/test_dataface_takeover.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
import logging

from dls_servbase_api.databases.constants import CookieFieldnames, Tablenames

# Object managing datafaces.
from dls_servbase_api.datafaces.context import Context as ClientContext
from dls_servbase_api.datafaces.datafaces import dls_servbase_datafaces_get_default

# Context creator.
from dls_servbase_lib.contexts.contexts import Contexts
from dls_servbase_lib.datafaces.context import Context as DatafaceContext
from dls_servbase_lib.datafaces.context import Context as ServerContext

# Base class for the tester.
from tests.base_context_tester import BaseContextTester
Expand Down Expand Up @@ -36,37 +32,37 @@ async def _main_coroutine(self, constants, output_directory):
dls_servbase_multiconf = self.get_multiconf()

context_configuration = await dls_servbase_multiconf.load()
dls_servbase_context = Contexts().build_object(context_configuration)

async with dls_servbase_context:
dataface = dls_servbase_datafaces_get_default()

# Write one record.
await dataface.insert(
Tablenames.COOKIES,
[
{
CookieFieldnames.UUID: "f0",
CookieFieldnames.CONTENTS: "{'a': 'f000'}",
}
],
)

# Make a new dataface context with the same specification.
dataface_specification = dls_servbase_multiconf.require(
"dls_servbase_dataface_specification"
)
servbase_specification = context_configuration[
"dls_servbase_dataface_specification"
]

dls_servbase_client_context = ClientContext(servbase_specification)

dls_servbase_server_context = ServerContext(servbase_specification)

async with dls_servbase_client_context:
async with dls_servbase_server_context:
dataface = dls_servbase_datafaces_get_default()

# Write one record.
await dataface.insert(
Tablenames.COOKIES,
[
{
CookieFieldnames.UUID: "f0",
CookieFieldnames.CONTENTS: "{'a': 'f000'}",
}
],
)

# Make a new dataface context with the same specification.
dls_servbase_server_context = ServerContext(servbase_specification)
# Activate the new dataface which should send shutdown to the old process.
async with DatafaceContext(dataface_specification):
async with dls_servbase_server_context:
all_sql = f"SELECT * FROM {Tablenames.COOKIES}"
records = await dataface.query(all_sql)

assert len(records) == 1
assert records[0][CookieFieldnames.UUID] == "f0"
assert records[0][CookieFieldnames.CONTENTS] == "{'a': 'f000'}"

# From the top level context's point of view, one of its processs is dead.
assert await dls_servbase_context.is_any_process_dead()

assert not await dls_servbase_context.is_any_process_alive()

0 comments on commit 3d8afdd

Please sign in to comment.