Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Add option for numpy-style docs via a class variable #266

Open
wants to merge 1 commit into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
159 changes: 100 additions & 59 deletions properties/base/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,15 @@
GENERIC_ERRORS = (ValueError, KeyError, TypeError, AttributeError)


def _document_properties(header, keys, props, doc_format):
"""Document given properties under a header"""
doc_str = '\n\n**{}:**\n\n'.format(header)
doc_str += '\n'.join(
('* ' + getattr(props[key], doc_format)() for key in keys)
)
return doc_str


class PropertyMetaclass(type):
"""Metaclass to establish behavior of **HasProperties** classes

Expand Down Expand Up @@ -122,70 +131,102 @@ def __new__(mcs, name, bases, classdict): #pyli
classdict[key] = handler.func
handler.func = key

# Determine if private properties should be documented or just public
_doc_private = False
# Determine documentation formatting
_doc_format = 'sphinx'
for base in reversed(bases):
_doc_private = getattr(base, '_doc_private', _doc_private)
_doc_private = classdict.get('_doc_private', _doc_private)
_doc_format = getattr(base, '_doc_format', _doc_format)
_doc_format = classdict.get('_doc_format', _doc_format)
if _doc_format and _doc_format not in ['sphinx', 'numpy']:
raise AttributeError(
"_doc_format must be 'sphinx', 'numpy', or None"
)
if _doc_format:

if not isinstance(_doc_private, bool):
raise AttributeError('_doc_private must be a boolean')
# Determine if private props should be documented or just public
_doc_private = False
for base in reversed(bases):
_doc_private = getattr(base, '_doc_private', _doc_private)
_doc_private = classdict.get('_doc_private', _doc_private)

if _doc_private:
documented_props = sorted(_props)
else:
documented_props = sorted(p for p in _props if p[0] != '_')
if not isinstance(_doc_private, bool):
raise AttributeError('_doc_private must be a boolean')

# Order the properties for the docs (default is alphabetical)
_doc_order = None
for base in reversed(bases):
_doc_order = getattr(base, '_doc_order', _doc_order)
if (
not isinstance(_doc_order, (list, tuple)) or
sorted(list(_doc_order)) != documented_props
):
_doc_order = None
_doc_order = classdict.get('_doc_order', _doc_order)
if _doc_order is None:
_doc_order = documented_props
elif not isinstance(_doc_order, (list, tuple)):
raise AttributeError(
'_doc_order must be a list of property names'
)
elif sorted(list(_doc_order)) != documented_props:
raise AttributeError(
'_doc_order must be unspecified or contain ALL property names'
)
if _doc_private:
documented_props = sorted(_props)
else:
documented_props = sorted(p for p in _props if p[0] != '_')

# Order the properties for the docs (default is alphabetical)
_doc_order = None
for base in reversed(bases):
_doc_order = getattr(base, '_doc_order', _doc_order)
if (
not isinstance(_doc_order, (list, tuple)) or
sorted(list(_doc_order)) != documented_props
):
_doc_order = None
_doc_order = classdict.get('_doc_order', _doc_order)
if _doc_order is None:
_doc_order = documented_props
elif not isinstance(_doc_order, (list, tuple)):
raise AttributeError(
'_doc_order must be a list of property names'
)
elif sorted(list(_doc_order)) != documented_props:
raise AttributeError(
'_doc_order must be unspecified or contain ALL '
'property names'
)

# Sort props into required, optional, and immutable
doc_str = classdict.get('__doc__', '')
req = [key for key in _doc_order
if key[0] != '_' and getattr(_props[key], 'required', False)]
opt = [key for key in _doc_order
if key[0] != '_' and not getattr(_props[key], 'required', True)]
imm = [key for key in _doc_order
if key[0] != '_' and not hasattr(_props[key], 'required')]
priv = [key for key in _doc_order
if key[0] == '_']

# Build the documentation based on above sorting
if req:
doc_str += '\n\n**Required Properties:**\n\n' + '\n'.join(
('* ' + _props[key].sphinx() for key in req)
)
if opt:
doc_str += '\n\n**Optional Properties:**\n\n' + '\n'.join(
('* ' + _props[key].sphinx() for key in opt)
)
if imm:
doc_str += '\n\n**Other Properties:**\n\n' + '\n'.join(
('* ' + _props[key].sphinx() for key in imm)
)
if priv:
doc_str += '\n\n**Private Properties:**\n\n' + '\n'.join(
('* ' + _props[key].sphinx() for key in priv)
)
classdict['__doc__'] = doc_str
# Sort props into required, optional, and immutable
doc_str = classdict.get('__doc__', '')
req = [
key for key in _doc_order
if key[0] != '_' and getattr(_props[key], 'required', False)
]
opt = [
key for key in _doc_order
if key[0] != '_' and not getattr(_props[key], 'required', True)
]
imm = [
key for key in _doc_order
if key[0] != '_' and not hasattr(_props[key], 'required')
]
priv = [
key for key in _doc_order
if key[0] == '_'
]

# Build the documentation based on above sorting
if req:
doc_str += _document_properties(
header='Required Properties',
keys=req,
props=_props,
doc_format=_doc_format,
)
if opt:
doc_str += _document_properties(
header='Optional Properties',
keys=opt,
props=_props,
doc_format=_doc_format,
)
if imm:
doc_str += _document_properties(
header='Other Properties',
keys=imm,
props=_props,
doc_format=_doc_format,
)
if priv:
doc_str += _document_properties(
header='Private Properties',
keys=priv,
props=_props,
doc_format=_doc_format,
)
classdict['__doc__'] = doc_str

# Create the new class
newcls = super(PropertyMetaclass, mcs).__new__(
Expand Down
4 changes: 4 additions & 0 deletions properties/basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,10 @@ def sphinx_class(self):
pref = text_type(self.__module__)
return classdoc.format(cls=self.__class__.__name__, pref=pref)

def numpy(self):
"""Generate numpy-formatted documentation for the Property"""
raise NotImplementedError()

def __call__(self, func):
return DynamicProperty(self.doc, func=func, prop=self)

Expand Down