From 9199631ea568692fc2e3d76008d734d0aac7d7b7 Mon Sep 17 00:00:00 2001 From: gwen Date: Mon, 14 Oct 2024 19:17:44 +0200 Subject: [PATCH] extinclude attempt --- docs/conf.py | 49 +- docs/ext/extinclude.py | 32 ++ docs/ext/xref.py | 42 ++ docs/fill.rst | 110 ++--- docs/gettingstarted.rst | 37 +- docs/library.rst | 4 + docs/tutorial.rst | 832 ---------------------------------- docs/tutorial/index.rst | 1 + docs/tutorial/preliminary.rst | 12 + 9 files changed, 201 insertions(+), 918 deletions(-) create mode 100644 docs/ext/extinclude.py create mode 100644 docs/ext/xref.py delete mode 100644 docs/tutorial.rst create mode 100644 docs/tutorial/preliminary.rst diff --git a/docs/conf.py b/docs/conf.py index 6262081de..4f7e19ffc 100755 --- a/docs/conf.py +++ b/docs/conf.py @@ -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 ` 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 diff --git a/docs/ext/extinclude.py b/docs/ext/extinclude.py new file mode 100644 index 000000000..b07982652 --- /dev/null +++ b/docs/ext/extinclude.py @@ -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, + } diff --git a/docs/ext/xref.py b/docs/ext/xref.py new file mode 100644 index 000000000..09606efd8 --- /dev/null +++ b/docs/ext/xref.py @@ -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 ` 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) + diff --git a/docs/fill.rst b/docs/fill.rst index d796bff25..db69bf4a7 100644 --- a/docs/fill.rst +++ b/docs/fill.rst @@ -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: diff --git a/docs/gettingstarted.rst b/docs/gettingstarted.rst index 894d12c35..10da51de9 100644 --- a/docs/gettingstarted.rst +++ b/docs/gettingstarted.rst @@ -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'} + diff --git a/docs/library.rst b/docs/library.rst index b9a91d548..6da8048fd 100644 --- a/docs/library.rst +++ b/docs/library.rst @@ -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() diff --git a/docs/tutorial.rst b/docs/tutorial.rst deleted file mode 100644 index c1941368f..000000000 --- a/docs/tutorial.rst +++ /dev/null @@ -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 `_ 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 `_ 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 !** diff --git a/docs/tutorial/index.rst b/docs/tutorial/index.rst index 0b9616020..7f4eb30eb 100644 --- a/docs/tutorial/index.rst +++ b/docs/tutorial/index.rst @@ -31,5 +31,6 @@ At first glance we have a choice between five variables: :titlesonly: :caption: The firefox tutorial steps + preliminary tutorial diff --git a/docs/tutorial/preliminary.rst b/docs/tutorial/preliminary.rst new file mode 100644 index 000000000..2a2220965 --- /dev/null +++ b/docs/tutorial/preliminary.rst @@ -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 + +