Skip to content

Commit

Permalink
Allow specifying members to exclude from documentation.
Browse files Browse the repository at this point in the history
This is helpful for adding more narrative text around individual
sections. And since we're not automatically adding links that can be
used for backreferences, this allows the user to do so.

This also solves the problem of moving low-level elements to a higher
level.
  • Loading branch information
jamadden committed Mar 2, 2017
1 parent 26ee8d6 commit fcd11db
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 13 deletions.
43 changes: 35 additions & 8 deletions ZConfig/_schema_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@
import sys
import textwrap

try:
from itertools import ifilterfalse
from itertools import ifilter
except ImportError:
# Py3
from itertools import filterfalse as ifilterfalse
ifilter = filter

import ZConfig.loader

from ZConfig._compat import AbstractBaseClass
Expand Down Expand Up @@ -126,34 +134,53 @@ def body(self):
class AbstractSchemaPrinter(AbstractBaseClass):


def __init__(self, schema, stream=None, allowed_names=()):
def __init__(self, schema, stream=None, allowed_names=(), excluded_names=()):
self.schema = schema
stream = stream or sys.stdout
self._explained = set()
self._seen_typenames = set()
self.fmt = self._schema_formatter(schema, stream)

if allowed_names:

def _make_predicate(names):
names = {x.lower() for x in names}
def predicate(name_info):
name, _ = name_info
return name and name.lower() in names
return predicate

def _make_filter(names, filt):
iter_all = self._iter_schema_items
allowed_names = {x.lower() for x in allowed_names}
def filtered():
for name, info in iter_all():
if name and name.lower() in allowed_names:
yield name, info
pred = _make_predicate(names)
def it():
return filt(pred, iter_all())
return it

self._iter_schema_items = filtered
if allowed_names:
self._iter_schema_items = _make_filter(allowed_names, ifilter)

if excluded_names:
excluded_names = {x.lower() for x in excluded_names}
self._iter_schema_items = _make_filter(excluded_names, ifilterfalse)
self._included = lambda st: st.name not in excluded_names

@abstractmethod
def _schema_formatter(self, schema, stream):
"Return a formatter"

def _included(self, st):
return True

def _explain(self, st):
if st.name in self._explained: # pragma: no cover
return

self._explained.add(st.name)

self.fmt.description(st.description)
if not self._included(st):
return

self.fmt.example(getattr(st, 'example', None))

for sub in st.getsubtypenames():
Expand Down
12 changes: 8 additions & 4 deletions ZConfig/sphinx.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@


from contextlib import contextmanager
import textwrap

try:
from docutils import nodes
Expand Down Expand Up @@ -154,10 +153,11 @@ def printSchema(self):

class SchemaToRstDirective(Directive):
required_arguments = 1
optional_arguments = 1
optional_arguments = 2
option_spec = {
'file': str,
'members': str
'members': str,
'excluded-members': str,
}
def run(self):
schema = load_schema(self.arguments[0],
Expand All @@ -167,7 +167,11 @@ def run(self):
if 'members' in self.options:
members = self.options['members'].split()

printer = RstSchemaPrinter(schema, allowed_names=members)
excluded_members = ()
if 'excluded-members' in self.options:
excluded_members = self.options['excluded-members'].split()

printer = RstSchemaPrinter(schema, allowed_names=members, excluded_names=excluded_members)
printer.fmt.settings = self.state.document.settings

printer.buildSchema()
Expand Down
21 changes: 21 additions & 0 deletions ZConfig/tests/test_schema2html.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,27 @@ def test_parse_package_limited_names(self):
self.assertIn("SyslogHandlerFactory", doc_text)
self.assertIn("FileHandlerFactory", doc_text)

def test_parse_package_excluded_names(self):
text = """
Document
========
.. zconfig:: ZConfig.components.logger
:members: ZConfig.logger.base-logger
:excluded-members: ZConfig.logger.handler
"""
document = self._parse(text)
doc_text = document.astext()

# Check that it produced output, limited to
# just that one part of the tree
# In this case, the root base-logger, but the handlers subtree
# was excluded.
self.assertIn("zconfig.logger.base-logger", doc_text)
self.assertNotIn("SMTPHandler", doc_text)
self.assertNotIn("syslog", doc_text)
self.assertNotIn("SyslogHandlerFactory", doc_text)
self.assertNotIn("FileHandlerFactory", doc_text)


def test_description_dedent(self):
text = """No leading whitespace on this line.
Expand Down
8 changes: 7 additions & 1 deletion doc/using-zconfig.rst
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,13 @@ For example, the value for ``key`` will evaluate to ``value``::
Configuring Logging
===================

Configuring handlers:
Configuring loggers:

.. zconfig:: ZConfig.components.logger
:members: ZConfig.logger.base-logger
:excluded-members: zconfig.logger.handler

Configuring handlers:

.. zconfig:: ZConfig.components.logger
:members: zconfig.logger.handler

0 comments on commit fcd11db

Please sign in to comment.