Package: darker
What?This utility reformats and checks Python source code files.
However, when run in a Git repository, it compares an old revision of the source tree
to a newer revision (or the working tree). It thenonly applies reformatting in regions which have changed in the Git working tree
between the two revisions, andonly reports those linting messages which appeared after the modifications to the
source code files.The reformatters supported are:Blackfor code reformattingisortfor sorting importsflyntfor turning old-style format strings to f-stringsSeeUsing lintersbelow for the list of supported linters.To easily run Darker as aPytestplugin, seepytest-darker.To integrate Darker with your IDE or withpre-commit,
see the relevant sections below in this document.Why?You want to start unifying code style in your project usingBlack.
Maybe you also like to standardize on how to order your imports,
or do static type checking or other static analysis for your code.However, instead of formatting the whole code base in one giant commit,
you’d like to only change formatting when you’re touching the code for other reasons.This can also be useful
when contributing to upstream codebases that are not under your complete control.Partial formatting is not supported byBlackitself,
for various good reasons, and so far there hasn’t been a plan to implemented it either
(134,142,245,370,511,830).
However, in September 2021 Black developers started to hint towards adding this feature
after all (1352). This might at least simplify Darker’s algorithm substantially.But for the time being, this is wheredarkerenters the stage.
This tool is for those who want to do partial formatting right now.Note that this tool is meant for special situations
when dealing with existing code bases.
You should just useBlackandisortas is when starting a project from scratch.You may also want to still consider whether reformatting the whole code base in one
commit would make sense in your particular case. You can ignore a reformatting commit
ingit blameusing theblame.ignoreRevsFileconfig option or--ignore-revon
the command line. For a deeper dive into this topic, seeAvoiding ruining git blamein Black documentation, or the articleWhy does Black insist on reformatting my entire project?fromŁukasz Langa(@ambv, the creator of Black). Here’s an excerpt:“When you make this single reformatting commit, everything that comes after issemantic changesso your commit history is clean in the sense that it actually
shows what changed in terms of meaning, not style. There are tools like darker that
allow you to only reformat lines that were touched since the last commit. However,
by doing that you forever expose yourself to commits that are a mix of semantic
changes with stylistic changes, making it much harder to see what changed.”How?To install or upgrade, use:pip install --upgrade darker~=1.7.3Or, if you’re usingCondafor package management:conda install -c conda-forge darker~=1.7.3 isort
conda update -c conda-forge darkerNote:It is recommended to use the ‘~=’ “compatible release” version
specifier for Darker. SeeGuarding against Black compatibility breakagefor more
information.Thedarker <myfile.py>ordarker <directory>command
reads the original file(s),
formats them usingBlack,
combines original and formatted regions based on edits,
and writes back over the original file(s).Alternatively, you can invoke the module directly through thepythonexecutable,
which may be preferable depending on your setup.
Usepython-mdarkerinstead ofdarkerin that case.By default,darkerjust runsBlackto reformat the code.
You can enable additional features with command line options:-i/--isort: Reorder imports usingisort. Note thatisortmust be
run in the same Python environment as the packages to process, as it imports
your modules to determine whether they are first or third party modules.-f/--flynt: Also convert string formatting to use f-strings using theflyntpackage-L<linter>/--lint<linter>: Run a supported linter (seeUsing linters)New in version 1.1.0:The-L/--lintoption.New in version 1.2.2:Package available inconda-forge.New in version 1.7.0:The-f/--flyntoptionExampleThis example walks you through a minimal practical use case for Darker.First, create an empty Git repository:$mkdir/tmp/test$cd/tmp/test$gitinitInitializedemptyGitrepositoryin/tmp/test/.git/In the root of that directory, create the ill-formatted Python fileour_file.py:ifTrue:print('hi')print()ifFalse:print('there')Commit that file:$gitaddour_file.py$gitcommit-m"Initial commit"[master(root-commit)a0c7c32]Initialcommit1filechanged,3insertions(+)createmode100644our_file.pyNow modify the first line in that file:ifTrue:print('CHANGED TEXT')print()ifFalse:print('there')You can ask Darker to show the diff for minimal reformatting
which makes edited lines conform to Black rules:$ darker --diff our_file.py--- our_file.py+++ our_file.py@@ -1,3 +1,4 @@-if True: print('CHANGED TEXT')+if True:+    print("CHANGED TEXT")print()if False: print('there')Alternatively, Darker can output the full reformatted file
(works only when a single Python file is provided on the command line):$darker--stdoutour_file.pyifTrue:print("CHANGED TEXT")print()ifFalse:print('there')If you omit the--diffand--stdoutoptions,
Darker replaces the files listed on the command line
with partially reformatted ones as shown above:$darkerour_file.pyNow the contents ofour_file.pywill have changed.
Note that the originalprint()andif False: ...lines have not been reformatted
since they had not been edited!ifTrue:print("CHANGED TEXT")print()ifFalse:print('there')You can also ask Darker to reformat edited lines in all Python files in the repository:$darker.Or, if you want to compare to another branch (or, in fact, any commit)
instead of the last commit:$darker--revisionmaster.Customizingdarker,Black,isort,flyntand linter behaviordarkerinvokesBlackandisortinternals directly instead of running their
binaries, so it needs to read and pass configuration options to them explicitly.
Project-specific default options fordarkeritself,Blackandisortare read from
the project’spyproject.tomlfile in the repository root.isortdoes also look for
a few other places for configuration.Mypy,Pylint,Flake8and other compatible linters are invoked as
subprocesses bydarker, so normal configuration mechanisms apply for each of those
tools. Linters can also be configured on the command line, for example:darker -L "mypy --strict" .
darker --lint "pylint --errors-only" .flynt(option-f/--flynt) is also invoked as a subprocess, but passing
command line options to it is currently not supported. Configuration files need to be
used instead.Darker does honor exclusion options in Black configuration files when recursing
directories, but the exclusions are only applied to Black reformatting. Isort and
linters are still run on excluded files. Also, individual files explicitly listed on the
command line are still reformatted even if they match exclusion patterns.For more details, see:Black documentation about pyproject.tomlisort documentation about config filespublic GitHub repositories which install and run Darkerflynt documentation about configuration filesThe followingcommand line argumentscan also be used to modify the defaults:-rREV,--revisionREVRevisions to compare. The default isHEAD..:WORKTREE:which compares the
latest commit to the working tree. Tags, branch names, commit hashes, and other
expressions likeHEAD~5work here. Also a range likemain...HEADormain...can be used to compare the best common ancestor. With the magic value:PRE-COMMIT:, Darker works in pre-commit compatible mode. Darker expects the
revision range from thePRE_COMMIT_FROM_REFandPRE_COMMIT_TO_REFenvironment variables. If those are not found, Darker works againstHEAD.
Also see--stdin-filename=for the:STDIN:special value.--diffDon’t write the files back, just output a diff for each file on stdout. Highlight
syntax if on a terminal and thepygmentspackage is available, or if enabled
by configuration.-d,--stdoutForce complete reformatted output to stdout, instead of in-place. Only valid if
there’s just one file to reformat. Highlight syntax if on a terminal and thepygmentspackage is available, or if enabled by configuration.--stdin-filenamePATHThe path to the file when passing it through stdin. Useful so Darker can find the
previous version from Git. Only valid with--revision=<rev1>..:STDIN:(HEAD..:STDIN:being the default if--stdin-filenameis enabled).--checkDon’t write the files back, just return the status. Return code 0 means nothing
would change. Return code 1 means some files would be reformatted.-f,--flyntAlso convert string formatting to use f-strings using theflyntpackage-i,--isortAlso sort imports using theisortpackage-LCMD,--lintCMDAlso run a linter on changed files.CMDcan be a name or path of the linter
binary, or a full quoted command line with the command and options. Linters read
their configuration as normally, and aren’t affected by-c/--config.
Linter output is syntax highlighted when thepygmentspackage is available if
run on a terminal and or enabled by explicitly (see--color).-cPATH,--configPATHAskblackandisortto read configuration fromPATH. Note that other
tools like flynt, Mypy, Pylint and Flake8 won’t use this configuration file.-v,--verboseShow steps taken and summarize modifications-q,--quietReduce amount of output--colorEnable syntax highlighting even for non-terminal output. Overrides the
environment variable PY_COLORS=0--no-colorDisable syntax highlighting even for terminal output. Overrides the environment
variable PY_COLORS=1-S,--skip-string-normalizationDon’t normalize string quotes or prefixes--no-skip-string-normalizationNormalize string quotes or prefixes. This can be used to overrideskip_string_normalization = truefrom a configuration file.--skip-magic-trailing-commaSkip adding trailing commas to expressions that are split by comma where each
element is on its own line. This includes function signatures. This can be used
to overrideskip_magic_trailing_comma = truefrom a configuration file.-lLENGTH,--line-lengthLENGTHHow many characters per line to allow [default: 88]-tVERSION,--target-versionVERSION[py33|py34|py35|py36|py37|py38|py39|py310|py311|py312] Python versions that
should be supported by Black’s output. [default: per-file auto-detection]-WWORKERS,--workersWORKERSHow many parallel workers to allow, or0for one per core [default: 1]To change default values for these options for a given project,
add a[tool.darker]or[tool.black]section topyproject.tomlin the
project’s root directory, or to a different TOML file specified using the-c/--configoption. For example:[tool.darker]src=["src/mypackage",]revision="master"diff=truecheck=trueisort=trueflynt=truelint=["pylint",]line-length=80# Passed to isort and Black, override their configlog_level="INFO"[tool.black]line-length=88# Overridden by [tool.darker] aboveskip-magic-trailing-comma=falseskip-string-normalization=falsetarget-version=['py311']exclude="test_*\.py"extend_exclude="/generated/"force_exclude=".*\.pyi"[tool.isort]profile="black"known_third_party=["pytest"]line_length=88# Overridden by [tool.darker] aboveWhileisortreads all of its options from the configuration file,Blackonly honors
the ones listed above when called bydarker. Other tools are invoked as
subprocesses and use their configuration mechanisms unmodified.Be careful to not use options which generate output which is unexpected for
other tools. For example, VSCode only expects the reformat diff, solint = [ ... ]can’t be used with it.New in version 1.0.0:The-c,-Sand-lcommand line options.isortis configured with-cand-l, too.New in version 1.1.0:The command line options-r/--revision--diff--check--no-skip-string-normalization-L/--lintNew in version 1.2.0:Support forcommit ranges in-r/--revision.a[tool.darker]section inpyproject.toml.New in version 1.2.2:Support for-r:PRE-COMMIT:/--revision=:PRE_COMMIT:New in version 1.3.0:The--skip-magic-trailing-commaand-d/--stdoutcommand line optionsNew in version 1.5.0:The-W/--workers,--colorand--no-colorcommand line optionsNew in version 1.7.0:The-t/--target-versioncommand line optionNew in version 1.7.0:The-f/--flyntcommand line optionEditor integrationMany editors have plugins or recipes for integratingBlack.
You may be able to adapt them to be used withdarker.
Seeeditor integrationin theBlackdocumentation.PyCharm/IntelliJ IDEAInstalldarker:$ pip install darkerLocate yourdarkerinstallation folder.On macOS / Linux / BSD:$ which darker
/usr/local/bin/darker  # possible locationOn Windows:$ where darker
%LocalAppData%\Programs\Python\Python36-32\Scripts\darker.exe  # possible locationOpen External tools in PyCharm/IntelliJ IDEAOn macOS:PyCharm->Preferences->Tools->External ToolsOn Windows / Linux / BSD:File->Settings->Tools->External ToolsClick the+icon to add a new external tool with the following values:Name: DarkerDescription: Use Black to auto-format regions changed since the last git commit.Program: <install_location_from_step_2>Arguments:"$FilePath$"If you need any extra command line arguments
like the ones which change Black behavior,
you can add them to theArgumentsfield, e.g.:--config /home/myself/black.cfg "$FilePath$"You can now format the currently opened file by selectingTools->External Tools->Darkeror right clicking on a file and selectingExternal Tools->DarkerOptionally, set up a keyboard shortcut atPreferences or Settings->Keymap->External Tools->External Tools - DarkerOptionally, rundarkeron every file save:Make sure you have theFile Watcherplugin installed.Go toPreferences or Settings->Tools->File Watchersand click+to add
a new watcher:Name: DarkerFile type: PythonScope: Project FilesProgram: <install_location_from_step_2>Arguments:$FilePath$Output paths to refresh:$FilePath$Working directory:$ProjectFileDir$Uncheck “Auto-save edited files to trigger the watcher”Visual Studio CodeInstalldarker:$ pip install darkerLocate yourdarkerinstallation folder.On macOS / Linux / BSD:$ which darker
/usr/local/bin/darker  # possible locationOn Windows:$ where darker
%LocalAppData%\Programs\Python\Python36-32\Scripts\darker.exe  # possible locationAdd these configuration options to VS code,Cmd-Shift-P,Open Settings (JSON):"python.formatting.provider": "black",
"python.formatting.blackPath": "<install_location_from_step_2>",
"python.formatting.blackArgs": [],VSCode will always add--diff--quietas arguments to Darker,
but you can also pass additional arguments in theblackArgsoption
(e.g.["--isort","--revision=master..."]).
Be sure tonotenable any linters here or inpyproject.tomlsince VSCode won’t be able to understand output from them.Note that VSCode first copies the file to reformat into a temporary<filename>.py.<hash>.tmpfile, then calls Black (or Darker in this case) on that
file, and brings the changes in the modified files back into the editor.
Darker is aware of this behavior, and will correctly compare.py.<hash>.tmpfiles
to corresponding.pyfiles from earlier repository revisions.VimUnlikeBlackand many other formatters,darkerneeds access to the Git history.
Therefore it does not work properly with classical auto reformat plugins.You can though ask vim to rundarkeron file save with the following in your.vimrc:setautoreadautocmdBufWritePost*.pysilent:!darker %BufWritePostto rundarkeronce the file has been saved,silentto not ask for confirmation each time,:!to run an external command,%for current file name.Vim should automatically reload the file.EmacsYou can integrate with Emacs using Steve Purcell’semacs-reformatterlibrary.Usinguse-package:(use-packagereformatter:hook((python-mode.darker-reformat-on-save-mode)):config(reformatter-definedarker-reformat:program"darker":stdinnil:stdoutnil:args(list"-q"input-file))This will automatically reformat the buffer on save.You have multiple functions available to launch it manually:darker-reformatdarker-reformat-regiondarker-reformat-bufferUsing as a pre-commit hookNew in version 1.2.1To use Darker locally as a Git pre-commit hook for a Python project,
do the following:Installpre-commitin your environment
(seepre-commit Installationfor details).Create a base pre-commit configuration:pre-commit sample-config >.pre-commit-config.yamlAppend to the created.pre-commit-config.yamlthe following lines:-repo:https://github.com/akaihola/darkerrev:1.7.3hooks:-id:darkerinstall the Git hook scripts and update to the newest version:pre-commit install
pre-commit autoupdateWhen auto-updating, care is being taken to protect you from possible incompatibilities
introduced by Black updates. SeeGuarding against Black compatibility breakagefor
more information.If you’d prefer to not update but keep a stable pre-commit setup, you can pin Black and
other reformatter/linter tools you use to known compatible versions, for example:-repo:https://github.com/akaihola/darkerrev:1.7.3hooks:-id:darkerargs:---isort---lint-mypy---lint-flake8---lint-pylintadditional_dependencies:-black==22.12.0-isort==5.11.4-mypy==0.990-flake8==5.0.4-pylint==2.15.5Using argumentsYou can provide arguments, such as enabling isort, by specifyingargs.
Note the inclusion of the isort Python package underadditional_dependencies:-repo:https://github.com/akaihola/darkerrev:1.7.3hooks:-id:darkerargs:[--isort]additional_dependencies:-isort~=5.9GitHub Actions integrationYou can use Darker within a GitHub Actions workflow
without setting your own Python environment.
Great for enforcing that modifications and additions to your code
match theBlackcode style.CompatibilityThis action is known to support all GitHub-hosted runner OSes. In addition, only
published versions of Darker are supported (i.e. whatever is available on PyPI).
You cansearch workflows in public GitHub repositoriesto see how Darker is being
used.UsageCreate a file named.github/workflows/darker.ymlinside your repository with:name:Linton:[push,pull_request]jobs:lint:runs-on:ubuntu-lateststeps:-uses:actions/checkout@v3with:fetch-depth:0-uses:actions/setup-python@v5-uses:akaihola/darker@1.7.3with:options:"--check--diff--isort--color"src:"./src"version:"~=1.7.3"lint:"flake8,pylint==2.13.1"There needs to be a working Python environment, set up usingactions/setup-pythonin the above example. Darker will be installed in an isolated virtualenv to prevent
conflicts with other workflows."uses:"specifies which Darker release to get the GitHub Action definition from.
We recommend to pin this to a specific release."version:"specifies which version of Darker to run in the GitHub Action.
It defaults to the same version as in"uses:",
but you can force it to use a different version as well.
Darker versions available from PyPI are supported, as well as commit SHAs or branch
names, prefixed with an@symbol (e.g.version: "@master").Therevision:"master..."(or"main...") option instructs Darker
to compare the current branch to the branching point from main branch
when determining which source code lines have been changed.
If omitted, the Darker GitHub Action will determine the commit range automatically."src:"defines the root directory to run Darker for.
This is typically the source tree, but you can use"."(the default)
to also reformat Python files like"setup.py"in the root of the whole repository.You can also configure other arguments passed to Darker via"options:".
It defaults to"--check--diff--color".
You can e.g. add"--isort"to sort imports, or"--verbose"for debug logging.To run linters through Darker, you can provide a comma separated list of linters using
thelint:option. Onlyflake8,pylintandmypyare supported. Other
linters may or may not work with Darker, depending on their message output format.
Versions can be constrained usingpipsyntax, e.g."flake8>=3.9.2".New in version 1.1.0:GitHub Actions integration. Modeled after howBlackdoes it,
thanks to Black authors for the example!New in version 1.4.1:Therevision:option, with smart default value if omitted.New in version 1.5.0:Thelint:option.Using lintersOne way to use Darker is to filter linter output to only those linter messages
which appeared after the modifications to source code files,
as well as old messages which concern modified lines.
Darker supports any linter with output in one of the following formats:<file>:<linenum>: <description>
<file>:<linenum>:<col>: <description>Most notably, the following linters/checkers have been verified to work with Darker:Mypyfor static type checkingPylintfor generic static checking of codeFlake8for style guide enforcementcov_to_lint.pyfor test coverageNew in version 1.1.0:Support forMypy,Pylint,Flake8and compatible linters.New in version 1.2.0:Support for test coverage output usingcov_to_lint.py.To run a linter, use the--lint/-Lcommand line option with the linter
command or a full command line to pass to a linter. Some examples:-Lflake8: enforce the Python style guide usingFlake8-L"mypy--strict": do static type checking usingMypy--lint="pylint--ignore='setup.py'": analyze code usingPylint-Lcov_to_lint.py: read.coverageand list non-covered modified linesNote:Full command lines aren’t fully tested on Windows. See issue#456for a
possible bug.Darker also groups linter output into blocks of consecutive lines
separated by blank lines.
Here’s an example ofcov_to_lint.pyoutput:$ darker --revision 0.1.0.. --check --lint cov_to_lint.py src
src/darker/__main__.py:94:  no coverage:             logger.debug("No changes in %s after isort", src)
src/darker/__main__.py:95:  no coverage:             break

src/darker/__main__.py:125: no coverage:         except NotEquivalentError:

src/darker/__main__.py:130: no coverage:             if context_lines == max_context_lines:
src/darker/__main__.py:131: no coverage:                 raise
src/darker/__main__.py:132: no coverage:             logger.debug(⚠ NOTE ⚠Don’t enable linting on the command line or in the configuration when
running Darker as a reformatter in VSCode. You will confuse VSCode
with unexpected output from Darker, as it only expect black’s outputSyntax highlightingDarker automatically enables syntax highlighting for the--diff,-d/--stdoutand-L/--lintoptions if it’s running on a terminal and thePygmentspackage is installed.You can force enable syntax highlighting on non-terminal output usingthecolor = trueoption in the[tool.darker]section ofpyproject.tomlof
your Python project’s root directory,thePY_COLORS=1environment variable, andthe--colorcommand line option fordarker.You can force disable syntax highlighting on terminal output usingthecolor = falseoption inpyproject.toml,thePY_COLORS=0environment variable, andthe--no-colorcommand line option.In the above lists, latter configuration methods override earlier ones, so the command
line options always take highest precedence.Guarding against Black compatibility breakageDarker accesses some Black internals which don’t belong to its public API. Darker is
thus subject to becoming incompatible with future versions of Black.To protect users against such breakage, we test Darker daily against theBlack main
branchand strive to proactively fix any potential incompatibilities through this
process. If a commit to Blackmainbranch introduces an incompatibility with
Darker, we will release a first patch version for Darker that prevents upgrading Black
and a second patch version that fixes the incompatibility. A hypothetical example:Darker 9.0.0; Black 35.12.0
-> OKDarker 9.0.0; Blackmain(after 35.12.0)
-> ERROR on CItest-futureworkflowDarker 9.0.1 released, with constraintBlack<=35.12.0-> OKBlack 36.1.0 released, but Darker 9.0.1 prevents upgrade; Black 35.12.0
-> OKDarker 9.0.2 released with a compatibility fix, constraint removed; Black 36.1.0
-> OKIf a Black release introduces an incompatibility before the second Darker patch version
that fixes it, the first Darker patch version will downgrade Black to the latest
compatible version:Darker 9.0.0; Black 35.12.0
-> OKDarker 9.0.0; Black 36.1.0
-> ERRORDarker 9.0.1, constraintBlack<=35.12.0; downgrades to Black 35.12.0
-> OKDarker 9.0.2 released with a compatibility fix, constraint removed; Black 36.1.0
-> OKTo be completely safe, you can pin both Darker and Black to known good versions, but
this may prevent you from receiving improvements in Black.It is recommended to use the ‘~=’ “compatible release” version specifier for
Darker to ensure you have the latest version before the next major release that may
cause compatibility issues.See issue#382and PR#430for more information.How does it work?Darker takes agit diffof your Python files,
records which lines of current files have been edited or added since the last commit.
It then runsBlackand notes which chunks of lines were reformatted.
Finally, only those reformatted chunks on which edited lines fall (even partially)
are applied to the edited file.Also, in case the--isortoption was specified,isortis run on each edited file before applyingBlack.
Similarly, each linter requested using the–lint <command>option is run,
and only those linting messages are displayed which appeared after the modifications to
the source code files,
or which were there already before but now fall on modified lines.LicenseBSD. SeeLICENSE.rst.Prior artblack-macchiatodarken(deprecated in favor of Darker; thanksCarreaufor inspiration!)Interesting code formatting and analysis projects to watchThe following projects are related toBlackor Darker in some way or another.
Some of them we might want to integrate to be part of a Darker run.blacken-docs– RunBlackon Python code blocks in documentation filesblackdoc– RunBlackon documentation code snippetsvelin– Reformat docstrings that follow thenumpydocconventiondiff-cov-lint– Pylint and coverage reports for git diff onlyxenon– Monitor code complexitypyupgrade– Upgrade syntax for newer versions of the language (see#51)yapf– Google’s Python formatteryapf_diff– applyyapfor other formatters to modified lines onlyContributors ✨SeeREADME.rstfor the list of contributors.This project follows theall-contributorsspecification.
Contributions of any kind are welcome!GitHub stars trend