Skip to content

Commit

Permalink
Simplify data store API to be more pythonic #93
Browse files Browse the repository at this point in the history
* Add dictionary based data store

* Allow attribute acces to data store

* Revert changes to skel to submit as seperate PR

* Add deprecation warnings for existing data store API
  • Loading branch information
surajbarkale authored and Vinay Shankar Shukla committed Jul 19, 2018
1 parent b8a023c commit cddda6d
Show file tree
Hide file tree
Showing 5 changed files with 205 additions and 39 deletions.
32 changes: 23 additions & 9 deletions docs/source/features.rst
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,8 @@ Data Stores

Step implementations can share custom data across scenarios, specifications and suites using data stores.
There are 3 different types of data stores based on the lifecycle of when it gets cleared.
These data stores provide a dict like interface for managing data. In addition to this, data keys
can also be accessed as attributes for convenience.

Scenario store
^^^^^^^^^^^^^^
Expand All @@ -185,14 +187,18 @@ This data store keeps values added to it in the lifecycle of the scenario execut

.. code::
from getgauge.python import DataStoreFactory
DataStoreFactory.scenario_data_store().put(key, value)
from getgauge.python import data_store
data_store.scenario[key] = value
# OR
data_store.scenario.key = value
**Retrieve a value:**

.. code::
DataStoreFactory.scenario_data_store().get(key)
data_store.scenario[key]
# OR
data_store.scenario.key
Specification store
^^^^^^^^^^^^^^^^^^^
Expand All @@ -205,14 +211,18 @@ executes.

.. code::
from getgauge.python import DataStoreFactory
DataStoreFactory.spec_data_store().put(key, value)
from getgauge.python import data_store
data_store.spec[key] = value
# OR
data_store.spec.key = value
**Retrieve a value:**

.. code::
DataStoreFactory.spec_data_store().get(key)
data_store.spec[key]
# OR
data_store.spec.key
Suite store
^^^^^^^^^^^
Expand All @@ -224,14 +234,18 @@ suite’s execution. Values are cleared after entire suite executes.

.. code::
from getgauge.python import DataStoreFactory
DataStoreFactory.suite_data_store().put(key, value);
from getgauge.python import data_store
data_store.suite[key] = value
# OR
data_store.suite.key = value
**Retrieve a value:**

.. code::
DataStoreFactory.suite_data_store().get(key);
data_store.suite[key]
# OR
data_store.suite.key
.. note::
Suite Store is not advised to be used when executing specs in parallel. The values are not retained between parallel streams of execution.
Expand Down
9 changes: 4 additions & 5 deletions getgauge/processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,13 @@
from threading import Timer

import ptvsd
import time

from getgauge.connection import read_message, send_message
from getgauge.executor import set_response_values, execute_method, run_hook
from getgauge.impl_loader import load_impls
from getgauge.messages.messages_pb2 import Message, StepPositionsResponse, TextDiff, CacheFileRequest
from getgauge.messages.spec_pb2 import Parameter, Span
from getgauge.python import Table, create_execution_context_from, DataStoreFactory
from getgauge.python import Table, create_execution_context_from, data_store
from getgauge.refactor import refactor_step
from getgauge.registry import registry, MessagesStore
from getgauge.static_loader import reload_steps
Expand Down Expand Up @@ -141,17 +140,17 @@ def _execute_after_step_hook(request, response, _socket):


def _init_scenario_data_store(request, response, _socket):
DataStoreFactory.scenario_data_store().clear()
data_store.scenario.clear()
set_response_values(request, response)


def _init_spec_data_store(request, response, _socket):
DataStoreFactory.spec_data_store().clear()
data_store.spec.clear()
set_response_values(request, response)


def _init_suite_data_store(request, response, _socket):
DataStoreFactory.suite_data_store().clear()
data_store.suite.clear()
set_response_values(request, response)


Expand Down
69 changes: 63 additions & 6 deletions getgauge/python.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
import inspect
import sys
import warnings

from getgauge.registry import registry, MessagesStore

try:
from collections.abc import MutableMapping
except ImportError:
from collections import MutableMapping


def step(step_text):
def _step(func):
Expand Down Expand Up @@ -221,9 +227,50 @@ def write_message(message):
MessagesStore.write_message(message)


class DataStore:
class DictObject(dict):
def __getattr__(self, name):
try:
return self[name]
except KeyError:
raise AttributeError("'{0}' object has no attribute '{1}'".format(self.__class__.__name__, name))

def __setattr__(self, name, value):
self[name] = value

def __delattr__(self, name):
try:
del self[name]
except KeyError:
raise AttributeError("'{0}' object has no attribute '{1}'".format(self.__class__.__name__, name))


class DataStoreContainer(object):
def __init__(self):
self.__data_store = {}
self.__scenario = DictObject()
self.__spec = DictObject()
self.__suite = DictObject()

@property
def scenario(self):
return self.__scenario

@property
def spec(self):
return self.__spec

@property
def suite(self):
return self.__suite


data_store = DataStoreContainer()


class DataStore:
def __init__(self, data_store=None):
if data_store is None:
data_store = {}
self.__data_store = data_store

def get(self, key):
return self.__data_store[key]
Expand All @@ -235,27 +282,37 @@ def is_present(self, key):
return key in self.__data_store

def clear(self):
self.__data_store = {}
self.__data_store.clear()

def __eq__(self, other):
return self.__data_store == other.__data_store


def _warn_datastore_deprecation(store_type):
warnings.warn(
"'DataStoreFactory.{0}_data_store()' is deprecated in favour of 'data_store.{0}'".format(store_type),
DeprecationWarning, stacklevel=3)
warnings.simplefilter('default', DeprecationWarning)


class DataStoreFactory:
__scenario_data_store = DataStore()
__spec_data_store = DataStore()
__suite_data_store = DataStore()
__scenario_data_store = DataStore(data_store.scenario)
__spec_data_store = DataStore(data_store.spec)
__suite_data_store = DataStore(data_store.suite)

@staticmethod
def scenario_data_store():
_warn_datastore_deprecation("scenario")
return DataStoreFactory.__scenario_data_store

@staticmethod
def spec_data_store():
_warn_datastore_deprecation("spec")
return DataStoreFactory.__spec_data_store

@staticmethod
def suite_data_store():
_warn_datastore_deprecation("suite")
return DataStoreFactory.__suite_data_store


Expand Down
26 changes: 13 additions & 13 deletions tests/test_processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,16 @@
from getgauge.messages.messages_pb2 import Message, StepValidateResponse, TextDiff, CacheFileRequest
from getgauge.messages.spec_pb2 import ProtoExecutionResult, Parameter, Span
from getgauge.processor import processors
from getgauge.python import DataStoreFactory, DataStore
from getgauge.python import data_store
from getgauge.registry import registry


class ProcessorTests(TestCase):
def setUp(self):
self.setUpPyfakefs()
DataStoreFactory.suite_data_store().clear()
DataStoreFactory.spec_data_store().clear()
DataStoreFactory.scenario_data_store().clear()
data_store.suite.clear()
data_store.spec.clear()
data_store.scenario.clear()
registry.clear()

def tearDown(self):
Expand All @@ -31,9 +31,9 @@ def test_Processor_kill_request(self):
SOCK_STREAM))

def test_Processor_suite_data_store_init_request(self):
DataStoreFactory.suite_data_store().put('suite', 'value')
data_store.suite['suite'] = 'value'

self.assertNotEqual(DataStore(), DataStoreFactory.suite_data_store())
self.assertNotEqual(0, len(data_store.suite))

response = Message()
processors[Message.SuiteDataStoreInit](None, response, None)
Expand All @@ -45,12 +45,12 @@ def test_Processor_suite_data_store_init_request(self):
self.assertEqual(0,
response.executionStatusResponse.executionResult.executionTime)

self.assertEqual(DataStore(), DataStoreFactory.suite_data_store())
self.assertDictEqual({}, data_store.suite)

def test_Processor_spec_data_store_init_request(self):
DataStoreFactory.spec_data_store().put('spec', 'value')
data_store.spec['spec'] = 'value'

self.assertNotEqual(DataStore(), DataStoreFactory.spec_data_store())
self.assertNotEqual(0, len(data_store.spec))

response = Message()
processors[Message.SpecDataStoreInit](None, response, None)
Expand All @@ -59,12 +59,12 @@ def test_Processor_spec_data_store_init_request(self):
self.assertEqual(False, response.executionStatusResponse.executionResult.failed)
self.assertEqual(0, response.executionStatusResponse.executionResult.executionTime)

self.assertEqual(DataStore(), DataStoreFactory.spec_data_store())
self.assertDictEqual({}, data_store.spec)

def test_Processor_scenario_data_store_init_request(self):
DataStoreFactory.scenario_data_store().put('scenario', 'value')
data_store.scenario['scenario'] = 'value'

self.assertNotEqual(DataStore(), DataStoreFactory.scenario_data_store())
self.assertNotEqual(0, len(data_store.scenario))

response = Message()
processors[Message.ScenarioDataStoreInit](None, response, None)
Expand All @@ -73,7 +73,7 @@ def test_Processor_scenario_data_store_init_request(self):
self.assertEqual(False, response.executionStatusResponse.executionResult.failed)
self.assertEqual(0, response.executionStatusResponse.executionResult.executionTime)

self.assertEqual(DataStore(), DataStoreFactory.scenario_data_store())
self.assertDictEqual({}, data_store.scenario)

def test_Processor_step_names_request(self):
registry.add_step('Step <a> with <b>', 'func', '')
Expand Down
Loading

0 comments on commit cddda6d

Please sign in to comment.