dnanhkhoa / nb_black

A simple extension for Jupyter Notebook and Jupyter Lab to beautify Python code automatically using black.
MIT License
367 stars 41 forks source link

Change Black linelength defaults? #10

Open coolum001 opened 5 years ago

coolum001 commented 5 years ago

Hi

First of all, thank you for this extension.

I plan to use nb_black and Jupyter Notebooks to generate blog posts, where code blocks are indented. This means I would like to set the line length to 65 (ie not the default).

I would also like to preserve single quote strings (a black option).

Can you indicate where in your code it might be possible to change the default parameters for black?

Second: I am running Jupyter lab Version 0.35.3, in an environment as below

python version : 3.7.1 (default, Dec 10 2018, 22:54:23) [MSC v.1915 64 bit (AMD64)]
python environment : ac5-py37
pandas version : 0.23.4
current wkg dir: C:\Users\donrc\Documents\JupyterNotebooks\OSMNXNotebookProject\develop
Notebook name: Communities.ipynb
Notebook run at: 2019-08-03 18:45:44.954269 local time
Notebook run at: 2019-08-03 08:45:44.954269 UTC
Notebook run on: Windows-10-10.0.18362-SP0

<IPython.core.display.Javascript object>

You will notice that Jupyterlab displays after each cell execution (I assume this is unintended behaviour)


<IPython.core.display.Javascript object>

I am running nb_black as below

(ac5-py37) C:\Users\donrc>conda list nb_black
# packages in environment at D:\Anaconda3\envs\ac5-py37:
#
# Name                    Version                   Build  Channel
nb_black                  1.0.6                      py_0    conda-forge

Now I can't reproduce this behaviour in smaller Notebooks, so you may see fit to ignore this until I can :)

Third, invoking %load_ext lab_black seems to stick with the notebook. Even if you reload the notebook and delete the %load_ext cell, the black formatting is still applied to new cells. You may wish to mention that you turn it off by a %unload_ext lab_black call.

Happy to supply more info if needed

Don Cameron

coolum001 commented 5 years ago

Hi Don Cameron here.

I think that the <IPython.core.display.Javascript object> display after each cell was because I had loaded nb_black into a JupyterLab, environment, and not lab_black. Unloading the correct extension fixed the problem.

Sorry to waste your time on this, Don

vmarceau commented 5 years ago

Hey Don,

Just curious if you have found a way to change the default parameters for black when using nb_black (especially max line length and single quotes)? I would also be interested in doing that.

Thanks!

michaelaye commented 5 years ago

configuring black itself doesn't work? https://black.readthedocs.io/en/stable/pyproject_toml.html

coolum001 commented 5 years ago

Hi I didn't want to reconfigure black as a whole, because I use it in VSCODE, and I want it as it is.

My use-case is creating blog entries from JupyterLab notebooks, where because my blog-engine (Pelican, built-texts theme, ipynb.markup plugin) has quite a narrow column width. So I just want the code in some Notebooks (but not all) formatted as narrow. For an example, see https://coolum001.github.io/jupyterblog.html

I have hacked up a solution:

in lab_black.py, there is a function defined _format_code(code)

My version of this now reads

    def _format_code(code):
#        return format_str(src_contents=code, mode=FileMode())
        my_mode = FileMode()
        my_mode.line_length = 65
        my_mode.string_normalization = False
        return format_str(src_contents=code, mode=my_mode)

When I stop being lazy, I might work out how to read parameters from the %load_ext command :) , but this works for me.

Thanks again for the package Don Cameron

coolum001 commented 5 years ago

Just to be completely accurate, the blog post referenced above was formatted with a lab_black.py locally in the same directory as the Notebook, with _format_code() defined as:

    def _format_code(code):
#        return format_str(src_contents=code, mode=FileMode())
        my_mode = FileMode()
        my_mode.line_length = 60
        my_mode.string_normalization = False
        return format_str(src_contents=code, mode=my_mode)
michaelaye commented 5 years ago

Note that the black configuration file works locally as well, so you can have completely different settings per folder by having that config file in there.

coolum001 commented 5 years ago

Michael

Didn't know that (obviously). I will give it a try, as being cleaner than having private versions of lab_black Thanks Don

coolum001 commented 5 years ago

Hi I have a directory with a pyproject.toml file that looks like:

[tool.black]
skip-string-normalization = true
line-length = 60

and a batch (command line) invocation of black works as expected, giving me the message:

Using configuration from C:\Users\donrc\Documents\JupyterNotebooks\SymAlgebraExamplesNotebookProject\develop\pyproject.toml.

However, %load_ext lab_black in a JupyterLab Notebook in the same directory, still converts single quotes to double. I suspect that the Black formatting is being done in a backend server or process, that can't see my directory to find my TOML file, because it has a different current working directory.

This happens even when I run the jupyter lab command that started that session, from inside that directory.

So I might have to go back to my handcrafted lab_black.py file after all :( Thanks Don Cameron

michaelaye commented 5 years ago

Ah that’s a pity. Maybe that could be changed in this package? It would be rather better if it worked exactly like called in a terminal. On Aug 8, 2019, 21:18 -0600, Don Cameron notifications@github.com, wrote:

Hi I have a directory with a pyproject.toml file that looks like: [tool.black] skip-string-normalization = true line-length = 60 and a batch (command line) invocation of black works as expected, giving me the message: Using configuration from C:\Users\donrc\Documents\JupyterNotebooks\SymAlgebraExamplesNotebookProject\develop\pyproject.toml. However, %load_ext lab_black in a JupyterLab Notebook in the same directory, still converts single quotes to double. I suspect that the Black formatting is being done in a backend server or process, that can't see my directory to find my TOML file, because it has a different current working directory. This happens even when I run the jupyter lab command that started that session, from inside that directory. So I might have to go back to my handcrafted lab_black.py file after all :( Thanks Don Cameron — You are receiving this because you commented. Reply to this email directly, view it on GitHub, or mute the thread.

dnanhkhoa commented 5 years ago

Hi, sorry for late reply and thank Michael for solutions so far, I will try to figure out why it cannot detect local configuration file

n8henrie commented 4 years ago

I'd also love to see this extension respect pyproject.toml's black settings, but no luck so far.

I've gone the route above and manually edited the file in my site-packages which works.

Looking at the black code, I think the issue is that format_str doesn't call the functions related to finding and loading the config file; it relies on the passed-in FileMode, which has been set to defaults in the extension. Unfortunately I don't see an easy way to import just the config loading code from black (tried by importing click.testing.FileRunner or something to that effect but couldn't sort it out).

n8henrie commented 4 years ago

Here's one way: https://github.com/arokem/python-matlab-bridge/blob/master/pymatbridge/matlab_magic.py#L68 accept kwargs into load_ipython_extension and allow users desiring custom configuration to manually import the extension and run the function with their config.

The below is just a proof-of-concept patch, I don't use yapf so I don't know if it breaks anything there, and I only implemented configuration for line-length. The screenshot below shows how to load it with config, and shows that if loaded with %load_ext, it successfully loads but will use the black defaults.

diff --git a/lab_black.py b/lab_black.py
index 9dbf6f4..bec76e3 100644
--- a/lab_black.py
+++ b/lab_black.py
@@ -23,10 +23,11 @@ from IPython.display import Javascript, display
 __BF_SIGNATURE__ = "__BF_HIDDEN_VARIABLE_{}__"

 if sys.version_info >= (3, 6, 0):
-    from black import format_str, FileMode
+    import black

-    def _format_code(code):
-        return format_str(src_contents=code, mode=FileMode())
+    def _format_code(code, mode):
+
+        return black.format_str(src_contents=code, mode=mode)

 else:
@@ -170,9 +171,10 @@ def _recover_magic_commands(cell, hidden_variables):

 class BlackFormatter(object):
-    def __init__(self, ip, is_lab):
+    def __init__(self, ip, is_lab, mode):
         self.shell = ip
         self.is_lab = is_lab
+        self.mode = mode

     def __set_cell(self, unformatted_cell, cell, cell_id=None):
         if self.is_lab:
@@ -215,7 +217,7 @@ class BlackFormatter(object):
                 # Transform magic commands into special variables
                 cell = _transform_magic_commands(unformatted_cell, hidden_variables)

-                formatted_code = _format_code(cell)
+                formatted_code = _format_code(cell, self.mode)

                 # Recover magic commands
                 formatted_code = _recover_magic_commands(
@@ -230,10 +232,14 @@ class BlackFormatter(object):
 black_formatter = None

-def load_ipython_extension(ip):
+def load_ipython_extension(ip, line_length=79):
     global black_formatter
+    mode = black.FileMode(
+        target_versions={black.TargetVersion.PY38}, line_length=line_length,
+    )
+
     if black_formatter is None:
-        black_formatter = BlackFormatter(ip, is_lab=True)
+        black_formatter = BlackFormatter(ip, is_lab=True, mode=mode)
         ip.events.register("post_run_cell", black_formatter.format_cell)

screenshot 2020-06-25 at 12 13 29 PM

msm1089 commented 4 years ago

@n8henrie 's solution is best if you may ned to configure the line length. Shorter way, if you will only ever need to change the line length rarely or never:

replace

https://github.com/dnanhkhoa/nb_black/blob/d4bd99e048da05dc1e2389c2b8fdf55ed9c9feb0/lab_black.py#L26-L29

with

    from black import format_str, FileMode, TargetVersion

    def _format_code(code):
        mode = FileMode(target_versions={TargetVersion.PY38},
                              line_length=79)
        return format_str(src_contents=code, mode=mode)

setting line_length as desired.

n8henrie commented 2 years ago

Work seems to have slowed down, so I created a fork that addresses this issue and adds a few other features: https://github.com/n8henrie/jupyter-black