# Patterns of Use

Several commons patterns of use for Sybil are covered here.

## Documentation and source examples in Restructured Text

If your project looks like this:

    ├─docs/
    │ ├─conf.py
    │ └─index.rst
    ├─src/
    │ └─myproj/
    │   └─__init__.py
    ├─conftest.py
    ├─pytest.ini
    └─setup.py

And if your documentation looks like this:

    My Project
    ==========

    .. invisible-code-block: python

        import myproj
        myproj.SEED = 0.3

    This project helps you bar your foo!

    >>> from myproj import foo
    >>> foo('baz')
    'baz bar bar bar'

With your examples in source code looking like this:

    """
    This package foo's stuff so it bars.

    :data:`SEED` is normally random, but if you like you
    can set it as follows:

    .. code-block:: python

        import myproj
        myproj.SEED = 0.2
    """
    import random

    SEED: float = random.random()

    def foo(text: str):
        """
        Put some ``bar`` on your ``foo``!
        >>> foo('stuff')
        'stuff bar bar'

        """
        return text+int(SEED*10)*' bar'

Then the following configuration in a `conftest.py` will ensure all your examples are correct:

    import pytest
    from sybil import Sybil
    from sybil.parsers.rest import DocTestParser, PythonCodeBlockParser

    @pytest.fixture(scope='session')
    def keep_seed():
        import myproj
        seed = myproj.SEED
        yield
        myproj.SEED = seed

    pytest_collect_file = Sybil(
        parsers=[
            DocTestParser(),
            PythonCodeBlockParser(),
        ],
        patterns=['*.rst', '*.py'],
        fixtures=['keep_seed']
    ).pytest()

## Documentation in MyST and source examples in Restructured Text

If your project looks like this:

    ├─docs/
    │ ├─conf.py
    │ └─index.md
    ├─src/
    │ └─myproj/
    │   └─__init__.py
    ├─conftest.py
    ├─pytest.ini
    └─setup.py

And if your documentation looks like this:

    My Project
    ####

    % invisible-code-block: python
    %
    %    import myproj
    %    myproj.SEED = 0.3

    This project helps you bar your foo!

    ```{doctest}
    >>> from myproj import foo
    >>> foo('baz')
    'baz bar bar bar'
    ```

With your examples in source code looking like this:

    """
    This package foo's stuff so it bars.

    :data:`SEED` is normally random, but if you like you
    can set it as follows:

    .. code-block:: python

        import myproj
        myproj.SEED = 0.2
    """
    import random

    SEED: float = random.random()

    def foo(text: str):
        """
        Put some ``bar`` on your ``foo``!
        >>> foo('stuff')
        'stuff bar bar'

        """
        return text+int(SEED*10)*' bar'

Then the following configuration in a `conftest.py` will ensure all your examples are correct:

    import pytest
    from sybil import Sybil
    from sybil.parsers.myst import (
        DocTestDirectiveParser as MarkdownDocTestParser,
        PythonCodeBlockParser as MarkdownPythonCodeBlockParser
    )
    from sybil.parsers.rest import (
        DocTestParser as ReSTDocTestParser,
        PythonCodeBlockParser as ReSTPythonCodeBlockParser
    )

    @pytest.fixture(scope='session')
    def keep_seed():
        import myproj
        seed = myproj.SEED
        yield
        myproj.SEED = seed

    markdown_examples = Sybil(
        parsers=[
            MarkdownDocTestParser(),
            MarkdownPythonCodeBlockParser(),
        ],
        patterns=['*.md'],
        fixtures=['keep_seed']
    )

    rest_examples = Sybil(
        parsers=[
            ReSTDocTestParser(),
            ReSTPythonCodeBlockParser(),
        ],
        patterns=['*.py'],
        fixtures=['keep_seed']
    )

    pytest_collect_file = (markdown_examples+rest_examples).pytest()

## Migrating from sphinx.ext.doctest

Sybil currently has partial support for sphinx.ext.doctest. The list below shows how to approach migrating or supporting the various directives from `sphinx.ext.doctest`. Adding further support won’t be hard, so if anything is missing that’s holding you back, please open an issue on GitHub. After that, it’s mainly left to stop running `make doctest`!

- `testsetup` can be replaced with invisible-code-block.

- `testcleanup` can be replaced with invisible-code-block.

- `doctest` is supported using the `DocTestDirectiveParser` as described in the doctest section. Some of the options aren’t supported, but their behaviour can be replaced by preceding the `doctest` with a skip directive.

- `testcode` and `testoutput` would need parsers and evaluators to be written, however, they could probably just be replaced with a doctest block.

- `groups` aren’t supported, but you can achieve test isolation using clear-namespace.