-
Notifications
You must be signed in to change notification settings - Fork 55
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
Allow to configure isort
through pyproject.toml
#13
Conversation
a35186f
to
613b535
Compare
This is another great addition! The patch touches a number of the same places as #11. Assuming both are wanted, which one do you think should be merged first, leaving conflicts to be solved in the other one? I'm ok with either. It makes sense to start reviewing the one to be merged first, and postponing review of the other after solving the conflicts. |
I'm ok with historical order -- let's merge #11 and then I'll solve conflicts here. |
Merged #4 as well. |
988a2af
to
77322d0
Compare
Solved conflicts |
77322d0
to
c7ecd66
Compare
@@ -35,7 +35,7 @@ | |||
import logging | |||
from functools import lru_cache | |||
from pathlib import Path | |||
from typing import Dict, List, Optional, Tuple, Union | |||
from typing import Dict, List, Optional, Tuple, Union, cast |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(I messed up the order of these comments, see the one beginning with "Now that we have three different types" further down in black_diff.py
first to understand what this all is about... sorry...)
And after logger = logging.getLogger(__name__)
on line 43 we could add
class BlackArgs(TypedDict, total=False):
config: str
line_length: int
skip_string_normalization: bool
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On line 47, we would use the BlackArgs
type:
def read_black_config(src: Path, value: Optional[str]) -> BlackArgs:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And we'd need to adjust src/darker/tests/test_black_diff.py
by importing BlackArgs
and using it to create the empty dict when calling run_black
:
from darker.black_diff import BlackArgs, read_black_config, run_black
# ...
def test_run_black(tmpdir):
src_contents = "print ( '42' )\n"
result = run_black(Path(tmpdir / "src.py"), src_contents, BlackArgs())
assert result == ['print("42")']
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With BlackArgs
, the signature of read_black_config()
on line 47 should change, and the return value on line 58 needs to be cast:
def read_black_config(src: Path, value: Optional[str]) -> BlackArgs:
# ...
return cast(
BlackArgs,
{
key: value
for key, value in (context.default_map or {}).items()
if key in ["line_length", "skip_string_normalization"]
},
)
@@ -63,7 +63,7 @@ def read_black_config(src: Path, value: Optional[str]) -> Dict[str, Union[bool, | |||
|
|||
|
|||
def run_black( | |||
src: Path, src_contents: str, black_args: Dict[str, Union[bool, int]] | |||
src: Path, src_contents: str, black_args: Dict[str, Union[bool, int, str]] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could use the new type here, too:
src: Path, src_contents: str, black_args: Dict[str, Union[bool, int, str]] | |
src: Path, src_contents: str, black_args: BlackArgs |
args = cast(Dict[str, Union[bool, int]], black_args) | ||
combined_args = {**defaults, **args} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There would be no need to cast here:
args = cast(Dict[str, Union[bool, int]], black_args) | |
combined_args = {**defaults, **args} | |
combined_args = {**defaults, **black_args} |
@@ -35,7 +35,7 @@ | |||
import logging | |||
from functools import lru_cache | |||
from pathlib import Path | |||
from typing import Dict, List, Optional, Tuple, Union | |||
from typing import Dict, List, Optional, Tuple, Union, cast |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now that we have three different types in black_args
, maybe it's time to use TypedDict
. This way we can get rid of the need to cast.
from typing import Dict, List, Optional, Tuple, Union, cast | |
import sys | |
from typing import List, Optional, cast | |
if sys.version_info >= (3, 8): | |
from typing import TypedDict | |
else: | |
from typing_extensions import TypedDict |
from typing import Dict, List, Optional, Tuple, Union, cast | ||
|
||
from black import find_project_root |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could use TypedDict
for IsortSettings
as well.
from typing import Dict, List, Optional, Tuple, Union, cast | |
from black import find_project_root | |
import sys | |
from typing import Optional, cast | |
from black import find_project_root | |
if sys.version_info >= (3, 8): | |
from typing import TypedDict | |
else: | |
from typing_extensions import TypedDict |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To support TypedDict
in Python <3.8, we need this in setup.cfg
:
install_requires =
black
typing-extensions ; python_version < "3.8"
|
||
logger = logging.getLogger(__name__) | ||
|
||
IsortSettings = Dict[str, Union[bool, int, List[str], str, Tuple[str], None]] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would be the type definition for IsortSettings
. No need to define types for keys we don't use.
IsortSettings = Dict[str, Union[bool, int, List[str], str, Tuple[str], None]] | |
class IsortSettings(TypedDict): | |
line_length: int |
src/darker/import_sorting.py
Outdated
_update_with_config_file, | ||
default, | ||
from_path, | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It becomes really weird to have these functions used in this module without the isort.settings.
prefix. But fixing that would require rethinking the way we run without isort
installed and mock isort
in unit tests. So let's think about that separately if at all.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
changed to import isort.settings as isort_settings
line_length=88, | ||
quiet=True, | ||
"SortImports(file_contents=..., check=True, {})".format( | ||
", ".join(f"{k}={v}" for k, v in isort_settings.items()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This becomes seriously verbose in debug mode:
DEBUG:darker.import_sorting:SortImports(file_contents=..., check=True, force_to_top=[], skip=[], skip_glob=[],
line_length=88, wrap_length=0, line_ending=None, sections=('FUTURE', 'STDLIB', 'THIRDPARTY', 'FIRSTPARTY',
'LOCALFOLDER'), no_sections=False, known_future_library=['__future__'], known_standard_library=['AL', 'BaseHTTPServer',
'Bastion', 'CGIHTTPServer', 'Carbon', 'ColorPicker', 'ConfigParser', 'Cookie', 'DEVICE', 'DocXMLRPCServer',
'EasyDialogs', 'FL', 'FrameWork', 'GL', 'HTMLParser', 'MacOS', 'MimeWriter', 'MiniAEFrame', 'Nav', 'PixMapWrapper',
'Queue', 'SUNAUDIODEV', 'ScrolledText', 'SimpleHTTPServer', 'SimpleXMLRPCServer', 'SocketServer', 'StringIO', 'Tix',
'Tkinter', 'UserDict', 'UserList', 'UserString', 'W', '__builtin__', 'abc', 'aepack', 'aetools', 'aetypes', 'aifc',
'al', 'anydbm', 'applesingle', 'argparse', 'array', 'ast', 'asynchat', 'asyncio', 'asyncore', 'atexit', 'audioop',
'autoGIL', 'base64', 'bdb', 'binascii', 'binhex', 'bisect', 'bsddb', 'buildtools', 'builtins', 'bz2', 'cPickle',
'cProfile', 'cStringIO', 'calendar', 'cd', 'cfmfile', 'cgi', 'cgitb', 'chunk', 'cmath', 'cmd', 'code', 'codecs',
'codeop', 'collections', 'colorsys', 'commands', 'compileall', 'compiler', 'concurrent', 'configparser', 'contextlib',
'contextvars', 'cookielib', 'copy', 'copy_reg', 'copyreg', 'crypt', 'csv', 'ctypes', 'curses', 'dataclasses',
'datetime', 'dbhash', 'dbm', 'decimal', 'difflib', 'dircache', 'dis', 'distutils', 'dl', 'doctest', 'dumbdbm',
'dummy_thread', 'dummy_threading', 'email', 'encodings', 'ensurepip', 'enum', 'errno', 'exceptions', 'faulthandler',
'fcntl', 'filecmp', 'fileinput', 'findertools', 'fl', 'flp', 'fm', 'fnmatch', 'formatter', 'fpectl', 'fpformat',
'fractions', 'ftplib', 'functools', 'future_builtins', 'gc', 'gdbm', 'gensuitemodule', 'getopt', 'getpass', 'gettext',
'gl', 'glob', 'grp', 'gzip', 'hashlib', 'heapq', 'hmac', 'hotshot', 'html', 'htmlentitydefs', 'htmllib', 'http',
'httplib', 'ic', 'icopen', 'imageop', 'imaplib', 'imgfile', 'imghdr', 'imp', 'importlib', 'imputil', 'inspect', 'io',
'ipaddress', 'itertools', 'jpeg', 'json', 'keyword', 'lib2to3', 'linecache', 'locale', 'logging', 'lzma', 'macerrors',
'macostools', 'macpath', 'macresource', 'mailbox', 'mailcap', 'marshal', 'math', 'md5', 'mhlib', 'mimetools',
'mimetypes', 'mimify', 'mmap', 'modulefinder', 'msilib', 'msvcrt', 'multifile', 'multiprocessing', 'mutex', 'netrc',
'new', 'nis', 'nntplib', 'numbers', 'operator', 'optparse', 'os', 'ossaudiodev', 'parser', 'pathlib', 'pdb', 'pickle',
'pickletools', 'pipes', 'pkgutil', 'platform', 'plistlib', 'popen2', 'poplib', 'posix', 'posixfile', 'pprint',
'profile', 'pstats', 'pty', 'pwd', 'py_compile', 'pyclbr', 'pydoc', 'queue', 'quopri', 'random', 're', 'readline',
'reprlib', 'resource', 'rexec', 'rfc822', 'rlcompleter', 'robotparser', 'runpy', 'sched', 'secrets', 'select',
'selectors', 'sets', 'sgmllib', 'sha', 'shelve', 'shlex', 'shutil', 'signal', 'site', 'sitecustomize', 'smtpd',
'smtplib', 'sndhdr', 'socket', 'socketserver', 'spwd', 'sqlite3', 'ssl', 'stat', 'statistics', 'statvfs', 'string',
'stringprep', 'struct', 'subprocess', 'sunau', 'sunaudiodev', 'symbol', 'symtable', 'sys', 'sysconfig', 'syslog',
'tabnanny', 'tarfile', 'telnetlib', 'tempfile', 'termios', 'test', 'textwrap', 'this', 'thread', 'threading', 'time',
'timeit', 'tkinter', 'token', 'tokenize', 'trace', 'traceback', 'tracemalloc', 'ttk', 'tty', 'turtle', 'turtledemo',
'types', 'typing', 'unicodedata', 'unittest', 'urllib', 'urllib2', 'urlparse', 'usercustomize', 'uu', 'uuid', 'venv',
'videoreader', 'warnings', 'wave', 'weakref', 'webbrowser', 'whichdb', 'winreg', 'winsound', 'wsgiref', 'xdrlib', 'xml',
'xmlrpc', 'xmlrpclib', 'zipapp', 'zipfile', 'zipimport', 'zlib'], known_third_party=['google.appengine.api', 'py',
'pytest'], known_first_party=[], multi_line_output=3, forced_separate=[], indent= , comment_prefix= #,
length_sort=False, add_imports=[], remove_imports=[], reverse_relative=False, force_single_line=False,
default_section=FIRSTPARTY, import_heading_future=, import_heading_stdlib=, import_heading_thirdparty=,
import_heading_firstparty=, import_heading_localfolder=, balanced_wrapping=False, use_parentheses=True,
order_by_type=True, atomic=False, lines_after_imports=-1, lines_between_sections=1, lines_between_types=0,
combine_as_imports=False, combine_star=False, keep_direct_and_as_imports=False, include_trailing_comma=True,
from_first=False, verbose=False, quiet=False, force_adds=False, force_alphabetical_sort_within_sections=False,
force_alphabetical_sort=False, force_grid_wrap=0, force_sort_within_sections=False, show_diff=False,
ignore_whitespace=False, no_lines_before=[], no_inline_sort=False, ignore_comments=False, safety_excludes=True,
case_sensitive=False)
Also strings are unquoted, so at least this would be appropriate:
", ".join(f"{k}={v}" for k, v in isort_settings.items()) | |
", ".join(f"{k}={v!r}" for k, v in isort_settings.items()) |
I experimented with an abbreviation mechanism as well, adding this function:
def abbreviate(value):
if isinstance(value, (tuple, list)) and len(value) > 2:
return "..."
return repr(value)
and using it here:
", ".join(f"{k}={v}" for k, v in isort_settings.items()) | |
", ".join(f"{k}={abbreviate(v)}" for k, v in isort_settings.items()) |
With that, debug output shrinks to:
DEBUG:darker.import_sorting:SortImports(file_contents=..., check=True, force_to_top=[], skip=[], skip_glob=[],
line_length=88, wrap_length=0, line_ending=None, sections=..., no_sections=False, known_future_library=['__future__'],
known_standard_library=..., known_third_party=..., known_first_party=[], multi_line_output=3, forced_separate=[],
indent=' ', comment_prefix=' #', length_sort=False, add_imports=[], remove_imports=[], reverse_relative=False,
force_single_line=False, default_section='FIRSTPARTY', import_heading_future='', import_heading_stdlib='',
import_heading_thirdparty='', import_heading_firstparty='', import_heading_localfolder='', balanced_wrapping=False,
use_parentheses=True, order_by_type=True, atomic=False, lines_after_imports=-1, lines_between_sections=1,
lines_between_types=0, combine_as_imports=False, combine_star=False, keep_direct_and_as_imports=False,
include_trailing_comma=True, from_first=False, verbose=False, quiet=False, force_adds=False,
force_alphabetical_sort_within_sections=False, force_alphabetical_sort=False, force_grid_wrap=0,
force_sort_within_sections=False, show_diff=False, ignore_whitespace=False, no_lines_before=[], no_inline_sort=False,
ignore_comments=False, safety_excludes=True, case_sensitive=False)
It's still quite a bit. Maybe we should replace all of those with a , ...
, or if we want to go through the trouble, only include items which differ from the defaults. What's your opinion?
Co-authored-by: Antti Kaihola <13725+akaihola@users.noreply.github.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pytest-isort
is broken since isort
v5.0, see stephrdev/pytest-isort#23
Also as darker
There is no isort.SortImports
anymore
…tings" This reverts commit 3e59c2c
I'm closing this in favor of #18 |
Now it's possible to customize
isort
parameters using the same pyproject.toml that containsblack
configuration.