Creating dynamic docstrings in a Python handle

I am trying to generate some class definitions dynamically (to wrap a C ++ extension). The following descriptor works fine, unless I try to access a docstring for a field using help (), it provides the default documentation for the descriptor, not the field. However, when I help (classname), it retrieves the docstring passed to the handle:

class FieldDescriptor(object): def __init__(self, name, doc='No documentation available.'): self.name = name self.__doc__ = doc def __get__(self, obj, dtype=None): if obj is None and dtype is not None: print 'Doc is:', self.__doc__ return self return obj.get_field(self.name) def __set__(self, obj, value): obj.set_field(self.name, value) class TestClass(object): def __init__(self): self.fdict = {'a': None, 'b': None} def get_field(self, name): return self.fdict[name] def set_field(self, name, value): self.fdict[name] = value fields = ['a', 'b'] def define_class(class_name, baseclass): class_obj = type(class_name, (baseclass,), {}) for field in fields: setattr(class_obj, field, FieldDescriptor(field, doc='field %s in class %s' % (field, class_name))) globals()[class_name] = class_obj if __name__ == '__main__': define_class('DerivedClass', TestClass) help(DerivedClass.a) help(DerivedClass) v = DerivedClass() help(va) 

"python test.py" outputs:

  Doc is: field a in class DerivedClass
 Help on FieldDescriptor in module __main__ object:

 class FieldDescriptor (__ builtin __. object)
  |  Methods defined here:
  |  
  |  __get __ (self, obj, dtype = None)
  |  
  |  __init __ (self, name, doc = 'No documentation available.')
  |  
  |  __set __ (self, obj, value)
  |  
  |  -------------------------------------------------- --------------------
  |  Data descriptors defined here:
  |  
  |  __dict__
  |  dictionary for instance variables (if defined)
  |  
  |  __weakref__
  |  list of weak references to the object (if defined)

 Doc is: field a in class DerivedClass
 Doc is: field b in class DerivedClass
 Help on class DerivedClass in module __main__:

 class DerivedClass (TestClass)
  |  Method resolution order:
  |  DerivedClass
  |  Testclass
  |  __builtin __. object
  |  
  |  Data descriptors defined here:
  |  
  |  a
  |  field a in class DerivedClass
  |  
  |  b
  |  field b in class DerivedClass
  |  
  |  -------------------------------------------------- --------------------
  |  Methods inherited from TestClass:
  |  
  |  __init __ (self)
  |  
  |  get_field (self, name)
  |  
  |  set_field (self, name, value)
  |  
  |  -------------------------------------------------- --------------------
  |  Data descriptors inherited from TestClass:
  |  
  |  __dict__
  |  dictionary for instance variables (if defined)
  |  
  |  __weakref__
  |  list of weak references to the object (if defined)

 Help on NoneType object:

 class NoneType (object)
  |  Methods defined here:
  |  
  |  __hash __ (...)
  |  x .__ hash __ () hash (x)
  |  
  |  __repr __ (...)
  |  x .__ repr __ () repr (x)

Any idea how to get descriptor.__doc__ for help(class.field) ? And is there a way around this and have something like getter function for doc instead of storing doc string in descriptor?

as:

 class FieldDescriptor(object): def __init__(self, name, doc='No documentation available.'): self.name = name self.__doc__ = doc def __get__(self, obj, dtype=None): if obj is None and dtype is not None: print 'Doc is:', self.__doc__ return self return obj.get_field(self.name) def __set__(self, obj, value): obj.set_field(self.name, value) # This is what I'd like to have def __doc__(self, obj, dtype): return dtype.generate_docstring(self.name) 

UPDATE: Actually, I started with this __get__ definition:

 def __get__(self, obj, dtype=None): return obj.get_field(self.name) 

The problem with this was that when I said:

 help(DerivedClass.a) 

Python None.get_field exception indicating that I was trying to call None.get_field . So help() calls the __get__ method with obj=None and dtype=DerivedClass . That is why I decided to return an instance of FieldDescriptor when obj = None and dtype! = None. My impression is: help(xyz) trying to display xyz.__doc__ . By this logic, if __get__ returns descriptor_instance , then descriptor_instance.__doc__ should be printed using help (), which is the case for the entire [ help(DerivedClass) ] class, but not for the single [ help(DerivedClass.a) ] field.

+6
source share
1 answer

What happens when you ask for help(DerivedClass.a) - python evaluates the expression inside the parentheses - this is the object returned by the __get__ descriptor __get__ , and they search for help (including docstring) for that object.

A way to create this work, including generating a dynamic docstring, is to have your __get__ method return a dynamically generated object that has the desired document line. But this object itself should be the correct proxy object for the original, and will create some overhead for your code - and many special cases.

In any case, the only way to make it work as you want is to modify the objects returned by __get__ themselves so that they behave the way you would like them to.

Id assumes that if all you need to help is a little bit of information as you do, maybe you want the objects returned from your __get__ to be classes that define the __repr__ method (and not just a __doc__ ).

+1
source

Source: https://habr.com/ru/post/912611/


All Articles