From 6d5e60f346ba0c93e029cf67e6b26bc8a7083514 Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Fri, 9 Jan 2015 18:09:36 -0500 Subject: [PATCH 1/2] Introduce and display relengapi metadata --- docs/development/@relengapi/blueprints.rst | 14 +++ relengapi/app.py | 40 ++++----- relengapi/blueprints/auth/__init__.py | 1 - .../auth/templates/auth_root_widget.html | 13 --- relengapi/lib/introspection.py | 61 +++++++++++++ relengapi/lib/layout.py | 18 +++- relengapi/static/relengapi.css | 10 +++ relengapi/templates/layout.html | 88 +++++++++++++++---- relengapi/tests/test_lib_introspection.py | 29 ++++++ setup.cfg | 4 + 10 files changed, 222 insertions(+), 56 deletions(-) delete mode 100644 relengapi/blueprints/auth/templates/auth_root_widget.html create mode 100644 relengapi/lib/introspection.py create mode 100644 relengapi/tests/test_lib_introspection.py diff --git a/docs/development/@relengapi/blueprints.rst b/docs/development/@relengapi/blueprints.rst index a8eef8d7..110cb40e 100644 --- a/docs/development/@relengapi/blueprints.rst +++ b/docs/development/@relengapi/blueprints.rst @@ -77,6 +77,20 @@ Any conflicts may identify fixes required for continued compatibility with the c git pull skeleton master +Project Metadata +---------------- + +You can include metadata about your project in a file named ``setup.cfg`` in the same directory as ``setup.py``. +Include them in a ``[relengapi]`` section. +The currently supported keys are self explanatory, and shown below. + +.. code-block:: none + + [relengapi] + repository_of_record = https://git.mozilla.org/?p=build/relengapi.git + bug_report_url = https://github.com/mozilla/build-relengapi/issues + + Other Useful Stuff ------------------ diff --git a/relengapi/app.py b/relengapi/app.py index 1ded806b..9e7bc1b2 100644 --- a/relengapi/app.py +++ b/relengapi/app.py @@ -4,7 +4,6 @@ import logging import os -import pkg_resources import relengapi import wsme.types @@ -16,6 +15,7 @@ from relengapi.lib import aws from relengapi.lib import celery from relengapi.lib import db +from relengapi.lib import introspection from relengapi.lib import layout from relengapi.lib import memcached from relengapi.lib import monkeypatches @@ -31,26 +31,6 @@ logger = logging.getLogger(__name__) -_blueprints = None - - -def get_blueprints(): - # get blueprints from pkg_resources. We're careful to load all of the - # blueprints exactly once and before registering any of them, as this - # ensures everything is imported before any of the @bp.register-decorated - # methods are called - global _blueprints - if not _blueprints: - # TODO: warn if relengapi_blueprints is used - entry_points = (list(pkg_resources.iter_entry_points('relengapi_blueprints')) - + list(pkg_resources.iter_entry_points('relengapi.blueprints'))) - _blueprints = [] - for ep in entry_points: - bp = ep.load() - bp.dist = ep.dist - _blueprints.append(bp) - return _blueprints - class BlueprintInfo(wsme.types.Base): @@ -73,6 +53,10 @@ class DistributionInfo(wsme.types.Base): #: Version of the distribution version = unicode + # TODO: ref docs + #: Additional RelengAPI-specific metadata + relengapi_metadata = {unicode: unicode} + class VersionInfo(wsme.types.Base): @@ -86,7 +70,7 @@ class VersionInfo(wsme.types.Base): def create_app(cmdline=False, test_config=None): - blueprints = get_blueprints() + blueprints = introspection.get_blueprints() app = Flask(__name__) env_var = 'RELENGAPI_SETTINGS' @@ -140,9 +124,15 @@ def root(): @api.apimethod(VersionInfo) def versions(): dists = {} - for dist in pkg_resources.WorkingSet(): - dists[dist.key] = DistributionInfo(project_name=dist.project_name, - version=dist.version) + for dist in introspection.get_distributions().itervalues(): + try: + relengapi_metadata = dist.relengapi_metadata + except AttributeError: + relengapi_metadata = {} + dists[dist.key] = DistributionInfo( + project_name=dist.project_name, + version=dist.version, + relengapi_metadata=relengapi_metadata) blueprints = {} for bp in app.relengapi_blueprints.itervalues(): blueprints[bp.name] = BlueprintInfo(distribution=bp.dist.key, diff --git a/relengapi/blueprints/auth/__init__.py b/relengapi/blueprints/auth/__init__.py index dcac7abc..79f667c4 100644 --- a/relengapi/blueprints/auth/__init__.py +++ b/relengapi/blueprints/auth/__init__.py @@ -13,7 +13,6 @@ bp = Blueprint('auth', __name__, template_folder='templates', static_folder='static') -bp.root_widget_template('auth_root_widget.html', priority=-100) @bp.route("/") diff --git a/relengapi/blueprints/auth/templates/auth_root_widget.html b/relengapi/blueprints/auth/templates/auth_root_widget.html deleted file mode 100644 index c728d8b3..00000000 --- a/relengapi/blueprints/auth/templates/auth_root_widget.html +++ /dev/null @@ -1,13 +0,0 @@ -{# This Source Code Form is subject to the terms of the Mozilla Public - # License, v. 2.0. If a copy of the MPL was not distributed with this - # file, You can obtain one at http://mozilla.org/MPL/2.0/. #} -{%- extends "root_widget.html" %} -{% block widget_title %}Account{% endblock %} -{% block widget_content %} -{% if current_user.is_authenticated() %} -

You are authenticated as {{ current_user.authenticated_email }}.

-{% else %} -

You are not logged in. -{% endif %} -{% endblock %} - diff --git a/relengapi/lib/introspection.py b/relengapi/lib/introspection.py new file mode 100644 index 00000000..b1f625da --- /dev/null +++ b/relengapi/lib/introspection.py @@ -0,0 +1,61 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +import ConfigParser +import pkg_resources + +_blueprints = None +_distributions = None + + +def _fetch(): + # get blueprints, dists, and so on from pkg_resources. + # + # We're careful to load all of the blueprints exactly once and before + # registering any of them, as this ensures everything is imported before + # any of the @bp.register-decorated methods are called + global _blueprints + global _distributions + + if not _distributions: + _distributions = {} + for dist in pkg_resources.WorkingSet(): + relengapi_metadata = _get_relengapi_metadata(dist) + if relengapi_metadata: + dist.relengapi_metadata = relengapi_metadata + _distributions[dist.key] = dist + + if not _blueprints: + _blueprints = [] + entry_points = (list(pkg_resources.iter_entry_points('relengapi_blueprints')) + + list(pkg_resources.iter_entry_points('relengapi.blueprints'))) + for ep in entry_points: + bp = ep.load() + # make sure we have only one copy of each Distribution + bp.dist = _distributions[ep.dist.key] + _blueprints.append(bp) + + +def _get_relengapi_metadata(dist): + req = dist.as_requirement() + try: + setup_cfg = pkg_resources.resource_stream(req, 'setup.cfg') + except Exception: + return {} + cfg = ConfigParser.RawConfigParser() + cfg.readfp(setup_cfg) + if not cfg.has_section('relengapi'): + return {} + + return {o: cfg.get('relengapi', o) for o in cfg.options('relengapi')} + + +def get_blueprints(): + _fetch() + return _blueprints + + +def get_distributions(): + _fetch() + return _distributions diff --git a/relengapi/lib/layout.py b/relengapi/lib/layout.py index 4d83a63d..b43c03f8 100644 --- a/relengapi/lib/layout.py +++ b/relengapi/lib/layout.py @@ -2,6 +2,9 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. +from flask import current_app +from flask import request + class Layout(object): @@ -10,7 +13,20 @@ def __init__(self, app): @app.context_processor def _context_processor(): - return dict(layout_extra_head_content=self.extra_head_content) + if request.blueprint: + blueprint = current_app.blueprints[request.blueprint] + else: + blueprint = current_app.blueprints['base'] + try: + relengapi_metadata = blueprint.dist.relengapi_metadata + except AttributeError: + base_dist = current_app.blueprints['base'].dist + relengapi_metadata = base_dist.relengapi_metadata + return { + 'blueprint': blueprint, + 'relengapi_metadata': relengapi_metadata, + 'layout_extra_head_content': self.extra_head_content, + } def add_head_content(self, content): """ diff --git a/relengapi/static/relengapi.css b/relengapi/static/relengapi.css index 6eb35b2f..867d06ba 100644 --- a/relengapi/static/relengapi.css +++ b/relengapi/static/relengapi.css @@ -8,3 +8,13 @@ body { background: #FFF url('background.jpg') no-repeat fixed top left; } +.navbar-about { + width: 300px; + padding: 0.5em; +} + +.navbar-user-info { + width: 300px; + padding: 0.5em; + padding-bottom: 0; +} diff --git a/relengapi/templates/layout.html b/relengapi/templates/layout.html index d298f64d..f6497b6f 100644 --- a/relengapi/templates/layout.html +++ b/relengapi/templates/layout.html @@ -41,30 +41,86 @@