robshakir / pyangbind

A plugin for pyang that creates Python bindings for a YANG model.
Other
203 stars 121 forks source link

Iterating through the Class structure #34

Closed mpainenz closed 8 years ago

mpainenz commented 8 years ago

Hi Rob,

Is there a good way to dynamically iterate through the class structure of a binding object?

At the moment I am doing the following:

        for element_name in obj._pyangbind_elements:
            element = getattr(obj, element_name, self._element_error)
            if hasattr(element, "get"):
                if isinstance(element, PybindBase):
                    # YANG Container that has its own GET
                    # Call recursive function, with create_table set to False
                    self._build_tables(element, False, parent_table, heirachy)
                else:
                    # YANG List, requires a new child table
                    if hasattr(element, "_contained_class"):
                        print 'list: ' + element_name
                        child_obj = self._get_item_from_list(element)

                        if create_table:
                            self._build_tables(child_obj, True, table, heirachy)
                        else:
                            self._build_tables(child_obj, True, parent_table, heirachy)
                    else:
                        print 'leaf list: ' + element_name
                    print element

I can't help but feel like there could be a simpler way to iterate through the hierarchy. What do you think?

Thanks, Mark

robshakir commented 8 years ago

There could definitely be a simpler way -- at the moment, the majority of my code uses the get() method to do things with this. Here are two example ways to iterate.

#!/usr/bin/env python

from tmp import test
x = test()
x.a_list.add(1)
x.test.number = 1

def recursive_get(obj):
    if hasattr(obj, "_pyangbind_elements"):
        for i in obj._pyangbind_elements:
            print i
            recursive_get(getattr(obj, i))
    else:
        pyb_attr = getattr(obj, "_pybind_generated_by", None)
        if pyb_attr == "YANGListType":
            for i in obj:
                print i
                recursive_get(obj[i])
        else:
            print obj

recursive_get(x)

print "\n------\n"

def base_get(obj):
  d = {}
  for element_name in obj._pyangbind_elements:
    element = getattr(obj, element_name, None)
    if hasattr(element, "yang_name"):
      # retrieve the YANG name method
      yang_name = getattr(element, "yang_name", None)
      element_id = yang_name()
    else:
      element_id = element_name
    if hasattr(element, "get"):
      # this is a YANG container that has its own
      # get method
      d[element_id] = element.get()
    else:
      d[element_id] = element
  return d

print base_get(x)

It'd certainly be possible to implement an iterator such that one could do 'for x in y' type constructs, I just haven't done this yet. The main use of iterating through objects is serialisation/deserialisation - some other examples of iterating are in lib/serialise: https://github.com/robshakir/pyangbind/blob/master/pyangbind/lib/serialise.py

r.

robshakir commented 8 years ago

Note, you don't need to rewrite the latter as this is the standard .get() method which will recurse anyway.

mpainenz commented 8 years ago

Yes, lib/serialise is where I based my code in my example.

Iter would be awesome... being able to do 'for leaf in pybinding.leafs', 'for leaflist in pybinding.leaflists', 'for container in pybinding.containers', 'for typedlist in pybinding.typedlists'...

Also, iterating through a typed list is another thing, at the moment I use something like:

list[pybind_list.keys()[0]]

I have yet to start iterating through, as I'm not quite there yet, but I found this cumbersome.

I will have a look at .get(), as that may solve a lot of these issues

mpainenz commented 8 years ago

I had thought that .get returned a python based class structure, so all objects would be returned using python built-in classes, is that not the case?

robshakir commented 8 years ago

How do you mean a typed list? A pyangbind.lib.yangtypes.TypedList item is just a list, so it can be iterated through using for x in leaf_list. A YANGListType list can be iterated through using for item in yang_list.

get() does not flatten leaf types, it just restructures the classes into nested dicts, one still needs to handle the fact that these are not really native types in serialisation or deserialisation.

r.

mpainenz commented 8 years ago

Sorry if I'm getting my Yang terminology mixed up with lists.

What I'm referring to is not a leaf list, rather a list of Yang defined types. When I saw the keyword TypedList, that's what I thought it was in reference to.

In the case of a list of Yang defined types with key values, what would be great would be to simplify the following:

for i in xrange(len(endpoint_list.keys())):
    endpoint = endpoint_list[endpoint_list.keys()[i]]

To instead be:

for endpoint in endpoint_list:

I assume that somehow the iter needs to be defined for each keyed list class.

robshakir commented 8 years ago

Sorry, I'm confused. I think the enhancement you're asking for is already implemented - this is specifically for a YANG list which has a key specified (the same should work where there is no key specified too).

print "iterating through keys, as per a dict"
for q in x.a_list:
    print "key: \t %s" % q
    print "value: \t %s" % x.a_list[q].get()

print "\n\n"
print "iterating through key,value tuples as per a dict"
for k,v in x.a_list.iteritems():
    print "%s->%s" % (k, v.get())

Please re-open this issue if you have problems with how this works.

r.

mpainenz commented 8 years ago

Thanks Rob, sorry for the confusion. This works well.