extinclude attempt

This commit is contained in:
gwen 2024-10-14 19:17:44 +02:00
parent 2d02da9939
commit 9199631ea5
9 changed files with 201 additions and 918 deletions

View file

@ -10,12 +10,14 @@
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
# import os
# import sys
import sys, os
# sys.path.insert(0, os.path.abspath('.'))
#sys.path.append(os.path.abspath('ext'))
sys.path.append('.')
# shows/hides the todos
todo_include_todos = True
todo_include_todos = False
# -- Project information -----------------------------------------------------
@ -40,32 +42,29 @@ release = '1.0'
# ones.
extensions = [
'sphinx.ext.extlinks', 'sphinx_lesson', 'sphinx.ext.todo'
#'myst_parser', 'sphinx.ext.extlinks'
'sphinx.ext.extlinks', 'sphinx_lesson', 'sphinx.ext.todo',
'sphinx.ext.extlinks', 'ext.xref', 'ext.extinclude'
]
#
#myst_enable_extensions = [
# "amsmath",
# "attrs_inline",
# "colon_fence",
# "deflist",
# "dollarmath",
# "fieldlist",
# "html_admonition",
# "html_image",
## "linkify",
# "replacements",
# "smartquotes",
# "strikethrough",
# "substitution",
# "tasklist",
#]
#uses the xref.py extension
xref_links = {"link_name" : ("user text", "url")}
link_name = "Sphinx External Links"
user_text = "modified External Links Extension"
url = "http://www.sphinx-doc.org/en/stable/ext/extlinks.html"
links = {
'tiramisu': ('Tiramisu', 'https://tiramisu.readthedocs.io/en/latest/'),
'tiramisu library': ('Tiramisu library homepage', 'https://forge.cloud.silique.fr/stove/tiramisu'),
}
xref_links.update(links)
# **extlinks** 'sphinx.ext.extlinks',
# enables syntax like :proxy:`my source <hello>` in the src files
extlinks = {'proxy': ('/proxy/%s.html',
'external link: ')}
extlinks = {'source': ('https://forge.cloud.silique.fr/stove/rougail-tutorials/src/commit/%s',
'source: %s')}
default_role = "code"
@ -111,7 +110,7 @@ language = 'en'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path.
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
exclude_patterns = ['.venv', 'build', '_build', 'Thumbs.db', '.DS_Store']
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = None

32
docs/ext/extinclude.py Normal file
View file

@ -0,0 +1,32 @@
from __future__ import annotations
from docutils import nodes
from sphinx.application import Sphinx
from sphinx.util.docutils import SphinxDirective, SphinxRole
from sphinx.util.typing import ExtensionMetadata
class HelloDirective(SphinxDirective):
"""A directive to say hello!"""
required_arguments = 1
def run(self) -> list[nodes.Node]:
paragraph_node = nodes.paragraph(text=f'hello {self.arguments[0]}!')
return [paragraph_node]
def setup(app: Sphinx) -> ExtensionMetadata:
app.add_directive('hello', HelloDirective)
return {
'version': '0.1',
'parallel_read_safe': True,
'parallel_write_safe': True,
}

42
docs/ext/xref.py Normal file
View file

@ -0,0 +1,42 @@
"""adds link url in the global scope
"""
from docutils import nodes
from sphinx.util import caption_ref_re
def xref( typ, rawtext, text, lineno, inliner, options={}, content=[] ):
title = target = text
titleistarget = True
# look if explicit title and target are given with `foo <bar>` syntax
brace = text.find('<')
if brace != -1:
titleistarget = False
m = caption_ref_re.match(text)
if m:
target = m.group(2)
title = m.group(1)
else:
# fallback: everything after '<' is the target
target = text[brace+1:]
title = text[:brace]
link = xref.links[target]
if brace != -1:
pnode = nodes.reference(target, title, refuri=link[1])
else:
pnode = nodes.reference(target, link[0], refuri=link[1])
return [pnode], []
def get_refs(app):
xref.links = app.config.xref_links
def setup(app):
app.add_config_value('xref_links', {}, True)
app.add_role('xref', xref)
app.connect("builder-inited", get_refs)

View file

@ -23,64 +23,64 @@ Parameters
Depending on the types of calculation, the parameters will be different:
.. list-table::
.. list-table::
:widths: 15 25 20 15
:header-rows: 1
* - Calculation type
* - Calculation type
- Parameter
- Comments
- Sample
* -
- **type**
* -
- **type**
`string`
`mandatory`
- Type of calculation, possible values are: jinja, variable, information, suffix or index
- jinja
* - Jinja
* - Jinja
- **jinja**
`string`
`mandatory`
- Template Jinja. For a multiple variable, each line represents a value.
- `{% if rougail.variable %}
- `{% if rougail.variable %}`
{{ rougail.variable }}
`{{ rougail.variable }}`
{% endif %}`
* - Jinja
- **params**
`list`
`{% endif %}`
* - Jinja
- **params**
`list`
- Additional parameters passed to the Jinja template
-
-
* - Variable (`mandatory`)
Information
- **variable**
- **variable**
`string`
- Name of associated variable
- rougail.variable
- rougail.variable
* - Variable
- **propertyerror**
- **propertyerror**
`boolean`
- If access to the variable is not possible due to a property (for example `disabled`) by default an error is returned. If the attribute is `false`, the calculated value is empty.
**Default value:** `true`
- false
- false
* - Information
- **information**
`string`
`mandatory`
- Name of the information whose value we want to retrieve.
- doc
@ -98,60 +98,60 @@ There are two types of parameter:
- parameter via a suffix: in the case of a variable in a dynamic family
- parameter via an index: in the case of a follower variable
.. list-table::
.. list-table::
:widths: 15 25 20 15
:header-rows: 1
* - Parameter type
* - Parameter type
- Parameter
- Comments
- Sample
* -
* -
- **name**
`string`
`mandatory`
- parameter's name
- my_param
* -
- my_param
* -
- **type**
`string`
`mandatory`
- parameter's type, possible values are: variable, information, suffix or index
- suffix
* - Variable
- **variable**
`string`
`mandatory`
- Variable's name
- Variable's name
- rougail.variable
* - Variable (`mandatory`) information
- **propertyerror**
- **propertyerror**
`boolean`
- If access to the variable is not possible due to a property (for example `disabled`) by default an error is returned. If the attribute is `False`, the parameter is not passed to the Jinja template.
- **Default value**: `True`
* - Variable
* - Variable
- **optional**
`boolean`
- The variable may not exist depending on YAML file imports.
- The variable may not exist depending on YAML file imports.
If the optional parameter is `True`, the parameter will simply be deleted if the variable does not exist.
Default value : `False`
Default value : `False`
- True
* - Information
- **information**
`string`
`mandatory`
- Name of the information whose value we want to retrieve.
- doc
@ -170,6 +170,7 @@ Inside those namespaces we can add families and variables.
Here is an hierarchic examples:
.. code-block::
rougail
├── variable1
├── family1
@ -210,6 +211,7 @@ Dynamic family
Hire is a dynamic family "{{ suffix }}":
.. code-block::
rougail
├── variable1: ["val1", "val2"]
├── {{ suffix }}
@ -254,7 +256,7 @@ Let's start with an example from a simple Jinja template:
type: jinja
jinja: 'no'
Here is a second example with a boolean variable:
Here is a second example with a boolean variable:
.. code-block:: yaml
@ -280,7 +282,7 @@ And a multiple value of the number type:
jinja: |
1
2
3
3
Let's create a variable whose value is returned by a python function:
@ -295,7 +297,7 @@ Let's create a variable whose value is returned by a python function:
Then let's create the `return_no` function:
.. code-block:: python
.. code-block:: python
def return_no():
return 'no'
@ -377,7 +379,7 @@ An example with an index type parameter:
Calculation via a variable
-----------------------------
Copy a variable in another:
Copy a variable in another:
.. code-block:: yaml
@ -481,7 +483,7 @@ Second example using the variable for all suffixes:
.. code-block:: yaml
---
version: '1.1'
version: '1.1'
varname:
multi: true
default:

View file

@ -1,27 +1,34 @@
.. |Tiramisu| replace:: Tiramisu
.. _tiramisu: https://forge.cloud.silique.fr/stove/tiramisu
Getting started
====================
What is a consistency handling system ?
------------------------------------------------
.. questions:: Question: "OK, I have understood that the Rougail stuff enables me to take advantage of |Tiramisu|. But what is all this for? What is exactly a consistency handling system? And again, what is this |Tiramisu| library used for?"
.. questions::
*Answer*: Well, let's explain what |Tiramisu| is and how we are using the |Tiramisu| library.
"OK, I have understood that the Rougail stuff enables me to take advantage of :xref:`tiramisu`.
But what is all this for?
What is exactly a consistency handling system?
And again, what is this :xref:`tiramisu` library used for?"
*Answer*: Well, now we explain what this :xref:`tiramisu` library is, and how we are using it.
.. glossary::
Tiramisu
|Tiramisu| is a consistency handling system that has initially been designed
:xref:`tiramisu` is a consistency handling system that has initially been designed
in the configuration management scope. To put it more simply,
this library is generally used to handle configuration options.
It manages variables and group of variables. In the Tiramisu scope we call
it *options* and *option descriptions*.
Here is the :xref:`tiramisu library`.
In the Rougail scope, we call it :term:`variable`\ s and :term:`families`.
In Rougail, the families and variables are located in the :term:`dictionaries`.
@ -57,7 +64,22 @@ The YAML dictionaries format
Before getting started with Rougail we need to learn the specifics of the YAML dictionaries file format (as well as some templating concepts).
.. FIXME parler de jinja https://jinja.palletsprojects.com
.. todo:: parler de jinja https://jinja.palletsprojects.com
.. _empty_dictionary:
Here is an empty rougail dictionary YAML file
.. code-block:: yaml
---
version: 1.1
:source:`v1.1_001/firefox/00-proxy.yml`
.. hello:: world
Here is a :term:`dictionary` example:
@ -189,3 +211,4 @@ We then have the output:
.. code-block:: python
{'rougail.world.name': 'rougail'}

View file

@ -170,6 +170,7 @@ Here an example to a lipogram option (in a string, we cannot use "e" character):
To add the new lipogram type in Rougail:
.. code-block:: python
>>> from rougail import Rougail, RougailConfig
>>> RougailConfig['dictionaries_dir'] = ['dict']
>>> RougailConfig['custom_types']['lipogram'] = LipogramOption
@ -178,12 +179,14 @@ Now, we can use lipogram type.
Here is a :file:`dict/00-base.yml` dictionary:
.. code-block:: yaml
---
version: '1.1'
var:
type: lipogram
.. code-block:: python
>>> rougail = Rougail()
>>> config = rougail.get_config()
>>> config.option('rougail.var').value.set('blah')
@ -212,6 +215,7 @@ We create a term:`dictionary` named :file:`dict/01-upgrade.yml` with version 1.0
.. code-block:: python
>>> from rougail import RougailUpgrade, RougailConfig
>>> RougailConfig['dictionaries_dir'] = ['dict']
>>> upgrade = RougailUpgrade()

View file

@ -1,832 +0,0 @@
Tutorial: a real world sample
==============================
.. demo:: Demonstration : configuring (the setting of) your favorite web browser
This tutorial shows to you an example of Rougail use on
how to set a proxy in the `Mozilla Firefox <https://www.mozilla.org/en-US/firefox/new/>`_ browser.
More precisely, this tutorial aims at reproducing this Mozilla Firefox settings page:
.. image:: images/firefox.png
.. important:: Here we are in the configuration validation use case,
that is the values entered by the user have to be validated.
It's a common use case, but not the only one.
Let's explain this use case.
The Firefox proxy configuration
-------------------------------------------
The `proxy` family
-------------------
Let's create our first :term:`dictionary`.
.. prerequisites:: Let's create a folder named `dict` and a dictionary file inside
We will put our dictionary files in this folder.
Then let's put our first dictionary file in this folder, named :file:`00-proxy.yml`
.. code-block:: yaml
:caption: the :file:`00-proxy.yml` file
:linenos:
---
version: '1.1'
proxy:
description: Proxy configuration in order to have access to the internet
type: family
We can see that we have defined a :term:`family` here, and this family is *empty*
(that is, the family container contains no variable yet).
.. admonition:: If a family is empty
We need to specify the :term:`family` type (line 5) here because if we don't,
the Rougail's type engine will infer it by default as a :term:`variable`.
It's because we don't have set any :term:`variable` inside.
.. note:: The variables will be created in several files for educational purposes.
Obviously all the variables can be put in the same file.
The proxy's configuration type
----------------------------------
In the Firefox configuration, it is possible to define several configuration modes,
from no proxy at all (`no proxy`) to a kind of automatic configuration mode from a file (`set up proxy configuration from a file`).
We're gonna create a first variable in this family with "Proxy mode" as the description.
Let's create a second :file:`dict/01-proxy_mode.yml` file.
.. code-block:: yaml
:caption: the :file:`001-proxy_mode.yml` file
:linenos:
---
version: '1.1'
proxy:
proxy_mode:
description: Proxy mode
type: choice
choices:
- No proxy
- Auto-detect proxy settings for this network
- Use system proxy settings
- Manual proxy configuration
- Automatic proxy configuration URL
default: No proxy
The `proxy_mode` variable requires a value (that is, `None` is not an option).
It shall have a value, but what if the user *does not* specify any value?
There is line 13, a possibility of setting a default value, wich is `No proxy` as the default.
The `proxy_mode` setting is "choice" (`type: choice`) means that
there is a list of available values that can be selected.
We say that the `proxy_mode` variable is *constrained* (by choices).
Line 8 to 12, we have the list of the possible (authorized) values:
- No proxy
- Auto-detect proxy settings for this network
- Use system proxy settings
- Manual proxy configuration
- Automatic proxy configuration URL
Now let's test our first two dictionaries:
>>> from rougail import Rougail, RougailConfig
>>> from pprint import pprint
>>> RougailConfig['dictionaries_dir'] = ['dict']
>>> rougail = Rougail()
>>> config = rougail.get_config()
>>> config.property.read_only()
>>> pprint(config.value.get(), sort_dicts=False)
{'rougail.proxy.proxy_mode': 'No proxy'}
The manual mode
------------------
.. questions:: OK then. What happens when you select the "Manual proxy configuration"?
A good configuration design is to place all the proxy's manual configuration in a :term:`family`.
Let's create the :file:`dict/02-proxy_manual.yml` dictionary:
.. code-block:: yaml
:caption: the the :file:`dict/02-proxy_manual.yml` file
---
version: '1.1'
proxy:
manual:
description: Manual proxy configuration
type: family
disabled:
type: jinja
jinja: |
{% if rougail.proxy.proxy_mode != 'Manual proxy configuration' %}
the proxy mode is not manual
{% endif %}
Well, if the user selects the "Manual proxy configuration" proxy mode, we want to see a new subfamily (that is, a new set of configuration variables) called `manual` to appear (which is disabled).
.. glossary::
subfamily
A subfamily is just a family inside a family, a family that contains a family.
.. questions:: What about this `Jinja` type?
If the :term:`Jinja` template returns some text, then the family will be `disabled`. Otherwise it is accessible.
Deactivating a family means that we will not be able to access it as well as the variables or families included in this family.
.. note:: If the Jinja template does not return any text, the variable will be **enabled**.
Here we are using the Jinja condition statement.
.. glossary::
Jinja
`Jinja <https://jinja.palletsprojects.com>`_ is a template engine.
we are using Jinja in a classical way, that is, Jinja allows us to handle different cases,
for example with the `if` statement.
The HTTP proxy configuration
------------------------------
In this family let's add a *subfamily* named `http_proxy`, containing the address and port configuration variables.
Let's create the :file:`dict/03-proxy_manual_http_proxy.yml` dictionary:
.. code-block:: yaml
:caption: the the :file:`dict/02-proxy_manual.yml` file
:linenos:
---
version: '1.1'
proxy:
manual:
http_proxy:
description: HTTP Proxy
address:
description: HTTP address
type: domainname
port:
description: HTTP Port
type: port
default: '8080'
Both variables `address` and `port` have particular types (respectively `domainname` line 9 and `port` line 12) to validate the values configured by the user.
.. note:: No need to specify the type of the `http_proxy` as a family type, because here we have declared variables inside of it.
Duplicating the HTTP configuration to HTTPS
---------------------------------------------
We then want to offer the user the possibility of providing the same proxy for the HTTPS requests. Let's create the :file:`dict/04-proxy_manual_http_use_for_https.yml` file:
.. code-block:: yaml
:caption: the :file:`dict/04-proxy_manual_http_use_for_https.yml` file
version: '1.1'
proxy:
manual:
use_for_https:
description: Also use this proxy for HTTPS
type: boolean
This variable is a `boolean` type, its default value is `True`.
HTTPS proxy configuration detail
-----------------------------------
Let's add a new subfamily named `ssl_proxy`, containing the `address` and `port` variables.
Let's create the :file:`dict/05-proxy_manual_ssl_proxy.yml` file:
.. code-block:: yaml
:caption: the :file:`dict/04-proxy_manual_http_use_for_https.yml` file
:linenos:
---
version: '1.1'
proxy:
manual:
ssl_proxy:
description: HTTPS Proxy
hidden:
type: variable
variable: rougail.proxy.manual.use_for_https
address:
description: HTTPS address
type: domainname
default:
type: jinja
jinja: |
{% if rougail.proxy.manual.use_for_https %}
{{ rougail.proxy.manual.http_proxy.address }}
{% endif %}
port:
description: HTTPS Port
type: port
default:
type: jinja
jinja: |
{% if rougail.proxy.manual.use_for_https %}
{{ rougail.proxy.manual.http_proxy.port }}
{% endif %}
Depending on the value of the `rougail.proxy.mandatory.use_for_https` variable, this family will appear or disappear (the `hidden` setting line 7). Unlike earlier, this time it is not necessary to use a Jinja function.
Let's notice that the family is not disabled because the variables will need to remain accessible (yet in `read-only` mode).
The address and port variables are copied from HTTP to HTTPS if `rougail.proxy.use_for_https` is set to `True`.
Now let's test all of it:
>>> from rougail import Rougail, RougailConfig
>>> from pprint import pprint
>>> RougailConfig['dictionaries_dir'] = ['dict']
>>> rougail = Rougail()
>>> config = rougail.get_config()
>>> config.property.read_only()
>>> pprint(config.value.get(), sort_dicts=False)
{'rougail.proxy.proxy_mode': 'No proxy'}
At this time the proxy is not configured yet, so we do not see any variables.
Let's look at what happens if we try to access the `rougail.proxy.manual` variable if we are not in manual mode:
.. code-block:: python
>>> pprint(config.option('rougail.proxy.manual').value.get(), sort_dicts=False)
We have an error (with the message defined in the Jinja template):
.. code-block:: shell
tiramisu.error.PropertiesOptionError: cannot access to
optiondescription "Manual proxy configuration" because
has property "disabled" (the mode proxy is not manual)
Let's configure the proxy in manual mode
>>> config.property.read_write()
>>> config.option('rougail.proxy.proxy_mode').value.set('Manual proxy configuration')
>>> config.option('rougail.proxy.manual.http_proxy.address').value.set('proxy.example')
>>> pprint(config.value.get(), sort_dicts=False)
We can see that the returned variables does have the desired values:
.. code-block:: python
{'rougail.proxy.proxy_mode': 'Manual proxy configuration',
'rougail.proxy.manual.http_proxy.address': 'proxy.example',
'rougail.proxy.manual.http_proxy.port': '8080',
'rougail.proxy.manual.use_for_https': True}
Let's set the `read_only` mode and have a look at the configuration again:
.. code-block:: python
>>> config.property.read_only()
>>> pprint(config.value.get(), sort_dicts=False)
{'rougail.proxy.proxy_mode': 'Manual proxy configuration',
'rougail.proxy.manual.http_proxy.address': 'proxy.example',
'rougail.proxy.manual.http_proxy.port': '8080',
'rougail.proxy.manual.use_for_https': True,
'rougail.proxy.manual.ssl_proxy.address': 'proxy.example',
'rougail.proxy.manual.ssl_proxy.port': '8080'}
In the `read_only` mode, we can see that the HTTPS configuration appears.
.. note:: We can see that `rougail.proxy.manual.http_proxy` values have been copied
in `rougail.proxy.manual.ssl_proxy` too.
Changing values programmatically
--------------------------------------
We are going to use the :term:`Tiramisu` API to manipulate programmatically the different variables.
First, let's set `rougail.proxy.manual.use_for_https` to `False`. It is now possible
to configure the HTTPS:
.. code-block:: python
>>> config.property.read_write()
>>> config.option('rougail.proxy.manual.use_for_https').value.set(False)
>>> config.option('rougail.proxy.manual.ssl_proxy.address').value.set('other.proxy.example')
>>> pprint(config.value.get(), sort_dicts=False)
{'rougail.proxy.proxy_mode': 'Manual proxy configuration',
'rougail.proxy.manual.http_proxy.address': 'proxy.example',
'rougail.proxy.manual.http_proxy.port': '8080',
'rougail.proxy.manual.use_for_https': False,
'rougail.proxy.manual.ssl_proxy.address': 'other.proxy.example',
'rougail.proxy.manual.ssl_proxy.port': '8080'}
The value of the variable `rougail.proxy.manual.ssl_proxy.address` has actually been modified.
But if this variable is hidden again, then the value comes back to the default value:
.. code-block:: python
>>> config.option('rougail.proxy.manual.use_for_https').value.set(False)
>>> config.property.read_only()
>>> pprint(config.value.get(), sort_dicts=False)
{'rougail.proxy.proxy_mode': 'Manual proxy configuration',
'rougail.proxy.manual.http_proxy.address': 'proxy.example',
'rougail.proxy.manual.http_proxy.port': '8080',
'rougail.proxy.manual.use_for_https': False,
'rougail.proxy.manual.ssl_proxy.address': 'proxy.example',
'rougail.proxy.manual.ssl_proxy.port': '8080'}
SOCK's proxy configuration
-------------------------------
Let's add a new :term:`subfamily` named `socks_proxy` with the `address`,
`port` and `version` variables.
Let's create the :file:`dict/06-proxy_manual_socks_proxy.yml` file:
.. code-block:: yaml
:caption: the :file:`dict/06-proxy_manual_socks_proxy.yml` file
---
version: '1.1'
proxy:
manual:
socks_proxy:
description: SOCKS Proxy
address:
description: SOCKS Address
type: domainname
port:
description: SOCKS Port
type: port
version:
description: SOCKS host version used by proxy
type: choice
choices:
- v4
- v5
default: v5
There's nothing new to learn with this file.
The automatic detection mode
------------------------------
Let's add a new variable named `auto`.
Let's create the :file:`dict/07-proxy_auto.yml` file:
.. code-block:: yaml
:caption: the :file:`dict/07-proxy_auto.yml` file
---
version: '1.1'
proxy:
auto:
type: web_address
description: Automatic proxy configuration URL
disabled:
type: jinja
jinja: |
{% if rougail.proxy.proxy_mode != 'Automatic proxy configuration URL' %}
the proxy mode is not automatic
{% endif %}
The `web_address` type imposes a value starting with `http://` or `https://`.
This variable is activated when the proxy is in automatic mode.
The proxy's exceptions
---------------------------
Finally, let's add a variable containing proxy exceptions.
Let's create the :file:`dict/07-proxy_no_proxy.yml` file:
.. code-block:: yaml
:caption: the :file:`dict/07-proxy_no_proxy.yml` file
:linenos:
---
version: '1.1'
proxy:
no_proxy:
description: Address for which proxy will be desactivated
multi: true
type: "domainname"
params:
allow_ip: true
allow_cidr_network: true
allow_without_dot: true
allow_startswith_dot: true
disabled:
type: jinja
jinja: |
{% if rougail.proxy.proxy_mode == 'No proxy' %}
proxy mode is no proxy
{% endif %}
mandatory: false
This `no_proxy` variable is much like a `domainname` type except that we add
a `params` line 7, we authorize the :
- IP
- CIDR networks
- machine names (without `'.'`)
- sub-domaines like `.example`
There can be multiple exceptions to the proxy, so the variable is :term:`multi` (line5).
This variable is only accessible if no proxy is defined (`disabled`).
.. glossary::
multi
A multi is a multiple variable, that is a variable that can have multiple values.
The `no_proxy` variable do not requires a value (that is, `None` is an option),
there is line 19 this statement `mandatory: false` which means that this variable is not mandatory.
Let's test it:
>>> from rougail import Rougail, RougailConfig
>>> from pprint import pprint
>>> RougailConfig['dictionaries_dir'] = ['dict']
>>> rougail = Rougail()
>>> config = rougail.get_config()
>>> config.property.read_write()
>>> config.option('rougail.proxy.proxy_mode').value.set('Manual proxy configuration')
>>> config.option('rougail.proxy.manual.http_proxy.address').value.set('proxy.example')
>>> config.option('rougail.proxy.no_proxy').value.set(['.example', '192.168.1.1'])
>>> config.property.read_only()
>>> pprint(config.value.get(), sort_dicts=False)
It outputs:
.. code-block:: python
{'rougail.proxy.proxy_mode': 'Manual proxy configuration',
'rougail.proxy.manual.http_proxy.address': 'proxy.example',
'rougail.proxy.manual.http_proxy.port': '8080',
'rougail.proxy.manual.use_for_https': True,
'rougail.proxy.manual.ssl_proxy.address': 'proxy.example',
'rougail.proxy.manual.ssl_proxy.port': '8080',
'rougail.proxy.manual.socks_proxy.address': None,
'rougail.proxy.manual.socks_proxy.port': None,
'rougail.proxy.manual.socks_proxy.version': 'v5',
'rougail.proxy.no_proxy': ['.example', '192.168.1.1']}
But not possible to put an invalid value:
.. code-block:: python
>>> config.option('rougail.proxy.no_proxy').value.set(['.example', '192.168.1.1', 'not valid'])
[..]
tiramisu.error.ValueOptionError: "not valid" is an invalid domain name for "Address for which proxy will be desactivated", could be a IP, otherwise must start with lowercase characters followed by lowercase characters, number, "-" and "." characters are allowed
The authentification request
--------------------------------
Nothing special when creating the authentication request. To do this, let's create a `dict/08-proxy_prompt_authentication.yml` file:
.. code-block:: yaml
:caption: the :file:`dict/08-proxy_prompt_authentication.yml` file
:linenos:
---
version: '1.1'
proxy:
prompt_authentication:
description: Prompt for authentication if password is saved
type: boolean
default: true
disabled:
type: jinja
jinja: |
{% if rougail.proxy.proxy_mode == 'No proxy' %}
proxy mode is no proxy
{% endif %}
The proxy SOCKS v5's DNS
------------------------------
The DNS variable for the SOCKS v5 proxy only appears if the proxy is configured and the version of the SOCKS proxy selected is `v5`.
Let's create a `dict/09-proxy_proxy_dns_socks5.yml` file:
.. code-block:: yaml
:caption: the :file:`dict/09-proxy_proxy_dns_socks5.yml` file
:linenos:
---
version: '1.1'
proxy:
proxy_dns_socks5:
description: Use proxy DNS when using SOCKS v5
type: boolean
default: false
disabled:
type: jinja
params:
socks_version:
type: variable
variable: rougail.proxy.manual.socks_proxy.version
propertyerror: false
jinja: |
{% if rougail.proxy.proxy_mode == 'No proxy' %}
the proxy mode is no proxy
{% elif socks_version is undefined or socks_version == 'v4' %}
socks version is v4
{% endif %}
The difficulty here is that the `rougail.proxy.manual.socks_proxy.version` variable
can be deactivated (and therefore not usable in a calculation).
.. FIXME definir ce qu'est une calculation
In this case, we will add a parameter (here called `socks_version`) which will contain,
if there is no property error, the value of the variable.
Otherwise the parameter will not be passed to the Jinja template.
This is why it is necessary to test in the Jinja template whether the `socks_version` variable really exists.
The DNS over HTTPS
----------------------
Finally we will configure DNS over HTTPS in the 10-proxy_dns_over_https.yml file:
Let's create a `dict/10-proxy_dns_over_https.yml` file:
.. code-block:: yaml
:caption: the :file:`dict/10-proxy_dns_over_https.yml` file
:linenos:
---
version: '1.1'
proxy:
dns_over_https:
description: DNS over HTTPS
enable_dns_over_https:
description: Enable DNS over HTTPS
type: boolean
default: false
provider:
description: Use Provider
type: choice
choices:
- Cloudflare
- NextDNS
- Custom
default: Cloudflare
disabled:
type: jinja
jinja: |
{% if not rougail.proxy.dns_over_https.enable_dns_over_https %}
Enable DNS over HTTPS is False
{% endif %}
custom_dns_url:
description: Custom DNS URL
type: web_address
disabled:
type: jinja
params:
provider:
type: variable
variable: rougail.proxy.dns_over_https.provider
propertyerror: false
jinja: |
{% if provider is not defined or provider != 'Custom' %}
provider is not custom
{% endif %}
validators:
- type: jinja
jinja: |
{% if rougail.proxy.dns_over_https.custom_dns_url.startswith('http://') %}
only https is allowed
{% endif %}
.. FIXME : define validators
The only particularity here is that we added additional validation (validators) to the `custom_dns_url` variable. Only an address starting with `https://` is allowed (not `http://`).
----
The FoxyProxy type's proxy configuration
--------------------------------------------
Here is now the integration of part of the Firefox FoxyProxy plugin.
The idea is to have a namespace specific to FoxyProxy and to find in it part of the settings that we will have made in the main namespace.
This is what the page looks like:
.. image:: images/foxyproxy.png
It is possible, in this plugin, to specify an unlimited number of proxies.
Our `proxy` family will no longer be of the `family` type as before but of another type : the :term:`leadership` type.
.. FIXME: expliquer ce qu'est le type leardership
Here is the complete content of the FoxyProxy type proxy configuration
(to be put in the `foxyproxy/00-base.yml` file):
.. code-block:: yaml
:caption: the :file:``foxyproxy/00-base.yml`` file
:linenos:
---
version: '1.1'
proxy:
_type: leadership
title:
description: Title or Description
multi: true
color:
description: Color
type:
type: choice
choices:
- HTTP
- HTTPS/SSL
- SOCKS5
- SOCKS4
- PAC URL
- WPAD
- System (use system settings)
- Direct (no proxy)
default: Direct (no proxy)
address:
description: IP address, DNS name, server name
multi: true
disabled:
type: jinja
jinja: |
{% if foxyproxy.proxy.type not in ['HTTP', 'HTTPS/SSL', 'SOCKS5', 'SOCKS4'] %}
proxy does not need address
{% endif %}
default:
type: jinja
params:
firefox_address:
type: variable
variable: rougail.proxy.manual.http_proxy.address
propertyerror: false
jinja: |
{% if firefox_address is not undefined %}
{{ firefox_address }}
{% endif %}
port:
description: Port
type: port
default:
type: jinja
params:
firefox_port:
type: variable
variable: rougail.proxy.manual.http_proxy.port
propertyerror: false
jinja: |
{% if firefox_port is not undefined %}
{{ firefox_port }}
{% endif %}
disabled:
type: jinja
jinja: |
{% if foxyproxy.proxy.type not in ['HTTP', 'HTTPS/SSL', 'SOCKS5', 'SOCKS4'] %}
proxy does not need port
{% endif %}
username:
description: Username
type: unix_user
mandatory:
type: jinja
jinja: |
{% if foxyproxy.proxy.password %}
username is mandatory
{% endif %}
disabled:
type: jinja
jinja: |
{% if foxyproxy.proxy.type not in ['HTTP', 'HTTPS/SSL', 'SOCKS5', 'SOCKS4'] %}
proxy does not need username
{% endif %}
password:
description: Password
type: secret
disabled:
type: jinja
jinja: |
{% if foxyproxy.proxy.type not in ['HTTP', 'HTTPS/SSL', 'SOCKS5', 'SOCKS4'] %}
proxy does not need password
{% endif %}
url:
type: web_address
disabled:
type: jinja
jinja: |
{% if foxyproxy.proxy.type not in ['PAC URL', 'WPAD'] %}
proxy does not need url
{% endif %}
A few comments:
- in the `foxyproxy.proxy` :term:`leader` family there is a variable named `type` (line 4), this may conflict with the `type` attribute (specified line 10). In this case, to specify the type we use the `_type` attribute
- a :term:`follower` variable can also be multiple
(which is the case for `foxyproxy.proxy.address`)
- `foxyproxy.proxy.username` (line 62) becomes :term:`mandatory` if `foxyproxy.proxy.password`
is specified, in fact a password without a username is meaningless
Let's test it:
>>> from rougail import Rougail, RougailConfig
>>> from pprint import pprint
>>> RougailConfig['dictionaries_dir'] = ['dict']
>>> RougailConfig['extra_dictionaries']['foxyproxy'] = ['foxyproxy/']
>>> rougail = Rougail()
>>> config = rougail.get_config()
>>> config.option('rougail.proxy.proxy_mode').value.set('Manual proxy configuration')
>>> config.option('rougail.proxy.manual.http_proxy.address').value.set('proxy.example')
>>> config.option('foxyproxy.proxy.title').value.set(['MyProxy'])
>>> config.option('foxyproxy.proxy.type', 0).value.set('HTTP')
>>> config.option('foxyproxy.proxy.color', 0).value.set('#00000')
>>> config.property.read_only()
>>> pprint(config.value.get(), sort_dicts=False)
The output is:
.. code-block:: python
{'rougail.proxy.proxy_mode': 'Manual proxy configuration',
'rougail.proxy.manual.http_proxy.address': 'proxy.example',
'rougail.proxy.manual.http_proxy.port': '8080',
'rougail.proxy.manual.use_for_https': True,
'rougail.proxy.manual.ssl_proxy.address': 'proxy.example',
'rougail.proxy.manual.ssl_proxy.port': '8080',
'rougail.proxy.manual.socks_proxy.address': None,
'rougail.proxy.manual.socks_proxy.port': None,
'rougail.proxy.manual.socks_proxy.version': 'v5',
'rougail.proxy.no_proxy': [],
'rougail.proxy.proxy_dns_socks5': False,
'rougail.proxy.dns_over_https.enable_dns_over_https': False,
'foxyproxy.proxy.title': [{'foxyproxy.proxy.title': 'MyProxy',
'foxyproxy.proxy.color': '#00000',
'foxyproxy.proxy.type': 'HTTP',
'foxyproxy.proxy.address': ['proxy.example'],
'foxyproxy.proxy.port': '8080',
'foxyproxy.proxy.username': None,
'foxyproxy.proxy.password': None}]}
The choice we made here is to make `foxyproxy.proxy.username` :term:`mandatory` if a password is specified in the `foxyproxy.proxy.password` variable.
It makes sense to have a username without a password (in this case the password will be requested when connecting to the proxy). But the opposite does not make sense.
From a user point of view this may seem disturbing (if you enter the password, you have to return to the previous option to specify the password).
It is possible to reverse the logic. If the `foxyproxy.proxy.username` variable is set, the `foxyproxy.proxy.password` variable becomes editable.
None of this two variables needs to be :term:`mandatory`.
If you prefer this option, here is a second extra dictionary :file:`foxyproxy/01-redefine.yml` which will redefine the behavior only of the `foxyproxy.proxy.username` and `foxyproxy.proxy.password` variables:
.. code-block:: yaml
:caption: the :file:`foxyproxy/01-redefine.yml` file
:linenos:
---
version: '1.1'
proxy:
username:
redefine: true
# suppress mandatory constrainte
mandatory: false
password:
redefine: true
hidden:
type: jinja
jinja: |
{% if not foxyproxy.proxy.username %}
no username defined
{% endif %}
**It's up to you to play now !**

View file

@ -31,5 +31,6 @@ At first glance we have a choice between five variables:
:titlesonly:
:caption: The firefox tutorial steps
preliminary
tutorial

View file

@ -0,0 +1,12 @@
Preliminaries
================
.. prerequisites:: Prerequisistes
You need to know how to:
- manipulate a rougail :term:`dictionary`
- use a rougail format version
- use the rougail command line