aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorskullydazed <skullydazed@users.noreply.github.com>2020-03-13 15:47:04 -0700
committerGitHub <noreply@github.com>2020-03-13 15:47:04 -0700
commitf81b0e35a6a25a9a6e633dc65a4900bed2458cfb (patch)
tree707e06f6cd2caeda4278cfd9751ee77bf15aa055
parent5e98eaaaff8fde1ce25b9bad6c00a982718cb467 (diff)
downloadqmk_firmware-f81b0e35a6a25a9a6e633dc65a4900bed2458cfb.tar.gz
qmk_firmware-f81b0e35a6a25a9a6e633dc65a4900bed2458cfb.zip
Add decorators for determining keyboard and keymap based on current directory (#8191)
* Use pathlib everywhere we can * Improvements based on @erovia's feedback * rework qmk compile and qmk flash to use pathlib * style * Remove the subcommand_name argument from find_keyboard_keymap() * add experimental decorators * Create decorators for finding keyboard and keymap based on current directory. Decorators were inspired by @Erovia's brilliant work on the proof of concept.
-rw-r--r--lib/python/qmk/cli/__init__.py5
-rwxr-xr-xlib/python/qmk/cli/compile.py37
-rw-r--r--lib/python/qmk/cli/flash.py47
-rw-r--r--lib/python/qmk/cli/list/keymaps.py3
-rwxr-xr-xlib/python/qmk/cli/new/keymap.py3
-rw-r--r--lib/python/qmk/commands.py69
-rw-r--r--lib/python/qmk/decorators.py85
-rw-r--r--lib/python/qmk/path.py2
8 files changed, 152 insertions, 99 deletions
diff --git a/lib/python/qmk/cli/__init__.py b/lib/python/qmk/cli/__init__.py
index 5149a6215..eb524217c 100644
--- a/lib/python/qmk/cli/__init__.py
+++ b/lib/python/qmk/cli/__init__.py
@@ -2,6 +2,8 @@
2 2
3We list each subcommand here explicitly because all the reliable ways of searching for modules are slow and delay startup. 3We list each subcommand here explicitly because all the reliable ways of searching for modules are slow and delay startup.
4""" 4"""
5from milc import cli
6
5from . import cformat 7from . import cformat
6from . import compile 8from . import compile
7from . import config 9from . import config
@@ -16,3 +18,6 @@ from . import kle2json
16from . import new 18from . import new
17from . import pyformat 19from . import pyformat
18from . import pytest 20from . import pytest
21
22if not hasattr(cli, 'config_source'):
23 cli.log.warning("Your QMK CLI is out of date. Please upgrade with `pip3 install --upgrade qmk` or by using your package manager.")
diff --git a/lib/python/qmk/cli/compile.py b/lib/python/qmk/cli/compile.py
index 3068c97d8..6480d624b 100755
--- a/lib/python/qmk/cli/compile.py
+++ b/lib/python/qmk/cli/compile.py
@@ -8,13 +8,17 @@ from argparse import FileType
8from milc import cli 8from milc import cli
9 9
10import qmk.path 10import qmk.path
11from qmk.commands import compile_configurator_json, create_make_command, find_keyboard_keymap, parse_configurator_json 11from qmk.decorators import automagic_keyboard, automagic_keymap
12from qmk.commands import compile_configurator_json, create_make_command, parse_configurator_json
12 13
13 14
14@cli.argument('filename', nargs='?', arg_only=True, type=FileType('r'), help='The configurator export to compile') 15@cli.argument('filename', nargs='?', arg_only=True, type=FileType('r'), help='The configurator export to compile')
15@cli.argument('-kb', '--keyboard', help='The keyboard to build a firmware for. Ignored when a configurator export is supplied.') 16@cli.argument('-kb', '--keyboard', help='The keyboard to build a firmware for. Ignored when a configurator export is supplied.')
16@cli.argument('-km', '--keymap', help='The keymap to build a firmware for. Ignored when a configurator export is supplied.') 17@cli.argument('-km', '--keymap', help='The keymap to build a firmware for. Ignored when a configurator export is supplied.')
18@cli.argument('-n', '--dry-run', arg_only=True, action='store_true', help="Don't actually build, just show the make command to be run.")
17@cli.subcommand('Compile a QMK Firmware.') 19@cli.subcommand('Compile a QMK Firmware.')
20@automagic_keyboard
21@automagic_keymap
18def compile(cli): 22def compile(cli):
19 """Compile a QMK Firmware. 23 """Compile a QMK Firmware.
20 24
@@ -22,8 +26,10 @@ def compile(cli):
22 26
23 If a keyboard and keymap are provided this command will build a firmware based on that. 27 If a keyboard and keymap are provided this command will build a firmware based on that.
24 """ 28 """
29 command = None
30
25 if cli.args.filename: 31 if cli.args.filename:
26 # If a configurator JSON was provided skip straight to compiling it 32 # If a configurator JSON was provided generate a keymap and compile it
27 # FIXME(skullydazed): add code to check and warn if the keymap already exists when compiling a json keymap. 33 # FIXME(skullydazed): add code to check and warn if the keymap already exists when compiling a json keymap.
28 user_keymap = parse_configurator_json(cli.args.filename) 34 user_keymap = parse_configurator_json(cli.args.filename)
29 keymap_path = qmk.path.keymap(user_keymap['keyboard']) 35 keymap_path = qmk.path.keymap(user_keymap['keyboard'])
@@ -32,16 +38,23 @@ def compile(cli):
32 cli.log.info('Wrote keymap to {fg_cyan}%s/%s/keymap.c', keymap_path, user_keymap['keymap']) 38 cli.log.info('Wrote keymap to {fg_cyan}%s/%s/keymap.c', keymap_path, user_keymap['keymap'])
33 39
34 else: 40 else:
35 # Perform the action the user specified 41 if cli.config.compile.keyboard and cli.config.compile.keymap:
36 user_keyboard, user_keymap = find_keyboard_keymap()
37 if user_keyboard and user_keymap:
38 # Generate the make command for a specific keyboard/keymap. 42 # Generate the make command for a specific keyboard/keymap.
39 command = create_make_command(user_keyboard, user_keymap) 43 command = create_make_command(cli.config.compile.keyboard, cli.config.compile.keymap)
44
45 elif not cli.config.compile.keyboard:
46 cli.log.error('Could not determine keyboard!')
47 elif not cli.config.compile.keymap:
48 cli.log.error('Could not determine keymap!')
40 49
41 else: 50 # Compile the firmware, if we're able to
42 cli.log.error('You must supply a configurator export, both `--keyboard` and `--keymap`, or be in a directory for a keyboard or keymap.') 51 if command:
43 cli.echo('usage: qmk compile [-h] [-b] [-kb KEYBOARD] [-km KEYMAP] [filename]') 52 cli.log.info('Compiling keymap with {fg_cyan}%s', ' '.join(command))
44 return False 53 if not cli.args.dry_run:
54 cli.echo('\n')
55 subprocess.run(command)
45 56
46 cli.log.info('Compiling keymap with {fg_cyan}%s\n\n', ' '.join(command)) 57 else:
47 subprocess.run(command) 58 cli.log.error('You must supply a configurator export, both `--keyboard` and `--keymap`, or be in a directory for a keyboard or keymap.')
59 cli.echo('usage: qmk compile [-h] [-b] [-kb KEYBOARD] [-km KEYMAP] [filename]')
60 return False
diff --git a/lib/python/qmk/cli/flash.py b/lib/python/qmk/cli/flash.py
index f669c3cb7..f8497071e 100644
--- a/lib/python/qmk/cli/flash.py
+++ b/lib/python/qmk/cli/flash.py
@@ -6,9 +6,11 @@ A bootloader must be specified.
6import subprocess 6import subprocess
7from argparse import FileType 7from argparse import FileType
8 8
9import qmk.path
10from milc import cli 9from milc import cli
11from qmk.commands import compile_configurator_json, create_make_command, find_keyboard_keymap, parse_configurator_json 10
11import qmk.path
12from qmk.decorators import automagic_keyboard, automagic_keymap
13from qmk.commands import compile_configurator_json, create_make_command, parse_configurator_json
12 14
13 15
14def print_bootloader_help(): 16def print_bootloader_help():
@@ -28,12 +30,15 @@ def print_bootloader_help():
28 cli.echo('For more info, visit https://docs.qmk.fm/#/flashing') 30 cli.echo('For more info, visit https://docs.qmk.fm/#/flashing')
29 31
30 32
31@cli.argument('-bl', '--bootloader', default='flash', help='The flash command, corresponding to qmk\'s make options of bootloaders.')
32@cli.argument('filename', nargs='?', arg_only=True, type=FileType('r'), help='The configurator export JSON to compile.') 33@cli.argument('filename', nargs='?', arg_only=True, type=FileType('r'), help='The configurator export JSON to compile.')
34@cli.argument('-b', '--bootloaders', action='store_true', help='List the available bootloaders.')
35@cli.argument('-bl', '--bootloader', default='flash', help='The flash command, corresponding to qmk\'s make options of bootloaders.')
33@cli.argument('-km', '--keymap', help='The keymap to build a firmware for. Use this if you dont have a configurator file. Ignored when a configurator file is supplied.') 36@cli.argument('-km', '--keymap', help='The keymap to build a firmware for. Use this if you dont have a configurator file. Ignored when a configurator file is supplied.')
34@cli.argument('-kb', '--keyboard', help='The keyboard to build a firmware for. Use this if you dont have a configurator file. Ignored when a configurator file is supplied.') 37@cli.argument('-kb', '--keyboard', help='The keyboard to build a firmware for. Use this if you dont have a configurator file. Ignored when a configurator file is supplied.')
35@cli.argument('-b', '--bootloaders', action='store_true', help='List the available bootloaders.') 38@cli.argument('-n', '--dry-run', arg_only=True, action='store_true', help="Don't actually build, just show the make command to be run.")
36@cli.subcommand('QMK Flash.') 39@cli.subcommand('QMK Flash.')
40@automagic_keyboard
41@automagic_keymap
37def flash(cli): 42def flash(cli):
38 """Compile and or flash QMK Firmware or keyboard/layout 43 """Compile and or flash QMK Firmware or keyboard/layout
39 44
@@ -42,12 +47,13 @@ def flash(cli):
42 47
43 If no file is supplied, keymap and keyboard are expected. 48 If no file is supplied, keymap and keyboard are expected.
44 49
45 If bootloader is omitted, the one according to the rules.mk will be used. 50 If bootloader is omitted the make system will use the configured bootloader for that keyboard.
46
47 """ 51 """
52 command = ''
53
48 if cli.args.bootloaders: 54 if cli.args.bootloaders:
49 # Provide usage and list bootloaders 55 # Provide usage and list bootloaders
50 cli.echo('usage: qmk flash [-h] [-b] [-kb KEYBOARD] [-km KEYMAP] [-bl BOOTLOADER] [filename]') 56 cli.echo('usage: qmk flash [-h] [-b] [-n] [-kb KEYBOARD] [-km KEYMAP] [-bl BOOTLOADER] [filename]')
51 print_bootloader_help() 57 print_bootloader_help()
52 return False 58 return False
53 59
@@ -60,16 +66,23 @@ def flash(cli):
60 cli.log.info('Wrote keymap to {fg_cyan}%s/%s/keymap.c', keymap_path, user_keymap['keymap']) 66 cli.log.info('Wrote keymap to {fg_cyan}%s/%s/keymap.c', keymap_path, user_keymap['keymap'])
61 67
62 else: 68 else:
63 # Perform the action the user specified 69 if cli.config.flash.keyboard and cli.config.flash.keymap:
64 user_keyboard, user_keymap = find_keyboard_keymap()
65 if user_keyboard and user_keymap:
66 # Generate the make command for a specific keyboard/keymap. 70 # Generate the make command for a specific keyboard/keymap.
67 command = create_make_command(user_keyboard, user_keymap, cli.args.bootloader) 71 command = create_make_command(cli.config.flash.keyboard, cli.config.flash.keymap, cli.args.bootloader)
68 72
69 else: 73 elif not cli.config.flash.keyboard:
70 cli.log.error('You must supply a configurator export or both `--keyboard` and `--keymap`.') 74 cli.log.error('Could not determine keyboard!')
71 cli.echo('usage: qmk flash [-h] [-b] [-kb KEYBOARD] [-km KEYMAP] [-bl BOOTLOADER] [filename]') 75 elif not cli.config.flash.keymap:
72 return False 76 cli.log.error('Could not determine keymap!')
73 77
74 cli.log.info('Flashing keymap with {fg_cyan}%s\n\n', ' '.join(command)) 78 # Compile the firmware, if we're able to
75 subprocess.run(command) 79 if command:
80 cli.log.info('Compiling keymap with {fg_cyan}%s', ' '.join(command))
81 if not cli.args.dry_run:
82 cli.echo('\n')
83 subprocess.run(command)
84
85 else:
86 cli.log.error('You must supply a configurator export, both `--keyboard` and `--keymap`, or be in a directory for a keyboard or keymap.')
87 cli.echo('usage: qmk flash [-h] [-b] [-n] [-kb KEYBOARD] [-km KEYMAP] [-bl BOOTLOADER] [filename]')
88 return False
diff --git a/lib/python/qmk/cli/list/keymaps.py b/lib/python/qmk/cli/list/keymaps.py
index d199d29bc..cec9ca022 100644
--- a/lib/python/qmk/cli/list/keymaps.py
+++ b/lib/python/qmk/cli/list/keymaps.py
@@ -1,12 +1,15 @@
1"""List the keymaps for a specific keyboard 1"""List the keymaps for a specific keyboard
2""" 2"""
3from milc import cli 3from milc import cli
4
4import qmk.keymap 5import qmk.keymap
6from qmk.decorators import automagic_keyboard
5from qmk.errors import NoSuchKeyboardError 7from qmk.errors import NoSuchKeyboardError
6 8
7 9
8@cli.argument("-kb", "--keyboard", help="Specify keyboard name. Example: 1upkeyboards/1up60hse") 10@cli.argument("-kb", "--keyboard", help="Specify keyboard name. Example: 1upkeyboards/1up60hse")
9@cli.subcommand("List the keymaps for a specific keyboard") 11@cli.subcommand("List the keymaps for a specific keyboard")
12@automagic_keyboard
10def list_keymaps(cli): 13def list_keymaps(cli):
11 """List the keymaps for a specific keyboard 14 """List the keymaps for a specific keyboard
12 """ 15 """
diff --git a/lib/python/qmk/cli/new/keymap.py b/lib/python/qmk/cli/new/keymap.py
index cbe50692e..5ae262856 100755
--- a/lib/python/qmk/cli/new/keymap.py
+++ b/lib/python/qmk/cli/new/keymap.py
@@ -4,12 +4,15 @@ import shutil
4from pathlib import Path 4from pathlib import Path
5 5
6import qmk.path 6import qmk.path
7from qmk.decorators import automagic_keyboard, automagic_keymap
7from milc import cli 8from milc import cli
8 9
9 10
10@cli.argument('-kb', '--keyboard', help='Specify keyboard name. Example: 1upkeyboards/1up60hse') 11@cli.argument('-kb', '--keyboard', help='Specify keyboard name. Example: 1upkeyboards/1up60hse')
11@cli.argument('-km', '--keymap', help='Specify the name for the new keymap directory') 12@cli.argument('-km', '--keymap', help='Specify the name for the new keymap directory')
12@cli.subcommand('Creates a new keymap for the keyboard of your choosing') 13@cli.subcommand('Creates a new keymap for the keyboard of your choosing')
14@automagic_keyboard
15@automagic_keymap
13def new_keymap(cli): 16def new_keymap(cli):
14 """Creates a new keymap for the keyboard of your choosing. 17 """Creates a new keymap for the keyboard of your choosing.
15 """ 18 """
diff --git a/lib/python/qmk/commands.py b/lib/python/qmk/commands.py
index cdb8ee037..3d4ed1616 100644
--- a/lib/python/qmk/commands.py
+++ b/lib/python/qmk/commands.py
@@ -1,12 +1,8 @@
1"""Helper functions for commands. 1"""Helper functions for commands.
2""" 2"""
3import json 3import json
4from pathlib import Path
5
6from milc import cli
7 4
8import qmk.keymap 5import qmk.keymap
9from qmk.path import is_keyboard, is_keymap_dir, under_qmk_firmware
10 6
11 7
12def create_make_command(keyboard, keymap, target=None): 8def create_make_command(keyboard, keymap, target=None):
@@ -59,71 +55,6 @@ def compile_configurator_json(user_keymap, bootloader=None):
59 return create_make_command(user_keymap['keyboard'], user_keymap['keymap'], bootloader) 55 return create_make_command(user_keymap['keyboard'], user_keymap['keymap'], bootloader)
60 56
61 57
62def find_keyboard_keymap():
63 """Returns `(keyboard_name, keymap_name)` based on the user's current environment.
64
65 This determines the keyboard and keymap name using the following precedence order:
66
67 * Command line flags (--keyboard and --keymap)
68 * Current working directory
69 * `keyboards/<keyboard_name>`
70 * `keyboards/<keyboard_name>/keymaps/<keymap_name>`
71 * `layouts/**/<keymap_name>`
72 * `users/<keymap_name>`
73 * Configuration
74 * cli.config.<subcommand>.keyboard
75 * cli.config.<subcommand>.keymap
76 """
77 # Check to make sure their copy of MILC supports config_source
78 if not hasattr(cli, 'config_source'):
79 cli.log.error("Your QMK CLI is out of date. Please upgrade using pip3 or your package manager.")
80 exit(1)
81
82 # State variables
83 relative_cwd = under_qmk_firmware()
84 keyboard_name = ""
85 keymap_name = ""
86
87 # If the keyboard or keymap are passed as arguments use that in preference to anything else
88 if cli.config_source[cli._entrypoint.__name__]['keyboard'] == 'argument':
89 keyboard_name = cli.config[cli._entrypoint.__name__]['keyboard']
90 if cli.config_source[cli._entrypoint.__name__]['keymap'] == 'argument':
91 keymap_name = cli.config[cli._entrypoint.__name__]['keymap']
92
93 if not keyboard_name or not keymap_name:
94 # If we don't have a keyboard_name and keymap_name from arguments try to derive one or both
95 if relative_cwd and relative_cwd.parts and relative_cwd.parts[0] == 'keyboards':
96 # Try to determine the keyboard and/or keymap name
97 current_path = Path('/'.join(relative_cwd.parts[1:]))
98
99 if current_path.parts[-2] == 'keymaps':
100 if not keymap_name:
101 keymap_name = current_path.parts[-1]
102 if not keyboard_name:
103 keyboard_name = '/'.join(current_path.parts[:-2])
104 elif not keyboard_name and is_keyboard(current_path):
105 keyboard_name = str(current_path)
106
107 elif relative_cwd and relative_cwd.parts and relative_cwd.parts[0] == 'layouts':
108 # Try to determine the keymap name from the community layout
109 if is_keymap_dir(relative_cwd) and not keymap_name:
110 keymap_name = relative_cwd.name
111
112 elif relative_cwd and relative_cwd.parts and relative_cwd.parts[0] == 'users':
113 # Try to determine the keymap name based on which userspace they're in
114 if not keymap_name and len(relative_cwd.parts) > 1:
115 keymap_name = relative_cwd.parts[1]
116
117 # If we still don't have a keyboard and keymap check the config
118 if not keyboard_name and cli.config[cli._entrypoint.__name__]['keyboard']:
119 keyboard_name = cli.config[cli._entrypoint.__name__]['keyboard']
120
121 if not keymap_name and cli.config[cli._entrypoint.__name__]['keymap']:
122 keymap_name = cli.config[cli._entrypoint.__name__]['keymap']
123
124 return (keyboard_name, keymap_name)
125
126
127def parse_configurator_json(configurator_file): 58def parse_configurator_json(configurator_file):
128 """Open and parse a configurator json export 59 """Open and parse a configurator json export
129 """ 60 """
diff --git a/lib/python/qmk/decorators.py b/lib/python/qmk/decorators.py
new file mode 100644
index 000000000..94e14bf37
--- /dev/null
+++ b/lib/python/qmk/decorators.py
@@ -0,0 +1,85 @@
1"""Helpful decorators that subcommands can use.
2"""
3import functools
4from pathlib import Path
5
6from milc import cli
7
8from qmk.path import is_keyboard, is_keymap_dir, under_qmk_firmware
9
10
11def automagic_keyboard(func):
12 """Sets `cli.config.<subcommand>.keyboard` based on environment.
13
14 This will rewrite cli.config.<subcommand>.keyboard if the user did not pass `--keyboard` and the directory they are currently in is a keyboard or keymap directory.
15 """
16 @functools.wraps(func)
17 def wrapper(*args, **kwargs):
18 # Check to make sure their copy of MILC supports config_source
19 if not hasattr(cli, 'config_source'):
20 cli.log.error("This subcommand requires a newer version of the QMK CLI. Please upgrade using `pip3 install --upgrade qmk` or your package manager.")
21 exit(1)
22
23 # Ensure that `--keyboard` was not passed and CWD is under `qmk_firmware/keyboards`
24 if cli.config_source[cli._entrypoint.__name__]['keyboard'] != 'argument':
25 relative_cwd = under_qmk_firmware()
26
27 if relative_cwd and len(relative_cwd.parts) > 1 and relative_cwd.parts[0] == 'keyboards':
28 # Attempt to extract the keyboard name from the current directory
29 current_path = Path('/'.join(relative_cwd.parts[1:]))
30
31 if 'keymaps' in current_path.parts:
32 # Strip current_path of anything after `keymaps`
33 keymap_index = len(current_path.parts) - current_path.parts.index('keymaps') - 1
34 current_path = current_path.parents[keymap_index]
35
36 if is_keyboard(current_path):
37 cli.config[cli._entrypoint.__name__]['keyboard'] = str(current_path)
38 cli.config_source[cli._entrypoint.__name__]['keyboard'] = 'keyboard_directory'
39
40 return func(*args, **kwargs)
41
42 return wrapper
43
44
45def automagic_keymap(func):
46 """Sets `cli.config.<subcommand>.keymap` based on environment.
47
48 This will rewrite cli.config.<subcommand>.keymap if the user did not pass `--keymap` and the directory they are currently in is a keymap, layout, or user directory.
49 """
50 @functools.wraps(func)
51 def wrapper(*args, **kwargs):
52 # Check to make sure their copy of MILC supports config_source
53 if not hasattr(cli, 'config_source'):
54 cli.log.error("This subcommand requires a newer version of the QMK CLI. Please upgrade using `pip3 install --upgrade qmk` or your package manager.")
55 exit(1)
56
57 # Ensure that `--keymap` was not passed and that we're under `qmk_firmware`
58 if cli.config_source[cli._entrypoint.__name__]['keymap'] != 'argument':
59 relative_cwd = under_qmk_firmware()
60
61 if relative_cwd and len(relative_cwd.parts) > 1:
62 # If we're in `qmk_firmware/keyboards` and `keymaps` is in our path, try to find the keyboard name.
63 if relative_cwd.parts[0] == 'keyboards' and 'keymaps' in relative_cwd.parts:
64 current_path = Path('/'.join(relative_cwd.parts[1:])) # Strip 'keyboards' from the front
65
66 if 'keymaps' in current_path.parts and current_path.name != 'keymaps':
67 while current_path.parent.name != 'keymaps':
68 current_path = current_path.parent
69 cli.config[cli._entrypoint.__name__]['keymap'] = current_path.name
70 cli.config_source[cli._entrypoint.__name__]['keyboard'] = 'keymap_directory'
71
72 # If we're in `qmk_firmware/layouts` guess the name from the community keymap they're in
73 elif relative_cwd.parts[0] == 'layouts' and is_keymap_dir(relative_cwd):
74 cli.config[cli._entrypoint.__name__]['keymap'] = relative_cwd.name
75 cli.config_source[cli._entrypoint.__name__]['keyboard'] = 'layouts_directory'
76
77 # If we're in `qmk_firmware/users` guess the name from the userspace they're in
78 elif relative_cwd.parts[0] == 'users':
79 # Guess the keymap name based on which userspace they're in
80 cli.config[cli._entrypoint.__name__]['keymap'] = relative_cwd.parts[1]
81 cli.config_source[cli._entrypoint.__name__]['keyboard'] = 'users_directory'
82
83 return func(*args, **kwargs)
84
85 return wrapper
diff --git a/lib/python/qmk/path.py b/lib/python/qmk/path.py
index bfaa43924..7306c433b 100644
--- a/lib/python/qmk/path.py
+++ b/lib/python/qmk/path.py
@@ -65,7 +65,7 @@ def normpath(path):
65 path = Path(path) 65 path = Path(path)
66 66
67 if path.is_absolute(): 67 if path.is_absolute():
68 return Path(path) 68 return path
69 69
70 return Path(os.environ['ORIG_CWD']) / path 70 return Path(os.environ['ORIG_CWD']) / path
71 71