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

Add a dump() function to tomllib #103188

Closed
PedanticHacker opened this issue Apr 2, 2023 · 10 comments
Closed

Add a dump() function to tomllib #103188

PedanticHacker opened this issue Apr 2, 2023 · 10 comments
Labels
3.12 bugs and security fixes stdlib Python modules in the Lib dir type-feature A feature request or enhancement

Comments

@PedanticHacker
Copy link
Contributor

Feature or enhancement

The tomllib library, introduced in Python 3.11, is only capable of reading a TOML file by using either its load() or loads() functions, but there is no possible way to use the tomllib library to create a TOML file.

I would like to urge the core Python developers to add a new function to tomllib, namely dump(), that would create a TOML file.

Pitch

The addition of a dump() function to tomllib would remove the dependency a Python developer has to introduce in his/her codebase by using a 3rd-party library just to create a TOML file.

The usage of the dump() function would look like this:

import tomllib

with open("pyproject.toml", "w") as f:
    data = tomllib.dump(f)
@PedanticHacker PedanticHacker added the type-feature A feature request or enhancement label Apr 2, 2023
@TeamSpen210
Copy link

The lack of a dump() function was quite deliberate, because it brings a lot of complex design questions, and isn't necessary for the use cases the module was added for. There's a section in PEP 680 specifically about this. A proposal for a write operation would need to overcome those. One other reason not mentioned there is that for simple files, you could just use regular string formatting to build the file directly.

@PedanticHacker
Copy link
Contributor Author

I am using a TOML file because I am dealing with settings in my Python application. It is sad I have to use a 3rd-party toml library instead of Python's own tomllib library, because I can only read from a TOML file with the built-in tomllib library in Python 3.11, but then I need to use the 3rd-party toml library when a setting in my application changes and I have to write the new value to my settings.toml file. It's inconvenient, to say the least.

It is, indeed, feasible to implement the dump() and dumps() functions in the tomllib library, just like the toml library has them.

This is how the toml library implements the dump() and dumps() functions:

def dump(o, f, encoder=None):
    """Writes out dict as toml to a file
    Args:
        o: Object to dump into toml
        f: File descriptor where the toml should be stored
        encoder: The ``TomlEncoder`` to use for constructing the output string
    Returns:
        String containing the toml corresponding to dictionary
    Raises:
        TypeError: When anything other than file descriptor is passed
    """

    if not f.write:
        raise TypeError("You can only dump an object to a file descriptor")
    d = dumps(o, encoder=encoder)
    f.write(d)
    return d


def dumps(o, encoder=None):
    """Stringifies input dict as toml
    Args:
        o: Object to dump into toml
        encoder: The ``TomlEncoder`` to use for constructing the output string
    Returns:
        String containing the toml corresponding to dict
    Examples:
        >>> import toml
        >>> output = {
        ... 'a': "I'm a string",
        ... 'b': ["I'm", "a", "list"],
        ... 'c': 2400
        ... }
        >>> toml.dumps(output)
        'a = "I\'m a string"\nb = [ "I\'m", "a", "list",]\nc = 2400\n'
    """

    retval = ""
    if encoder is None:
        encoder = TomlEncoder(o.__class__)
    addtoretval, sections = encoder.dump_sections(o, "")
    retval += addtoretval
    outer_objs = [id(o)]
    while sections:
        section_ids = [id(section) for section in sections.values()]
        for outer_obj in outer_objs:
            if outer_obj in section_ids:
                raise ValueError("Circular reference detected")
        outer_objs += section_ids
        newsections = encoder.get_empty_table()
        for section in sections:
            addtoretval, addtosections = encoder.dump_sections(
                sections[section], section)

            if addtoretval or (not addtoretval and not addtosections):
                if retval and retval[-2:] != "\n\n":
                    retval += "\n"
                retval += "[" + section + "]\n"
                if addtoretval:
                    retval += addtoretval
            for s in addtosections:
                newsections[section + "." + s] = addtosections[s]
        sections = newsections
    return retval

@AlexWaygood AlexWaygood added stdlib Python modules in the Lib dir 3.12 bugs and security fixes labels Apr 2, 2023
@AlexWaygood
Copy link
Member

Cc. @hukkin and @hauntsaninja for tomllib.

I'm -1 on this idea. For simple use cases, you can just use string formatting, as @TeamSpen210 says. If you have a more complex use case, you probably shouldn't expect to find a solution to your problem in the standard library.

@PedanticHacker
Copy link
Contributor Author

Can you guys provide an example how to use string formatting to build a TOML file directly?

@AlexWaygood
Copy link
Member

AlexWaygood commented Apr 2, 2023

profile = "black"

with open("pyproject.toml", "w", encoding="utf-8") as f:
    f.write(f"""\
[tool.isort]
profile = "{profile}"
"""
    )

@PedanticHacker
Copy link
Contributor Author

PedanticHacker commented Apr 2, 2023

But isn’t this superfluous? A dump() function would make the code so much more concise.

@ericvsmith
Copy link
Member

You haven't answered the concerns raised in the PEP. Adding something to the stdlib is a much bigger design job than putting something in a 3rd party library.

I suggest just putting the dump() and dumps() code you pasted above into your own module. As you say, they're not big.

If you're serious, I suggest creating a thread on https://discuss.python.org/c/ideas/6, which will be seen by a larger audience.

@AlexWaygood
Copy link
Member

Yes, given the amount of discussion and consideration that went into the PEP, I think a discussion on https://discuss.python.org/c/ideas/6 would be warranted here before we added functions like these to the stdlib. As such, I'm closing this for now. We can reopen if significant demand for these functions is demonstrated in a discuss.python.org thread.

@AlexWaygood AlexWaygood closed this as not planned Won't fix, can't repro, duplicate, stale Apr 2, 2023
@hauntsaninja
Copy link
Contributor

If you want a small and simple library that is focussed on writing TOML, I recommend tomli_w. (toml is somewhat unmaintained and e.g. doesn't support TOML v1.0)

The issue here is as much about design as it is about demand. If you end up making a post elsewhere, make sure to read https://peps.python.org/pep-0680/#including-an-api-for-writing-toml first

@Torxed
Copy link

Torxed commented Aug 3, 2023

If you adopt a format - make sure to actually support it..

@python python locked as resolved and limited conversation to collaborators Aug 3, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
3.12 bugs and security fixes stdlib Python modules in the Lib dir type-feature A feature request or enhancement
Projects
None yet
Development

No branches or pull requests

6 participants