OK, that makes sense.
This seems to be an "expert" setting, I guess it would be mainly used
from extensions.
> IMO, Sphinx extensions
> are better to enhance Sphinx by code, not a configuration. In this
> case, TemplateBridge is a python code. So no reason not to release it
> an extension.
Yes, that sounds reasonable.
But my case is different.
The option I'm talking about is not supposed to be selecting some code
that the user wrote.
Instead, it is supposed to import an existing module and use that,
without writing any code.
This is my example again:
nbsphinx_custom_formats = {
'.Rmd': ['jupytext.reads', {'fmt': '.Rmd'}],
}
In this case, "jupytext.reads" is an already existing function, see:
https://jupytext.readthedocs.io/en/latest/using-library.html#reading-notebooks-from-many-text-formats
The user is not expected to write any code here.
> > I don't think this is a good Python style.
> >
> > It's basically a somewhat disguised form of monkey-patching, isn't it?
>
> Really? If so, Sphinx is a large amount of monkey-patching.
No, Sphinx does it differently.
For example in the setup() function we get an "app" object which the
function is allowed to manipulate.
But we don't manipulate the "sphinx" module itself (or any of its
submodules), right?
> I think
> providing API is not monkey-patching.
That's why I said "a disguised form of monkey-patching".
This was your example:
> ```
> # in nbsphinx/__init__.py
> custom_formats = {}
>
> def add_custom_format(suffix: str, reader: Callable) -> None:
> custom_formats[suffix] = reader
> ```
Without the provided API, the user could still do this:
```
import nbsphinx
nbsphinx.custom_formats[suffix] = reader
```
I would call this monkey-patching.
Your hypothetical function add_custom_format() probably makes this a
bit simpler, but it's mostly the same thing, isn't it?
And I think it's not good style (neither with nor without the helper function).
> As commented above, providing APIs are appropriate way to enhance the
> programs by code, I think.
Yes, definitely.
> And I consider the your idea; dictionary
> styled nbsphinx_custom_formats is a kind of code. It tries to
> represent code as data. It likes an idea of S-expressions.
Yes, I guess it is somewhere in the grey area between code and data.
But it still uses only Python literals.
AFAICT that's the main method to specify Sphinx configuration: by
assigning Python literals to some pre-defined variables.
I don't really like passing the dictionary, but it's the only
reasonable way I found to describe arbitrary keyword arguments in form
of Python literals.
> I agree my idea is not beautiful and simple. But I think it is
> difficult to do that simply because what you'd like to do is a
> programming, not a configuration.
Well, I would say it's somewhere in between.
From a user's point of view it shouldn't "feel" like writing code.
I guess users would mainly copy those lines from the documentation and
just change the relevant strings.
For a user, this might just look like JSON. I think the only
difference is that trailing commas are not allowed in JSON.
> > Also, I guess it wouldn't work with Sphinx's caching mechanism, would it?
> >
> > If a user would change the implementation of their setup() function,
> > Sphinx wouldn't re-build, right?
>
> Yes. It is hard to detect the change of the implementation.
>
> But is it available on your idea? If I set my custom function to
> `nbsphinx_custom_formats`, it is also hard to detect the change of the
> implementation.
>
> nbsphinx_custom_formats = {
> '.Rmd': ['mymodule.reads', {'fmt': '.Rmd'}],
> }
Sure, it wouldn't detect changes in the implementation of mymodule.reads().
But that's (typically) not implemented/changed by the user, so it
doesn't matter in this case.
But if some keyword arguments are changed, the caching mechanism will
easily detect that.
And that's the important part.
> Sphinx can only detects the change of configuration value, not the
> change of the target function.
Yes, that's totally fine for me.
That's one reason why I would prefer a configuration value over a
custom user-defined setup() function.
> > > I don't know this is easy to understand for users.
> >
> > I think having to create a setup() function is generally harder to
> > understand than assigning something to a configuration value.
> > I think "normal" users shouldn't worry at all about a setup() function.
>
> If my understanding correct, "normal" users you said are able to write
> python code, right?
Most users will know some programming language that's used in the
Jupyter ecosystem.
This doesn't have to be Python.
But probably the person who assembles the Jupyter notebooks didn't create them?
In this case they probably don't know Python.
And the shouldn't have to.
> Because I saw a lambda function in your first post (I don't know it is
> already implemented or not).
> If so, I don't think it is not difficult way to me.
The lambda function was just a work-around to be able to pass further
arguments to the conversion function.
I think it's nicer if no lambda function is needed.
> If not, I thought using configuration is also difficult for "normal"
> users. I think it is better to simplify it by lessening feature.
I agree, it's better to avoid the lambda function, which could be very
confusing for Python beginners.
Oh, that is ugly!
That's a horrible API!
A "normal" user should not have to *define* a config value.
And having to call app.add_transform() for "simple" usage is also bad.
This is an excellent example to show that a "normal" user should not
be required to define a setup() function.
It's hideous.
And it's exactly what I'm trying to avoid.
> > Do you have any ideas that don't involve the setup() function?
>
> Sorry, I have no idea more.
OK, no problem.
If something else comes to your mind, please let me know.
For now I have two options, both have their disadvantages, none of
them is really good:
1) define a setup() function
2) use a dict containing a list containing another dict
Both are kinda bad, but for me its clear that option (2) is still
significantly better.
It would be great is somebody could come up with an even better option, though!
cheers,
Matthias