Skip to content

Commit aff1d4d

Browse files
authored
Rewrite substitution parser (#2861)
1 parent 24bf148 commit aff1d4d

File tree

7 files changed

+526
-89
lines changed

7 files changed

+526
-89
lines changed

docs/changelog/2732.feature.rst

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
Rewrite substitution replacement parser - by :user:`masenf`
2+
3+
* ``\`` acts as a proper escape for ``\`` in ini-style substitutions
4+
* The resulting value of a substitution is no longer reprocessed in the context
5+
of the broader string. (Prior to this change, ini-values were repeatedly re-substituted until
6+
the expression no longer had modifications)
7+
* Migrate and update "Substitutions" section of Configuration page from v3 docs.
8+
* ```find_replace_part`` is removed from ``tox.config.loader.ini.replace``
9+
* New names exported from ``tox.config.loader.ini.replace``:
10+
* ``find_replace_expr``
11+
* ``MatchArg``
12+
* ``MatchError``
13+
* ``MatchExpression``
14+
* Note: the API for ``replace`` itself is unchanged.

docs/config.rst

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -765,3 +765,144 @@ Example configuration:
765765
766766
[tox]
767767
skip_missing_interpreters = true
768+
769+
Substitutions
770+
-------------
771+
772+
Any ``key=value`` setting in an ini-file can make use of **value substitution**
773+
through the ``{...}`` string-substitution pattern.
774+
775+
The string inside the curly braces may reference a global or per-environment config key as described above.
776+
777+
The backslash character ``\`` will act as an escape for a following: ``\``,
778+
``{``, ``}``, ``:``, ``[``, or ``]``, otherwise the backslash will be
779+
reproduced literally::
780+
781+
commands =
782+
python -c 'print("\{posargs} = \{}".format("{posargs}"))'
783+
python -c 'print("host: \{}".format("{env:HOSTNAME:host\: not set}")'
784+
785+
Special substitutions that accept additional colon-delimited ``:`` parameters
786+
cannot have a space after the ``:`` at the beginning of line (e.g. ``{posargs:
787+
magic}`` would be parsed as factorial ``{posargs``, having value magic).
788+
789+
Environment variable substitutions
790+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
791+
792+
If you specify a substitution string like this::
793+
794+
{env:KEY}
795+
796+
then the value will be retrieved as ``os.environ['KEY']``
797+
and raise an Error if the environment variable
798+
does not exist.
799+
800+
801+
Environment variable substitutions with default values
802+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
803+
804+
If you specify a substitution string like this::
805+
806+
{env:KEY:DEFAULTVALUE}
807+
808+
then the value will be retrieved as ``os.environ['KEY']``
809+
and replace with DEFAULTVALUE if the environment variable does not
810+
exist.
811+
812+
If you specify a substitution string like this::
813+
814+
{env:KEY:}
815+
816+
then the value will be retrieved as ``os.environ['KEY']``
817+
and replace with an empty string if the environment variable does not
818+
exist.
819+
820+
Substitutions can also be nested. In that case they are expanded starting
821+
from the innermost expression::
822+
823+
{env:KEY:{env:DEFAULT_OF_KEY}}
824+
825+
the above example is roughly equivalent to
826+
``os.environ.get('KEY', os.environ['DEFAULT_OF_KEY'])``
827+
828+
Interactive shell substitution
829+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
830+
831+
.. versionadded:: 3.4.0
832+
833+
It's possible to inject a config value only when tox is running in interactive shell (standard input)::
834+
835+
{tty:ON_VALUE:OFF_VALUE}
836+
837+
The first value is the value to inject when the interactive terminal is
838+
available, the second value is the value to use when it's not (optiona). A good
839+
use case for this is e.g. passing in the ``--pdb`` flag for pytest.
840+
841+
.. _`command positional substitution`:
842+
.. _`positional substitution`:
843+
844+
Substitutions for positional arguments in commands
845+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
846+
847+
.. versionadded:: 1.0
848+
849+
If you specify a substitution string like this::
850+
851+
{posargs:DEFAULTS}
852+
853+
then the value will be replaced with positional arguments as provided
854+
to the tox command::
855+
856+
tox arg1 arg2
857+
858+
In this instance, the positional argument portion will be replaced with
859+
``arg1 arg2``. If no positional arguments were specified, the value of
860+
DEFAULTS will be used instead. If DEFAULTS contains other substitution
861+
strings, such as ``{env:*}``, they will be interpreted.,
862+
863+
Use a double ``--`` if you also want to pass options to an underlying
864+
test command, for example::
865+
866+
tox -- --opt1 ARG1
867+
868+
will make the ``--opt1 ARG1`` appear in all test commands where ``[]`` or
869+
``{posargs}`` was specified. By default (see ``args_are_paths``
870+
setting), ``tox`` rewrites each positional argument if it is a relative
871+
path and exists on the filesystem to become a path relative to the
872+
``changedir`` setting.
873+
874+
Substitution for values from other sections
875+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
876+
877+
.. versionadded:: 1.4
878+
879+
Values from other sections can be referred to via::
880+
881+
{[sectionname]valuename}
882+
883+
which you can use to avoid repetition of config values.
884+
You can put default values in one section and reference them in others to avoid repeating the same values:
885+
886+
.. code-block:: ini
887+
888+
[base]
889+
deps =
890+
pytest
891+
mock
892+
pytest-xdist
893+
894+
[testenv:dulwich]
895+
deps =
896+
dulwich
897+
{[base]deps}
898+
899+
[testenv:mercurial]
900+
deps =
901+
mercurial
902+
{[base]deps}
903+
904+
Other Substitutions
905+
~~~~~~~~~~~~~~~~~~~
906+
907+
* ``{}`` - replaced as ``os.pathsep``
908+
* ``{/}`` - replaced as ``os.sep``

0 commit comments

Comments
 (0)