Skip to main content

class-defintion stateful module

Project description

https://badge.fury.io/py/pfstate.svg https://travis-ci.org/FNNDSC/pfstate.svg?branch=master https://img.shields.io/badge/python-3.5%2B-blue.svg

Overview

This repository provides pfstate – a library / module that maintains state in the object/class definition (and not in a class instance). The module uses the tree C_snode data abstraction internally (see elsewhere for C_snode) as well as some internal methods to set/get this internal data in various ways.

pfstate

Most simply, pfstate is a module that keeps state in a class definition (as opposed to a class instance).

States are created in the global space typically by deriving a class and calling the state_create() method of the base class. Note that a “derived” class is still only setting state in the same global space – and not in a separate derived-class space.

THIS IS IMPORTANT:

  • There is only ever ONE global state in the context of a single system. Thus, if various different modules use pfstate it is probably best practice to always set the **kwargs that are sent to the derived class with

**dict(kwargs, useGlobalState = True)

It was primarily created in the context of custom ThreadedHTTPServer classes. Creating a ThreadedHTTPServer in python involves instantiating the ThreadedHTTPServer, and in the constructor providing a derived BaseHTTPRequestHandler object. The design pattern has some structural shortcomings – most notably that the difficulty in setting internal BaseHTTPRequestHandler data from the level of the ThreadedHTTPServer. One mechanism to overcome this is to share a common single pfstate object across the scope of both the server and the handler.

Moreover, each call to the ThreadedHTTPServer re-initializes the handler object derived from BaseHTTPRequestHandler, so any state information in that object instance is lost across calls.

By using the pfstate module, however, in the handler object, state information can be preserved across calls to the ThreadedHTTPServer by keeping state in the object and not an instance of the object.

In some ways, this can be thought of a cleaner way to avoid using a global variable.

Consult the source code for full detail. However, as a simple overview, the recommended method of using this module is to define a subclass containing the state-specific information in a dictionary, and then to initialize the class.

Note, it is vitally important that this derived class check the initialization of the base object data so as to not re-initialize an already stateful object and hence lose any additional state information.

from    pfstate             import S
from    argparse            import RawTextHelpFormatter
from    argparse            import ArgumentParser

str_desc        = "some program description"
str_version     = "1.0.0"
str_name        = "Example module"

parser          = ArgumentParser(
                    description = str_desc,
                    formatter_class = RawTextHelpFormatter
                )

parser.add_argument(
    '--msg',
    action  = 'store',
    dest    = 'msg',
    default = '',
    help    = 'Message payload for internalctl control.'
)

# Create a test class
class D(S):
    """
    A derived class with problem-specific state

    See https://github.com/FNNDSC/pfstate for more information.

    """

    def __init__(self, *args, **kwargs):
        """
        Constructor
        """
        self.state_create(
        {
            'additionalState': {
                'desc':         'Additional state information',
                'theAnswer':    42,
                'theQuestion':  'What do you get if you multiple six by nine',
                'foundBy':      'Arthur Dent',
                'note':     {
                    'additional':   'was this really Arthur Dent, though?',
                    'action':   {
                        'item1':    'further research might be needed'
                    }
                }
            },
            'earthState': {
                'current':      'Destroyed',
                'reason':       'Facilitate Hyperspace bypass',
                'survivors': {
                    'humans':   ['Arthur Dent', 'Ford Prefect', 'Trillian'],
                    'dolphins': 'Most of them',
                    'note': {
                        'exception':    'Ford Prefect is not a human'
                    }
                }
            }
        },
        *args, **kwargs)

state   = D(
    version     = str_version,
    name        = str_name,
    desc        = str_desc,
    args        = vars(args)
)

# Now create a different derived class --
# This will still add to the global state
class E(S):
    """
    A new derived class with different state -- this is still
    added to the same global space
    """

    def __init__(self, arg, *args, **kwargs):
        """
        Constructor
        """
        if 'randomFact' not in arg.keys():
            arg['randomFact']   = "Vogon poetry is the third worst poetry in the universe."
        self.state_create(
        {
            'Vogons': {
                'desc'          :   'Slug-like but vaguely humanoid',
                'preferredJob'  :   'Galactic bureaucrats',
                'randomFact'    :   arg['randomFact'],
                'note':     {
                    'additional':   'Vogons are the worst marksmen in the galaxy.',
                    'source':   {
                        'name':    'Marvin the Paranoid Android'
                    }
                }
            }
        },
        *args, **kwargs)

class demo:
    """Just an example class that as part of its initialization adds to state
    """
    def __init__(self, arg, *args, **kwargs):
        """example class constructor

        Args:
            arg ([type]): some unspecified type arg input
        """
        self.newState       = E(arg, *args, **dict(kwargs, useGlobalState = True))
        print(self.newState('/this/desc'))

example = demo(
            {
                'randomFact' : "Vogon poetry is the third worst poetry in the universe"
            }, name = "example"
        )

if len(args.test):
    if args.test == 'tree':
        print(state.T)
    else:
        print(json.dumps(state.as_dict(node = args.test)))

if len(args.msg):
    d_control = state.internalctl_process(request = json.loads(args.msg))
    print(
        json.dumps(
            d_control,
            indent = 4
        )
    )

Installation

Installation is relatively straightforward, and we recommend using python `pip to simply install the module, preferably in a python virtual environment.

Python Virtual Environment

On Ubuntu, install the Python virtual environment creator

python3 -m venv <virtualEnvPath>

Install the module

pip install pfstate

Usage

For usage of pstate, consult the relevant wiki pages <https://github.com/FNNDSC/pfstate/wiki/pfstate-overview>`.

Command line arguments

[--state <directive>]
If specified, return some state detail. Usually this is some
path into an internal state tree node. If the <directive> is
the actual text 'tree', then return the entre state object
representation

[--msg '<JSON_formatted>']
An optional JSON formatted string exemplifying how to get and
set internal variables.

--msg '
{
    "action": "internalctl",
    "meta": {
                "var":     "/",
                "get":      "value"
            }
}'

--msg '
{   "action": "internalctl",
    "meta": {
                "var":     "/service/megalodon",
                "set":     {
                    "compute": {
                        "addr": "10.20.1.71:5010",
                        "baseURLpath": "api/v1/cmd/",
                        "status": "undefined"
                    },
                    "data": {
                        "addr": "10.20.1.71:5055",
                        "baseURLpath": "api/v1/cmd/",
                        "status": "undefined"
                    }
                }
            }
}'

[--configFileLoad <file>]
Load configuration information from the JSON formatted <file>.

[--configFileSave <file>]
Save configuration information to the JSON formatted <file>.

[-x|--desc]
Provide an overview help page.

[-y|--synopsis]
Provide a synopsis help summary.

[--version]
Print internal version number and exit.

[--debugToDir <dir>]
A directory to contain various debugging output -- these are typically
JSON object strings capturing internal state. If empty string (default)
then no debugging outputs are captured/generated. If specified, then
``pfcon`` will check for dir existence and attempt to create if
needed.

[-v|--verbosity <level>]
Set the verbosity level. "0" typically means no/minimal output. Allows
for more fine tuned output control as opposed to '--quiet' that effectively silences everything.

EXAMPLES

$>pfstate --state '/earthState'     # return a dictionary representation of
                                    # this node in the internal test data

$>pfstate --state 'tree'            # return the raw internal test data

$>pfstate  \
    --msg '
        {  "action": "internalctl",
        "meta": {
                    "var":     "/service/megalodon",
                    "set":     {
                        "compute": {
                            "addr": "10.20.1.71:5010",
                            "baseURLpath": "api/v1/cmd/",
                            "status": "undefined"
                        },
                        "data": {
                            "addr": "10.20.1.71:5055",
                            "baseURLpath": "api/v1/cmd/",
                            "status": "undefined"
                        }
                    }
                }
        }'

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pfstate-3.0.2.tar.gz (14.3 kB view hashes)

Uploaded Source

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page