Merge "Enforce inclusion of pulic proxy methods in docs"
This commit is contained in:
		| @@ -18,6 +18,7 @@ import sys | ||||
| import openstackdocstheme | ||||
|  | ||||
| sys.path.insert(0, os.path.abspath('../..')) | ||||
| sys.path.insert(0, os.path.abspath('.')) | ||||
| # -- General configuration ---------------------------------------------------- | ||||
|  | ||||
| # Add any Sphinx extension module names here, as strings. They can be | ||||
| @@ -25,8 +26,12 @@ sys.path.insert(0, os.path.abspath('../..')) | ||||
| extensions = [ | ||||
|     'sphinx.ext.autodoc', | ||||
|     'sphinx.ext.intersphinx', | ||||
|     'enforcer' | ||||
| ] | ||||
|  | ||||
| # When True, this will raise an exception that kills sphinx-build. | ||||
| enforcer_warnings_as_errors = False | ||||
|  | ||||
| # autodoc generation is a bit aggressive and a nuisance when doing heavy | ||||
| # text edit cycles. | ||||
| # execute "export SPHINX_DEBUG=1" in your terminal to disable | ||||
|   | ||||
							
								
								
									
										113
									
								
								doc/source/enforcer.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										113
									
								
								doc/source/enforcer.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,113 @@ | ||||
| import importlib | ||||
| import os | ||||
|  | ||||
| from bs4 import BeautifulSoup | ||||
| from sphinx import errors | ||||
|  | ||||
| # NOTE: We do this because I can't find any way to pass "-v" | ||||
| # into sphinx-build through pbr... | ||||
| DEBUG = True if os.getenv("ENFORCER_DEBUG") else False | ||||
|  | ||||
| WRITTEN_METHODS = set() | ||||
|  | ||||
|  | ||||
| class EnforcementError(errors.SphinxError): | ||||
|     """A mismatch between what exists and what's documented""" | ||||
|     category = "Enforcer" | ||||
|  | ||||
|  | ||||
| def get_proxy_methods(): | ||||
|     """Return a set of public names on all proxies""" | ||||
|     names = ["openstack.bare_metal.v1._proxy", | ||||
|              "openstack.block_store.v2._proxy", | ||||
|              "openstack.cluster.v1._proxy", | ||||
|              "openstack.compute.v2._proxy", | ||||
|              "openstack.database.v1._proxy", | ||||
|              "openstack.identity.v2._proxy", | ||||
|              "openstack.identity.v3._proxy", | ||||
|              "openstack.image.v1._proxy", | ||||
|              "openstack.image.v2._proxy", | ||||
|              "openstack.key_manager.v1._proxy", | ||||
|              "openstack.message.v1._proxy", | ||||
|              "openstack.message.v2._proxy", | ||||
|              "openstack.metric.v1._proxy", | ||||
|              "openstack.network.v2._proxy", | ||||
|              "openstack.object_store.v1._proxy", | ||||
|              "openstack.orchestration.v1._proxy", | ||||
|              "openstack.telemetry.v2._proxy", | ||||
|              "openstack.telemetry.alarm.v2._proxy", | ||||
|              "openstack.workflow.v2._proxy"] | ||||
|  | ||||
|     modules = (importlib.import_module(name) for name in names) | ||||
|  | ||||
|     methods = set() | ||||
|     for module in modules: | ||||
|         # We're not going to use the Proxy for anything other than a `dir` | ||||
|         # so just pass a dummy value so we can create the instance. | ||||
|         instance = module.Proxy("") | ||||
|         # We only document public names | ||||
|         names = [name for name in dir(instance) if not name.startswith("_")] | ||||
|         good_names = [module.__name__ + ".Proxy." + name for name in names] | ||||
|         methods.update(good_names) | ||||
|  | ||||
|     return methods | ||||
|  | ||||
|  | ||||
| def page_context(app, pagename, templatename, context, doctree): | ||||
|     """Handle html-page-context-event | ||||
|  | ||||
|     This event is emitted once the builder has the contents to create | ||||
|     an HTML page, but before the template is rendered. This is the point | ||||
|     where we'll know what documentation is going to be written, so | ||||
|     gather all of the method names that are about to be included | ||||
|     so we can check which ones were or were not processed earlier | ||||
|     by autodoc. | ||||
|     """ | ||||
|     if "users/proxies" in pagename: | ||||
|         soup = BeautifulSoup(context["body"], "html.parser") | ||||
|         dts = soup.find_all("dt") | ||||
|         ids = [dt.get("id") for dt in dts] | ||||
|  | ||||
|         written = 0 | ||||
|         for id in ids: | ||||
|             if id is not None and "_proxy.Proxy" in id: | ||||
|                 WRITTEN_METHODS.add(id) | ||||
|                 written += 1 | ||||
|  | ||||
|         if DEBUG: | ||||
|             app.info("ENFORCER: Wrote %d proxy methods for %s" % ( | ||||
|                      written, pagename)) | ||||
|  | ||||
|  | ||||
| def build_finished(app, exception): | ||||
|     """Handle build-finished event | ||||
|  | ||||
|     This event is emitted once the builder has written all of the output. | ||||
|     At this point we just compare what we know was written to what we know | ||||
|     exists within the modules and share the results. | ||||
|  | ||||
|     When enforcer_warnings_as_errors=True in conf.py, this method | ||||
|     will raise EnforcementError on any failures in order to signal failure. | ||||
|     """ | ||||
|     all_methods = get_proxy_methods() | ||||
|  | ||||
|     app.info("ENFORCER: %d proxy methods exist" % len(all_methods)) | ||||
|     app.info("ENFORCER: %d proxy methods written" % len(WRITTEN_METHODS)) | ||||
|     missing = all_methods - WRITTEN_METHODS | ||||
|     missing_count = len(missing) | ||||
|     app.info("ENFORCER: Found %d missing proxy methods " | ||||
|              "in the output" % missing_count) | ||||
|  | ||||
|     for name in sorted(missing): | ||||
|         app.warn("ENFORCER: %s was not included in the output" % name) | ||||
|  | ||||
|     if app.config.enforcer_warnings_as_errors: | ||||
|         raise EnforcementError( | ||||
|             "There are %d undocumented proxy methods" % missing_count) | ||||
|  | ||||
|  | ||||
| def setup(app): | ||||
|     app.add_config_value("enforcer_warnings_as_errors", False, "env") | ||||
|  | ||||
|     app.connect("html-page-context", page_context) | ||||
|     app.connect("build-finished", build_finished) | ||||
| @@ -3,6 +3,7 @@ | ||||
| # process, which may cause wedges in the gate later. | ||||
| hacking<0.11,>=0.10.0 | ||||
|  | ||||
| beautifulsoup4 # MIT | ||||
| coverage>=4.0 # Apache-2.0 | ||||
| fixtures>=3.0.0 # Apache-2.0/BSD | ||||
| mock>=2.0 # BSD | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Jenkins
					Jenkins