-
Notifications
You must be signed in to change notification settings - Fork 2.5k
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
Black's public API #779
Comments
In general, maybe we should take a moment to consider the APIs we're "setting in stone" for the first stable release in April. |
Dropping in here - I'd like this, but also for formatting an AST for producing nicely formatted generated code (no parse step). Also having |
Any progress? It would be useful for me as well |
For what it's worth, I've stumbled on this issue a number of times trying to find a way to programmatically call black without the use of a shell (i.e., in a CI pipeline). I've found that it's easy enough to do this using As for formatting a single string programmatically like this, I don't know how that could be done yet short of creating the file and sending it to black via this call. 🤔 |
As somebody who uses black in a code-generation pipeline, being able to hand black a string (instead of a path to a file) is hugely convenient. |
A standard, documented API would be wonderful. One of my use cases is a unit test to check that all code is formatted with |
Darker is one of the tools which invoke |
In akaihola/darker#164 (comment) we noticed that Consider this test script import io
import sys
from contextlib import redirect_stdout
from subprocess import PIPE, check_output
from black import FileMode, diff, format_str, main
path = sys.argv[1]
print("============================= format_str =============================")
with open(path) as f:
content = f.read()
format_str_result = format_str(content, mode=FileMode())
print(diff(content, format_str_result, path, path))
print("================================ main ================================")
with redirect_stdout(io.TextIOWrapper(io.BytesIO())) as main_stdout:
main(["--quiet", "--diff", path], standalone_mode=False)
main_stdout.seek(0)
print(main_stdout.read())
print("============================= subprocess =============================")
subprocess_result = check_output(["black", "--quiet", "--diff", path], encoding="UTF-8")
print(subprocess_result) If you create a valid Python source code file with just a few newlines: echo "
" >newlines.py and reformat it using the above script: python compare_black.py newlines.py
============================= format_str =============================
--- /tmp/myfile.py
+++ /tmp/myfile.py
@@ -1,5 +0,0 @@
-
-
-
-
-
================================ main ================================
============================= subprocess =============================
You see that I haven't tried to find other cases where these outputs don't match. I wonder if it only happens to all-whitespace files. |
From if not src_contents.strip():
raise NothingChanged
dst_contents = format_str(src_contents, mode=mode) So actually try:
format_str_result = format_file_contents(content, fast=False, mode=FileMode())
except NothingChanged:
format_str_result = content |
Is there currently a way to directly pass an ast to black? I am working on an internal tool which performs some code generation. |
No, and IMO it's basically a foregone conclusion Black won't be supporting an AST as input even within a semi or fully private "API". The formatting logic is tightly coupled to the shape and design of the AST produced by blib2to3 - I just tested passing in an AST from 3.8's ast module and it immediately crashed. Even we allow blib2to3's AST instances to be passed in as input, that's not great since it's not unlikely we'd change it. Since blib2to3 is our custom fork of lib2to3, there's definitely a few non-standard modifications in there (and probably lacks features that other consumers would want).
You don't have to use a file,
Yep.
Unfortunately even giving the option of doing something even when it's not declared as stable won't stop people, and we would rather not have more of a maintenance burden. The only thing that could change this situation is if we adopt a third-party externally provided AST when we switch parser (see #2318). There would still be a discussion about whether this is too niche / too internal of a case to support but at least maintainability-wise / technically it would be possible. |
Okay, than I will probably use that.
I just took a quick look at the issue as well as the linked resources. I guess it would be nice if the chosen solution was compatible with the stdlib ast module as this would open possibilities for very neat code generation using black. However I understand that this is probably at the very bottom of the wishlist and would only be a nice-to-have for people who are willing to use unstable internal APIs. For now I will take the detour using |
Hi all, If Black were to commit to a public API, what's your hunch about which functions will be included in it? I'd like to fix the inconsistent results on all-blank Python files between Black and Darker, and for that I need to decide whether to use |
Stable is looming closer than ever, so should we use it as an opportunity to finally define this public API? |
No, we should avoid feature creep for the stable release. We can add a defined API in a later release. |
Given how close the stable release is and how packed the TODO list is already (mypyc, removing Python 2, power operator spacing, ESP, stability policy enforcement w/ diff-shades?, graduating black's Python 3.10 support) I'd much prefer deferring this to a future release so we can take our time to properly design an API as Jelle said. |
#1544 suggested having a dedicated function for checking a code sample for modifications. Could be nice. |
We could try to take this forward. Perhaps we can first commit to the simplest API and then expand as needed. So, we could only expose a function for formatting a string of code that returns the formatted string or an exception if something went wrong. Currently, the best candidate we have is probably: def format_file_contents(src_contents: str, *, fast: bool, mode: Mode) -> FileContent:
... Which seems reasonable. Through that function we would also have to commit to
Some more things to include, either later or at the same time could be:
Thoughts! |
Thanks @felix-hilden!
In Darker, we use
Currently Darker parses Black configuration files by itself and passes some of the configuration options to The complete list of Black internals used by Darker is: # `FileMode as Mode` required to satisfy mypy==0.782. Strange.
from black import FileMode as Mode
from black import (
TargetVersion,
assert_equivalent,
parse_ast,
stringify_ast,
find_project_root,
find_pyproject_toml,
parse_pyproject_toml,
format_str,
re_compile_maybe_verbose,
)
from black.const import DEFAULT_EXCLUDES, DEFAULT_INCLUDES
from black.files import gen_python_files
from black.report import Report |
Hi team, is there any plan to make format_cell() a public Python API? It helps a lot to format code strings within a notebook cell. I noticed that this function has been applied in jupyter_black. This has not been a public python API yet. I'd like to apply this API to simplify the codebase in a project, but I'm also a little bit worried about if it will be maintained in future releases. If you have a plan to add it as a public API, I'm more than happy to help. |
With previous versions, it was possible to just use
black.format_str(string)
orblack.format_str(string, line_length=82)
.Now one needs to do:
It's not a big deal in a module, but
format_str
was also useful in notebooks to display examples of Python code programmatically generated (for example byastunparse.unparse(tree)
).Therefore, it could be nice to support also
black.format_str(string)
andblack.format_str(string, line_length=82)
.The
line_length
argument is useful when one wants to get code adapted for a presentation (made with Jupyter for example).The text was updated successfully, but these errors were encountered: