diff options
Diffstat (limited to 'lib/python/qmk')
-rw-r--r-- | lib/python/qmk/cli/__init__.py | 13 | ||||
-rw-r--r-- | lib/python/qmk/cli/cformat.py | 6 | ||||
-rwxr-xr-x | lib/python/qmk/cli/compile.py | 10 | ||||
-rw-r--r-- | lib/python/qmk/cli/config.py | 96 | ||||
-rwxr-xr-x | lib/python/qmk/cli/doctor.py | 5 | ||||
-rwxr-xr-x | lib/python/qmk/cli/hello.py | 6 | ||||
-rw-r--r-- | lib/python/qmk/cli/json/__init__.py | 5 | ||||
-rwxr-xr-x | lib/python/qmk/cli/json/keymap.py | 20 | ||||
-rw-r--r-- | lib/python/qmk/cli/new/__init__.py | 1 | ||||
-rwxr-xr-x | lib/python/qmk/cli/new/keymap.py | 17 | ||||
-rwxr-xr-x | lib/python/qmk/cli/pyformat.py | 5 | ||||
-rw-r--r-- | lib/python/qmk/cli/pytest.py (renamed from lib/python/qmk/cli/nose2.py) | 8 | ||||
-rw-r--r-- | lib/python/qmk/path.py | 1 | ||||
-rw-r--r-- | lib/python/qmk/tests/test_cli_commands.py | 39 |
14 files changed, 195 insertions, 37 deletions
diff --git a/lib/python/qmk/cli/__init__.py b/lib/python/qmk/cli/__init__.py index e69de29bb..fb4e0ecb4 100644 --- a/lib/python/qmk/cli/__init__.py +++ b/lib/python/qmk/cli/__init__.py | |||
@@ -0,0 +1,13 @@ | |||
1 | """QMK CLI Subcommands | ||
2 | |||
3 | We list each subcommand here explicitly because all the reliable ways of searching for modules are slow and delay startup. | ||
4 | """ | ||
5 | from . import cformat | ||
6 | from . import compile | ||
7 | from . import config | ||
8 | from . import doctor | ||
9 | from . import hello | ||
10 | from . import json | ||
11 | from . import new | ||
12 | from . import pyformat | ||
13 | from . import pytest | ||
diff --git a/lib/python/qmk/cli/cformat.py b/lib/python/qmk/cli/cformat.py index 91e650368..d2382bdbd 100644 --- a/lib/python/qmk/cli/cformat.py +++ b/lib/python/qmk/cli/cformat.py | |||
@@ -6,9 +6,9 @@ import subprocess | |||
6 | from milc import cli | 6 | from milc import cli |
7 | 7 | ||
8 | 8 | ||
9 | @cli.argument('files', nargs='*', help='Filename(s) to format.') | 9 | @cli.argument('files', nargs='*', arg_only=True, help='Filename(s) to format.') |
10 | @cli.entrypoint("Format C code according to QMK's style.") | 10 | @cli.subcommand("Format C code according to QMK's style.") |
11 | def main(cli): | 11 | def cformat(cli): |
12 | """Format C code according to QMK's style. | 12 | """Format C code according to QMK's style. |
13 | """ | 13 | """ |
14 | clang_format = ['clang-format', '-i'] | 14 | clang_format = ['clang-format', '-i'] |
diff --git a/lib/python/qmk/cli/compile.py b/lib/python/qmk/cli/compile.py index 7e14ad8fb..6646891b3 100755 --- a/lib/python/qmk/cli/compile.py +++ b/lib/python/qmk/cli/compile.py | |||
@@ -14,11 +14,11 @@ import qmk.keymap | |||
14 | import qmk.path | 14 | import qmk.path |
15 | 15 | ||
16 | 16 | ||
17 | @cli.argument('filename', nargs='?', type=FileType('r'), help='The configurator export to compile') | 17 | @cli.argument('filename', nargs='?', arg_only=True, type=FileType('r'), help='The configurator export to compile') |
18 | @cli.argument('-kb', '--keyboard', help='The keyboard to build a firmware for. Ignored when a configurator export is supplied.') | 18 | @cli.argument('-kb', '--keyboard', help='The keyboard to build a firmware for. Ignored when a configurator export is supplied.') |
19 | @cli.argument('-km', '--keymap', help='The keymap to build a firmware for. Ignored when a configurator export is supplied.') | 19 | @cli.argument('-km', '--keymap', help='The keymap to build a firmware for. Ignored when a configurator export is supplied.') |
20 | @cli.entrypoint('Compile a QMK Firmware.') | 20 | @cli.subcommand('Compile a QMK Firmware.') |
21 | def main(cli): | 21 | def compile(cli): |
22 | """Compile a QMK Firmware. | 22 | """Compile a QMK Firmware. |
23 | 23 | ||
24 | If a Configurator export is supplied this command will create a new keymap, overwriting an existing keymap if one exists. | 24 | If a Configurator export is supplied this command will create a new keymap, overwriting an existing keymap if one exists. |
@@ -41,9 +41,9 @@ def main(cli): | |||
41 | # Compile the keymap | 41 | # Compile the keymap |
42 | command = ['make', ':'.join((user_keymap['keyboard'], user_keymap['keymap']))] | 42 | command = ['make', ':'.join((user_keymap['keyboard'], user_keymap['keymap']))] |
43 | 43 | ||
44 | elif cli.config.general.keyboard and cli.config.general.keymap: | 44 | elif cli.config.compile.keyboard and cli.config.compile.keymap: |
45 | # Generate the make command for a specific keyboard/keymap. | 45 | # Generate the make command for a specific keyboard/keymap. |
46 | command = ['make', ':'.join((cli.config.general.keyboard, cli.config.general.keymap))] | 46 | command = ['make', ':'.join((cli.config.compile.keyboard, cli.config.compile.keymap))] |
47 | 47 | ||
48 | else: | 48 | else: |
49 | cli.log.error('You must supply a configurator export or both `--keyboard` and `--keymap`.') | 49 | cli.log.error('You must supply a configurator export or both `--keyboard` and `--keymap`.') |
diff --git a/lib/python/qmk/cli/config.py b/lib/python/qmk/cli/config.py new file mode 100644 index 000000000..d6c774e65 --- /dev/null +++ b/lib/python/qmk/cli/config.py | |||
@@ -0,0 +1,96 @@ | |||
1 | """Read and write configuration settings | ||
2 | """ | ||
3 | import os | ||
4 | import subprocess | ||
5 | |||
6 | from milc import cli | ||
7 | |||
8 | |||
9 | def print_config(section, key): | ||
10 | """Print a single config setting to stdout. | ||
11 | """ | ||
12 | cli.echo('%s.%s{fg_cyan}={fg_reset}%s', section, key, cli.config[section][key]) | ||
13 | |||
14 | |||
15 | @cli.argument('-ro', '--read-only', action='store_true', help='Operate in read-only mode.') | ||
16 | @cli.argument('configs', nargs='*', arg_only=True, help='Configuration options to read or write.') | ||
17 | @cli.subcommand("Read and write configuration settings.") | ||
18 | def config(cli): | ||
19 | """Read and write config settings. | ||
20 | |||
21 | This script iterates over the config_tokens supplied as argument. Each config_token has the following form: | ||
22 | |||
23 | section[.key][=value] | ||
24 | |||
25 | If only a section (EG 'compile') is supplied all keys for that section will be displayed. | ||
26 | |||
27 | If section.key is supplied the value for that single key will be displayed. | ||
28 | |||
29 | If section.key=value is supplied the value for that single key will be set. | ||
30 | |||
31 | If section.key=None is supplied the key will be deleted. | ||
32 | |||
33 | No validation is done to ensure that the supplied section.key is actually used by qmk scripts. | ||
34 | """ | ||
35 | if not cli.args.configs: | ||
36 | # Walk the config tree | ||
37 | for section in cli.config: | ||
38 | for key in cli.config[section]: | ||
39 | print_config(section, key) | ||
40 | |||
41 | return True | ||
42 | |||
43 | # Process config_tokens | ||
44 | save_config = False | ||
45 | |||
46 | for argument in cli.args.configs: | ||
47 | # Split on space in case they quoted multiple config tokens | ||
48 | for config_token in argument.split(' '): | ||
49 | # Extract the section, config_key, and value to write from the supplied config_token. | ||
50 | if '=' in config_token: | ||
51 | key, value = config_token.split('=') | ||
52 | else: | ||
53 | key = config_token | ||
54 | value = None | ||
55 | |||
56 | if '.' in key: | ||
57 | section, config_key = key.split('.', 1) | ||
58 | else: | ||
59 | section = key | ||
60 | config_key = None | ||
61 | |||
62 | # Validation | ||
63 | if config_key and '.' in config_key: | ||
64 | cli.log.error('Config keys may not have more than one period! "%s" is not valid.', key) | ||
65 | return False | ||
66 | |||
67 | # Do what the user wants | ||
68 | if section and config_key and value: | ||
69 | # Write a config key | ||
70 | log_string = '%s.%s{fg_cyan}:{fg_reset} %s {fg_cyan}->{fg_reset} %s' | ||
71 | if cli.args.read_only: | ||
72 | log_string += ' {fg_red}(change not written)' | ||
73 | |||
74 | cli.echo(log_string, section, config_key, cli.config[section][config_key], value) | ||
75 | |||
76 | if not cli.args.read_only: | ||
77 | if value == 'None': | ||
78 | del cli.config[section][config_key] | ||
79 | else: | ||
80 | cli.config[section][config_key] = value | ||
81 | save_config = True | ||
82 | |||
83 | elif section and config_key: | ||
84 | # Display a single key | ||
85 | print_config(section, config_key) | ||
86 | |||
87 | elif section: | ||
88 | # Display an entire section | ||
89 | for key in cli.config[section]: | ||
90 | print_config(section, key) | ||
91 | |||
92 | # Ending actions | ||
93 | if save_config: | ||
94 | cli.save_config() | ||
95 | |||
96 | return True | ||
diff --git a/lib/python/qmk/cli/doctor.py b/lib/python/qmk/cli/doctor.py index 5a713b20f..3474422a8 100755 --- a/lib/python/qmk/cli/doctor.py +++ b/lib/python/qmk/cli/doctor.py | |||
@@ -11,8 +11,8 @@ from glob import glob | |||
11 | from milc import cli | 11 | from milc import cli |
12 | 12 | ||
13 | 13 | ||
14 | @cli.entrypoint('Basic QMK environment checks') | 14 | @cli.subcommand('Basic QMK environment checks') |
15 | def main(cli): | 15 | def doctor(cli): |
16 | """Basic QMK environment checks. | 16 | """Basic QMK environment checks. |
17 | 17 | ||
18 | This is currently very simple, it just checks that all the expected binaries are on your system. | 18 | This is currently very simple, it just checks that all the expected binaries are on your system. |
@@ -36,6 +36,7 @@ def main(cli): | |||
36 | else: | 36 | else: |
37 | try: | 37 | try: |
38 | subprocess.run([binary, '--version'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=5, check=True) | 38 | subprocess.run([binary, '--version'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=5, check=True) |
39 | cli.log.info('Found {fg_cyan}%s', binary) | ||
39 | except subprocess.CalledProcessError: | 40 | except subprocess.CalledProcessError: |
40 | cli.log.error("{fg_red}Can't run `%s --version`", binary) | 41 | cli.log.error("{fg_red}Can't run `%s --version`", binary) |
41 | ok = False | 42 | ok = False |
diff --git a/lib/python/qmk/cli/hello.py b/lib/python/qmk/cli/hello.py index bc0cb6de1..bee28c301 100755 --- a/lib/python/qmk/cli/hello.py +++ b/lib/python/qmk/cli/hello.py | |||
@@ -6,8 +6,8 @@ from milc import cli | |||
6 | 6 | ||
7 | 7 | ||
8 | @cli.argument('-n', '--name', default='World', help='Name to greet.') | 8 | @cli.argument('-n', '--name', default='World', help='Name to greet.') |
9 | @cli.entrypoint('QMK Hello World.') | 9 | @cli.subcommand('QMK Hello World.') |
10 | def main(cli): | 10 | def hello(cli): |
11 | """Log a friendly greeting. | 11 | """Log a friendly greeting. |
12 | """ | 12 | """ |
13 | cli.log.info('Hello, %s!', cli.config.general.name) | 13 | cli.log.info('Hello, %s!', cli.config.hello.name) |
diff --git a/lib/python/qmk/cli/json/__init__.py b/lib/python/qmk/cli/json/__init__.py index e69de29bb..f4ebfc45b 100644 --- a/lib/python/qmk/cli/json/__init__.py +++ b/lib/python/qmk/cli/json/__init__.py | |||
@@ -0,0 +1,5 @@ | |||
1 | """QMK CLI JSON Subcommands | ||
2 | |||
3 | We list each subcommand here explicitly because all the reliable ways of searching for modules are slow and delay startup. | ||
4 | """ | ||
5 | from . import keymap | ||
diff --git a/lib/python/qmk/cli/json/keymap.py b/lib/python/qmk/cli/json/keymap.py index e2d0b5809..a65acd619 100755 --- a/lib/python/qmk/cli/json/keymap.py +++ b/lib/python/qmk/cli/json/keymap.py | |||
@@ -9,10 +9,10 @@ from milc import cli | |||
9 | import qmk.keymap | 9 | import qmk.keymap |
10 | 10 | ||
11 | 11 | ||
12 | @cli.argument('-o', '--output', help='File to write to') | 12 | @cli.argument('-o', '--output', arg_only=True, help='File to write to') |
13 | @cli.argument('filename', help='Configurator JSON file') | 13 | @cli.argument('filename', arg_only=True, help='Configurator JSON file') |
14 | @cli.entrypoint('Create a keymap.c from a QMK Configurator export.') | 14 | @cli.subcommand('Create a keymap.c from a QMK Configurator export.') |
15 | def main(cli): | 15 | def json_keymap(cli): |
16 | """Generate a keymap.c from a configurator export. | 16 | """Generate a keymap.c from a configurator export. |
17 | 17 | ||
18 | This command uses the `qmk.keymap` module to generate a keymap.c from a configurator export. The generated keymap is written to stdout, or to a file if -o is provided. | 18 | This command uses the `qmk.keymap` module to generate a keymap.c from a configurator export. The generated keymap is written to stdout, or to a file if -o is provided. |
@@ -28,8 +28,8 @@ def main(cli): | |||
28 | exit(1) | 28 | exit(1) |
29 | 29 | ||
30 | # Environment processing | 30 | # Environment processing |
31 | if cli.config.general.output == ('-'): | 31 | if cli.args.output == ('-'): |
32 | cli.config.general.output = None | 32 | cli.args.output = None |
33 | 33 | ||
34 | # Parse the configurator json | 34 | # Parse the configurator json |
35 | with open(qmk.path.normpath(cli.args.filename), 'r') as fd: | 35 | with open(qmk.path.normpath(cli.args.filename), 'r') as fd: |
@@ -38,17 +38,17 @@ def main(cli): | |||
38 | # Generate the keymap | 38 | # Generate the keymap |
39 | keymap_c = qmk.keymap.generate(user_keymap['keyboard'], user_keymap['layout'], user_keymap['layers']) | 39 | keymap_c = qmk.keymap.generate(user_keymap['keyboard'], user_keymap['layout'], user_keymap['layers']) |
40 | 40 | ||
41 | if cli.config.general.output: | 41 | if cli.args.output: |
42 | output_dir = os.path.dirname(cli.config.general.output) | 42 | output_dir = os.path.dirname(cli.args.output) |
43 | 43 | ||
44 | if not os.path.exists(output_dir): | 44 | if not os.path.exists(output_dir): |
45 | os.makedirs(output_dir) | 45 | os.makedirs(output_dir) |
46 | 46 | ||
47 | output_file = qmk.path.normpath(cli.config.general.output) | 47 | output_file = qmk.path.normpath(cli.args.output) |
48 | with open(output_file, 'w') as keymap_fd: | 48 | with open(output_file, 'w') as keymap_fd: |
49 | keymap_fd.write(keymap_c) | 49 | keymap_fd.write(keymap_c) |
50 | 50 | ||
51 | cli.log.info('Wrote keymap to %s.', cli.config.general.output) | 51 | cli.log.info('Wrote keymap to %s.', cli.args.output) |
52 | 52 | ||
53 | else: | 53 | else: |
54 | print(keymap_c) | 54 | print(keymap_c) |
diff --git a/lib/python/qmk/cli/new/__init__.py b/lib/python/qmk/cli/new/__init__.py index e69de29bb..c6a26939b 100644 --- a/lib/python/qmk/cli/new/__init__.py +++ b/lib/python/qmk/cli/new/__init__.py | |||
@@ -0,0 +1 @@ | |||
from . import keymap | |||
diff --git a/lib/python/qmk/cli/new/keymap.py b/lib/python/qmk/cli/new/keymap.py index b378e5ab4..5efb81c93 100755 --- a/lib/python/qmk/cli/new/keymap.py +++ b/lib/python/qmk/cli/new/keymap.py | |||
@@ -6,15 +6,15 @@ import shutil | |||
6 | from milc import cli | 6 | from milc import cli |
7 | 7 | ||
8 | 8 | ||
9 | @cli.argument('-k', '--keyboard', help='Specify keyboard name. Example: 1upkeyboards/1up60hse') | 9 | @cli.argument('-kb', '--keyboard', help='Specify keyboard name. Example: 1upkeyboards/1up60hse') |
10 | @cli.argument('-u', '--username', help='Specify any name for the new keymap directory') | 10 | @cli.argument('-km', '--keymap', help='Specify the name for the new keymap directory') |
11 | @cli.entrypoint('Creates a new keymap for the keyboard of your choosing') | 11 | @cli.subcommand('Creates a new keymap for the keyboard of your choosing') |
12 | def main(cli): | 12 | def new_keymap(cli): |
13 | """Creates a new keymap for the keyboard of your choosing. | 13 | """Creates a new keymap for the keyboard of your choosing. |
14 | """ | 14 | """ |
15 | # ask for user input if keyboard or username was not provided in the command line | 15 | # ask for user input if keyboard or username was not provided in the command line |
16 | keyboard = cli.config.general.keyboard if cli.config.general.keyboard else input("Keyboard Name: ") | 16 | keyboard = cli.config.new_keymap.keyboard if cli.config.new_keymap.keyboard else input("Keyboard Name: ") |
17 | username = cli.config.general.username if cli.config.general.username else input("Username: ") | 17 | keymap = cli.config.new_keymap.keymap if cli.config.new_keymap.keymap else input("Keymap Name: ") |
18 | 18 | ||
19 | # generate keymap paths | 19 | # generate keymap paths |
20 | kb_path = os.path.join(os.getcwd(), "keyboards", keyboard) | 20 | kb_path = os.path.join(os.getcwd(), "keyboards", keyboard) |
@@ -36,6 +36,5 @@ def main(cli): | |||
36 | shutil.copytree(keymap_path_default, keymap_path, symlinks=True) | 36 | shutil.copytree(keymap_path_default, keymap_path, symlinks=True) |
37 | 37 | ||
38 | # end message to user | 38 | # end message to user |
39 | cli.log.info("%s keymap directory created in: %s\n" + | 39 | cli.log.info("%s keymap directory created in: %s", username, keymap_path) |
40 | "Compile a firmware file with your new keymap by typing: \n" + | 40 | cli.log.info("Compile a firmware with your new keymap by typing: \n" + "qmk compile -kb %s -km %s", keyboard, username) |
41 | "qmk compile -kb %s -km %s", username, keymap_path, keyboard, username) | ||
diff --git a/lib/python/qmk/cli/pyformat.py b/lib/python/qmk/cli/pyformat.py index b1f8c02b2..a53ba40c0 100755 --- a/lib/python/qmk/cli/pyformat.py +++ b/lib/python/qmk/cli/pyformat.py | |||
@@ -5,12 +5,13 @@ from milc import cli | |||
5 | import subprocess | 5 | import subprocess |
6 | 6 | ||
7 | 7 | ||
8 | @cli.entrypoint("Format python code according to QMK's style.") | 8 | @cli.subcommand("Format python code according to QMK's style.") |
9 | def main(cli): | 9 | def pyformat(cli): |
10 | """Format python code according to QMK's style. | 10 | """Format python code according to QMK's style. |
11 | """ | 11 | """ |
12 | try: | 12 | try: |
13 | subprocess.run(['yapf', '-vv', '-ri', 'bin/qmk', 'lib/python'], check=True) | 13 | subprocess.run(['yapf', '-vv', '-ri', 'bin/qmk', 'lib/python'], check=True) |
14 | cli.log.info('Successfully formatted the python code in `bin/qmk` and `lib/python`.') | 14 | cli.log.info('Successfully formatted the python code in `bin/qmk` and `lib/python`.') |
15 | |||
15 | except subprocess.CalledProcessError: | 16 | except subprocess.CalledProcessError: |
16 | cli.log.error('Error formatting python code!') | 17 | cli.log.error('Error formatting python code!') |
diff --git a/lib/python/qmk/cli/nose2.py b/lib/python/qmk/cli/pytest.py index c6c9c67b3..14613e1d9 100644 --- a/lib/python/qmk/cli/nose2.py +++ b/lib/python/qmk/cli/pytest.py | |||
@@ -2,17 +2,19 @@ | |||
2 | 2 | ||
3 | QMK script to run unit and integration tests against our python code. | 3 | QMK script to run unit and integration tests against our python code. |
4 | """ | 4 | """ |
5 | import sys | ||
5 | from milc import cli | 6 | from milc import cli |
6 | 7 | ||
7 | 8 | ||
8 | @cli.entrypoint('QMK Python Unit Tests') | 9 | @cli.subcommand('QMK Python Unit Tests') |
9 | def main(cli): | 10 | def pytest(cli): |
10 | """Use nose2 to run unittests | 11 | """Use nose2 to run unittests |
11 | """ | 12 | """ |
12 | try: | 13 | try: |
13 | import nose2 | 14 | import nose2 |
15 | |||
14 | except ImportError: | 16 | except ImportError: |
15 | cli.log.error('Could not import nose2! Please install it with {fg_cyan}pip3 install nose2') | 17 | cli.log.error('Could not import nose2! Please install it with {fg_cyan}pip3 install nose2') |
16 | return False | 18 | return False |
17 | 19 | ||
18 | nose2.discover() | 20 | nose2.discover(argv=['nose2', '-v']) |
diff --git a/lib/python/qmk/path.py b/lib/python/qmk/path.py index cf087265f..2149625cc 100644 --- a/lib/python/qmk/path.py +++ b/lib/python/qmk/path.py | |||
@@ -2,6 +2,7 @@ | |||
2 | """ | 2 | """ |
3 | import logging | 3 | import logging |
4 | import os | 4 | import os |
5 | from pkgutil import walk_packages | ||
5 | 6 | ||
6 | from qmk.errors import NoSuchKeyboardError | 7 | from qmk.errors import NoSuchKeyboardError |
7 | 8 | ||
diff --git a/lib/python/qmk/tests/test_cli_commands.py b/lib/python/qmk/tests/test_cli_commands.py new file mode 100644 index 000000000..2fc6e0f72 --- /dev/null +++ b/lib/python/qmk/tests/test_cli_commands.py | |||
@@ -0,0 +1,39 @@ | |||
1 | import subprocess | ||
2 | |||
3 | |||
4 | def check_subcommand(command, *args): | ||
5 | cmd = ['bin/qmk', command] + list(args) | ||
6 | return subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) | ||
7 | |||
8 | |||
9 | def test_cformat(): | ||
10 | assert check_subcommand('cformat', 'tmk_core/common/backlight.c').returncode == 0 | ||
11 | |||
12 | |||
13 | def test_compile(): | ||
14 | assert check_subcommand('compile', '-kb', 'handwired/onekey/pytest', '-km', 'default').returncode == 0 | ||
15 | |||
16 | |||
17 | def test_config(): | ||
18 | result = check_subcommand('config') | ||
19 | assert result.returncode == 0 | ||
20 | assert 'general.color' in result.stdout | ||
21 | |||
22 | |||
23 | def test_doctor(): | ||
24 | result = check_subcommand('doctor') | ||
25 | assert result.returncode == 0 | ||
26 | assert 'QMK Doctor is checking your environment.' in result.stderr | ||
27 | assert 'QMK is ready to go' in result.stderr | ||
28 | |||
29 | |||
30 | def test_hello(): | ||
31 | result = check_subcommand('hello') | ||
32 | assert result.returncode == 0 | ||
33 | assert 'Hello,' in result.stderr | ||
34 | |||
35 | |||
36 | def test_pyformat(): | ||
37 | result = check_subcommand('pyformat') | ||
38 | assert result.returncode == 0 | ||
39 | assert 'Successfully formatted the python code' in result.stderr | ||