Sphinx document module properties - python

I have a module that should have a #property, I solved this by setting a class as the module. I got the idea from this answer: Lazy module variables--can it be done?
I wanted this to be repeatable and easy to use so I made a metaclass for it. This works like a charm.
The problem is that when using Sphinx to generate documentation properties don't get documented. Everything else is documented as expected. I have no idea how to fix this, maybe this is a problem with Sphinx?
The module:
import sys
import types
class ClassAsModule(type):
def __new__(cls, name, bases, attrs):
# Make sure the name of the class is the module name.
name = attrs.pop('__module__')
# Create a class.
cls = type.__new__(cls, name, bases, attrs)
# Instantiate the class and register it.
sys.modules[name] = cls = cls(name)
# Update the dict so dir works properly
cls.__dict__.update(attrs)
class TestClass(types.ModuleType):
"""TestClass docstring."""
__metaclass__ = ClassAsModule
#property
def some_property(self):
"""Property docstring."""
pass
def meth():
"""meth doc"""
pass
And a copy-paste to generate/view Sphinx documentation:
sphinx-apidoc . -o doc --full
sphinx-build doc html
xdg-open html/module.html
The most essential part is to document the class' properties. Bonus points to also document original module members.
EDIT: The class should be documented as the module it is in. The class is used this way and should thus appear this way in Sphinx.
Example of desired output:
Module Foo
TestClass docstring.
some_property
Property docstring.
meth()
meth doc
EDIT 2: I found something that may aid in finding a solution. When having a regular module foo with the following content:
#: Property of foo
prop = 'test'
Sphinx documents this like:
foo.prop = 'test'
Property of foo
The same works if prop is an attribute of a class. I haven't figured out why it doesn't work in my special case.

Here's my understanding.
The theory is: making a mutant your class act like a module this (a bit hacky) way makes sphinx think that he doesn't need (to parse) properties from modules (because it's a class-level paradigm). So, for sphinx, TestClass is a module.
First of all, to make sure that the culprit is the code for making a class act like a module - let's remove it:
class ClassAsModule(type):
pass
we'll see in docs:
package Package
script Module
class package.script.ClassAsModule
Bases: type
class package.script.TestClass
Bases: module
TestClass docstring.
meth()
meth doc
some_property
Property docstring.
As you see, sphinx read the property without any problems. Nothing special here.
Possible solution for your problem is to avoid using #property decorator and replace it with calling property class constructor. E.g.:
import sys
import types
class ClassAsModule(type):
def __new__(cls, name, bases, attrs):
# Make sure the name of the class is the module name.
name = attrs.pop('__module__')
# Create a class.
cls = type.__new__(cls, name, bases, attrs)
# Instantiate the class and register it.
sys.modules[name] = cls = cls(name)
# Update the dict so dir works properly
cls.__dict__.update(attrs)
class TestClass(types.ModuleType):
"""TestClass docstring."""
__metaclass__ = ClassAsModule
def get_some_property(self):
"""Property docstring."""
pass
some_property = property(get_some_property)
def meth(self):
"""meth doc"""
pass
For this code sphinx generates:
package Package
script Module
TestClass docstring.
package.script.get_some_property(self)
Property docstring.
package.script.meth(self)
meth doc
May be the answer is a piece of nonsense, but I hope it'll point you to the right direction.

The way I've found that works best is to keep the file contents the same as if you were writing a regular module, then at the end replace the embryonic module in sys.modules:
"""Module docstring. """
import sys
import types
def _some_property(self):
pass
some_property = property(_some_property)
"""Property docstring."""
def meth():
"""meth doc"""
pass
def _make_class_module(name):
mod = sys.modules[name]
cls = type('ClassModule', (types.ModuleType,), mod.__dict__)
clsmod = cls(name)
clsmod.__dict__.update(mod.__dict__)
clsmod.__wrapped__ = mod
sys.modules[name] = clsmod
_make_class_module(__name__)
Text documentation:
mymod Module
************
Module docstring.
mymod.meth()
meth doc
mymod.some_property = None
Property docstring.
For the version of Sphinx I'm using (v1.1.3), it looks like you have to apply the property constructor explicitly (you can't use it as a decorator), and the docstring has to go in the file at the top level, on the line after the constructor call that creates the property (it doesn't work as a docstring inside the property getter). The source is still fairly readable, though.

Related

Mypy plugin for dectorator that registers attributes

I am trying to write a mypy plugin that allows mypy to know about instance attributes that are getting added dynamically via some decorator of my package.
This decorator works as follows:
# module
class A:
pass
def register_with_a(name):
def inner(cls):
A.name = cls # actual code does some destriptor magic to work with instances
return cls
return inner
This lets the user write custom accessors, e.g.:
# user code
#module.register_with_a("x")
class X:
pass
a = module.A()
a.x # should be class X
Now I want to enable this via a mypy plugin (first question: does this even work?)
For now I have this:
# mypy_plugin
from mypy.plugin import Plugin
from mypy.plugins.common import add_attribute_to_class
def register_fallback(ctx):
name = ctx.reason.args[0].value
add_attribute_to_class(
api=ctx.api,
cls=???, # How to get the ClassDef from module.A?
name=name,
typ=???, # What is this exactly?
)
class APlugin(Plugin):
def get_class_decorator_hook(fullname):
if fullname == "module.register_with_a":
return register_callback
return None
def plugin(version):
return APlugin
I managed to get the "name" argument of the decorator.
Am I even allowed to call add_attribute_to_class in this context?
How do I get the ClassDef of my module class?
What is the Type of the decorated class and how do I get it?

What is the best way to change the class that is called by an inherited method after class initialization in Python?

I am attempting to create a parallel class structure in two separate modules, where the methods of classes in one module e.g. source.py are registered to identically named modules in receiving.py via inheritance.
My issue is that if an inherited method in receiving.py calls another class in the module, it will point back to the class in source.py, and not in receiving.py. I understand this is expected, but how can I change which class the method points to during or after mapping, so the method in the receiving.py class will call the class from receiving.py.
Here is a schematic to help illustrate the problem. schematic
Minimally sufficient example:
source.py
class Foo:
origin = 'src'
#classmethod
def get_origin(cls):
return cls.origin
#classmethod
def get_origin_bar(cls):
return Bar.origin
class Bar:
origin = 'src'
receiving.py
class Tag:
pass
class Foo(Tag):
origin = 'rec'
class Bar(Tag):
origin = 'rec'
def register_bases(base, module):
"""
Recursive function that adds __bases__ from DataJoint Tables in base to matching classes in module.
:param base (module): source module with classes to.
:param module (module): mapping between classes and methods
"""
for name in dir(base):
base_cls = getattr(base, name)
module_cls = getattr(module, name) if hasattr(module, name) else None
if module_cls and inspect.isclass(module_cls) and issubclass(module_cls, receiving.Tag):
assert base_cls not in module_cls.__bases__, f'Class "{name}" of base already in __base__ of module.'
module_cls.__bases__ = (base_cls, *module_cls.__bases__)
register_bases(base_cls, module_cls)
import inspect
import source, receiving
# Before registering bases
receiving.Foo.get_origin()
-> AttributeError: type object 'Foo' has no attribute 'get_origin'
receiving.Foo.get_origin_bar()
-> AttributeError: type object 'Foo' has no attribute 'get_origin_bar'
# register bases
register_bases(source, receiving)
# after registering bases
receiving.Foo.get_origin()
-> 'rec'
receiving.Foo.get_origin_bar()
-> 'src'
What I need is for the last line to say 'rec', indicating that method get_origin_bar called receiving.Bar.origin instead of source.Bar.origin.

Is it possible to change the name of a class (type) with a python decorator?

I'm trying to autocode 100's of database table models by decorating my Django (ORM) models (class definitions) to derive their class names from the file name. But I think my "depth of decoration" is too shallow. Do I need a function def or class definition within my __call__ method? Can it not be done with something simple like this?
# decorators.py
import os
from inspect import get_module
class prefix_model_name_with_filename(object):
'Decorator to prefix the class __name__ with the *.pyc file name'
def __init__(self, sep=None):
self.sep = sep or ''
def __call__(self, cls):
model_name = os.path.basename(getmodule(cls).__file__).split('.')[0]
setattr(cls, '__name__', model_name + self.sep + getattr(cls, '__name__'))
return cls
Example usage of the decorator
# models.py
from django.db import models
import decorators
#decorators.prefix_model_name_with_filename
class ShipMeth(models.Model):
ship_meth = models.CharField(max_length=1, primary_key=True)
The model doesn't exist in the module definition with the new name and I can't use it without it looking in the decorator class (rather than the model class) to find attributes!
>>> from sec_mirror.models import ShipMeth
>>> ShipMeth.__name__
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
/home/Hobson/.virtualenvs/dev/lib/python2.7/site-packages/django/core/management/commands/shell.pyc in <module>()
----> 1 ShipMeth.__name__
AttributeError: 'prefix_model_name_with_filename' object has no attribute '__name__'
Do I need to decorate the module somehow?
You're using the class itself as the decorator, so only its __init__ is called. Your __call__ is never called.
Use an instance instead:
#decorators.prefix_model_name_with_filename()
About your updated question: A decorator can't change the actual name used to refer to an object in the enclosing namespace. (You can brute-force it by sticking a new name into a particular namespace, but it's up to you to ensure that the namespace you put the name into is the one where the object was originally defined.) The decorator syntax:
#deco
class Foo(object):
...
Is equivalent to
class Foo(object):
...
Foo = deco(Foo)
Note that the last line says Foo =, because the original class was defined with class Foo. You can't change that. The decorated object is always reassigned to the same name it originally had.
There are hackish ways to get around this. You can make your decorator write a new name to the global namespace, or even look at the decorated object's __module__ attribute and write a new name to that namespace. However, this isn't really what decorators are for, and it's going to make your code confusing. Decorators are for modifying/wrapping objects, not modifying the namespaces from which those objects are accessed.
Maybe that's the work for a metaclass:
import os
def cls_changer(name, parents, attrs):
model_name = os.path.basename(__file__).split('.')[0]
return type(model_name+name, parents, attrs)
class A(object):
__metaclass__ = cls_changer
pass
print A.__name__
The previous example just create new classes with the name changed but if you want your module to reflect the changes you need to this (Note my python script is named untitled0.py):
import os
def cls_changer(name, parents, attrs):
model_name = os.path.basename(__file__).split('.')[0]
res = type(model_name+name, parents, attrs)
gl = globals()
gl[model_name+name] = res
return res
class A(object):
__metaclass__ = cls_changer
pass
print A.__name__
print untitled0A
Output:
untitled0A
<class '__main__.untitled0A'>

Python name of class in class body

Is it possible to get the class name within the body of a class definition?
For example,
class Foo():
x = magic() # x should now be 'Foo'
I know that I can do this statically outside of the class body using a class method:
class Bar():
#classmethod
def magic(cls):
print cls.__name__
Bar.magic()
However this isn't what I want, I want the class name in the class body
Ok - got one more solution - this one is actually not that complex!
import traceback
def magic():
return traceback.extract_stack()[-2][2]
class Something(object):
print magic()
It will print out "Something". I'm not sure if extracted stack format is standardised in any way, but it works for python 2.6 (and 2.7 and 3.1)
AFAIK, the class object is not available until the class definition has been "executed", so it's not possible to get it during class definition.
If you need the class name for later use but don't use it during class definition (e.g. to compute other field names, or some such thing), then you can still automate the process using a class decorator.
def classname ( field ):
def decorator ( klass ):
setattr(klass, field, klass.__name__)
return klass
return decorator
(Caveat: not tested.)
With this definition, you can get something like:
#classname(field='x')
class Foo:
pass
and you would get field x with the class name in it, as in:
print Foo.x
Here you have a working solution for your specific case, but beware (I wrote it mainly to demonstrate that it IS indeed possible to do something like this):
You shouldn't use it
It is very specific
It has many limitations
I was just having fun with this
It is black magic
It may not work for your use case
It is not threadsafe
Do I have already said that you shouldn't use it?
Anyway, here you have the code:
import inspect
def NameAwareClassType():
frameInfo = inspect.getouterframes(inspect.currentframe())[1]
codeContext = frameInfo[4][0]
className = codeContext.split(' ', 1)[1].split('(', 1)[0]
class ClassNameGlobalRemoverType(type):
def __new__(mcs, name, bases, dict):
if name == className:
del globals()['__clsname__']
return type.__new__(mcs, name, bases, dict)
class NameAwareClass(object):
__metaclass__ = ClassNameGlobalRemoverType
globals()['__clsname__'] = className
return NameAwareClass
class A(NameAwareClassType()):
print __clsname__
def __init__(self):
pass
print __clsname__
Edit: https://gist.github.com/1085475 — there you have a version which allows to use __clsname__ during method execution; makes not much sense, as self.__class__.__name__ is a better approach and the __clsname__ variable does not hold a string anymore (I'm having fun experimenting with this)
I don't know of an elegant way to do this in Python 2.x -- but it's an interpreted language which means that something relatively simple along the following lines will do what you want and would be safe if you're sure of the code being executed:
classdef = """\
class %(classname)s(object):
x = '%(classname)s'
print x
"""
exec classdef % {'classname': 'Foo'}
foo = Foo()
print foo
class Bar():
#classmethod
def magic(cls):
return cls.__name__
#property
def x(self):
return self.magic()
def y(self):
return self.x
>>> a = Bar()
>>> a.x
'Bar'
>>> a.y()
'Bar'
This way you can use x as an attribute, at least within any instance and static methods. In class methods, you can just get the class name from the cls attribute anyway.

How to call a static method of a class using method name and class name

Starting with a class like this:
class FooClass(object):
#staticmethod
def static_method(x):
print x
normally, I would call the static method of the class with:
FooClass.static_method('bar')
Is it possible to invoke this static method having just the class name and the method name?
class_name = 'FooClass'
method_name = 'static_method'
You shouldn't mess with locals() as suggested in other answers. If you have your classname as a string and need to resolve it, use registry of some sort. A dictionary will work fine. E.g.
class FooClass(object):
#staticmethod
def static_method(x):
print x
registry = {'FooClass':FooClass}
(I assume you will want to add many more classes to this registry)
the lookup then becomes almost trivial:
getattr(registry['FooClass'], 'static_method')("bar")
Here is a crude way:
>>> class FooClass(object):
#staticmethod
def static_method(x):
print x
>>> class_name = 'FooClass'
>>> method_name = 'static_method'
>>> getattr(locals().get(class_name), method_name)("bar")
bar
Breakup:
locals().get(class_name)
First, locate the class. In this case I'm using locals() as I know that the class is available in the local dictionary. This will fail if the class is not present in the local dictionary.
Next, find the method of the class.
getattr(locals().get(class_name), method_name)
This uses getattr().
Finally, call the method.
getattr(locals().get(class_name), method_name)("bar")
you can use getattr twice. the first time on the module that contains the class and the second time on the class itself
class_name = 'Foo'
method_name = 'Bar'
cls = getattr(mod, clsname)
method = getattr(cls, method_name)
method(args)
This is not as flexible as building a registry (which you can do with a decorator) but if you are not going to do that, than this is a far better alternative than messing with the stack and stylistically, cleaner than messing with sys.modules.
Note that a module can import itself with no adverse effects. so the classes don't have to be in a different module for this to work.
If the class type is accessible from the current module, you can access the class in the globals() dict and the method using getattr:
class FooClass(object):
#staticmethod
def static_method(x):
print x
class_name = 'FooClass'
method_name = 'static_method'
getattr(globals()[class_name], method_name)("test")

Categories

Resources