Package: gridappsd-python
gridappsd-pythonPython library for developing applications and services against the gridappsd apiRequirementsThe gridappsd-python library requires a  python version >= 3.6 and < 4 in order to work properly (Note no testing
has been done with python 4 to date).InstallationThe recommended installation ofgridappsd-pythonis in a separate virtual environment.  Executing the following
will create an environment calledgriddapps-env.python3-mvenvgridappsd-envSourcing the gridappsd-env activates the newly created python environment.sourcegridappsd-env/bin/activateUpgrade pip to the latest (some packages require 19.0+ version of pip).python-mpipinstallpip--upgradeInstall the latestgridappsd-pythonand its dependencies in the virtual environment.pipinstallgridappsd-pythonVerifying things are working properlyThe following code snippet assumes you have created a gridappsd instance using the steps inhttps://github.com/GRIDAPPSD/gridappsd-docker.Create a test script (tester.py) with the following content.fromgridappsdimportGridAPPSDdefon_message_callback(header,message):print(f"header:{header}message:{message}")# Note these should be changed on the server in a cyber secure environment!username="app_user"password="1234App"# Note: there are other parameters for connecting to# systems other than localhostgapps=GridAPPSD(username=username,password=password)assertgapps.connectedgapps.send('send.topic',{"foo":"bar"})# Note we are sending the function not executing the function in the second parametergapps.subscribe('subscribe.topic',on_message_callback)gapps.send('subcribe.topic','A message about subscription')time.sleep(5)gapps.close()Start up the gridappsd-docker enabled platform.  Then run the following to execute the tester.py scriptpythontester.pyApplication DevelopersDeploymentPlease seeDOCKER_CONTAINER.mdfor working within the docker application base container.Local DevelopmentDeveloping applications against gridappsd using thegridappsd-pythonlibrary should follow the same steps
as above, however with a couple of environmental variables specified.  The following environmental variables are
available to provide the same context that would be available from inside the application docker container.  These
are useful to know for developing your application outside of the docker context (e.g. in a python notebook).NOTE: you can also define these your ~./bashrc file so you don't have to specify them all the time# export allows all processes started by this shell to have access to the global variable# address where the gridappsd server is running - default localhostexportGRIDAPPSD_ADDRESS=localhost# port to connect to on the gridappsd server (the stomp client port)exportGRIDAPPSD_PORT=61613# username to connect to the gridappsd serverexportGRIDAPPSD_USER=app_user# password to connect to the gridappsd serverexportGRIDAPPSD_PASSWORD=1234App# Note these should be changed on the server in a cyber secure environment!The following is the same tester code as above, but with the environment variables set.  The environment variables
should be set in your environment when running the application inside our docker container.fromgridappsdimportGridAPPSDdefon_message_callback(header,message):print(f"header:{header}message:{message}")# Create GridAPPSD object and connect to the gridappsd server.gapps=GridAPPSD()assertgapps.connectedgapps.send('send.topic',{"foo":"bar"})# Note we are sending the function not executing the function in the second parametergapps.subscribe('subscribe.topic',on_message_callback)gapps.send('subcribe.topic','A message about subscription')time.sleep(5)gapps.close()DevelopersThis project uses poetry to build the environment for execution.  Follow the instructionshttps://python-poetry.org/docs/#installationto install poetry.  As a developer I prefer not to have poetry installed
in the same virtual environment that my projects are in.Clone the github repository:gitclonehttps://github.com/GRIDAPPSD/gridappsd-python-bdevelopcdgridappsd-pythonThe following commands build and install a local wheel into an environment created just for this package.# Build the project (stores in dist directory both .tar.gz and .whl file)poetrybuild# Install the wheel into the environment and the dev dependenciespoetryinstall# Install only the library dependenciespoetryinstall--no-devNote:Poetry does not have a setup.py that you can install in editable mode like with pip install -e ., however
you can extract the generated setup.py file from the built tar.gz file in the dist directory.  Just extract the
.tar.gz file and copy the setup.py file from the extracted directory to the root of gridappsd-python.  Then you can
enable editing through pip install -e. as normal.TestingTesting has become an integral part of the software lifecycle.  Thegridappsd-pythonlibrary has both unit and
integration tests available to be run.  In order to execute these, you must have installed the gridappsd-python library
as above with dev-dependencies.During the testing phase the docker containers required for the tests are downloaded from
dockerhub and started.  By default thedeveloptag is used to test the library using pytest.One can customize the docker image tag by setting the environmental
variableGRIDAPPSD_TAG_ENVeither byexport GRIDAPPSD_TAG_ENV=other_tagor by executing
pytest with the following:# Export environmental variables and all tests will use the same tag (other_tag) to pull from docker hub.# Default tag is developexportGRIDAPPSD_TAG_ENV=other_tag
pytest# Tests also require the username and password to be avaialable as environmental variables# in order for them to properly run these testsexportGRIDAPPSD_USER=userexportGRIDAPPSD_PASSWORD=pass

pytestNOTE: the first running the tests will download all of the docker images associated with theGOSS-GridAPPS-Drepository.  This process may take some time.Running tests created in a new projectThegridappsd-pythonlibrary exposes a testing environment through thegridappsd.docker_handlermodule.  Including the followingconftest.pyin the root of your base test directory allows tests to reference these.  Using these fixtures will start all of the
base containers required forgridappsdto run.# conftest.py# Create a conftest.py file in the root of the tests directory to enable usage throughout the tests directory and below.## Tested project structure an layout## project-folder\#   mainmodule\#     __init__.py#     myapplication.py#   tests\#     conftest.py#     test_myapplication.py#   README.mdimportloggingimportosimportsysimportpytestfromgridappsdimportGridAPPSD,GOSSfromgridappsd.docker_handlerimportrun_dependency_containers,run_gridappsd_container,Containerslevels=dict(CRITICAL=50,FATAL=50,ERROR=40,WARNING=30,WARN=30,INFO=20,DEBUG=10,NOTSET=0)# Get string representation of the log level passedLOG_LEVEL=os.environ.get("LOG_LEVEL","INFO")# Make sure the level passed is one of the valid levels.ifLOG_LEVELnotinlevels.keys():raiseAttributeError("Invalid LOG_LEVEL environmental variable set.")# Set the numeric version of log level to pass to the basicConfig functionLOG_LEVEL=levels[LOG_LEVEL]logging.basicConfig(stream=sys.stdout,level=LOG_LEVEL,format="%(asctime)s|%(levelname)s|%(name)s|%(message)s")logging.getLogger("urllib3.connectionpool").setLevel(logging.INFO)logging.getLogger("docker.utils.config").setLevel(logging.INFO)logging.getLogger("docker.auth").setLevel(logging.INFO)STOP_CONTAINER_AFTER_TEST=os.environ.get('GRIDAPPSD_STOP_CONTAINERS_AFTER_TESTS',True)@pytest.fixture(scope="module")defdocker_dependencies():print("Docker dependencies")# Containers.reset_all_containers()withrun_dependency_containers(stop_after=STOP_CONTAINER_AFTER_TEST)asdep:yielddepprint("Cleanup docker dependencies")@pytest.fixturedefgridappsd_client(request,docker_dependencies):withrun_gridappsd_container(stop_after=STOP_CONTAINER_AFTER_TEST):gappsd=GridAPPSD()gappsd.connect()assertgappsd.connectedmodels=gappsd.query_model_names()assertmodelsisnotNoneifrequest.clsisnotNone:request.cls.gridappsd_client=gappsdyieldgappsdgappsd.disconnect()@pytest.fixturedefgoss_client(docker_dependencies):withrun_gridappsd_container(stop_after=STOP_CONTAINER_AFTER_TEST):goss=GOSS()goss.connect()assertgoss.connectedyieldgossUsing the above fixtures from inside a test module and test function looks like the following:# Example test function using the gridappsd_client fixture@mock.patch.dict(os.environ,{"GRIDAPPSD_APPLICATION_ID":"helics_goss_bridge.py"})deftest_gridappsd_status(gridappsd_client):gappsd=gridappsd_clientassert"helics_goss_bridge.py"==gappsd.get_application_id()assertgappsd.get_application_status()==ProcessStatusEnum.STARTING.valueassertgappsd.get_service_status()==ProcessStatusEnum.STARTING.valuegappsd.set_application_status("RUNNING")assertgappsd.get_service_status()==ProcessStatusEnum.RUNNING.valueassertgappsd.get_application_status()==ProcessStatusEnum.RUNNING.valuegappsd.set_service_status("COMPLETE")assertgappsd.get_service_status()==ProcessStatusEnum.COMPLETE.valueassertgappsd.get_application_status()==ProcessStatusEnum.COMPLETE.value# Invalidgappsd.set_service_status("Foo")assertgappsd.get_service_status()==ProcessStatusEnum.COMPLETE.valueassertgappsd.get_application_status()==ProcessStatusEnum.COMPLETE.value