Skip to main content

Python, Classes, Objects, REST and JSON

Perhaps I am doing this the wrong way, but when building REST services in Python 2.7 and trying to be all fancy and object oriented, returning JSON starts to get complicated.


For example, while building a Docker Managment/Statistics tool, I wanted to make sure that the data I retrieve from cAdvisor is nicely handled in classes so that all the data is easy to use and controlled, so I create classes for the cAdvisor data, including container and host machine information.




class hostmachine:
    def __init__(self,
                 cpu_frequency=None
                 ,instance_type = None
                 , memory_capacity= None
                 , num_cores = None
                  ):
        self.cpu_frequency = cpu_frequency
        self.instance_type = instance_type
        self.memory_capacity = memory_capacity
        self.num_cores = num_cores

class cadvisordata:

    def __init__(self
                 , name = None
                 , interval = None
                 , dockerid = None
                 , cpu = None
                 , network = None
                 , diskio = None
                 , machine = None
                 , machinedata = None
                 , ts_start = None
                 , ts_computed = None
                 , datetime_start = None
                 , datetime_computed = datetime.now()
                 , cadvisorpath = None):
        """

        :param name: The name of the container
        :param interval: the interval that the stats were computed accross
        :param dockerid: the internal docker id
        :param cpu: cpu stats
        :param network: network stats
        :param diskio: disk stats
        :param machine: host machine info
        :param ts_start: the timestamp of the first stats entry
        :param ts_computed: the timestamp of the end of  the computation
        :param datetime_start:
        :param datetime_computed:
        """
        self.name = name
        self.dockerid = dockerid
        self.cpu = network
        self.network = network
        self.diskio = diskio
        self.machine = machine
        self.ts_start = ts_start
        self.ts_computed = ts_computed
        self.datetime_start = datetime_start
        self.datetime_computed = datetime_computed
        self.cadvisorpath = cadvisorpath
        self.interval = interval

The "machine" object is inside of the cadvisordata object...

The basic use of these objects is to return as JSON in a rest call, but of course, if I try to just use json.dumps() on the machine.__dict__, i will get an error, since the embedded object will not be parsed as a dict.

So what to do?



Found this very helpful tip on stack exchange....

Which got me thinking, how can I make this something a bit more built-in?

So here was my approach, to include the function as a library function

def todict(obj, classkey=None):
    if isinstance(obj, dict):
        data = {}
        for (k, v) in obj.items():
            data[k] = todict(v, classkey)
        return data
    elif hasattr(obj, "_ast"):
        return todict(obj._ast())
    elif hasattr(obj, "__iter__"):
        return [todict(v, classkey) for v in obj]
    elif hasattr(obj, "__dict__"):
        data = dict([(key, todict(value, classkey))
            for key, value in obj.__dict__.iteritems()
            if not callable(value) and not key.startswith('_')])
        if classkey is not None and hasattr(obj, "__class__"):
            data[classkey] = obj.__class__.__name__
        return data
    else:
        return obj


Then create a method inside of the class in question:

def asdict(self):
        """
        Converts the object and all embedded objects to embedded dictionaries

        :return: a dictionary of the current object.
        :rtype dict
        """
        return(todict(self))

So now I call cadvisorInstance.asdict() instead of cadvisorInstance.__dict__



But this is not very reusable, and doesn't seem very pythonic... I wonder if anyone has done this in a more elegant way? Perhaps creating a base class to inherit from?




Comments

Popular posts from this blog

A more sane approach to elastix call recordings

We put together this solution a while ago (thanks Brother Miles!), but never documented it, thought it would a good idea to put it here for anyone else who might need this (and anyone who uses Elastix for high call volume, probably does. The challenge. Elastix/Freepbx has good call recording abilities, but very strangely just dumps everything into a single directory. This can get REALLY bad if you have tens of thousands of calls per day, I have run accross a few scripts to convert and mode call recordings after they are saved, but that is a waste of resources, and makes it hard to integrate call recording UI's. So we figured out the 2 places that need to be changed so that all recorded calls are saved in a Year/Month/day file structure. Just 2 files For regular calls, we need to alter the included and symlinked extensions.conf, this is found at /etc/asterisk/extensions.conf we just need to add the macro-dialout-trunk-predial-hook, which already exists as a secti...

Cloud Enterprise Project/Program Management Tools

In our ongoing quest for an integrated, cloud/Open Source hybrid Business Operating System, we need to pick and implement a tool for project/Program management. Environment Our organization has 8 separate companies, which we need to track project progress across all 8.  For some companies, end users (project resources) will enter information themselves. For other companies and projects, a project co-ordinator will track progress manually from resource input. Task tracking can be on many levels, some projects will be quite detailed, other will be tracked at the milestone level only. Basic Requirements In order for this component to be successfull, it needs to satisfy the following requirements. Integrate with GApps for authentication. Integrate with GApps for document associations Support a fairly large number of project contributers/users (about 100). Handle multiple projects. Enable reporting on multiple levels, from detailed to executive overview. ...

Installing dependancies for Sun Ray on Centos 5.8

Ooo the beauty of Linux based proprietary thin clients... One of the best parts is that operating systems get updated, and SRSS doesn't. So re-installing/updating SRSS can be a bit of a moving target. So here are notes from my latest install. Dependencies and prerequisites yum install glib dhcp openldap-clients openldap tftp-server libXp openmotif22 openssl compat-libstdc++-33 libusb-devel compat-openldap gdbm.i386 openldap-devel pdksh libXfont yum groupinstall "Development Tools" wget http://pkgs.repoforge.org/htop/htop-1.0.1-1.el5.rf.x86_64.rpm yum install htop-1.0.1-1.el5.rf.x86_64.rpm (ok, not really required, but helpful on gauging load easily) wget ftp://mirror.switch.ch/pool/3/mirror/centos/5.8/os/i386/CentOS/glib-1.2.10-20.el5.i386.rpm yum install  glib-1.2.10-20.el5.i386.rpm --nogpgcheck -y cd /usr/lib ln -sf libldap-2.3.so.0.2.31 libldap.so.199 ln -sf libgdbm.so.2.0.0 libgdbm.so.3 ln -sf liblber-2.3.so.0.2.31 liblber.so.199 extract the include...