Git Product home page Git Product logo

blue's People

Contributors

browniebroke avatar chriscarini avatar effigies avatar eseifert avatar grantjenks avatar hauntsaninja avatar luizdepra avatar refi64 avatar rotten avatar warsaw avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

blue's Issues

Don't expand brackets

black expands brackets into multiple lines, where this:

raise ValidationError({
    NON_FIELD_ERRORS: [_("Don't do that!")],
})
raise ValidationError(
    {
        NON_FIELD_ERRORS: [_("Don't do that!")],
    }
)

The coalesced bracket version is much to be preferred in my opinion. It would be great to have a configuration option to coalesce rather than expand brackets.

Support Python 3.10 features, especially match, request

Currently blue (version 0.7.0) supports most features of Python 3.10 via black (version 21.7.b0), but not the match statement. Black version 21.12b0 apparently does (see black issue #2662. However, running blue with that version of black produces this error:

  $ blue src
  Traceback (most recent call last):
    File ".../venv/bin/blue", line 8, in <module>
      sys.exit(main())
    File ".../venv/lib/python3.10/site-packages/blue/__init__.py", line 385, in main
      assert config_param.name == 'config'
  AssertionError

Apparently, blue does not allow the option --target-version py310 nor the equivalent to be set in the pyproject.toml file (tools.black ...).
Version info for current version of blue:

$ poetry show blue
name         : blue
version      : 0.7.0
description  : Blue -- Some folks like black but I prefer blue.

dependencies
 - black 21.7b0
 - flake8 3.8.4

Given that blue only patches black and that black does not consider version 21.12b0 to fully support match statements, is it premature to ask that blue can allow the interim version of black to be used with it?

Backport latest black changes (Python 3.9)

I noticed blue is failing for a specific Python 3.9 syntax:

def get_dec_dec():
  def get_dec():
    def dec(func):
      print(func)
    return dec
  return get_dec

@get_dec_dec()()
def foo():
  ...
error: cannot format example.py: Cannot parse: 8:14: @get_dec_dec()()
Oh no! πŸ’₯ πŸ’” πŸ’₯
1 file failed to reformat

It would be nice to support this so I was wondering if it would be possible to port changes from the latest black to blue.

Other items to monkey-patch?

Some examples?

  • CACHE_DIR = Path(user_cache_dir("black", version=__version__))
  • STDIN_PLACEHOLDER = "__BLACK_STDIN_FILENAME__"
  • Docstrings referencing "black"?
  • config = pyproject_toml.get("tool", {}).get("black", {})
  • BREAK_MARK = "@@@@@ BLACK BREAKPOINT MARKER @@@@@"
  • f"INTERNAL ERROR: Black produced invalid code: {exc}. Please report a bug"

I think the most significant things may be the CACHE_DIR and the pyproject.toml config to avoid corrupting/conflicting with Black.

Odd single-quote to double-quote change

From flufl.lock 's setup.py. I'm not sure why blue changes some single-quotes to double quotes in some cases. Note that it doesn't transform them all, and the ones it does change don't have embedded single-quotes.

@@ -11,37 +11,37 @@
 with open('README.rst') as fp:
     readme = fp.read()
 
 
 setup(
-    name='flufl.lock',
+    name="flufl.lock",
     version=__version__,
     author='Barry Warsaw',
     author_email='[email protected]',
     description=__doc__,
     long_description=readme,
     long_description_content_type='text/x-rst',
     license='Apache 2.0',
     keywords='locking locks lock',
     url='https://flufllock.readthedocs.io',
     download_url='https://pypi.python.org/pypi/flufl.lock',
-    packages=find_namespace_packages(where='.', exclude=['test*', 'docs']),
+    packages=find_namespace_packages(where=".", exclude=['test*', 'docs']),
     namespace_packages=['flufl'],
     include_package_data=True,
     # readthedocs builds fail unless zip_safe is False.
     zip_safe=False,
     python_requires='>=3.6',
     install_requires=[
         'atpublic',
         'psutil',
         'typing_extensions;python_version<"3.8"',
-        ],
+    ],
     project_urls={
         'Documentation': 'https://flufllock.readthedocs.io',
         'Source': 'https://gitlab.com/warsaw/flufl.lock.git',
         'Tracker': 'https://gitlab.com/warsaw/flufl.lock/issues',
-        },
+    },
     classifiers=[
         'Development Status :: 5 - Production/Stable',
         'Development Status :: 6 - Mature',
         'Intended Audience :: Developers',
         'License :: OSI Approved :: Apache Software License',

Add a way to skip formatting a single statement

The idea is to add a # fmt: skip comment hanging at the end of a line and that would skip reformatting that statement. It avoids the need to wrap the line in fmt: on/fmt: off boundaries.

Fails to build documentation

When building documentation for 0.5.1 (from the tarball from GitHub) for the package for openSUSE I got this failure:

[    4s] + cd docs
[    4s] + PYTHONPATH=../build/lib
[    4s] + make html
[    4s] Running Sphinx v3.3.1
[    4s] 
[    4s] Configuration error:
[    4s] There is a programmable error in your configuration file:
[    4s] 
[    4s] Traceback (most recent call last):
[    4s]   File "/usr/lib/python3.8/site-packages/sphinx/config.py", line 319, in eval_config_file
[    4s]     execfile_(filename, namespace)
[    4s]   File "/usr/lib/python3.8/site-packages/sphinx/util/pycompat.py", line 89, in execfile_
[    4s]     exec(code, _globals)
[    4s]   File "/home/abuild/rpmbuild/BUILD/blue-0.5.1/docs/conf.py", line 22, in <module>
[    4s]     project = blue.__title__.title()
[    4s] AttributeError: module 'blue' has no attribute '__title__'
[    4s] 
[    4s] make: *** [Makefile:20: html] Error 2

Add β€œtestimonials” or similar section

Got a nice message from Luciano:

"Thank you so much for making blue, Grant & contributors!"
-- Luciano Ramalho

And he said we could put it in our readme :)

Probably would be good to link to the Python list email ... says more nice things.

Better formatting of continued fractions

Black makes a mess of this code in Lib/statistics.py:

def _normal_dist_inv_cdf(p, mu, sigma):
    # There is no closed-form solution to the inverse CDF for the normal
    # distribution, so we use a rational approximation instead:
    # Wichura, M.J. (1988). "Algorithm AS241: The Percentage Points of the
    # Normal Distribution".  Applied Statistics. Blackwell Publishing. 37
    # (3): 477–484. doi:10.2307/2347330. JSTOR 2347330.
    q = p - 0.5
    if fabs(q) <= 0.425:
        r = 0.180625 - q * q
        # Hash sum: 55.88319_28806_14901_4439
        num = (((((((2.50908_09287_30122_6727e+3 * r +
                     3.34305_75583_58812_8105e+4) * r +
                     6.72657_70927_00870_0853e+4) * r +
                     4.59219_53931_54987_1457e+4) * r +
                     1.37316_93765_50946_1125e+4) * r +
                     1.97159_09503_06551_4427e+3) * r +
                     1.33141_66789_17843_7745e+2) * r +
                     3.38713_28727_96366_6080e+0) * q
        den = (((((((5.22649_52788_52854_5610e+3 * r +
                     2.87290_85735_72194_2674e+4) * r +
                     3.93078_95800_09271_0610e+4) * r +
                     2.12137_94301_58659_5867e+4) * r +
                     5.39419_60214_24751_1077e+3) * r +
                     6.87187_00749_20579_0830e+2) * r +
                     4.23133_30701_60091_1252e+1) * r +
                     1.0)
        x = num / den
        return mu + (x * sigma)
    ...

For this code in Lib/test/test_random.py, Black does a nice job with the internal list, but it turns the rest of the formula into confetti:

def gamma(z, sqrt2pi=(2.0*pi)**0.5):
    # Reflection to right half of complex plane
    if z < 0.5:
        return pi / sin(pi*z) / gamma(1.0-z)
    # Lanczos approximation with g=7
    az = z + (7.0 - 0.5)
    return az ** (z-0.5) / exp(az) * sqrt2pi * fsum([
        0.9999999999995183,
        676.5203681218835 / z,
        -1259.139216722289 / (z+1.0),
        771.3234287757674 / (z+2.0),
        -176.6150291498386 / (z+3.0),
        12.50734324009056 / (z+4.0),
        -0.1385710331296526 / (z+5.0),
        0.9934937113930748e-05 / (z+6.0),
        0.1659470187408462e-06 / (z+7.0),
    ])

It's not clear what general formatting rule should be applied, but it is clear that Black makes choices that don't work well for numeric and scientific computing. Almost any non-trivial formula is made worse by running it through Black. Sympy may have some useful guidance here β€” they've already spent some time wresting with formula formatting.

Don't split a series of semicolon terminated statements

People put in semicolons intentionally. It is almost never an accidental occurrence. We should respect the user's explicit intention.

In debugging code, there are natural logical pairings that are almost always done together. This facilitates easily inserting, moving, copying, and removal of the logical pair (which the user thinks of a single discrete step rather than a series of steps):

import pdb; pdb.pm()

For timing code, the start/action/end/report cycle form a natural, logical grouping that leaves the timed steps in their logical, consecutive sequence:

start = time(); step1(x); end = time(); print(end - start)
start = time(); step2(y); end = time(); print(end - start)

In Lib/cgitb.py, these statements naturally occur as a pair:

import cgitb; cgitb.enable()

In Tools/scripts/var_accessbenchmark.py, the existing code looks pleasing to the eye and makes it easy to see how many times v_local is being called:

def read_local(trials=trials):
    v_local = 1
    for t in trials:
        v_local;    v_local;    v_local;    v_local;    v_local
        v_local;    v_local;    v_local;    v_local;    v_local
        v_local;    v_local;    v_local;    v_local;    v_local
        v_local;    v_local;    v_local;    v_local;    v_local
        v_local;    v_local;    v_local;    v_local;    v_local

In Lib/idlelib/multicall.py, we see a typical use in grouping constants. These could be split on separate lines but it distracts from the overall flow of reading the module and makes it more difficult to relate back to the source of this information. I could go either way on this one but think it is best to respect the author's intention:

# the event type constants, which define the meaning of mc_type
MC_KEYPRESS=0; MC_KEYRELEASE=1; MC_BUTTONPRESS=2; MC_BUTTONRELEASE=3;
MC_ACTIVATE=4; MC_CIRCULATE=5; MC_COLORMAP=6; MC_CONFIGURE=7;
MC_DEACTIVATE=8; MC_DESTROY=9; MC_ENTER=10; MC_EXPOSE=11; MC_FOCUSIN=12;
MC_FOCUSOUT=13; MC_GRAVITY=14; MC_LEAVE=15; MC_MAP=16; MC_MOTION=17;
MC_MOUSEWHEEL=18; MC_PROPERTY=19; MC_REPARENT=20; MC_UNMAP=21; MC_VISIBILITY=22;

# the modifier state constants, which define the meaning of mc_state
MC_SHIFT = 1<<0; MC_CONTROL = 1<<2; MC_ALT = 1<<3; MC_META = 1<<5
MC_OPTION = 1<<6; MC_COMMAND = 1<<7

In Lib/mailcap.py, we see this pair several times. It is a common pattern for consuming a value and advancing an index. The reason the author put them on the same line is that it makes it easy to see verify that every consumed value also advances the index. It would be a mistake to do the first without the second. So, the formatter should respect the logical grouping of statements:

c = field[i:i+1]; i = i+1

Hanging comments inside multi-line expressions are squeezed

It would be nice to retain the whitespace following lines even in multi-line expressions, such as list or dict definitions. Here is a (truncated) example where we are defining named fields in a binary format:

 header_dtd = [
-    ('sizeof_hdr', 'i4'),      # 0; must be 348
-    ('data_type', 'S10'),      # 4; unused
-    ('db_name', 'S18'),        # 14; unused
-    ('extents', 'i4'),         # 32; unused
-    ('session_error', 'i2'),   # 36; unused
-    ('regular', 'S1'),         # 38; unused
-    ('dim_info', 'u1'),        # 39; MRI slice ordering code
-    ('dim', 'i2', (8,)),       # 40; data array dimensions
+    ('sizeof_hdr', 'i4'),  # 0; must be 348
+    ('data_type', 'S10'),  # 4; unused
+    ('db_name', 'S18'),  # 14; unused
+    ('extents', 'i4'),  # 32; unused
+    ('session_error', 'i2'),  # 36; unused
+    ('regular', 'S1'),  # 38; unused
+    ('dim_info', 'u1'),  # 39; MRI slice ordering code
+    ('dim', 'i2', (8,)),  # 40; data array dimensions
 ]

I realize this is going to be hard to get right 100% of the time, especially if other parts of the expression need to be shifted. I can just #fmt: off it, but would there be any interest in handling this case?

Using: blue, version 0.9.0, based on black 22.1.0

Follow-up to #20 and #31.

Formatting around "and"

I'm not thrilled with this diff:

     def __eq__(self, that):
         if not isinstance(that, type(self)):
             return NotImplemented
-        return (self.__slots__ == that.__slots__
-                and all(item == iota for item, iota in zip(self, that)))
+        return self.__slots__ == that.__slots__ and all(
+            item == iota for item, iota in zip(self, that)
+        )
 
     def __repr__(self):

tests/test_blue.py::test_good_dirs[config_pyproject] fails

I get:

[    8s] /home/abuild/rpmbuild/BUILD/blue-0.8.0/blue/__init__.py:397: in main
[    8s]     black.main()
[    8s] /usr/lib/python3.9/site-packages/click/core.py:1128: in __call__
[    8s]     return self.main(*args, **kwargs)
[    8s] /usr/lib/python3.9/site-packages/click/core.py:1052: in main
[    8s]     with self.make_context(prog_name, args, **extra) as ctx:
[    8s] /usr/lib/python3.9/site-packages/click/core.py:914: in make_context
[    8s]     self.parse_args(ctx, args)
[    8s] /usr/lib/python3.9/site-packages/click/core.py:1370: in parse_args
[    8s]     value, args = param.handle_parse_result(ctx, opts, args)
[    8s] /usr/lib/python3.9/site-packages/click/core.py:2347: in handle_parse_result
[    8s]     value = self.process_value(ctx, value)
[    8s] /usr/lib/python3.9/site-packages/click/core.py:2309: in process_value
[    8s]     value = self.callback(ctx, self, value)
[    8s] /home/abuild/rpmbuild/BUILD/blue-0.8.0/blue/__init__.py:357: in read_configs
[    8s]     result = black.read_pyproject_toml(ctx, param, value)
[    8s] /usr/lib/python3.9/site-packages/black/__init__.py:119: in read_pyproject_toml
[    8s]     config = parse_pyproject_toml(value)
[    8s] /home/abuild/rpmbuild/BUILD/blue-0.8.0/blue/__init__.py:267: in parse_pyproject_toml
[    8s]     pyproject_toml = tomli.load(f)  # type: ignore  # due to deprecated API usage
[    8s] _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
[    8s]
[    8s] __fp = <_io.TextIOWrapper name='/home/abuild/rpmbuild/BUILD/blue-0.8.0/tests/config_pyproject/pyproject.toml' mode='r' encoding='utf8'>
[    8s]
[    8s]     def load(__fp: BinaryIO, *, parse_float: ParseFloat = float) -> dict[str, Any]:
[    8s]         """Parse TOML from a binary file object."""
[    8s]         b = __fp.read()
[    8s]         try:
[    8s]             s = b.decode()
[    8s]         except AttributeError:
[    8s] >           raise TypeError(
[    8s]                 "File must be opened in binary mode, e.g. use `open('foo.toml', 'rb')`"
[    8s]             ) from None
[    8s] E           TypeError: File must be opened in binary mode, e.g. use `open('foo.toml', 'rb')`
[    8s]
[    8s] /usr/lib/python3.9/site-packages/tomli/_parser.py:63: TypeError

Details at:
https://build.opensuse.org/package/live_build_log/devel:languages:python/python-blue/openSUSE_Tumbleweed/x86_64

In compound expressions, don't add whitespace inside terms

Per PEP8:

If operators with different priorities are used, consider adding whitespace around the operators with the lowest priority(ies). Use your own judgment; however, never use more than one space, and always have the same amount of whitespace on both sides of a binary operator:

# Correct:
i = i + 1
submitted += 1
x = x*2 - 1
hypot2 = x*x + y*y
c = (a+b) * (a-b)

Note that SymPy does the right thing, so we know there is enough semantic information to do this correctly:

>>> from sympy import var
>>> var('x')
x
>>> ((3*x + 5) * (2*x - 4)).expand()
6*x**2 - 2*x - 20

blue -t py39 is not supported

% blue -t py39
Usage: blue [OPTIONS] [SRC]...
Try 'blue -h' for help.

Error: Invalid value for '-t' / '--target-version': invalid choice: py39. (choose from py27, py33, py34, py35, py36, py37, py38)

What it should do is:

% blue -t py39
No Path provided. Nothing to do 😴

This is because we're depending on black 20.8b1 which also doesn't support py39. However black git HEAD does support it so maybe we just wait for the next (non-beta) release. Alternatively, maybe we can just monkeypatch support aligning py39 to py38.

Formatting of `os.environ` wraps on the os.environ

Something about this just reads/seems strange to me - it's not what I'd expect.

-        os.environ['JAVA_HOME'] = '/Library/Java/JavaVirtualMachines/jdk1.8.0_121.jdk/Contents/Home/'
+        os.environ[
+            'JAVA_HOME'
+        ] = '/Library/Java/JavaVirtualMachines/jdk1.8.0_121.jdk/Contents/Home/'

blue --version should be more accurate

Right now, it reports the black version number, but it should probably do both. I.e. instead of

% blue --version
blue, version 20.8b1

It should say

% blue --version
blue, version 0.5.2; based on black 20.8b1

or some such.

Leave multiplication alone

Scientific programming has a lot of formulas, it would help if a formatter leaves multiplication alone.

So,

a + 4*b - c**3

should be left alone.

test_good_dirs fails with strange error

When packaging for openSUSE (after applying all patches from #66, so that is not the issue) I get these errors:

[   28s] + PYTHONPATH=/home/abuild/rpmbuild/BUILDROOT/python-blue-0.8.0-0.x86_64/usr/lib/python3.9/site-packages
[   28s] + PYTHONDONTWRITEBYTECODE=1
[   28s] + pytest-3.9 --ignore=_build.python39 --ignore=_build.python310 --ignore=_build.python38 -v
[   28s] ============================= test session starts ==============================
[   28s] platform linux -- Python 3.9.12, pytest-6.2.5, py-1.11.0, pluggy-1.0.0 -- /usr/bin/python3.9
[   28s] cachedir: .pytest_cache
[   28s] rootdir: /home/abuild/rpmbuild/BUILD/blue-0.8.0, configfile: tox.ini, testpaths: blue, docs, tests
[   29s] collecting ... collected 8 items
[   29s] 
[   29s] tests/test_blue.py::test_good_dirs[config_setup] FAILED                  [ 12%]
[   29s] tests/test_blue.py::test_good_dirs[config_tox] FAILED                    [ 25%]
[   29s] tests/test_blue.py::test_good_dirs[config_blue] FAILED                   [ 37%]
[   29s] tests/test_blue.py::test_good_dirs[config_pyproject] FAILED              [ 50%]
[   29s] tests/test_blue.py::test_good_dirs[good_cases] FAILED                    [ 62%]
[   29s] tests/test_blue.py::test_bad_dirs[bad_cases] FAILED                      [ 75%]
[   29s] tests/test_blue.py::test_main PASSED                                     [ 87%]
[   29s] tests/test_blue.py::test_version PASSED                                  [100%]
[   29s] 
[   29s] =================================== FAILURES ===================================
[   29s] _________________________ test_good_dirs[config_setup] _________________________
[   29s] 
[   29s] monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x7f702f5393a0>
[   29s] test_dir = PosixPath('/home/abuild/rpmbuild/BUILD/blue-0.8.0/tests/config_setup')
[   29s] 
[   29s]     @pytest.mark.parametrize(
[   29s]         'test_dir',
[   29s]         [
[   29s]             'config_setup',
[   29s]             'config_tox',
[   29s]             'config_blue',
[   29s]             'config_pyproject',
[   29s]             'good_cases',
[   29s]         ],
[   29s]     )
[   29s]     def test_good_dirs(monkeypatch, test_dir):
[   29s]         test_dir = tests_dir / test_dir
[   29s]         monkeypatch.chdir(test_dir)
[   29s]         monkeypatch.setattr('sys.argv', ['blue', '--check', '.'])
[   29s]         for path in test_dir.rglob('*'):
[   29s]             path.touch()  # Invalidate file caches in Blue.
[   29s]         black.find_project_root.cache_clear()
[   29s]         with pytest.raises(SystemExit) as exc_info:
[   29s]             asyncio.set_event_loop(asyncio.new_event_loop())
[   29s]             blue.main()
[   29s] >       assert exc_info.value.code == 0
[   29s] E       assert 123 == 0
[   29s] E         +123
[   29s] E         -0
[   29s] 
[   29s] /home/abuild/rpmbuild/BUILD/blue-0.8.0/tests/test_blue.py:32: AssertionError
[   29s] ----------------------------- Captured stderr call -----------------------------
[   29s] error: cannot format example.py: list_comments() got an unexpected keyword argument 'preview'
[   29s] 
[   29s] Oh no! πŸ’₯ πŸ’” πŸ’₯
[   29s] 1 file would fail to reformat.
[   29s] __________________________ test_good_dirs[config_tox] __________________________
[   29s] 
[   29s] monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x7f702f5b0070>
[   29s] test_dir = PosixPath('/home/abuild/rpmbuild/BUILD/blue-0.8.0/tests/config_tox')
[   29s] 
[   29s]     @pytest.mark.parametrize(
[   29s]         'test_dir',
[   29s]         [
[   29s]             'config_setup',
[   29s]             'config_tox',
[   29s]             'config_blue',
[   29s]             'config_pyproject',
[   29s]             'good_cases',
[   29s]         ],
[   29s]     )
[   29s]     def test_good_dirs(monkeypatch, test_dir):
[   29s]         test_dir = tests_dir / test_dir
[   29s]         monkeypatch.chdir(test_dir)
[   29s]         monkeypatch.setattr('sys.argv', ['blue', '--check', '.'])
[   29s]         for path in test_dir.rglob('*'):
[   29s]             path.touch()  # Invalidate file caches in Blue.
[   29s]         black.find_project_root.cache_clear()
[   29s]         with pytest.raises(SystemExit) as exc_info:
[   29s]             asyncio.set_event_loop(asyncio.new_event_loop())
[   29s]             blue.main()
[   29s] >       assert exc_info.value.code == 0
[   29s] E       assert 123 == 0
[   29s] E         +123
[   29s] E         -0
[   29s] 
[   29s] /home/abuild/rpmbuild/BUILD/blue-0.8.0/tests/test_blue.py:32: AssertionError
[   29s] ----------------------------- Captured stderr call -----------------------------
[   29s] error: cannot format example.py: list_comments() got an unexpected keyword argument 'preview'
[   29s] 
[   29s] Oh no! πŸ’₯ πŸ’” πŸ’₯
[   29s] 1 file would fail to reformat.
[   29s] _________________________ test_good_dirs[config_blue] __________________________
[   29s] 
[   29s] monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x7f702f5aa550>
[   29s] test_dir = PosixPath('/home/abuild/rpmbuild/BUILD/blue-0.8.0/tests/config_blue')
[   29s] 
[   29s]     @pytest.mark.parametrize(
[   29s]         'test_dir',
[   29s]         [
[   29s]             'config_setup',
[   29s]             'config_tox',
[   29s]             'config_blue',
[   29s]             'config_pyproject',
[   29s]             'good_cases',
[   29s]         ],
[   29s]     )
[   29s]     def test_good_dirs(monkeypatch, test_dir):
[   29s]         test_dir = tests_dir / test_dir
[   29s]         monkeypatch.chdir(test_dir)
[   29s]         monkeypatch.setattr('sys.argv', ['blue', '--check', '.'])
[   29s]         for path in test_dir.rglob('*'):
[   29s]             path.touch()  # Invalidate file caches in Blue.
[   29s]         black.find_project_root.cache_clear()
[   29s]         with pytest.raises(SystemExit) as exc_info:
[   29s]             asyncio.set_event_loop(asyncio.new_event_loop())
[   29s]             blue.main()
[   29s] >       assert exc_info.value.code == 0
[   29s] E       assert 123 == 0
[   29s] E         +123
[   29s] E         -0
[   29s] 
[   29s] /home/abuild/rpmbuild/BUILD/blue-0.8.0/tests/test_blue.py:32: AssertionError
[   29s] ----------------------------- Captured stderr call -----------------------------
[   29s] error: cannot format example.py: list_comments() got an unexpected keyword argument 'preview'
[   29s] 
[   29s] Oh no! πŸ’₯ πŸ’” πŸ’₯
[   29s] 1 file would fail to reformat.
[   29s] _______________________ test_good_dirs[config_pyproject] _______________________
[   29s] 
[   29s] monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x7f702f5b4b20>
[   29s] test_dir = PosixPath('/home/abuild/rpmbuild/BUILD/blue-0.8.0/tests/config_pyproject')
[   29s] 
[   29s]     @pytest.mark.parametrize(
[   29s]         'test_dir',
[   29s]         [
[   29s]             'config_setup',
[   29s]             'config_tox',
[   29s]             'config_blue',
[   29s]             'config_pyproject',
[   29s]             'good_cases',
[   29s]         ],
[   29s]     )
[   29s]     def test_good_dirs(monkeypatch, test_dir):
[   29s]         test_dir = tests_dir / test_dir
[   29s]         monkeypatch.chdir(test_dir)
[   29s]         monkeypatch.setattr('sys.argv', ['blue', '--check', '.'])
[   29s]         for path in test_dir.rglob('*'):
[   29s]             path.touch()  # Invalidate file caches in Blue.
[   29s]         black.find_project_root.cache_clear()
[   29s]         with pytest.raises(SystemExit) as exc_info:
[   29s]             asyncio.set_event_loop(asyncio.new_event_loop())
[   29s]             blue.main()
[   29s] >       assert exc_info.value.code == 0
[   29s] E       assert 123 == 0
[   29s] E         +123
[   29s] E         -0
[   29s] 
[   29s] /home/abuild/rpmbuild/BUILD/blue-0.8.0/tests/test_blue.py:32: AssertionError
[   29s] ----------------------------- Captured stderr call -----------------------------
[   29s] error: cannot format example.py: list_comments() got an unexpected keyword argument 'preview'
[   29s] 
[   29s] Oh no! πŸ’₯ πŸ’” πŸ’₯
[   29s] 1 file would fail to reformat.
[   29s] __________________________ test_good_dirs[good_cases] __________________________
[   29s] 
[   29s] monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x7f702f5eaee0>
[   29s] test_dir = PosixPath('/home/abuild/rpmbuild/BUILD/blue-0.8.0/tests/good_cases')
[   29s] 
[   29s]     @pytest.mark.parametrize(
[   29s]         'test_dir',
[   29s]         [
[   29s]             'config_setup',
[   29s]             'config_tox',
[   29s]             'config_blue',
[   29s]             'config_pyproject',
[   29s]             'good_cases',
[   29s]         ],
[   29s]     )
[   29s]     def test_good_dirs(monkeypatch, test_dir):
[   29s]         test_dir = tests_dir / test_dir
[   29s]         monkeypatch.chdir(test_dir)
[   29s]         monkeypatch.setattr('sys.argv', ['blue', '--check', '.'])
[   29s]         for path in test_dir.rglob('*'):
[   29s]             path.touch()  # Invalidate file caches in Blue.
[   29s]         black.find_project_root.cache_clear()
[   29s]         with pytest.raises(SystemExit) as exc_info:
[   29s]             asyncio.set_event_loop(asyncio.new_event_loop())
[   29s]             blue.main()
[   29s] >       assert exc_info.value.code == 0
[   29s] E       assert 123 == 0
[   29s] E         +123
[   29s] E         -0
[   29s] 
[   29s] /home/abuild/rpmbuild/BUILD/blue-0.8.0/tests/test_blue.py:32: AssertionError
[   29s] ----------------------------- Captured stderr call -----------------------------
[   29s] error: cannot format tdq_docstrings.py: list_comments() got an unexpected keyword argument 'preview'
[   29s] error: cannot format decorators.py: list_comments() got an unexpected keyword argument 'preview'
[   29s] error: cannot format demo.py: list_comments() got an unexpected keyword argument 'preview'
[   29s] 
[   29s] Oh no! πŸ’₯ πŸ’” πŸ’₯
[   29s] 3 files would fail to reformat.
[   29s] ___________________________ test_bad_dirs[bad_cases] ___________________________
[   29s] 
[   29s] monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x7f702f4898e0>
[   29s] test_dir = PosixPath('/home/abuild/rpmbuild/BUILD/blue-0.8.0/tests/bad_cases')
[   29s] 
[   29s]     @pytest.mark.parametrize(
[   29s]         'test_dir',
[   29s]         ['bad_cases'],
[   29s]     )
[   29s]     def test_bad_dirs(monkeypatch, test_dir):
[   29s]         test_dir = tests_dir / test_dir
[   29s]         monkeypatch.chdir(test_dir)
[   29s]         monkeypatch.setattr('sys.argv', ['blue', '--check', '.'])
[   29s]         for path in test_dir.rglob('*'):
[   29s]             path.touch()  # Invalidate file caches in Blue.
[   29s]         black.find_project_root.cache_clear()
[   29s]         with pytest.raises(SystemExit) as exc_info:
[   29s]             asyncio.set_event_loop(asyncio.new_event_loop())
[   29s]             blue.main()
[   29s] >       assert exc_info.value.code == 1
[   29s] E       assert 123 == 1
[   29s] E         +123
[   29s] E         -1
[   29s] 
[   29s] /home/abuild/rpmbuild/BUILD/blue-0.8.0/tests/test_blue.py:49: AssertionError
[   29s] ----------------------------- Captured stderr call -----------------------------
[   29s] error: cannot format sdq_docstrings.py: list_comments() got an unexpected keyword argument 'preview'
[   29s] error: cannot format tsq_docstrings.py: list_comments() got an unexpected keyword argument 'preview'
[   29s] error: cannot format ssq_docstrings.py: list_comments() got an unexpected keyword argument 'preview'
[   29s] error: cannot format demo.py: list_comments() got an unexpected keyword argument 'preview'
[   29s] 
[   29s] Oh no! πŸ’₯ πŸ’” πŸ’₯
[   29s] 4 files would fail to reformat.
[   29s] =========================== short test summary info ============================
[   29s] FAILED tests/test_blue.py::test_good_dirs[config_setup] - assert 123 == 0
[   29s] FAILED tests/test_blue.py::test_good_dirs[config_tox] - assert 123 == 0
[   29s] FAILED tests/test_blue.py::test_good_dirs[config_blue] - assert 123 == 0
[   29s] FAILED tests/test_blue.py::test_good_dirs[config_pyproject] - assert 123 == 0
[   29s] FAILED tests/test_blue.py::test_good_dirs[good_cases] - assert 123 == 0
[   29s] FAILED tests/test_blue.py::test_bad_dirs[bad_cases] - assert 123 == 1
[   29s] ========================= 6 failed, 2 passed in 0.22s ==========================
[   29s] error: Bad exit status from /var/tmp/rpm-tmp.JscPCz (%check)

Complete build log with all packages used and steps taken.

What is the most interesting (in my opinion) is that list_comments() function (if I am not mistaken) own function of this package, so it is strange that tests call it incorrectly. Also, it is strange, that I cannot find the word preview in the exploded packages.

What’s going on, please?

Verbose output include information about the settings `blue` is using for execution

When trying to add a setting in setup.cfg for line-length, it would be nice to have confirmation that this setting is getting picked up and used during execution of blue:

$ > git diff
diff --git a/setup.cfg b/setup.cfg
index 5bfb82f..2650e6e 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -16,6 +16,9 @@ ignore = E203, E266, E501, W503, PIE781
max-complexity = 12
max-line-length = 100

+[blue]
+line-length = 100
+
[isort]
include_trailing_comma = true
indent = '    '

$ > blue -v src/ test/
src/foobar.egg-info/PKG-INFO ignored: matches the .gitignore file content
src/foobar.egg-info/SOURCES.txt ignored: matches the .gitignore file content
src/foobar.egg-info/requires.txt ignored: matches the .gitignore file content
src/foobar.egg-info/top_level.txt ignored: matches the .gitignore file content
src/foobar.egg-info/dependency_links.txt ignored: matches the .gitignore file content
src/the_company_that_pays_me/foobar/__pycache__/utils.cpython-37.pyc ignored: matches the .gitignore file content
src/the_company_that_pays_me/__pycache__/__init__.cpython-37.pyc ignored: matches the .gitignore file content
test/__pycache__/test_utils.cpython-37-pytest-6.1.2.pyc ignored: matches the .gitignore file content
reformatted /Users/ChrisCarini/code/insights-hub-data_trunk/test/test_utils.py
reformatted /Users/ChrisCarini/code/insights-hub-data_trunk/src/the_company_that_pays_me/foobar/utils.py
All done! ✨ 🍰 ✨
2 files reformatted, 1 files left unchanged.

$ >

Unable to run Blue formatting

Seems like I'm unable to get blue to run anymore...

I consistently get the following error:
Traceback (most recent call last): File "/Users/gturbyne/workspace/sonarqube-tools/build/sonarqube-tools/environments/satellites/codeQuality/bin/blue", line 5, in <module> from blue import main File "/Users/gturbyne/workspace/sonarqube-tools/build/sonarqube-tools/environments/satellites/codeQuality/lib/python3.7/site-packages/blue/__init__.py", line 10, in <module> import black File "/Users/gturbyne/workspace/sonarqube-tools/build/sonarqube-tools/environments/satellites/codeQuality/lib/python3.7/site-packages/black/__init__.py", line 371, in <module> ) -> None: File "/Users/gturbyne/workspace/sonarqube-tools/build/sonarqube-tools/environments/satellites/python/lib/python3.7/site-packages/click/decorators.py", line 170, in decorator _param_memo(f, OptionClass(param_decls, **attrs)) File "/Users/gturbyne/workspace/sonarqube-tools/build/sonarqube-tools/environments/satellites/python/lib/python3.7/site-packages/click/core.py", line 1460, in __init__ Parameter.__init__(self, param_decls, type=type, **attrs) TypeError: __init__() got an unexpected keyword argument 'hidden' DURATION: 0.51s

blue is incompatible with flake8 v5

Flake8 v5 was just released earlier today. Unfortunately this breaks blue as it now crashes with this error:

Traceback (most recent call last):
  File "/tmp/tox/prisma-client-py/lint/lib/python3.9/site-packages/blue/__init__.py", line 397, in <module>
    BaseConfigParser = flake8_config.ConfigParser              # flake8 v4
AttributeError: module 'flake8.options.config' has no attribute 'ConfigParser'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/tmp/tox/prisma-client-py/lint/bin/blue", line 5, in <module>
    from blue import main
  File "/tmp/tox/prisma-client-py/lint/lib/python3.9/site-packages/blue/__init__.py", line 399, in <module>
    BaseConfigParser = flake8_config.MergedConfigParser        # flake8 v3
AttributeError: module 'flake8.options.config' has no attribute 'MergedConfigParser'

Constraining the flake8 dependency to v4 fixes the error.

pre-commit missing hook.yaml

I was trying to configure blue with a pre-commit hook in my repo.

Unfortunately it doesn't work by simply replacing the black repo with the blue repo (and updating the name and version) in my .pre-commit-config.yaml . The pre-commit command complains that the blue repo is missing a .pre-commit-hooks.yaml file.

I think it should be as simple as dropping one that looks like the black one in the top level directory.

Here is the black hooks file:
https://github.com/psf/black/blob/main/.pre-commit-hooks.yaml

This was how I configured my .pre-commit-config.yaml:

repos:
-   repo: https://github.com/grantjenks/blue.git
    rev: v0.6.0
    hooks:
    - id: blue
      args:
        - -l 120
        - -t py38

I tried manually configuring a .pre-commit-hooks.yaml file in my own repo with the blue id. Not surprisingly, that didn't really help. I'm not quite sure yet how to get blue to run as a pre-commit hook without that file being in the grantjenks/blue repo top level. Suggestions?

Style Request - More whitespace for json blobs with nested dicts

I have a black formatted json blob that looks like the following:

    return jsonify(data={
        'id': 123,
        'name': 'A Content Type',
        'type': 'json',
        'field': [
            {
                'name': 'headline',
                'type': 'string'
            },
            {
                'name': 'url',
                'type': 'string'
            }
        ]
    })

After formatting with blue the formatting was changed to the following:

    return jsonify(
        data={
            'id': 123,
            'name': 'A Content Type',
            'type': 'json',
            'field': [{'name': 'headline', 'type': 'string'}, {'name': 'url', 'type': 'string'}],
        }
    )

My own personal preference is towards the original formatting since it's much easier to identify field count and pick out the name fields at a glance.

Don't require blank lines after @typing.overload ellipses

I have a PR that uses @typing.overload. You'll notice that in the stdlib documentation, there are no blank lines after the ellipsis in the overload definitions. @gvanrossum even says that they don't traditionally do this. However, without the blank lines, black (and thus blue) complains. Perhaps blue can do better.

E.g.

@overload
def process(response: None) -> None:
    ...
@overload
def process(response: int) -> tuple[int, str]:
    ...
@overload
def process(response: bytes) -> str:
    ...
def process(response):
    <actual implementation>

Alternative design: hooks/entry-points vs monkey-patch

It occurred to me this evening that an alternative design is possible. Rather than monkey-patch black, we could just run black and then post-process the result. The post-processor would simply visit and correct all the string quoting. I'm not sure I like the idea of a "wrapper" but it is less invasive.

Maybe black could be "extensible" through hooks/entry-points in some way. The post-processing could be a supported feature.

Don't squeeze hanging comments to the left

Let's consider not squeezing hanging comments to the left, e.g. not doing this:

-    def _coerce(self, value, default: Optional[int] = 0) -> Optional[int]:      # type: ignore[no-untyped-def]
+    def _coerce(self, value, default: Optional[int] = 0) -> Optional[int]:  # type: ignore[no-untyped-def]

And here's an egregious example from flufl.i18n:

-    aqua = 'aardvarks'                          # noqa: F841
-    blue = 'badgers'                            # noqa: F841
-    cyan = 'cats'                               # noqa: F841
+    aqua = 'aardvarks'  # noqa: F841
+    blue = 'badgers'  # noqa: F841
+    cyan = 'cats'  # noqa: F841

The latter is definitely less readable.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.