@ -10,3 +10,5 @@ dependencies:
|
||||
- psutil ==5.2.2
|
||||
- requests ==2.13.0
|
||||
- stevedore ==1.21.0
|
||||
# for examples
|
||||
- pycountry ==17.9.23
|
||||
|
||||
24
docs/_templates/alabaster/__init__.py
vendored
Normal file
24
docs/_templates/alabaster/__init__.py
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
import os
|
||||
|
||||
from alabaster import _version as version
|
||||
|
||||
|
||||
def get_path():
|
||||
"""
|
||||
Shortcut for users whose theme is next to their conf.py.
|
||||
"""
|
||||
# Theme directory is defined as our parent directory
|
||||
return os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
|
||||
|
||||
|
||||
def update_context(app, pagename, templatename, context, doctree):
|
||||
context['alabaster_version'] = version.__version__
|
||||
|
||||
def setup(app):
|
||||
# add_html_theme is new in Sphinx 1.6+
|
||||
if hasattr(app, 'add_html_theme'):
|
||||
theme_path = os.path.abspath(os.path.dirname(__file__))
|
||||
app.add_html_theme('alabaster', theme_path)
|
||||
app.connect('html-page-context', update_context)
|
||||
return {'version': version.__version__,
|
||||
'parallel_read_safe': True}
|
||||
2
docs/_templates/alabaster/_version.py
vendored
Normal file
2
docs/_templates/alabaster/_version.py
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
__version_info__ = (0, 7, 10)
|
||||
__version__ = '.'.join(map(str, __version_info__))
|
||||
57
docs/_templates/alabaster/about.html
vendored
Normal file
57
docs/_templates/alabaster/about.html
vendored
Normal file
@ -0,0 +1,57 @@
|
||||
{% if theme_logo %}
|
||||
<p class="logo">
|
||||
<a href="{{ pathto(master_doc) }}">
|
||||
<img class="logo" src="{{ pathto('_static/' ~ theme_logo, 1) }}" alt="Logo"/>
|
||||
{% if theme_logo_name|lower == 'true' %}
|
||||
<h1 class="logo logo-name">{{ project }}</h1>
|
||||
{% endif %}
|
||||
</a>
|
||||
</p>
|
||||
{% else %}
|
||||
<h1 class="logo"><a href="{{ pathto(master_doc) }}">{{ project }}</a></h1>
|
||||
{% endif %}
|
||||
|
||||
{% if theme_description %}
|
||||
<p class="blurb">{{ theme_description }}</p>
|
||||
{% endif %}
|
||||
|
||||
{% if theme_github_user and theme_github_repo %}
|
||||
{% if theme_github_button|lower == 'true' %}
|
||||
<p>
|
||||
<iframe src="https://ghbtns.com/github-btn.html?user={{ theme_github_user }}&repo={{ theme_github_repo }}&type={{ theme_github_type }}&count={{ theme_github_count }}&size=large&v=2"
|
||||
allowtransparency="true" frameborder="0" scrolling="0" width="200px" height="35px"></iframe>
|
||||
</p>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% if theme_travis_button|lower != 'false' %}
|
||||
{% if theme_travis_button|lower == 'true' %}
|
||||
{% set path = theme_github_user + '/' + theme_github_repo %}
|
||||
{% else %}
|
||||
{% set path = theme_travis_button %}
|
||||
{% endif %}
|
||||
<p>
|
||||
<a href="https://travis-ci.org/{{ path }}">
|
||||
<img
|
||||
alt="https://secure.travis-ci.org/{{ path }}.svg?branch={{ theme_badge_branch }}"
|
||||
src="https://secure.travis-ci.org/{{ path }}.svg?branch={{ theme_badge_branch }}"
|
||||
/>
|
||||
</a>
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
{% if theme_codecov_button|lower != 'false' %}
|
||||
{% if theme_codecov_button|lower == 'true' %}
|
||||
{% set path = theme_github_user + '/' + theme_github_repo %}
|
||||
{% else %}
|
||||
{% set path = theme_codecov_button %}
|
||||
{% endif %}
|
||||
<p>
|
||||
<a href="https://codecov.io/github/{{ path }}">
|
||||
<img
|
||||
alt="https://codecov.io/github/{{ path }}/coverage.svg?branch={{ theme_badge_branch }}"
|
||||
src="https://codecov.io/github/{{ path }}/coverage.svg?branch={{ theme_badge_branch }}"
|
||||
/>
|
||||
</a>
|
||||
</p>
|
||||
{% endif %}
|
||||
9
docs/_templates/alabaster/donate.html
vendored
Normal file
9
docs/_templates/alabaster/donate.html
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
{% if theme_gratipay_user or theme_gittip_user %}
|
||||
<h3>Donate</h3>
|
||||
<p>
|
||||
Consider supporting the authors on <a href="https://www.gratipay.com/">Gratipay</a>:
|
||||
<script data-gratipay-username="{{ theme_gratipay_user or theme_gittip_user }}"
|
||||
data-gratipay-widget="button"
|
||||
src="//gttp.co/v1.js"></script>
|
||||
</p>
|
||||
{% endif %}
|
||||
82
docs/_templates/alabaster/layout.html
vendored
Normal file
82
docs/_templates/alabaster/layout.html
vendored
Normal file
@ -0,0 +1,82 @@
|
||||
{%- extends "basic/layout.html" %}
|
||||
|
||||
{%- block extrahead %}
|
||||
{{ super() }}
|
||||
<link rel="stylesheet" href="{{ pathto('_static/custom.css', 1) }}" type="text/css" />
|
||||
{% if theme_touch_icon %}
|
||||
<link rel="apple-touch-icon" href="{{ pathto('_static/' ~ theme_touch_icon, 1) }}" />
|
||||
{% endif %}
|
||||
{% if theme_canonical_url %}
|
||||
<link rel="canonical" href="{{ theme_canonical_url }}{{ pagename }}.html"/>
|
||||
{% endif %}
|
||||
<meta name="viewport" content="width=device-width, initial-scale=0.9, maximum-scale=0.9" />
|
||||
{% endblock %}
|
||||
|
||||
{# Disable base theme's top+bottom related navs; we have our own in sidebar #}
|
||||
{%- block relbar1 %}{% endblock %}
|
||||
{%- block relbar2 %}{% endblock %}
|
||||
|
||||
{# Nav should appear before content, not after #}
|
||||
{%- block content %}
|
||||
{%- if theme_fixed_sidebar|lower == 'true' %}
|
||||
<div class="document">
|
||||
{{ sidebar() }}
|
||||
{%- block document %}
|
||||
<div class="documentwrapper">
|
||||
{%- if render_sidebar %}
|
||||
<div class="bodywrapper">
|
||||
{%- endif %}
|
||||
<div class="body" role="main">
|
||||
{% block body %} {% endblock %}
|
||||
</div>
|
||||
{%- if render_sidebar %}
|
||||
</div>
|
||||
{%- endif %}
|
||||
</div>
|
||||
{%- endblock %}
|
||||
<div class="clearer"></div>
|
||||
</div>
|
||||
{%- else %}
|
||||
{{ super() }}
|
||||
{%- endif %}
|
||||
{%- endblock %}
|
||||
|
||||
{%- block footer %}
|
||||
<div class="footer">
|
||||
{% if show_copyright %}©{{ copyright }}.{% endif %}
|
||||
{% if theme_show_powered_by|lower == 'true' %}
|
||||
{% if show_copyright %}|{% endif %}
|
||||
Powered by <a href="http://sphinx-doc.org/">Sphinx {{ sphinx_version }}</a>
|
||||
& <a href="https://github.com/bitprophet/alabaster">Alabaster {{ alabaster_version }}</a>
|
||||
{% endif %}
|
||||
{%- if show_source and has_source and sourcename %}
|
||||
{% if show_copyright or theme_show_powered_by %}|{% endif %}
|
||||
<a href="{{ pathto('_sources/' + sourcename, true)|e }}"
|
||||
rel="nofollow">{{ _('Page source') }}</a>
|
||||
{%- endif %}
|
||||
</div>
|
||||
|
||||
{% if theme_github_banner|lower != 'false' %}
|
||||
<a href="https://github.com/{{ theme_github_user }}/{{ theme_github_repo }}" class="github">
|
||||
<img style="position: absolute; top: 0; right: 0; border: 0;" src="{{ pathto('_static/' ~ theme_github_banner, 1) if theme_github_banner|lower != 'true' else 'https://s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png' }}" alt="Fork me on GitHub" class="github"/>
|
||||
</a>
|
||||
{% endif %}
|
||||
|
||||
{% if theme_analytics_id %}
|
||||
<script type="text/javascript">
|
||||
|
||||
var _gaq = _gaq || [];
|
||||
_gaq.push(['_setAccount', '{{ theme_analytics_id }}']);
|
||||
_gaq.push(['_setDomainName', 'none']);
|
||||
_gaq.push(['_setAllowLinker', true]);
|
||||
_gaq.push(['_trackPageview']);
|
||||
|
||||
(function() {
|
||||
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
|
||||
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
|
||||
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
|
||||
})();
|
||||
|
||||
</script>
|
||||
{% endif %}
|
||||
{%- endblock %}
|
||||
10
docs/_templates/alabaster/navigation.html
vendored
Normal file
10
docs/_templates/alabaster/navigation.html
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
<h3>{{ _('Navigation') }}</h3>
|
||||
{{ toctree(includehidden=theme_sidebar_includehidden, collapse=theme_sidebar_collapse) }}
|
||||
{% if theme_extra_nav_links %}
|
||||
<hr />
|
||||
<ul>
|
||||
{% for text, uri in theme_extra_nav_links.items() %}
|
||||
<li class="toctree-l1"><a href="{{ uri }}">{{ text }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
21
docs/_templates/alabaster/relations.html
vendored
Normal file
21
docs/_templates/alabaster/relations.html
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
<div class="relations">
|
||||
<h3>Related Topics</h3>
|
||||
<ul>
|
||||
<li><a href="{{ pathto(master_doc) }}">Documentation overview</a><ul>
|
||||
{%- for parent in parents %}
|
||||
<li><a href="{{ parent.link|e }}">{{ parent.title }}</a><ul>
|
||||
{%- endfor %}
|
||||
{%- if prev %}
|
||||
<li>Previous: <a href="{{ prev.link|e }}" title="{{ _('previous chapter')
|
||||
}}">{{ prev.title }}</a></li>
|
||||
{%- endif %}
|
||||
{%- if next %}
|
||||
<li>Next: <a href="{{ next.link|e }}" title="{{ _('next chapter')
|
||||
}}">{{ next.title }}</a></li>
|
||||
{%- endif %}
|
||||
{%- for parent in parents %}
|
||||
</ul></li>
|
||||
{%- endfor %}
|
||||
</ul></li>
|
||||
</ul>
|
||||
</div>
|
||||
706
docs/_templates/alabaster/static/alabaster.css_t
vendored
Normal file
706
docs/_templates/alabaster/static/alabaster.css_t
vendored
Normal file
@ -0,0 +1,706 @@
|
||||
{% set theme_body_bg = theme_body_bg or theme_base_bg %}
|
||||
{% set theme_code_highlight_bg = theme_code_highlight_bg or theme_body_bg %}
|
||||
{% set theme_sidebar_header = theme_sidebar_header or theme_gray_1 %}
|
||||
{% set theme_sidebar_link = theme_sidebar_link or theme_gray_1 %}
|
||||
{% set theme_anchor_hover_fg = theme_anchor_hover_fg or theme_gray_1 %}
|
||||
|
||||
{% set theme_footnote_border = theme_footnote_border or theme_gray_2 %}
|
||||
{% set theme_pre_bg = theme_pre_bg or theme_gray_2 %}
|
||||
|
||||
|
||||
{# set up admonition styling #}
|
||||
{# - basic level #}
|
||||
{% set theme_admonition_xref_bg = theme_admonition_xref_bg or theme_xref_bg %}
|
||||
{% set theme_admonition_bg = theme_admonition_bg or theme_gray_2 %}
|
||||
{% set theme_note_bg = theme_note_bg or theme_gray_2 %}
|
||||
{% set theme_seealso_bg = theme_seealso_bg or theme_gray_2 %}
|
||||
|
||||
{# - critical level #}
|
||||
{% set theme_danger_bg = theme_danger_bg or theme_pink_1 %}
|
||||
{% set theme_danger_border = theme_danger_border or theme_pink_2 %}
|
||||
{% set theme_danger_shadow = theme_danger_shadow or theme_pink_3 %}
|
||||
|
||||
{% set theme_error_bg = theme_error_bg or theme_pink_1 %}
|
||||
{% set theme_error_border = theme_error_border or theme_pink_2 %}
|
||||
{% set theme_error_shadow = theme_error_shadow or theme_pink_3 %}
|
||||
|
||||
{# - warning level #}
|
||||
{% set theme_caution_bg = theme_caution_bg or theme_pink_1 %}
|
||||
{% set theme_caution_border = theme_caution_border or theme_pink_2 %}
|
||||
|
||||
{% set theme_attention_bg = theme_attention_bg or theme_pink_1 %}
|
||||
{% set theme_attention_border = theme_attention_border or theme_pink_2 %}
|
||||
|
||||
{% set theme_warn_bg = theme_warn_bg or theme_pink_1 %}
|
||||
{% set theme_warn_border = theme_warn_border or theme_pink_2 %}
|
||||
|
||||
{# - normal level #}
|
||||
{% set theme_important_bg = theme_important_bg or theme_gray_2 %}
|
||||
{% set theme_tip_bg = theme_tip_bg or theme_gray_2 %}
|
||||
{% set theme_hint_bg = theme_hint_bg or theme_gray_2 %}
|
||||
|
||||
{# /set up admonition styling #}
|
||||
|
||||
{% set theme_shadow = theme_shadow or theme_gray_2 %}
|
||||
|
||||
|
||||
{% set theme_topic_bg = theme_topic_bg or theme_gray_2 %}
|
||||
|
||||
{% set theme_narrow_sidebar_link = theme_narrow_sidebar_link or theme_gray_3 %}
|
||||
{% set theme_sidebar_hr = theme_sidebar_hr or theme_gray_3 %}
|
||||
|
||||
|
||||
@import url("basic.css");
|
||||
|
||||
/* -- page layout ----------------------------------------------------------- */
|
||||
|
||||
body {
|
||||
font-family: {{ theme_font_family }};
|
||||
font-size: {{ theme_font_size }};
|
||||
background-color: {{ theme_base_bg }};
|
||||
color: {{ theme_base_text }};
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
|
||||
div.document {
|
||||
width: {{ theme_page_width }};
|
||||
margin: 30px auto 0 auto;
|
||||
}
|
||||
|
||||
div.documentwrapper {
|
||||
float: left;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
div.bodywrapper {
|
||||
margin: 0 0 0 {{ theme_sidebar_width }};
|
||||
}
|
||||
|
||||
div.sphinxsidebar {
|
||||
width: {{ theme_sidebar_width }};
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
hr {
|
||||
border: 1px solid {{ theme_hr_border }};
|
||||
}
|
||||
|
||||
div.body {
|
||||
background-color: {{ theme_body_bg }};
|
||||
color: {{ theme_body_text }};
|
||||
padding: 0 30px 0 30px;
|
||||
}
|
||||
|
||||
div.body > .section {
|
||||
text-align: {{ theme_body_text_align }};
|
||||
}
|
||||
|
||||
div.footer {
|
||||
width: {{ theme_page_width }};
|
||||
margin: 20px auto 30px auto;
|
||||
font-size: 14px;
|
||||
color: {{ theme_footer_text }};
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
div.footer a {
|
||||
color: {{ theme_footer_text }};
|
||||
}
|
||||
|
||||
p.caption {
|
||||
font-family: {{ theme_caption_font_family }};
|
||||
font-size: {{ theme_caption_font_size }};
|
||||
}
|
||||
|
||||
{% if theme_show_related|lower == 'false' %}
|
||||
div.relations {
|
||||
display: none;
|
||||
}
|
||||
{% endif %}
|
||||
|
||||
div.sphinxsidebar a {
|
||||
color: {{ theme_sidebar_link }};
|
||||
text-decoration: none;
|
||||
border-bottom: 1px dotted {{ theme_sidebar_link_underscore }};
|
||||
}
|
||||
|
||||
div.sphinxsidebar a:hover {
|
||||
border-bottom: 1px solid {{ theme_sidebar_link_underscore }};
|
||||
}
|
||||
|
||||
div.sphinxsidebarwrapper {
|
||||
padding: 18px 10px;
|
||||
}
|
||||
|
||||
div.sphinxsidebarwrapper p.logo {
|
||||
padding: 0;
|
||||
margin: -10px 0 0 0px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
div.sphinxsidebarwrapper h1.logo {
|
||||
margin-top: -10px;
|
||||
text-align: center;
|
||||
margin-bottom: 5px;
|
||||
text-align: {{ theme_logo_text_align }};
|
||||
}
|
||||
|
||||
div.sphinxsidebarwrapper h1.logo-name {
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
div.sphinxsidebarwrapper p.blurb {
|
||||
margin-top: 0;
|
||||
font-style: {{ theme_description_font_style }};
|
||||
}
|
||||
|
||||
div.sphinxsidebar h3,
|
||||
div.sphinxsidebar h4 {
|
||||
font-family: {{ theme_head_font_family }};
|
||||
color: {{ theme_sidebar_header }};
|
||||
font-size: 24px;
|
||||
font-weight: normal;
|
||||
margin: 0 0 5px 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
div.sphinxsidebar h4 {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
div.sphinxsidebar h3 a {
|
||||
color: {{ theme_sidebar_link }};
|
||||
}
|
||||
|
||||
div.sphinxsidebar p.logo a,
|
||||
div.sphinxsidebar h3 a,
|
||||
div.sphinxsidebar p.logo a:hover,
|
||||
div.sphinxsidebar h3 a:hover {
|
||||
border: none;
|
||||
}
|
||||
|
||||
div.sphinxsidebar p {
|
||||
color: {{ theme_sidebar_text }};
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
div.sphinxsidebar ul {
|
||||
margin: 10px 0;
|
||||
padding: 0;
|
||||
color: {{ theme_sidebar_list }};
|
||||
}
|
||||
|
||||
div.sphinxsidebar ul li.toctree-l1 > a {
|
||||
font-size: 120%;
|
||||
}
|
||||
|
||||
div.sphinxsidebar ul li.toctree-l2 > a {
|
||||
font-size: 110%;
|
||||
}
|
||||
|
||||
div.sphinxsidebar input {
|
||||
border: 1px solid {{ theme_sidebar_search_button }};
|
||||
font-family: {{ theme_font_family }};
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
div.sphinxsidebar hr {
|
||||
border: none;
|
||||
height: 1px;
|
||||
color: {{ theme_sidebar_hr }};
|
||||
background: {{ theme_sidebar_hr }};
|
||||
|
||||
text-align: left;
|
||||
margin-left: 0;
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
/* -- body styles ----------------------------------------------------------- */
|
||||
|
||||
a {
|
||||
color: {{ theme_link }};
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: {{ theme_link_hover }};
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
div.body h1,
|
||||
div.body h2,
|
||||
div.body h3,
|
||||
div.body h4,
|
||||
div.body h5,
|
||||
div.body h6 {
|
||||
font-family: {{ theme_head_font_family }};
|
||||
font-weight: normal;
|
||||
margin: 30px 0px 10px 0px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
div.body h1 { margin-top: 0; padding-top: 0; font-size: 240%; }
|
||||
div.body h2 { font-size: 180%; }
|
||||
div.body h3 { font-size: 150%; }
|
||||
div.body h4 { font-size: 130%; }
|
||||
div.body h5 { font-size: 100%; }
|
||||
div.body h6 { font-size: 100%; }
|
||||
|
||||
a.headerlink {
|
||||
color: {{ theme_anchor }};
|
||||
padding: 0 4px;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a.headerlink:hover {
|
||||
color: {{ theme_anchor_hover_fg }};
|
||||
background: {{ theme_anchor_hover_bg }};
|
||||
}
|
||||
|
||||
div.body p, div.body dd, div.body li {
|
||||
line-height: 1.4em;
|
||||
}
|
||||
|
||||
div.admonition {
|
||||
margin: 20px 0px;
|
||||
padding: 10px 30px;
|
||||
background-color: {{ theme_admonition_bg }};
|
||||
border: 1px solid {{ theme_admonition_border }};
|
||||
}
|
||||
|
||||
div.admonition tt.xref, div.admonition code.xref, div.admonition a tt {
|
||||
background-color: {{ theme_admonition_xref_bg }};
|
||||
border-bottom: 1px solid {{ theme_admonition_xref_border }};
|
||||
}
|
||||
|
||||
div.admonition p.admonition-title {
|
||||
font-family: {{ theme_head_font_family }};
|
||||
font-weight: normal;
|
||||
font-size: 24px;
|
||||
margin: 0 0 10px 0;
|
||||
padding: 0;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
div.admonition p.last {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
div.highlight {
|
||||
background-color: {{ theme_code_highlight_bg }};
|
||||
}
|
||||
|
||||
dt:target, .highlight {
|
||||
background: {{ theme_highlight_bg }};
|
||||
}
|
||||
|
||||
div.warning {
|
||||
background-color: {{ theme_warn_bg }};
|
||||
border: 1px solid {{ theme_warn_border }};
|
||||
}
|
||||
|
||||
div.danger {
|
||||
background-color: {{ theme_danger_bg }};
|
||||
border: 1px solid {{ theme_danger_border }};
|
||||
-moz-box-shadow: 2px 2px 4px {{ theme_danger_shadow }};
|
||||
-webkit-box-shadow: 2px 2px 4px {{ theme_danger_shadow }};
|
||||
box-shadow: 2px 2px 4px {{ theme_danger_shadow }};
|
||||
}
|
||||
|
||||
div.error {
|
||||
background-color: {{ theme_error_bg }};
|
||||
border: 1px solid {{ theme_error_border }};
|
||||
-moz-box-shadow: 2px 2px 4px {{ theme_error_shadow }};
|
||||
-webkit-box-shadow: 2px 2px 4px {{ theme_error_shadow }};
|
||||
box-shadow: 2px 2px 4px {{ theme_error_shadow }};
|
||||
}
|
||||
|
||||
div.caution {
|
||||
background-color: {{ theme_caution_bg }};
|
||||
border: 1px solid {{ theme_caution_border }};
|
||||
}
|
||||
|
||||
div.attention {
|
||||
background-color: {{ theme_attention_bg }};
|
||||
border: 1px solid {{ theme_attention_border }};
|
||||
}
|
||||
|
||||
div.important {
|
||||
background-color: {{ theme_important_bg }};
|
||||
border: 1px solid {{ theme_important_border }};
|
||||
}
|
||||
|
||||
div.note {
|
||||
background-color: {{ theme_note_bg }};
|
||||
border: 1px solid {{ theme_note_border }};
|
||||
}
|
||||
|
||||
div.tip {
|
||||
background-color: {{ theme_tip_bg }};
|
||||
border: 1px solid {{ theme_tip_border }};
|
||||
}
|
||||
|
||||
div.hint {
|
||||
background-color: {{ theme_hint_bg }};
|
||||
border: 1px solid {{ theme_hint_border }};
|
||||
}
|
||||
|
||||
div.seealso {
|
||||
background-color: {{ theme_seealso_bg }};
|
||||
border: 1px solid {{ theme_seealso_border }};
|
||||
}
|
||||
|
||||
div.topic {
|
||||
background-color: {{ theme_topic_bg }};
|
||||
}
|
||||
|
||||
p.admonition-title {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
p.admonition-title:after {
|
||||
content: ":";
|
||||
}
|
||||
|
||||
pre, tt, code {
|
||||
font-family: {{theme_code_font_family}};
|
||||
font-size: {{ theme_code_font_size }};
|
||||
}
|
||||
|
||||
.hll {
|
||||
background-color: {{theme_code_highlight}};
|
||||
margin: 0 -12px;
|
||||
padding: 0 12px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
img.screenshot {
|
||||
}
|
||||
|
||||
tt.descname, tt.descclassname, code.descname, code.descclassname {
|
||||
font-size: 0.95em;
|
||||
}
|
||||
|
||||
tt.descname, code.descname {
|
||||
padding-right: 0.08em;
|
||||
}
|
||||
|
||||
img.screenshot {
|
||||
-moz-box-shadow: 2px 2px 4px {{ theme_shadow }};
|
||||
-webkit-box-shadow: 2px 2px 4px {{ theme_shadow }};
|
||||
box-shadow: 2px 2px 4px {{ theme_shadow }};
|
||||
}
|
||||
|
||||
table.docutils {
|
||||
border: 1px solid {{ theme_table_border }};
|
||||
-moz-box-shadow: 2px 2px 4px {{ theme_shadow }};
|
||||
-webkit-box-shadow: 2px 2px 4px {{ theme_shadow }};
|
||||
box-shadow: 2px 2px 4px {{ theme_shadow }};
|
||||
}
|
||||
|
||||
table.docutils td, table.docutils th {
|
||||
border: 1px solid {{ theme_table_border }};
|
||||
padding: 0.25em 0.7em;
|
||||
}
|
||||
|
||||
table.field-list, table.footnote {
|
||||
border: none;
|
||||
-moz-box-shadow: none;
|
||||
-webkit-box-shadow: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
table.footnote {
|
||||
margin: 15px 0;
|
||||
width: 100%;
|
||||
border: 1px solid {{ theme_footnote_border }};
|
||||
background: {{ theme_footnote_bg }};
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
table.footnote + table.footnote {
|
||||
margin-top: -15px;
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
table.field-list th {
|
||||
padding: 0 0.8em 0 0;
|
||||
}
|
||||
|
||||
table.field-list td {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
table.field-list p {
|
||||
margin-bottom: 0.8em;
|
||||
}
|
||||
|
||||
/* Cloned from
|
||||
* https://github.com/sphinx-doc/sphinx/commit/ef60dbfce09286b20b7385333d63a60321784e68
|
||||
*/
|
||||
.field-name {
|
||||
-moz-hyphens: manual;
|
||||
-ms-hyphens: manual;
|
||||
-webkit-hyphens: manual;
|
||||
hyphens: manual;
|
||||
}
|
||||
|
||||
table.footnote td.label {
|
||||
width: .1px;
|
||||
padding: 0.3em 0 0.3em 0.5em;
|
||||
}
|
||||
|
||||
table.footnote td {
|
||||
padding: 0.3em 0.5em;
|
||||
}
|
||||
|
||||
dl {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
dl dd {
|
||||
margin-left: 30px;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
margin: 0 0 0 30px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
ul, ol {
|
||||
/* Matches the 30px from the narrow-screen "li > ul" selector below */
|
||||
margin: 10px 0 10px 30px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
pre {
|
||||
background: {{ theme_pre_bg }};
|
||||
padding: 7px 30px;
|
||||
margin: 15px 0px;
|
||||
line-height: 1.3em;
|
||||
}
|
||||
|
||||
div.viewcode-block:target {
|
||||
background: {{ theme_viewcode_target_bg }};
|
||||
}
|
||||
|
||||
dl pre, blockquote pre, li pre {
|
||||
margin-left: 0;
|
||||
padding-left: 30px;
|
||||
}
|
||||
|
||||
tt, code {
|
||||
background-color: {{ theme_code_bg }};
|
||||
color: {{ theme_code_text }};
|
||||
/* padding: 1px 2px; */
|
||||
}
|
||||
|
||||
tt.xref, code.xref, a tt {
|
||||
background-color: {{ theme_xref_bg }};
|
||||
border-bottom: 1px solid {{ theme_xref_border }};
|
||||
}
|
||||
|
||||
a.reference {
|
||||
text-decoration: none;
|
||||
border-bottom: 1px dotted {{ theme_link }};
|
||||
}
|
||||
|
||||
/* Don't put an underline on images */
|
||||
a.image-reference, a.image-reference:hover {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
a.reference:hover {
|
||||
border-bottom: 1px solid {{ theme_link_hover }};
|
||||
}
|
||||
|
||||
a.footnote-reference {
|
||||
text-decoration: none;
|
||||
font-size: 0.7em;
|
||||
vertical-align: top;
|
||||
border-bottom: 1px dotted {{ theme_link }};
|
||||
}
|
||||
|
||||
a.footnote-reference:hover {
|
||||
border-bottom: 1px solid {{ theme_link_hover }};
|
||||
}
|
||||
|
||||
a:hover tt, a:hover code {
|
||||
background: {{ theme_code_hover }};
|
||||
}
|
||||
|
||||
|
||||
@media screen and (max-width: 870px) {
|
||||
|
||||
div.sphinxsidebar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
div.document {
|
||||
width: 100%;
|
||||
|
||||
}
|
||||
|
||||
div.documentwrapper {
|
||||
margin-left: 0;
|
||||
margin-top: 0;
|
||||
margin-right: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
div.bodywrapper {
|
||||
margin-top: 0;
|
||||
margin-right: 0;
|
||||
margin-bottom: 0;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
ul {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
li > ul {
|
||||
/* Matches the 30px from the "ul, ol" selector above */
|
||||
margin-left: 30px;
|
||||
}
|
||||
|
||||
.document {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.footer {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.bodywrapper {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.footer {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.github {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@media screen and (max-width: 875px) {
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 20px 30px;
|
||||
}
|
||||
|
||||
div.documentwrapper {
|
||||
float: none;
|
||||
background: {{ theme_base_bg }};
|
||||
}
|
||||
|
||||
div.sphinxsidebar {
|
||||
display: block;
|
||||
float: none;
|
||||
width: 102.5%;
|
||||
{%- if theme_fixed_sidebar|lower == 'true' %}
|
||||
margin: -20px -30px 20px -30px;
|
||||
{%- else %}
|
||||
margin: 50px -30px -20px -30px;
|
||||
{%- endif %}
|
||||
padding: 10px 20px;
|
||||
background: {{ theme_narrow_sidebar_bg }};
|
||||
color: {{ theme_narrow_sidebar_fg }};
|
||||
}
|
||||
|
||||
div.sphinxsidebar h3, div.sphinxsidebar h4, div.sphinxsidebar p,
|
||||
div.sphinxsidebar h3 a {
|
||||
color: {{ theme_base_bg }};
|
||||
}
|
||||
|
||||
div.sphinxsidebar a {
|
||||
color: {{ theme_narrow_sidebar_link }};
|
||||
}
|
||||
|
||||
div.sphinxsidebar p.logo {
|
||||
display: none;
|
||||
}
|
||||
|
||||
div.document {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
div.footer {
|
||||
display: none;
|
||||
}
|
||||
|
||||
div.bodywrapper {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
div.body {
|
||||
min-height: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.rtd_doc_footer {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.document {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.footer {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.footer {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.github {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
{%- if theme_fixed_sidebar|lower == 'true' %}
|
||||
@media screen and (min-width: 876px) {
|
||||
div.sphinxsidebar {
|
||||
position: fixed;
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
{%- endif %}
|
||||
|
||||
|
||||
/* misc. */
|
||||
|
||||
.revsys-inline {
|
||||
display: none!important;
|
||||
}
|
||||
|
||||
/* Make nested-list/multi-paragraph items look better in Releases changelog
|
||||
* pages. Without this, docutils' magical list fuckery causes inconsistent
|
||||
* formatting between different release sub-lists.
|
||||
*/
|
||||
div#changelog > div.section > ul > li > p:only-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
/* Hide fugly table cell borders in ..bibliography:: directive output */
|
||||
table.docutils.citation, table.docutils.citation td, table.docutils.citation th {
|
||||
border: none;
|
||||
/* Below needed in some edge cases; if not applied, bottom shadows appear */
|
||||
-moz-box-shadow: none;
|
||||
-webkit-box-shadow: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
1
docs/_templates/alabaster/static/custom.css
vendored
Normal file
1
docs/_templates/alabaster/static/custom.css
vendored
Normal file
@ -0,0 +1 @@
|
||||
/* This file intentionally left blank. */
|
||||
88
docs/_templates/alabaster/support.py
vendored
Normal file
88
docs/_templates/alabaster/support.py
vendored
Normal file
@ -0,0 +1,88 @@
|
||||
# flake8: noqa
|
||||
|
||||
from pygments.style import Style
|
||||
from pygments.token import Keyword, Name, Comment, String, Error, \
|
||||
Number, Operator, Generic, Whitespace, Punctuation, Other, Literal
|
||||
|
||||
|
||||
# Originally based on FlaskyStyle which was based on 'tango'.
|
||||
class Alabaster(Style):
|
||||
background_color = "#f8f8f8" # doesn't seem to override CSS 'pre' styling?
|
||||
default_style = ""
|
||||
|
||||
styles = {
|
||||
# No corresponding class for the following:
|
||||
#Text: "", # class: ''
|
||||
Whitespace: "underline #f8f8f8", # class: 'w'
|
||||
Error: "#a40000 border:#ef2929", # class: 'err'
|
||||
Other: "#000000", # class 'x'
|
||||
|
||||
Comment: "italic #8f5902", # class: 'c'
|
||||
Comment.Preproc: "noitalic", # class: 'cp'
|
||||
|
||||
Keyword: "bold #004461", # class: 'k'
|
||||
Keyword.Constant: "bold #004461", # class: 'kc'
|
||||
Keyword.Declaration: "bold #004461", # class: 'kd'
|
||||
Keyword.Namespace: "bold #004461", # class: 'kn'
|
||||
Keyword.Pseudo: "bold #004461", # class: 'kp'
|
||||
Keyword.Reserved: "bold #004461", # class: 'kr'
|
||||
Keyword.Type: "bold #004461", # class: 'kt'
|
||||
|
||||
Operator: "#582800", # class: 'o'
|
||||
Operator.Word: "bold #004461", # class: 'ow' - like keywords
|
||||
|
||||
Punctuation: "bold #000000", # class: 'p'
|
||||
|
||||
# because special names such as Name.Class, Name.Function, etc.
|
||||
# are not recognized as such later in the parsing, we choose them
|
||||
# to look the same as ordinary variables.
|
||||
Name: "#000000", # class: 'n'
|
||||
Name.Attribute: "#c4a000", # class: 'na' - to be revised
|
||||
Name.Builtin: "#004461", # class: 'nb'
|
||||
Name.Builtin.Pseudo: "#3465a4", # class: 'bp'
|
||||
Name.Class: "#000000", # class: 'nc' - to be revised
|
||||
Name.Constant: "#000000", # class: 'no' - to be revised
|
||||
Name.Decorator: "#888", # class: 'nd' - to be revised
|
||||
Name.Entity: "#ce5c00", # class: 'ni'
|
||||
Name.Exception: "bold #cc0000", # class: 'ne'
|
||||
Name.Function: "#000000", # class: 'nf'
|
||||
Name.Property: "#000000", # class: 'py'
|
||||
Name.Label: "#f57900", # class: 'nl'
|
||||
Name.Namespace: "#000000", # class: 'nn' - to be revised
|
||||
Name.Other: "#000000", # class: 'nx'
|
||||
Name.Tag: "bold #004461", # class: 'nt' - like a keyword
|
||||
Name.Variable: "#000000", # class: 'nv' - to be revised
|
||||
Name.Variable.Class: "#000000", # class: 'vc' - to be revised
|
||||
Name.Variable.Global: "#000000", # class: 'vg' - to be revised
|
||||
Name.Variable.Instance: "#000000", # class: 'vi' - to be revised
|
||||
|
||||
Number: "#990000", # class: 'm'
|
||||
|
||||
Literal: "#000000", # class: 'l'
|
||||
Literal.Date: "#000000", # class: 'ld'
|
||||
|
||||
String: "#4e9a06", # class: 's'
|
||||
String.Backtick: "#4e9a06", # class: 'sb'
|
||||
String.Char: "#4e9a06", # class: 'sc'
|
||||
String.Doc: "italic #8f5902", # class: 'sd' - like a comment
|
||||
String.Double: "#4e9a06", # class: 's2'
|
||||
String.Escape: "#4e9a06", # class: 'se'
|
||||
String.Heredoc: "#4e9a06", # class: 'sh'
|
||||
String.Interpol: "#4e9a06", # class: 'si'
|
||||
String.Other: "#4e9a06", # class: 'sx'
|
||||
String.Regex: "#4e9a06", # class: 'sr'
|
||||
String.Single: "#4e9a06", # class: 's1'
|
||||
String.Symbol: "#4e9a06", # class: 'ss'
|
||||
|
||||
Generic: "#000000", # class: 'g'
|
||||
Generic.Deleted: "#a40000", # class: 'gd'
|
||||
Generic.Emph: "italic #000000", # class: 'ge'
|
||||
Generic.Error: "#ef2929", # class: 'gr'
|
||||
Generic.Heading: "bold #000080", # class: 'gh'
|
||||
Generic.Inserted: "#00A000", # class: 'gi'
|
||||
Generic.Output: "#888", # class: 'go'
|
||||
Generic.Prompt: "#745334", # class: 'gp'
|
||||
Generic.Strong: "bold #000000", # class: 'gs'
|
||||
Generic.Subheading: "bold #800080", # class: 'gu'
|
||||
Generic.Traceback: "bold #a40000", # class: 'gt'
|
||||
}
|
||||
122
docs/_templates/alabaster/theme.conf
vendored
Normal file
122
docs/_templates/alabaster/theme.conf
vendored
Normal file
@ -0,0 +1,122 @@
|
||||
[theme]
|
||||
inherit = basic
|
||||
stylesheet = alabaster.css
|
||||
pygments_style = alabaster.support.Alabaster
|
||||
|
||||
[options]
|
||||
logo =
|
||||
logo_name = false
|
||||
logo_text_align = left
|
||||
description =
|
||||
description_font_style = normal
|
||||
github_user =
|
||||
github_repo =
|
||||
github_button = true
|
||||
github_banner = false
|
||||
github_type = watch
|
||||
github_count = true
|
||||
badge_branch = master
|
||||
travis_button = false
|
||||
codecov_button = false
|
||||
gratipay_user =
|
||||
gittip_user =
|
||||
analytics_id =
|
||||
touch_icon =
|
||||
canonical_url =
|
||||
extra_nav_links =
|
||||
sidebar_includehidden = true
|
||||
sidebar_collapse = true
|
||||
show_powered_by = true
|
||||
show_related = false
|
||||
|
||||
gray_1 = #444
|
||||
gray_2 = #EEE
|
||||
gray_3 = #AAA
|
||||
|
||||
pink_1 = #FCC
|
||||
pink_2 = #FAA
|
||||
pink_3 = #D52C2C
|
||||
|
||||
base_bg = #fff
|
||||
base_text = #000
|
||||
hr_border = #B1B4B6
|
||||
body_bg =
|
||||
body_text = #3E4349
|
||||
body_text_align = left
|
||||
footer_text = #888
|
||||
link = #004B6B
|
||||
link_hover = #6D4100
|
||||
sidebar_header =
|
||||
sidebar_text = #555
|
||||
sidebar_link =
|
||||
sidebar_link_underscore = #999
|
||||
sidebar_search_button = #CCC
|
||||
sidebar_list = #000
|
||||
sidebar_hr =
|
||||
anchor = #DDD
|
||||
anchor_hover_fg =
|
||||
anchor_hover_bg = #EAEAEA
|
||||
table_border = #888
|
||||
shadow =
|
||||
|
||||
# Admonition options
|
||||
## basic level
|
||||
admonition_bg =
|
||||
admonition_border = #CCC
|
||||
note_bg =
|
||||
note_border = #CCC
|
||||
seealso_bg =
|
||||
seealso_border = #CCC
|
||||
|
||||
## critical level
|
||||
danger_bg =
|
||||
danger_border =
|
||||
danger_shadow =
|
||||
error_bg =
|
||||
error_border =
|
||||
error_shadow =
|
||||
|
||||
## normal level
|
||||
tip_bg =
|
||||
tip_border = #CCC
|
||||
hint_bg =
|
||||
hint_border = #CCC
|
||||
important_bg =
|
||||
important_border = #CCC
|
||||
|
||||
## warning level
|
||||
caution_bg =
|
||||
caution_border =
|
||||
attention_bg =
|
||||
attention_border =
|
||||
warn_bg =
|
||||
warn_border =
|
||||
|
||||
topic_bg =
|
||||
code_highlight_bg =
|
||||
highlight_bg = #FAF3E8
|
||||
xref_border = #fff
|
||||
xref_bg = #FBFBFB
|
||||
admonition_xref_border = #fafafa
|
||||
admonition_xref_bg =
|
||||
footnote_bg = #FDFDFD
|
||||
footnote_border =
|
||||
pre_bg =
|
||||
narrow_sidebar_bg = #333
|
||||
narrow_sidebar_fg = #FFF
|
||||
narrow_sidebar_link =
|
||||
font_size = 17px
|
||||
caption_font_size = inherit
|
||||
viewcode_target_bg = #ffd
|
||||
code_bg = #ecf0f3
|
||||
code_text = #222
|
||||
code_hover = #EEE
|
||||
code_font_size = 0.9em
|
||||
code_font_family = 'Consolas', 'Menlo', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace
|
||||
font_family = 'goudy old style', 'minion pro', 'bell mt', Georgia, 'Hiragino Mincho Pro', serif
|
||||
head_font_family = 'Garamond', 'Georgia', serif
|
||||
caption_font_family = inherit
|
||||
code_highlight = #FFC
|
||||
page_width = 940px
|
||||
sidebar_width = 220px
|
||||
fixed_sidebar = false
|
||||
62
docs/_templates/base.html
vendored
Normal file
62
docs/_templates/base.html
vendored
Normal file
@ -0,0 +1,62 @@
|
||||
{%- extends "alabaster/layout.html" %}
|
||||
|
||||
|
||||
{%- block extrahead %}
|
||||
{{ super() }}
|
||||
<style>
|
||||
div.related {
|
||||
width: 940px;
|
||||
margin: 30px auto 0 auto;
|
||||
}
|
||||
@media screen and (max-width: 875px) {
|
||||
div.related {
|
||||
visibility: hidden;
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{%- block footer %}
|
||||
{{ relbar() }}
|
||||
|
||||
<div class="footer">
|
||||
{% if show_copyright %}©{{ copyright }}.{% endif %}
|
||||
{% if theme_show_powered_by|lower == 'true' %}
|
||||
{% if show_copyright %}|{% endif %}
|
||||
Powered by <a href="http://sphinx-doc.org/">Sphinx {{ sphinx_version }}</a>
|
||||
& <a href="https://github.com/bitprophet/alabaster">Alabaster {{ alabaster_version }}</a>
|
||||
{% endif %}
|
||||
{%- if show_source and has_source and sourcename %}
|
||||
{% if show_copyright or theme_show_powered_by %}|{% endif %}
|
||||
<a href="{{ pathto('_sources/' + sourcename, true)|e }}"
|
||||
rel="nofollow">{{ _('Page source') }}</a>
|
||||
{%- endif %}
|
||||
</div>
|
||||
|
||||
{% if theme_github_banner|lower != 'false' %}
|
||||
<a href="https://github.com/{{ theme_github_user }}/{{ theme_github_repo }}" class="github">
|
||||
<img style="position: absolute; top: 0; right: 0; border: 0;"
|
||||
src="{{ pathto('_static/' ~ theme_github_banner, 1) if theme_github_banner|lower != 'true' else 'https://s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png' }}"
|
||||
alt="Fork me on GitHub" class="github"/>
|
||||
</a>
|
||||
{% endif %}
|
||||
|
||||
{% if theme_analytics_id %}
|
||||
<script type="text/javascript">
|
||||
var _gaq = _gaq || [];
|
||||
_gaq.push(['_setAccount', '{{ theme_analytics_id }}']);
|
||||
_gaq.push(['_setDomainName', 'none']);
|
||||
_gaq.push(['_setAllowLinker', true]);
|
||||
_gaq.push(['_trackPageview']);
|
||||
(function () {
|
||||
var ga = document.createElement('script');
|
||||
ga.type = 'text/javascript';
|
||||
ga.async = true;
|
||||
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
|
||||
var s = document.getElementsByTagName('script')[0];
|
||||
s.parentNode.insertBefore(ga, s);
|
||||
})();
|
||||
</script>
|
||||
{% endif %}
|
||||
{%- endblock %}
|
||||
9
docs/_templates/index.html
vendored
9
docs/_templates/index.html
vendored
@ -1,7 +1,8 @@
|
||||
{% extends "layout.html" %}
|
||||
{% set title = _('Bonobo — Data processing for humans') %}
|
||||
{% block body %}
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% set title = _('Bonobo — Data processing for humans') %}
|
||||
|
||||
{% block body %}
|
||||
<h1 style="text-align: center">
|
||||
<img class="logo" src="{{ pathto('_static/bonobo.png', 1) }}" title="Bonobo" alt="Bonobo"
|
||||
style=" width: 128px; height: 128px;"/>
|
||||
@ -9,7 +10,7 @@
|
||||
|
||||
<p>
|
||||
{% trans %}
|
||||
<b>Bonobo</b> is an Extract Transform Load framework for the Python (3.5+) language.
|
||||
<b>Bonobo</b> is an <b>Extract Transform Load</b> (or ETL) framework for the <b>Python (3.5+)</b> language.
|
||||
{% endtrans %}
|
||||
</p>
|
||||
|
||||
|
||||
7
docs/_templates/layout.html
vendored
Normal file
7
docs/_templates/layout.html
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
{%- extends "base.html" %}
|
||||
|
||||
{%- block content %}
|
||||
{{ relbar() }}
|
||||
{{ super() }}
|
||||
{%- endblock %}
|
||||
|
||||
6
docs/_templates/sidebarlogo.html
vendored
6
docs/_templates/sidebarlogo.html
vendored
@ -1,10 +1,10 @@
|
||||
<a href="{{ pathto(master_doc) }}" style="border: none">
|
||||
<h1 style="text-align: center; margin-top: 0;">
|
||||
<h1 style="text-align: center; margin: 0;">
|
||||
<img class="logo" src="{{ pathto('_static/bonobo.png', 1) }}" title="Bonobo" style="width: 48px; height: 48px; vertical-align: bottom"/>
|
||||
Bonobo
|
||||
</h1>
|
||||
</a>
|
||||
|
||||
<p>
|
||||
Data processing for human beings.
|
||||
<p style="text-align: center">
|
||||
Data processing for humans.
|
||||
</p>
|
||||
|
||||
@ -75,9 +75,9 @@ html_theme = 'alabaster'
|
||||
html_theme_options = {
|
||||
'github_user': 'python-bonobo',
|
||||
'github_repo': 'bonobo',
|
||||
'github_button': True,
|
||||
'show_powered_by': False,
|
||||
'show_related': True,
|
||||
'github_button': 'true',
|
||||
'show_powered_by': 'false',
|
||||
'show_related': 'true',
|
||||
}
|
||||
|
||||
html_sidebars = {
|
||||
|
||||
3
docs/genindex.rst
Normal file
3
docs/genindex.rst
Normal file
@ -0,0 +1,3 @@
|
||||
Full Index
|
||||
==========
|
||||
|
||||
@ -1,11 +1,211 @@
|
||||
Graphs
|
||||
======
|
||||
|
||||
Writing graphs
|
||||
::::::::::::::
|
||||
Graphs are the glue that ties transformations together. It's the only data-structure bonobo can execute directly. Graphs
|
||||
must be acyclic, and can contain as much nodes as your system can handle. Although this number can be rather high in
|
||||
theory, extreme practical cases usually do not exceed hundreds of nodes (and this is already extreme, really).
|
||||
|
||||
Debugging graphs
|
||||
|
||||
Definitions
|
||||
:::::::::::
|
||||
|
||||
Graph
|
||||
|
||||
A directed acyclic graph of transformations, that Bonobo can inspect and execute.
|
||||
|
||||
Node
|
||||
|
||||
A transformation within a graph. The transformations are stateless, and have no idea whether or not they are
|
||||
included in a graph, multiple graph, or not at all.
|
||||
|
||||
|
||||
Creating a graph
|
||||
::::::::::::::::
|
||||
|
||||
Graphs should be instances of :class:`bonobo.Graph`. The :func:`bonobo.Graph.add_chain` method can take as many
|
||||
positional parameters as you want.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import bonobo
|
||||
|
||||
graph = bonobo.Graph()
|
||||
graph.add_chain(a, b, c)
|
||||
|
||||
Resulting graph:
|
||||
|
||||
.. graphviz::
|
||||
|
||||
digraph {
|
||||
rankdir = LR;
|
||||
stylesheet = "../_static/graphs.css";
|
||||
|
||||
BEGIN [shape="point"];
|
||||
BEGIN -> "a" -> "b" -> "c";
|
||||
}
|
||||
|
||||
Non-linear graphs
|
||||
:::::::::::::::::
|
||||
|
||||
Divergences / forks
|
||||
-------------------
|
||||
|
||||
To create two or more divergent data streams ("fork"), you should specify `_input` kwarg to `add_chain`.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import bonobo
|
||||
|
||||
graph = bonobo.Graph()
|
||||
graph.add_chain(a, b, c)
|
||||
graph.add_chain(f, g, _input=b)
|
||||
|
||||
|
||||
Resulting graph:
|
||||
|
||||
.. graphviz::
|
||||
|
||||
digraph {
|
||||
rankdir = LR;
|
||||
stylesheet = "../_static/graphs.css";
|
||||
|
||||
BEGIN [shape="point"];
|
||||
BEGIN -> "a" -> "b" -> "c";
|
||||
"b" -> "f" -> "g";
|
||||
}
|
||||
|
||||
.. note:: Both branch will receive the same data, at the same time.
|
||||
|
||||
Convergences / merges
|
||||
---------------------
|
||||
|
||||
To merge two data streams ("merge"), you can use the `_output` kwarg to `add_chain`, or use named nodes (see below).
|
||||
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import bonobo
|
||||
|
||||
graph = bonobo.Graph()
|
||||
|
||||
# Here we mark _input to None, so normalize won't get the "begin" impulsion.
|
||||
graph.add_chain(normalize, store, _input=None)
|
||||
|
||||
# Add two different chains
|
||||
graph.add_chain(a, b, _output=normalize)
|
||||
graph.add_chain(f, g, _output=normalize)
|
||||
|
||||
|
||||
Resulting graph:
|
||||
|
||||
.. graphviz::
|
||||
|
||||
digraph {
|
||||
rankdir = LR;
|
||||
stylesheet = "../_static/graphs.css";
|
||||
|
||||
BEGIN [shape="point"];
|
||||
BEGIN -> "a" -> "b" -> "normalize";
|
||||
|
||||
BEGIN2 [shape="point"];
|
||||
BEGIN2 -> "f" -> "g" -> "normalize";
|
||||
|
||||
"normalize" -> "store"
|
||||
}
|
||||
|
||||
.. note::
|
||||
|
||||
This is not a "join" or "cartesian product". Any data that comes from `b` or `g` will go through `normalize`, one at
|
||||
a time. Think of the graph edges as data flow pipes.
|
||||
|
||||
|
||||
Named nodes
|
||||
:::::::::::
|
||||
|
||||
Using above code to create convergences can lead to hard to read code, because you have to define the "target" stream
|
||||
before the streams that logically goes to the beginning of the transformation graph. To overcome that, one can use
|
||||
"named" nodes:
|
||||
|
||||
graph.add_chain(x, y, z, _name='zed')
|
||||
graph.add_chain(f, g, h, _input='zed')
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import bonobo
|
||||
|
||||
graph = bonobo.Graph()
|
||||
|
||||
# Add two different chains
|
||||
graph.add_chain(a, b, _output="load")
|
||||
graph.add_chain(f, g, _output="load")
|
||||
|
||||
# Here we mark _input to None, so normalize won't get the "begin" impulsion.
|
||||
graph.add_chain(normalize, store, _input=None, _name="load")
|
||||
|
||||
|
||||
Resulting graph:
|
||||
|
||||
.. graphviz::
|
||||
|
||||
digraph {
|
||||
rankdir = LR;
|
||||
stylesheet = "../_static/graphs.css";
|
||||
|
||||
BEGIN [shape="point"];
|
||||
BEGIN -> "a" -> "b" -> "normalize (load)";
|
||||
|
||||
BEGIN2 [shape="point"];
|
||||
BEGIN2 -> "f" -> "g" -> "normalize (load)";
|
||||
|
||||
"normalize (load)" -> "store"
|
||||
}
|
||||
|
||||
|
||||
Inspecting graphs
|
||||
:::::::::::::::::
|
||||
|
||||
Bonobo is bundled with an "inspector", that can use graphviz to let you visualize your graphs.
|
||||
|
||||
Read `How to inspect and visualize your graph <https://www.bonobo-project.org/how-to/inspect-an-etl-jobs-graph>`_.
|
||||
|
||||
|
||||
Executing graphs
|
||||
::::::::::::::::
|
||||
|
||||
There are two options to execute a graph (which have a similar result, but are targeting different use cases).
|
||||
|
||||
* You can use the bonobo command line interface, which is the highest level interface.
|
||||
* You can use the python API, which is lower level but allows to use bonobo from within your own code (for example, a
|
||||
django management command).
|
||||
|
||||
Executing a graph with the command line interface
|
||||
-------------------------------------------------
|
||||
|
||||
If there is no good reason not to, you should use `bonobo run ...` to run transformation graphs found in your python
|
||||
source code files.
|
||||
|
||||
.. code-block:: shell-session
|
||||
|
||||
$ bonobo run file.py
|
||||
|
||||
You can also run a python module:
|
||||
|
||||
.. code-block:: shell-session
|
||||
|
||||
$ bonobo run -m my.own.etlmod
|
||||
|
||||
In each case, bonobo's CLI will look for an instance of :class:`bonobo.Graph` in your file/module, create the plumbery
|
||||
needed to execute it, and run it.
|
||||
|
||||
If you're in an interactive terminal context, it will use :class:`bonobo.ext.console.ConsoleOutputPlugin` for display.
|
||||
|
||||
If you're in a jupyter notebook context, it will (try to) use :class:`bonobo.ext.jupyter.JupyterOutputPlugin`.
|
||||
|
||||
Executing a graph using the internal API
|
||||
----------------------------------------
|
||||
|
||||
To integrate bonobo executions in any other python code, you should use :func:`bonobo.run`. It behaves very similar to
|
||||
the CLI, and reading the source you should be able to figure out its usage quite easily.
|
||||
|
||||
|
||||
|
||||
|
||||
@ -1,13 +1,14 @@
|
||||
Guides
|
||||
======
|
||||
|
||||
Here are a few guides and best practices to work with bonobo.
|
||||
This section will guide you through your journey with Bonobo ETL.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
graphs
|
||||
introduction
|
||||
transformations
|
||||
graphs
|
||||
services
|
||||
environment
|
||||
purity
|
||||
|
||||
106
docs/guide/introduction.rst
Normal file
106
docs/guide/introduction.rst
Normal file
@ -0,0 +1,106 @@
|
||||
Introduction
|
||||
============
|
||||
|
||||
The first thing you need to understand before you use Bonobo, or not, is what it does and what it does not, so you can
|
||||
understand if it could be a good fit for your use cases.
|
||||
|
||||
How it works?
|
||||
:::::::::::::
|
||||
|
||||
**Bonobo** is an **Extract Transform Load** framework aimed at coders, hackers, or any other person who's at ease with
|
||||
terminals and source code files.
|
||||
|
||||
It is a **data streaming** solution, that treat datasets as ordered collections of independant rows, allowing to process
|
||||
them "first in, first out" using a set of transformations organized together in a directed graph.
|
||||
|
||||
Let's take a few examples:
|
||||
|
||||
.. graphviz::
|
||||
|
||||
digraph {
|
||||
rankdir = LR;
|
||||
stylesheet = "../_static/graphs.css";
|
||||
|
||||
BEGIN [shape="point"];
|
||||
END [shape="none" label="..."];
|
||||
BEGIN -> "A" -> "B" -> "C" -> "END";
|
||||
}
|
||||
|
||||
One of the simplest, by the book, cases, is an extractor sending to a transformation, itself sending to a loader.
|
||||
|
||||
Bonobo will send an "impulsion" to all transformations linked to the little black dot on the left, here `A`.
|
||||
`A`'s main topic will be to extract data from somewhere (a file, an endpoint, a database...) and generate some output.
|
||||
As soon as the first row of `A`'s output is available, Bonobo will start asking `B` to process it. As soon as the first
|
||||
row of `B`'s output is available, Bonobo will start asking `C` to process it.
|
||||
|
||||
While `B` and `C` are processing, `A` continues to generate data.
|
||||
|
||||
This approach can be efficient, depending on your requirements, because you may rely on a lot of services that may be
|
||||
long to answer or unreliable, and you don't have to handle optimizations, parallelism or retry logic by yourself.
|
||||
|
||||
.. graphviz::
|
||||
|
||||
digraph {
|
||||
rankdir = LR;
|
||||
stylesheet = "../_static/graphs.css";
|
||||
|
||||
BEGIN [shape="point"];
|
||||
END [shape="none" label="..."];
|
||||
END2 [shape="none" label="..."];
|
||||
BEGIN -> "A" -> "B" -> "END";
|
||||
"A" -> "C" -> "END2";
|
||||
}
|
||||
|
||||
In this case, any output row of `A`, will be **sent to both** `B` and `C` simultaneously. Again, `A` will continue its
|
||||
processing while `B` and `C` are working.
|
||||
|
||||
|
||||
.. graphviz::
|
||||
|
||||
digraph {
|
||||
rankdir = LR;
|
||||
stylesheet = "../_static/graphs.css";
|
||||
|
||||
BEGIN [shape="point"];
|
||||
BEGIN2 [shape="point"];
|
||||
END [shape="none" label="..."];
|
||||
BEGIN -> "A" -> "C" -> "END";
|
||||
BEGIN2 -> "B" -> "C";
|
||||
}
|
||||
|
||||
|
||||
What is it not?
|
||||
:::::::::::::::
|
||||
|
||||
**Bonobo** is not:
|
||||
|
||||
* A data science, or statistical analysis tool, which need to treat the dataset as a whole and not as a collection of
|
||||
independant rows. If this is your need, you probably want to look at `pandas <https://pandas.pydata.org/>`_.
|
||||
|
||||
* A workflow or scheduling solution for independant data-engineering tasks. If you're looking to manage your sets of
|
||||
data processing tasks as a whole, you probably want to look at `airflow <https://airflow.incubator.apache.org/>`_.
|
||||
Although there is no Bonobo extension yet that handles that, it does make sense to integrate Bonobo jobs in an airflow
|
||||
(or other similar tool) workflow.
|
||||
|
||||
* A big data solution, `as defined by wikipedia <https://en.wikipedia.org/wiki/Big_data>`_. We're aiming at "small
|
||||
scale" data processing, which can be still quite huge for humans, but not for computers. If you don't know whether or
|
||||
not this is sufficient for your needs, it probably means you're not in the "big data" land.
|
||||
|
||||
|
||||
Where to jump next?
|
||||
:::::::::::::::::::
|
||||
|
||||
If you did not run through it yet, we highly suggest that you go through the :doc:`tutorial </tutorial/index>` first.
|
||||
|
||||
Then, you can jump to the following guides, in no particuliar order:
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
transformations
|
||||
graphs
|
||||
services
|
||||
environment
|
||||
purity
|
||||
|
||||
|
||||
@ -1,14 +1,10 @@
|
||||
Services and dependencies
|
||||
=========================
|
||||
|
||||
:Last-Modified: 20 may 2017
|
||||
You'll want to use external systems within your transformations, including databases, HTTP APIs, other web services,
|
||||
filesystems, etc.
|
||||
|
||||
You'll probably want to use external systems within your transformations. Those systems may include databases, apis
|
||||
(using http, for example), filesystems, etc.
|
||||
|
||||
You can start by hardcoding those services. That does the job, at first.
|
||||
|
||||
If you're going a little further than that, you'll feel limited, for a few reasons:
|
||||
Hardcoding those services is a good first step, but as your codebase grows, will show limits rather quickly.
|
||||
|
||||
* Hardcoded and tightly linked dependencies make your transformations hard to test, and hard to reuse.
|
||||
* Processing data on your laptop is great, but being able to do it on different target systems (or stages), in different
|
||||
@ -16,70 +12,77 @@ If you're going a little further than that, you'll feel limited, for a few reaso
|
||||
pre-production environment, or production system. Maybe you have similar systems for different clients and want to select
|
||||
the system at runtime. Etc.
|
||||
|
||||
Service injection
|
||||
:::::::::::::::::
|
||||
Definition of service dependencies
|
||||
::::::::::::::::::::::::::::::::::
|
||||
|
||||
To solve this problem, we introduce a light dependency injection system. It allows to define named dependencies in
|
||||
To solve this problem, we introduce a light dependency injection system. It allows to define **named dependencies** in
|
||||
your transformations, and provide an implementation at runtime.
|
||||
|
||||
Class-based transformations
|
||||
---------------------------
|
||||
For function-based transformations, you can use the :func:`bonobo.config.use` decorator to mark the dependencies. You'll
|
||||
still be able to call it manually, providing the implementation yourself, but in a bonobo execution context, it will
|
||||
be resolve and injected automatically, as long as you provided an implementation to the executor (more on that below).
|
||||
|
||||
To define a service dependency in a class-based transformation, use :class:`bonobo.config.Service`, a special
|
||||
descriptor (and subclass of :class:`bonobo.config.Option`) that will hold the service names and act as a marker
|
||||
for runtime resolution of service instances.
|
||||
.. code-block:: python
|
||||
|
||||
Let's define such a transformation:
|
||||
from bonobo.config import use
|
||||
|
||||
@use('orders_database')
|
||||
def select_all(database):
|
||||
yield from database.query('SELECT * FROM foo;')
|
||||
|
||||
For class based transformations, you can use :class:`bonobo.config.Service`, a special descriptor (and subclass of
|
||||
:class:`bonobo.config.Option`) that will hold the service names and act as a marker for runtime resolution of service
|
||||
instances.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from bonobo.config import Configurable, Service
|
||||
|
||||
class JoinDatabaseCategories(Configurable):
|
||||
database = Service('primary_sql_database')
|
||||
database = Service('orders_database')
|
||||
|
||||
def __call__(self, database, row):
|
||||
def call(self, database, row):
|
||||
return {
|
||||
**row,
|
||||
'category': database.get_category_name_for_sku(row['sku'])
|
||||
}
|
||||
|
||||
This piece of code tells bonobo that your transformation expect a service called "primary_sql_database", that will be
|
||||
Both pieces of code tells bonobo that your transformation expect a service called "orders_database", that will be
|
||||
injected to your calls under the parameter name "database".
|
||||
|
||||
Function-based transformations
|
||||
------------------------------
|
||||
Providing implementations at run-time
|
||||
-------------------------------------
|
||||
|
||||
No implementation yet, but expect something similar to CBT API, maybe using a `@Service(...)` decorator. See
|
||||
`issue #70 <https://github.com/python-bonobo/bonobo/issues/70>`_.
|
||||
|
||||
Provide implementation at run time
|
||||
----------------------------------
|
||||
|
||||
Let's see how to execute it:
|
||||
Bonobo will expect you to provide a dictionary of all service implementations required by your graph.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import bonobo
|
||||
|
||||
graph = bonobo.graph(
|
||||
*before,
|
||||
JoinDatabaseCategories(),
|
||||
*after,
|
||||
)
|
||||
graph = bonobo.graph(...)
|
||||
|
||||
def get_services():
|
||||
return {
|
||||
'orders_database': my_database_service,
|
||||
}
|
||||
|
||||
if __name__ == '__main__':
|
||||
bonobo.run(
|
||||
graph,
|
||||
services={
|
||||
'primary_sql_database': my_database_service,
|
||||
}
|
||||
)
|
||||
bonobo.run(graph, services=get_services())
|
||||
|
||||
A dictionary, or dictionary-like, "services" named argument can be passed to the :func:`bonobo.run` helper. The
|
||||
"dictionary-like" part is the real keyword here. Bonobo is not a DIC library, and won't become one. So the implementation
|
||||
provided is pretty basic, and feature-less. But you can use much more evolved libraries instead of the provided
|
||||
stub, and as long as it works the same (a.k.a implements a dictionary-like interface), the system will use it.
|
||||
|
||||
.. note::
|
||||
|
||||
A dictionary, or dictionary-like, "services" named argument can be passed to the :func:`bonobo.run` API method.
|
||||
The "dictionary-like" part is the real keyword here. Bonobo is not a DIC library, and won't become one. So the
|
||||
implementation provided is pretty basic, and feature-less. But you can use much more evolved libraries instead of
|
||||
the provided stub, and as long as it works the same (a.k.a implements a dictionary-like interface), the system will
|
||||
use it.
|
||||
|
||||
Command line interface will look at services in two different places:
|
||||
|
||||
* A `get_services()` function present at the same level of your graph definition.
|
||||
* A `get_services()` function in a `_services.py` file in the same directory as your graph's file, allowing to reuse the
|
||||
same service implementations for more than one graph.
|
||||
|
||||
Solving concurrency problems
|
||||
----------------------------
|
||||
@ -87,7 +90,7 @@ Solving concurrency problems
|
||||
If a service cannot be used by more than one thread at a time, either because it's just not threadsafe, or because
|
||||
it requires to carefully order the calls made (apis that includes nonces, or work on results returned by previous
|
||||
calls are usually good candidates), you can use the :class:`bonobo.config.Exclusive` context processor to lock the
|
||||
use of a dependency for a time period.
|
||||
use of a dependency for the time of the context manager (`with` statement)
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@ -101,18 +104,10 @@ use of a dependency for a time period.
|
||||
api.last_call()
|
||||
|
||||
|
||||
Service configuration (to be decided and implemented)
|
||||
:::::::::::::::::::::::::::::::::::::::::::::::::::::
|
||||
|
||||
* There should be a way to configure default service implementation for a python file, a directory, a project ...
|
||||
* There should be a way to override services when running a transformation.
|
||||
* There should be a way to use environment for service configuration.
|
||||
|
||||
Future and proposals
|
||||
::::::::::::::::::::
|
||||
|
||||
This is the first proposed implementation and it will evolve, but looks a lot like how we used bonobo ancestor in
|
||||
production.
|
||||
This first implementation and it will evolve. Base concepts will stay, though.
|
||||
|
||||
May or may not happen, depending on discussions.
|
||||
|
||||
|
||||
@ -1,8 +1,90 @@
|
||||
Transformations
|
||||
===============
|
||||
|
||||
Here is some guidelines on how to write transformations, to avoid the convention-jungle that could happen without
|
||||
a few rules.
|
||||
Transformations are the smallest building blocks in Bonobo ETL.
|
||||
|
||||
They are written using standard python callables (or iterables, if you're writing transformations that have no input,
|
||||
a.k.a extractors).
|
||||
|
||||
Definitions
|
||||
:::::::::::
|
||||
|
||||
Transformation
|
||||
|
||||
The base building block of Bonobo, anything you would insert in a graph as a node. Mostly, a callable or an iterable.
|
||||
|
||||
Extractor
|
||||
|
||||
Special case transformation that use no input. It will be only called once, and its purpose is to generate data,
|
||||
either by itself or by requesting it from an external service.
|
||||
|
||||
Loader
|
||||
|
||||
Special case transformation that feed an external service with data. For convenience, it can also yield the data but
|
||||
a "pure" loader would have no output (although yielding things should have no bad side effect).
|
||||
|
||||
Callable
|
||||
|
||||
Anything one can call, in python. Can be a function, a python builtin, or anything that implements `__call__`
|
||||
|
||||
Iterable
|
||||
|
||||
Something we can iterate on, in python, so basically anything you'd be able to use in a `for` loop.
|
||||
|
||||
|
||||
Function based transformations
|
||||
::::::::::::::::::::::::::::::
|
||||
|
||||
The most basic transformations are function-based. Which means that you define a function, and it will be used directly
|
||||
in a graph.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def get_representation(row):
|
||||
return repr(row)
|
||||
|
||||
graph = bonobo.Graph(
|
||||
[...],
|
||||
get_representation,
|
||||
[...],
|
||||
)
|
||||
|
||||
|
||||
It does not allow any configuration, but if it's an option, prefer it as it's simpler to write.
|
||||
|
||||
|
||||
Class based transformations
|
||||
:::::::::::::::::::::::::::
|
||||
|
||||
For less basic use cases, you'll want to use classes to define some of your transformations. It's also a better choice
|
||||
to build reusable blocks, as you'll be able to create parametrizable transformations that the end user will be able to
|
||||
configure at the last minute.
|
||||
|
||||
|
||||
Configurable
|
||||
------------
|
||||
|
||||
.. autoclass:: bonobo.config.Configurable
|
||||
|
||||
Options
|
||||
-------
|
||||
|
||||
.. autoclass:: bonobo.config.Option
|
||||
|
||||
Services
|
||||
--------
|
||||
|
||||
.. autoclass:: bonobo.config.Service
|
||||
|
||||
Methods
|
||||
-------
|
||||
|
||||
.. autoclass:: bonobo.config.Method
|
||||
|
||||
ContextProcessors
|
||||
-----------------
|
||||
|
||||
.. autoclass:: bonobo.config.ContextProcessor
|
||||
|
||||
|
||||
Naming conventions
|
||||
@ -44,50 +126,35 @@ can be used as a graph node, then use camelcase names:
|
||||
upper = Apply(str.upper)
|
||||
|
||||
|
||||
Function based transformations
|
||||
::::::::::::::::::::::::::::::
|
||||
Testing
|
||||
:::::::
|
||||
|
||||
As Bonobo use plain old python objects as transformations, it's very easy to unit test your transformations using your
|
||||
favourite testing framework. We're using pytest internally for Bonobo, but it's up to you to use the one you prefer.
|
||||
|
||||
If you want to test a transformation with the surrounding context provided (for example, service instances injected, and
|
||||
context processors applied), you can use :class:`bonobo.execution.NodeExecutionContext` as a context processor and have
|
||||
bonobo send the data to your transformation.
|
||||
|
||||
The most basic transformations are function-based. Which means that you define a function, and it will be used directly
|
||||
in a graph.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def get_representation(row):
|
||||
return repr(row)
|
||||
from bonobo.constants import BEGIN, END
|
||||
from bonobo.execution import NodeExecutionContext
|
||||
|
||||
graph = bonobo.Graph(
|
||||
[...],
|
||||
get_representation,
|
||||
)
|
||||
with NodeExecutionContext(
|
||||
JsonWriter(filename), services={'fs': ...}
|
||||
) as context:
|
||||
|
||||
# Write a list of rows, including BEGIN/END control messages.
|
||||
context.write(
|
||||
BEGIN,
|
||||
Bag({'foo': 'bar'}),
|
||||
Bag({'foo': 'baz'}),
|
||||
END
|
||||
)
|
||||
|
||||
It does not allow any configuration, but if it's an option, prefer it as it's simpler to write.
|
||||
|
||||
|
||||
Class based transformations
|
||||
:::::::::::::::::::::::::::
|
||||
|
||||
A lot of logic is a bit more complex, and you'll want to use classes to define some of your transformations.
|
||||
|
||||
The :class:`bonobo.config.Configurable` class gives you a few toys to write configurable transformations.
|
||||
|
||||
Options
|
||||
-------
|
||||
|
||||
.. autoclass:: bonobo.config.Option
|
||||
|
||||
Services
|
||||
--------
|
||||
|
||||
.. autoclass:: bonobo.config.Service
|
||||
|
||||
Methods
|
||||
-------
|
||||
|
||||
.. autoclass:: bonobo.config.Method
|
||||
|
||||
ContextProcessors
|
||||
-----------------
|
||||
|
||||
.. autoclass:: bonobo.config.ContextProcessor
|
||||
# Out of the bonobo main loop, we need to call `step` explicitely.
|
||||
context.step()
|
||||
context.step()
|
||||
|
||||
|
||||
@ -11,6 +11,10 @@ Bonobo
|
||||
reference/index
|
||||
faq
|
||||
contribute/index
|
||||
|
||||
|
||||
.. toctree::
|
||||
:hidden:
|
||||
|
||||
genindex
|
||||
modindex
|
||||
|
||||
|
||||
Reference in New Issue
Block a user