Skip to content

Commit

Permalink
Merge pull request #44 from regisb/regisb/toggles-config-and-tools
Browse files Browse the repository at this point in the history
[BD-21] Add featuretoggle configuration and sphinx extension
  • Loading branch information
robrap authored Aug 13, 2020
2 parents a6c0310 + cb555d6 commit 97cd339
Show file tree
Hide file tree
Showing 10 changed files with 217 additions and 14 deletions.
15 changes: 7 additions & 8 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

**JIRA:** Link to JIRA ticket

**Dependencies:** dependencies on other outstanding PRs, issues, etc.
**Dependencies:** dependencies on other outstanding PRs, issues, etc.

**Merge deadline:** List merge deadline (if any)

**Installation instructions:** List any non-trivial installation
**Installation instructions:** List any non-trivial installation
instructions.

**Testing instructions:**
Expand All @@ -16,8 +16,8 @@ instructions.
3. If C happened instead - check failed.

**Reviewers:**
- [ ] tag reviewer
- [ ] tag reviewer
- [ ] tag reviewer
- [ ] tag reviewer

**Merge checklist:**
- [ ] All reviewers approved
Expand All @@ -26,14 +26,13 @@ instructions.
- [ ] Changelog record added
- [ ] Documentation updated (not only docstrings)
- [ ] Commits are squashed
- [ ] PR author is listed in AUTHORS

**Post merge:**
- [ ] Create a tag
- [ ] Check new version is pushed to PyPi after tag-triggered build is
- [ ] Check new version is pushed to PyPi after tag-triggered build is
finished.
- [ ] Delete working branch (if not needed anymore)

**Author concerns:** List any concerns about this PR - inelegant
solutions, hacks, quick-and-dirty implementations, concerns about
**Author concerns:** List any concerns about this PR - inelegant
solutions, hacks, quick-and-dirty implementations, concerns about
migrations, etc.
8 changes: 5 additions & 3 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@ Change Log

.. There should always be an "Unreleased" section for changes pending release.
Unreleased
~~~~~~~~~~
[0.5.0] - 2020-08-06
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

* Add ADR 0001-config-and-tools.rst for adding a placing in this repository for shared annotation configs and supporting tools.
* Add ``featuretoggles`` Sphinx extension
* Include ``config_and_tools`` folder in pip-installable package
* Add ADR 0001-config-and-tools.rst for adding a place in this repository for shared annotation configs and supporting tools.

[0.4.0] - 2020-07-22
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Expand Down
2 changes: 1 addition & 1 deletion MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ include CONTRIBUTING.rst
include LICENSE.txt
include README.rst
include requirements/base.in
recursive-include code_annotations *.html *.png *.gif *js *.css *jpg *jpeg *svg *py
recursive-include code_annotations *.html *.png *.gif *js *.css *jpg *jpeg *svg *py *.yaml *.yml
2 changes: 1 addition & 1 deletion code_annotations/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
Extensible tools for parsing annotations in codebases.
"""

__version__ = '0.4.0'
__version__ = '0.5.0'
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# This code-annotations configuration file supports OEP-17, Feature Toggles.

source_path: ./
report_path: reports
safelist_path: .annotation_safe_list.yml
coverage_target: 50.0
annotations:
documented_elsewhere:
- ".. documented_elsewhere:":
- ".. documented_elsewhere_name:":
feature_toggle:
- ".. toggle_name:":
- ".. toggle_implementation:":
choices: [ExperimentWaffleFlag, WaffleFlag, WaffleSample, WaffleSwitch, CourseWaffleFlag, ConfigurationModel, DjangoSetting]
- ".. toggle_default:":
- ".. toggle_description:":
- ".. toggle_category:":
- ".. toggle_use_cases:":
choices: [incremental_release, launch_date, monitored_rollout, graceful_degradation, beta_testing, vip, opt_out, opt_in, open_edx]
- ".. toggle_creation_date:":
- ".. toggle_expiration_date:":
- ".. toggle_warnings:":
- ".. toggle_tickets:":
- ".. toggle_status:":
extensions:
python:
- py
rst_template: doc.rst.j2
151 changes: 151 additions & 0 deletions code_annotations/config_and_tools/sphinx/extensions/featuretoggles.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
"""
Sphinx extension for viewing feature toggle annotations.
"""
import os

import pkg_resources

from code_annotations.base import AnnotationConfig
from code_annotations.find_static import StaticSearch
from docutils import nodes
from sphinx.util.docutils import SphinxDirective


def find_feature_toggles(source_path):
"""
Find the feature toggles as defined in the configuration file.
Return:
toggles (dict): feature toggles indexed by name.
"""
config_path = pkg_resources.resource_filename(
"code_annotations",
os.path.join("config_and_tools", "config", "feature_toggle_annotations.yaml"),
)
config = AnnotationConfig(
config_path, verbosity=0, source_path_override=source_path
)
results = StaticSearch(config).search()

toggles = {}
current_entry = {}
for filename, entries in results.items():
for entry in entries:
key = entry["annotation_token"]
value = entry["annotation_data"]
if key == ".. toggle_name:":
toggle_name = value
toggles[toggle_name] = {
"filename": filename,
"line_number": entry["line_number"],
}
current_entry = toggles[toggle_name]
else:
current_entry[key] = value

return toggles


class FeatureToggles(SphinxDirective):
"""
Sphinx directive to list the feature toggles in a single documentation page.
Use this directive as follows::
.. featuretoggles::
This directive supports the following configuration parameters:
- ``featuretoggles_source_path``: absolute path to the repository file tree. E.g:
featuretoggles_source_path = os.path.join(os.path.dirname(__file__), "..", "..")
- ``featuretoggles_repo_url``: Github repository where the code is hosted. E.g:
featuretoggles_repo_url = "https://github.com/edx/myrepo"
- ``featuretoggles_repo_version``: current version of the git repository. E.g:
import git
try:
repo = git.Repo(search_parent_directories=True)
featuretoggles_repo_version = repo.head.object.hexsha
except git.InvalidGitRepositoryError:
featuretoggles_repo_version = "master"
"""

required_arguments = 0
optional_arguments = 0
option_spec = {}

def run(self):
"""
Public interface of the Directive class.
Return:
nodes (list): nodes to be appended to the resulting document.
"""
toggle_nodes = list(self.iter_nodes())
return [nodes.section("", *toggle_nodes, ids=["featuretoggles"])]

def iter_nodes(self):
"""
Iterate on the docutils nodes generated by this directive.
"""
toggles = find_feature_toggles(self.env.config.featuretoggles_source_path)
for toggle_name in sorted(toggles):
toggle = toggles[toggle_name]
yield nodes.title(text=toggle_name)
toggle_default_value = toggle.get(".. toggle_default:", "Not defined")
toggle_default_node = nodes.literal(text=quote_value(toggle_default_value))
yield nodes.paragraph("", "Default: ", toggle_default_node)
yield nodes.paragraph(
"",
"Source: ",
nodes.reference(
text="{} (line {})".format(
toggle["filename"], toggle["line_number"]
),
refuri="{}/blob/{}/{}#L{}".format(
self.env.config.featuretoggles_repo_url,
self.env.config.featuretoggles_repo_version,
toggle["filename"],
toggle["line_number"],
),
),
)
yield nodes.paragraph(text=toggle.get(".. toggle_description:", ""))


def quote_value(value):
"""
Quote a Python object if it is string-like.
"""
if value in ("True", "False", "None"):
return str(value)
try:
float(value)
return str(value)
except ValueError:
pass
if isinstance(value, str):
return '"{}"'.format(value)
return str(value)


def setup(app):
"""
Declare the Sphinx extension.
"""
app.add_config_value(
"featuretoggles_source_path", os.path.abspath(".."), "env",
)
app.add_config_value("featuretoggles_repo_url", "", "env")
app.add_config_value("featuretoggles_repo_version", "master", "env")
app.add_directive("featuretoggles", FeatureToggles)

return {
"version": "0.1",
"parallel_read_safe": True,
"parallel_write_safe": True,
}
2 changes: 1 addition & 1 deletion docs/extensions.rst
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Extensions
----------

Code Annotations uses `Stevedore`_ to allow new lanuages to be statically searched in an easily extensible fashion. All
Code Annotations uses `Stevedore`_ to allow new languages to be statically searched in an easily extensible fashion. All
language searches, even the ones that come by default, are implemented as extensions. A language extension is
responsible for finding all comments in files of the given type. Note that extensions are only used in the Static Search
and not in Django Search, as Django models are obviously all written in Python.
Expand Down
1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Contents:
static_search
django_search
configuration
sphinx_extensions
extensions
testing
modules
Expand Down
22 changes: 22 additions & 0 deletions docs/sphinx_extensions.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
Sphinx extensions
-----------------

``featuretoggles``
==================

This package can be used to document the feature toggles in your code base. To do so,
add the following to your ``conf.py``::

extensions = ["code_annotations.config_and_tools.sphinx.extensions.featuretoggles"]
featuretoggles_source_path = os.path.abspath(
os.path.join(os.path.dirname(__file__), "..")
)
featuretoggles_repo_url = "https://github.com/edx/yourrepo"
try:
featuretoggles_repo_version = git.Repo(search_parent_directories=True).head.object.hexsha
except git.InvalidGitRepositoryError:
pass

Then, in an ``.rst`` file::

.. featuretoggles::

0 comments on commit 97cd339

Please sign in to comment.