Skip to main content

A pyramid plugin that describes a pyramid application URL hierarchy via inspection.

Project description

A Pyramid plugin that makes a Pyramid application self-documenting via inspection/reflection to:

  1. Describe the application URL structure,

  2. Extract documentation from Python comments, and

  3. Generate formal syntax using commenting conventions.

The resulting documentation can be served by the application to an HTTP request or displayed on the command line. It has built-in support for plain-text hierachies, reStructuredText, HTML, PDF, JSON, YAML, WADL, and XML, however other custom formats can be added easily.

Exposing an application’s structure via HTTP is useful to dynamically generate an API description (via WADL, JSON, or YAML) or to create documentation directly from source code.

On the command-line it is useful to get visibility into an application’s URL structure and hierarchy so that it can be understood and maintained.

Project Info

TL;DR

Install:

$ pip install pyramid-describe

Command-line example:

$ pdescribe example.ini --format txt
/                       # The application root.
├── contact/            # Contact manager.
   ├── <POST>          # Creates a new 'contact' object.
   └── {CONTACTID}     # RESTful access to a specific contact.
       ├── <DELETE>    # Delete this contact.
       ├── <GET>       # Get this contact's details.
       └── <PUT>       # Update this contact's details.
├── login               # Authenticate against the server.
└── logout              # Remove authentication tokens.

Examples of the above application in all other formats with built-in support are available at: text (pure-ASCII), reStructuredText, HTML, PDF, JSON, YAML, WADL, and XML.

Enable the plugin:

def main(global_config, **settings):
  # ...
  config.include('pyramid_describe')
  # ...

And make the documentation available publicly at “/describe”:

[app:main]
describe.attach                          = /describe
describe.formats                         = html pdf
describe.format.html.default.cssPath     = myapp:style/doc-html.css
describe.format.html+pdf.default.cssPath = myapp:style/doc-pdf.css
describe.format.default.pdfkit.options   = {page-size: Letter}

Note that there are many options to control how the resulting documentation is made available – see Options.

Installation

Install with the usual python mechanism, e.g. here with pip:

$ pip install pyramid-describe

Usage

There are three mechanisms to use pyramid-describe: via standard pyramid inclusion which will add routes to the current application, by explicitly embedding a pyramid_describe.DescribeController instance, or by directly calling the pyramid_describe.Describer object methods.

Pyramid Inclusion

Pyramid-describe can be added via standard pyramid inclusion, either in the INI file or directly in your main function. For example:

def main(global_config, **settings):
  # ...
  config.include('pyramid_describe')

When using pyramid inclusion, pyramid-describe expects to find configuration options in the application settings. See the Options section for a list of all supported options, with a short example here:

[app:main]

describe.attach                        = /doc
describe.formats                       = html json pdf
describe.format.default.title          = My Application
describe.format.html.default.cssPath   = myapp:static/doc.css
describe.entries.filters               = myapp.describe.entry_filter

Note that multiple describers, each with different configurations, can be added via pyramid inclusion by using the describe.prefixes option.

DescribeController

Pyramid-describe can also be added to your application by embedding a DescribeController object. The DescribeController constructor takes the following parameters:

view:

An instance of pyramid.interfaces.IView, which is the view that should be inspected and reflected.

root:

The root path to the specified URL, so that host-relative URLs can be generated to the views found.

settings:

A dictionary of all the options to apply to this describer. Note that in this case, the options should not have any prefix.

Example:

from pyramid_describe import DescribeController

def main(global_config, **settings):
  # ...
  config.include('pyramid_controllers')

  settings = {
    'formats'                       : ['html', 'json', 'pdf'],
    'format.default.title'          : 'My Application',
    'format.html.default.cssPath'   : 'myapp:static/doc.css',
    'entries.filters'               : 'myapp.describe.entry_filter',
  }

  config.add_controller('MyAppDescriber', '/doc', DescribeController(settings))

Describer

Pyramid-describe can also be added to your application by directly calling the Describer’s functionality. This is an even lower-level approach than, but still quite similar to, embedding the DescribeController; the constructor takes the same settings parameter as the DescribeController, and then a call to the describe method actually generates the output. The describe method takes as parameters a context and a format, and returns a dictionary with the following attributes:

content_type:

The MIME content-type associated with the rendered output.

charset:

The character set that the output is encoded in.

content:

The actual rendering output.

Example:

from pyramid_describe import Describer

def my_describer(request):

  settings = {
    'formats'                       : ['html', 'json', 'pdf'],
    'format.default.title'          : 'My Application',
    'format.html.default.cssPath'   : 'myapp:static/doc.css',
    'entries.filters'               : 'myapp.describe.entry_filter',
  }

  describer = Describer(settings=settings)
  context   = dict(request=request)
  result    = describer.describe(context=context, format='pdf')

  request.response.content_type = result['content_type']
  request.response.charset      = result['charset']
  request.response.body         = result['content']

  return request.response

Documentation Conventions

By default, the documentation that is extracted from your handlers’ pydocs is parsed and converted using:

  • Docorator extraction

  • Common text-role definitions

  • Field-list aliasing of numpydoc sections

  • Numpydoc parsing

  • Inter-endpoint linking and referencing

This behavior can be disabled or extended by setting the entries.parsers setting (see Options). Here is an example that employs each of these functions (see below for an in-depth explanation):

class MyController(RestController):

  @expose
  def deactivate(self, request):
    '''
    @PUBLIC, @DEPRECATED(1.3.23)

    The current object is deleted. Please note that this endpoint is
    deprecated; please use the more RESTful :doc.link:`DELETE:..`
    endpoint instead.

    @INTERNAL: OOPS! This method was accidentally carried over from
    the Java implementation. The `soap-to-rest` tool needs to be
    analyzed to figure out why this happened.

    :doc.copy:`DELETE:..`
    '''

  @expose
  def get(self, request):
    '''
    :doc.import:`myapp:doc/mycontroller.rst`
    '''

  @expose
  def delete(self, request):
    '''
    @PUBLIC, @FROZEN

    The current object is deleted.

    :Parameters:

    recursive : bool, optional, default: false

      If true, recursively deletes any dependent objects too.

    permanent : bool, optional, default: false, @INTERNAL

      If true, the objects and all records are permanently purged
      from the network. Reserved for internal administrators.

    :Returns:

    HTTPOk

      The object(s) were successfully deleted.

    :Raises:

    HTTPForbidden

      The current user does not have sufficient privileges.

    HTTPNotFound

      The specified object does not exist.
    '''

Docorator Extraction

Docorators are decorators for documentation. For example, you may decorate a particular endpoint with @BETA to declare that this endpoint is not finalized yet.

Pyramid-describe will inspect an entry’s .doc text and convert them to class names. The class names are applied to different element levels depending on where they are found:

  • Docorators on the first line apply to the entire entry.

  • Docorators at the beginning of a paragraph apply to that paragraph only.

  • Docorators at the beginning of a section title apply to that section.

  • Docorators in the numpydoc type specification apply to that parameter/return/raise or other formal numpydoc object.

Docorators must follow one of the following syntaxes:

  • Simple tag style: @TAG, where TAG can be any alphanumeric sequence.

  • Parameterized declaration style: @TAG(PARAMS), where TAG can be any alphanumeric sequence, and PARAMS can be anything except the closing parenthesis.

Docorators are converted to class names using the following rules:

  • Prefixed with doc-.

  • All letters are lowercased.

  • All non-alphanumeric characters are replaced with a dash (“-“).

  • Consecutive dashes are replaced with one dash.

  • Terminating dashes are dropped.

Thus the docorator @DEPRECATED(1.3.23) becomes doc-deprecated-1-3-23.

IMPORTANT: pyramid-describe does not apply any special processing to docorators beyond identifying them and applying the class names to the appropriate content. It is therefore up to the calling application to filter these in any way, for example hiding entries (or portions thereof) that have the doc-internal, i.e. that were marked with @INTERNAL.

Common Text-Role Definitions

The text-roles class, meth, and func are not by default defined by docutils. Pyramid-describe gives a very bare-bones implementation (it just aliases them as “literal” style nodes). If these text-roles are used by the calling application, a more thorough implementation (that actually performs linking to API documentation) is probably desirable. Pyramid-describe does not have access to this information and is therefore outside of its scope.

Field List Aliasing of Sections

All of the section headers that are specially processed by numpydoc can also be specified as lone “field list” elements. For example, the following two declarations are treated identically:

def function_name(self, request):
  '''
  Parameters
  ----------

  This endpoint does not take any parameters.
  '''
def function_name(self, request):
  '''
  :Parameters:

  This endpoint does not take any parameters.
  '''

The list of supported headers is extracted at runtime from numpydoc.docscrape.NumpyDocString()._parsed_data.keys().

Numpydoc

By default, the pydoc text is parsed by numpydoc, and the Parameters, Other Parameters, Returns, and Raises sections are extracted and converted into formal structured properties of the entry. See numpydoc for format and syntax details.

Inter-Endpoint Linking

Pyramid-describe allows for entry documentation to refer and link to other endpoint documentation. Specifically, the following text-roles are provided:

  • :doc.link:\`[METHOD:]PATH\`:

    Links to the specified endpoint. If METHOD is specified, then the link points directly to that HTTP method. PATH can be either absolute (i.e. starting with a slash /) or relative (i.e. starting with either ./ or ../). Note that unlike “href” syntax, ./ refers to the current endpoint, not the current endpoint’s parent. Some examples, assuming the current endpoint is /foo/bar:

    • :doc.link:\`GET:/index\`: links to the GET method of “/index”

    • :doc.link:\`PUT:.\`: links to the PUT method of “/foo/bar”

    • :doc.link:\`POST:./zog\`: links to the POST method of “/foo/bar/zog”

    • :doc.link:\`POST:../zog\`: links to the POST method of “/foo/zog”

  • :doc.copy:\`[METHOD:]PATH[:SECTION]\`:

    Inlines the specified remote endpoint’s documentation here. The METHOD and PATH apply as for :doc.link:\`...\`. The optional SECTION parameter is a comma-separated list of which sections to inline – if not specified or empty, the entire endpoint’s documentation is inlined; if the wildcard *, then all named sections are inlined, but not the main description.

    Note that section referencing will only work correctly if the entries are decorated with the parsed sections. This is one of the things that numpydoc-style parsing does when enabled (so don’t disable it! :-).

  • :doc.import:\`ASSET-SPEC\`:

    Inlines the specified asset, which is loaded using either pkg_resources or python import. When using pkg_resources, the spec must be in the format [PACKAGE:]PATH. If the PACKAGE is omitted, then the PATH is taken to be relative to the current module.

    If the asset cannot be loaded using pkg_resources, a standard python import is tried. If this succeeds, it is either called (if callable) with no arguments or cast to a string with str(symbol).

Options

Project details


Release history Release notifications | RSS feed

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page