Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How-to use needs-render-context with a function without getting an unpickable warning #1206

Open
PhilipPartsch opened this issue Jul 17, 2024 · 2 comments

Comments

@PhilipPartsch
Copy link
Contributor

I try to use needs-render-context and get

cannot cache unpickable configuration value: 'needs_render_context' (because it contains a function, class, or module object)

Is there an option, to configure it without getting the warning?

I have set: suppress_warnings = ["config.cache"] in conf.py, but this is only a work around.

@PhilipPartsch PhilipPartsch changed the title How-to use needs-render-context with a function without How-to use needs-render-context with a function without getting an unpickable warning Jul 18, 2024
@AlexanderLanin
Copy link
Contributor

AlexanderLanin commented Aug 27, 2024

<deprecated, nevermind> :D

The trouble is sphinx-needs takes a str() of the parameter. Which is a dynamic address for functions. So what I did so far was wrap the functions in a class, but this has stopped working (not sure which version). Now we run into pickle trouble when we inject a class in that way.

class puml_participant:  # noqa: N801
    def __repr__(self):
        """
        Encapsulating the function in a class, forces cache compatibility as
        sphinx-needs will internally call str(obj) to determine the cache key.
        """
        return "[my puml_participant class]"

    def __call__(self, need: dict | str):
        """Create a PlantUML participant string for a need."""
        ...

needs_render_context = {
    "puml_participant": puml_participant(),
}

@AlexanderLanin
Copy link
Contributor

AlexanderLanin commented Aug 27, 2024

I tried injecting into jinja2uml instead of render_context. It's not the best API, but very first attempts look promising....

def add_custom_uml_functions(functions: dict[str, Callable]):
    orig_jinja2uml = sphinx_needs.directives.needuml.jinja2uml

    def wrapper(  # noqa: PLR0913
        app: sphinx.application.Sphinx,
        fromdocname: None | str,
        uml_content: str,
        parent_need_id: str,
        key: str,
        processed_need_ids: dict,
        kwargs: dict[str, Any],
    ) -> tuple[str, dict]:
        # inject functions into kwargs, which will be appended to data
        kwargs.update(functions)
        return orig_jinja2uml(
            app,
            fromdocname,
            uml_content,
            parent_need_id,
            key,
            processed_need_ids,
            kwargs,
        )

    frame = inspect.currentframe()
    assert frame
    name_of_this_function = frame.f_code.co_name
    is_replaced = str(orig_jinja2uml).find(name_of_this_function) != -1
    if not is_replaced:
        # print("custom uml functions added")
        sphinx_needs.directives.needuml.jinja2uml = wrapper

And in setup() or somewhere:

    add_custom_uml_functions(
        {
            "puml_participant": puml_participant,
        }
    )

edit 10 hours later: now it's failing :(

[esbonio.lsp] Traceback (most recent call last):
  File "/usr/local/lib/python3.12/site-packages/esbonio/lsp/sphinx/__init__.py", line 235, in build
    self.app.build(force_all, filenames)
  File "/usr/local/lib/python3.12/site-packages/sphinx/application.py", line 378, in build
    self.builder.build_update()
  File "/usr/local/lib/python3.12/site-packages/sphinx/builders/__init__.py", line 297, in build_update
    self.build(to_build,
  File "/usr/local/lib/python3.12/site-packages/sphinx/builders/__init__.py", line 334, in build
    pickle.dump(self.env, f, pickle.HIGHEST_PROTOCOL)
_pickle.PicklingError: Can't pickle <function puml_participant at 0x7f04f32c2de0>: attribute lookup puml_participant on __main__ failed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants