Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c8d966afa7 | |||
| 1ea8f02927 | |||
| 3683f86b4d | |||
| 1d622467e4 | |||
| a5c8efddb4 | |||
| 8d75fd3dca | |||
| 72c46efc74 | |||
| fdc42051d5 | |||
| a4859aa15c | |||
| 7ce14c0f74 | |||
| 94831732c6 | |||
| 819cbea904 | |||
| 65bcd35588 | |||
| ebe8db89d7 | |||
| 9c6db3844f | |||
| fb372d11ec | |||
| f23614d245 | |||
| 427e6addda | |||
| 67698a52e0 | |||
| c54aaa9c80 | |||
| 6681535ae1 | |||
| fb365b6850 | |||
| 7ff0f36338 |
+6
-1
@@ -1 +1,6 @@
|
|||||||
include *.md LICENSE
|
include *.md
|
||||||
|
include *.txt
|
||||||
|
include LICENSE
|
||||||
|
include qmk
|
||||||
|
include release
|
||||||
|
recursive-include qmk_cli *.py
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# QMK CLI
|
# QMK CLI
|
||||||
|
|
||||||
A program to help users work with QMK
|
A program to help users work with [QMK Firmware](https://qmk.fm/).
|
||||||
|
|
||||||
# Features
|
# Features
|
||||||
|
|
||||||
|
|||||||
+71
-45
@@ -17,6 +17,7 @@ import argparse
|
|||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
import shlex
|
||||||
import sys
|
import sys
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from tempfile import NamedTemporaryFile
|
from tempfile import NamedTemporaryFile
|
||||||
@@ -35,6 +36,10 @@ except ImportError:
|
|||||||
|
|
||||||
import argcomplete
|
import argcomplete
|
||||||
import colorama
|
import colorama
|
||||||
|
from appdirs import user_config_dir
|
||||||
|
|
||||||
|
# Disable logging until we can configure it how the user wants
|
||||||
|
logging.basicConfig(filename='/dev/null')
|
||||||
|
|
||||||
# Log Level Representations
|
# Log Level Representations
|
||||||
EMOJI_LOGLEVELS = {
|
EMOJI_LOGLEVELS = {
|
||||||
@@ -47,6 +52,7 @@ EMOJI_LOGLEVELS = {
|
|||||||
}
|
}
|
||||||
EMOJI_LOGLEVELS['FATAL'] = EMOJI_LOGLEVELS['CRITICAL']
|
EMOJI_LOGLEVELS['FATAL'] = EMOJI_LOGLEVELS['CRITICAL']
|
||||||
EMOJI_LOGLEVELS['WARN'] = EMOJI_LOGLEVELS['WARNING']
|
EMOJI_LOGLEVELS['WARN'] = EMOJI_LOGLEVELS['WARNING']
|
||||||
|
UNICODE_SUPPORT = sys.stdout.encoding.lower().startswith('utf')
|
||||||
|
|
||||||
# ANSI Color setup
|
# ANSI Color setup
|
||||||
# Regex was gratefully borrowed from kfir on stackoverflow:
|
# Regex was gratefully borrowed from kfir on stackoverflow:
|
||||||
@@ -97,11 +103,12 @@ class ANSIFormatter(logging.Formatter):
|
|||||||
|
|
||||||
|
|
||||||
class ANSIEmojiLoglevelFormatter(ANSIFormatter):
|
class ANSIEmojiLoglevelFormatter(ANSIFormatter):
|
||||||
"""A log formatter that makes the loglevel an emoji.
|
"""A log formatter that makes the loglevel an emoji on UTF capable terminals.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def format(self, record):
|
def format(self, record):
|
||||||
record.levelname = EMOJI_LOGLEVELS[record.levelname].format(**ansi_colors)
|
if UNICODE_SUPPORT:
|
||||||
|
record.levelname = EMOJI_LOGLEVELS[record.levelname].format(**ansi_colors)
|
||||||
return super(ANSIEmojiLoglevelFormatter, self).format(record)
|
return super(ANSIEmojiLoglevelFormatter, self).format(record)
|
||||||
|
|
||||||
|
|
||||||
@@ -144,13 +151,15 @@ class Configuration(object):
|
|||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
self._config = {}
|
self._config = {}
|
||||||
self.default_container = ConfigurationOption
|
|
||||||
|
def __getattr__(self, key):
|
||||||
|
return self.__getitem__(key)
|
||||||
|
|
||||||
def __getitem__(self, key):
|
def __getitem__(self, key):
|
||||||
"""Returns a config section, creating it if it doesn't exist yet.
|
"""Returns a config section, creating it if it doesn't exist yet.
|
||||||
"""
|
"""
|
||||||
if key not in self._config:
|
if key not in self._config:
|
||||||
self.__dict__[key] = self._config[key] = ConfigurationOption()
|
self.__dict__[key] = self._config[key] = ConfigurationSection(self)
|
||||||
|
|
||||||
return self._config[key]
|
return self._config[key]
|
||||||
|
|
||||||
@@ -161,30 +170,34 @@ class Configuration(object):
|
|||||||
def __delitem__(self, key):
|
def __delitem__(self, key):
|
||||||
if key in self.__dict__ and key[0] != '_':
|
if key in self.__dict__ and key[0] != '_':
|
||||||
del self.__dict__[key]
|
del self.__dict__[key]
|
||||||
del self._config[key]
|
if key in self._config:
|
||||||
|
del self._config[key]
|
||||||
|
|
||||||
|
|
||||||
class ConfigurationOption(Configuration):
|
class ConfigurationSection(Configuration):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, parent, *args, **kwargs):
|
||||||
super(ConfigurationOption, self).__init__(*args, **kwargs)
|
super(ConfigurationSection, self).__init__(*args, **kwargs)
|
||||||
self.default_container = dict
|
self.parent = parent
|
||||||
|
|
||||||
def __getitem__(self, key):
|
def __getitem__(self, key):
|
||||||
"""Returns a config section, creating it if it doesn't exist yet.
|
"""Returns a config value, pulling from the `default` section as a fallback.
|
||||||
"""
|
"""
|
||||||
if key not in self._config:
|
if key in self._config:
|
||||||
self.__dict__[key] = self._config[key] = None
|
return self._config[key]
|
||||||
|
|
||||||
return self._config[key]
|
elif key in self.parent.default:
|
||||||
|
return self.parent.default[key]
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def handle_store_boolean(self, *args, **kwargs):
|
def handle_store_boolean(self, *args, **kwargs):
|
||||||
"""Does the add_argument for action='store_boolean'.
|
"""Does the add_argument for action='store_boolean'.
|
||||||
"""
|
"""
|
||||||
kwargs['add_dest'] = False
|
|
||||||
disabled_args = None
|
disabled_args = None
|
||||||
disabled_kwargs = kwargs.copy()
|
disabled_kwargs = kwargs.copy()
|
||||||
disabled_kwargs['action'] = 'store_false'
|
disabled_kwargs['action'] = 'store_false'
|
||||||
|
disabled_kwargs['dest'] = self.get_argument_name(*args, **kwargs)
|
||||||
disabled_kwargs['help'] = 'Disable ' + kwargs['help']
|
disabled_kwargs['help'] = 'Disable ' + kwargs['help']
|
||||||
kwargs['action'] = 'store_true'
|
kwargs['action'] = 'store_true'
|
||||||
kwargs['help'] = 'Enable ' + kwargs['help']
|
kwargs['help'] = 'Enable ' + kwargs['help']
|
||||||
@@ -219,11 +232,6 @@ class SubparserWrapper(object):
|
|||||||
self.subparser.completer = completer
|
self.subparser.completer = completer
|
||||||
|
|
||||||
def add_argument(self, *args, **kwargs):
|
def add_argument(self, *args, **kwargs):
|
||||||
if kwargs.get('add_dest', True):
|
|
||||||
kwargs['dest'] = self.submodule + '_' + self.cli.get_argument_name(*args, **kwargs)
|
|
||||||
if 'add_dest' in kwargs:
|
|
||||||
del kwargs['add_dest']
|
|
||||||
|
|
||||||
if 'action' in kwargs and kwargs['action'] == 'store_boolean':
|
if 'action' in kwargs and kwargs['action'] == 'store_boolean':
|
||||||
return handle_store_boolean(self, *args, **kwargs)
|
return handle_store_boolean(self, *args, **kwargs)
|
||||||
|
|
||||||
@@ -254,12 +262,16 @@ class MILC(object):
|
|||||||
self._entrypoint = None
|
self._entrypoint = None
|
||||||
self._inside_context_manager = False
|
self._inside_context_manager = False
|
||||||
self.ansi = ansi_colors
|
self.ansi = ansi_colors
|
||||||
|
self.arg_only = []
|
||||||
self.config = Configuration()
|
self.config = Configuration()
|
||||||
self.config_file = None
|
self.config_file = None
|
||||||
self.prog_name = sys.argv[0][:-3] if sys.argv[0].endswith('.py') else sys.argv[0]
|
|
||||||
self.version = os.environ.get('QMK_VERSION', 'unknown')
|
self.version = os.environ.get('QMK_VERSION', 'unknown')
|
||||||
self.release_lock()
|
self.release_lock()
|
||||||
|
|
||||||
|
# Figure out our program name
|
||||||
|
self.prog_name = sys.argv[0][:-3] if sys.argv[0].endswith('.py') else sys.argv[0]
|
||||||
|
self.prog_name = self.prog_name.split('/')[-1]
|
||||||
|
|
||||||
# Initialize all the things
|
# Initialize all the things
|
||||||
self.initialize_argparse()
|
self.initialize_argparse()
|
||||||
self.initialize_logging()
|
self.initialize_logging()
|
||||||
@@ -273,7 +285,7 @@ class MILC(object):
|
|||||||
self._description = self._arg_parser.description = self._arg_defaults.description = value
|
self._description = self._arg_parser.description = self._arg_defaults.description = value
|
||||||
|
|
||||||
def echo(self, text, *args, **kwargs):
|
def echo(self, text, *args, **kwargs):
|
||||||
"""Print colorized text to stdout, as long as stdout is a tty.
|
"""Print colorized text to stdout.
|
||||||
|
|
||||||
ANSI color strings (such as {fg-blue}) will be converted into ANSI
|
ANSI color strings (such as {fg-blue}) will be converted into ANSI
|
||||||
escape sequences, and the ANSI reset sequence will be added to all
|
escape sequences, and the ANSI reset sequence will be added to all
|
||||||
@@ -284,11 +296,10 @@ class MILC(object):
|
|||||||
if args and kwargs:
|
if args and kwargs:
|
||||||
raise RuntimeError('You can only specify *args or **kwargs, not both!')
|
raise RuntimeError('You can only specify *args or **kwargs, not both!')
|
||||||
|
|
||||||
if sys.stdout.isatty():
|
args = args or kwargs
|
||||||
args = args or kwargs
|
text = format_ansi(text)
|
||||||
text = format_ansi(text)
|
|
||||||
|
|
||||||
print(text % args)
|
print(text % args)
|
||||||
|
|
||||||
def initialize_argparse(self):
|
def initialize_argparse(self):
|
||||||
"""Prepare to process arguments from sys.argv.
|
"""Prepare to process arguments from sys.argv.
|
||||||
@@ -313,21 +324,21 @@ class MILC(object):
|
|||||||
self.release_lock()
|
self.release_lock()
|
||||||
|
|
||||||
def completer(self, completer):
|
def completer(self, completer):
|
||||||
"""Add an arpcomplete completer to this subcommand.
|
"""Add an argcomplete completer to this subcommand.
|
||||||
"""
|
"""
|
||||||
self._arg_parser.completer = completer
|
self._arg_parser.completer = completer
|
||||||
|
|
||||||
def add_argument(self, *args, **kwargs):
|
def add_argument(self, *args, **kwargs):
|
||||||
"""Wrapper to add arguments to both the main and the shadow argparser.
|
"""Wrapper to add arguments to both the main and the shadow argparser.
|
||||||
"""
|
"""
|
||||||
|
if 'action' in kwargs and kwargs['action'] == 'store_boolean':
|
||||||
|
return handle_store_boolean(self, *args, **kwargs)
|
||||||
|
|
||||||
if kwargs.get('add_dest', True) and args[0][0] == '-':
|
if kwargs.get('add_dest', True) and args[0][0] == '-':
|
||||||
kwargs['dest'] = 'general_' + self.get_argument_name(*args, **kwargs)
|
kwargs['dest'] = 'general_' + self.get_argument_name(*args, **kwargs)
|
||||||
if 'add_dest' in kwargs:
|
if 'add_dest' in kwargs:
|
||||||
del kwargs['add_dest']
|
del kwargs['add_dest']
|
||||||
|
|
||||||
if 'action' in kwargs and kwargs['action'] == 'store_boolean':
|
|
||||||
return handle_store_boolean(self, *args, **kwargs)
|
|
||||||
|
|
||||||
self.acquire_lock()
|
self.acquire_lock()
|
||||||
self._arg_parser.add_argument(*args, **kwargs)
|
self._arg_parser.add_argument(*args, **kwargs)
|
||||||
|
|
||||||
@@ -396,7 +407,7 @@ class MILC(object):
|
|||||||
if self.args and self.args.general_config_file:
|
if self.args and self.args.general_config_file:
|
||||||
return self.args.general_config_file
|
return self.args.general_config_file
|
||||||
|
|
||||||
return os.path.abspath(os.path.expanduser('~/.%s.ini' % self.prog_name))
|
return os.path.join(user_config_dir(appname='qmk', appauthor='QMK'), '%s.ini' % self.prog_name)
|
||||||
|
|
||||||
def get_argument_name(self, *args, **kwargs):
|
def get_argument_name(self, *args, **kwargs):
|
||||||
"""Takes argparse arguments and returns the dest name.
|
"""Takes argparse arguments and returns the dest name.
|
||||||
@@ -413,6 +424,11 @@ class MILC(object):
|
|||||||
raise RuntimeError('You must run this before the with statement!')
|
raise RuntimeError('You must run this before the with statement!')
|
||||||
|
|
||||||
def argument_function(handler):
|
def argument_function(handler):
|
||||||
|
if 'arg_only' in kwargs and kwargs['arg_only']:
|
||||||
|
arg_name = self.get_argument_name(*args, **kwargs)
|
||||||
|
self.arg_only.append(arg_name)
|
||||||
|
del kwargs['arg_only']
|
||||||
|
|
||||||
if handler is self._entrypoint:
|
if handler is self._entrypoint:
|
||||||
self.add_argument(*args, **kwargs)
|
self.add_argument(*args, **kwargs)
|
||||||
|
|
||||||
@@ -485,15 +501,20 @@ class MILC(object):
|
|||||||
if argument in ('subparsers', 'entrypoint'):
|
if argument in ('subparsers', 'entrypoint'):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if '_' not in argument:
|
if '_' in argument:
|
||||||
continue
|
section, option = argument.split('_', 1)
|
||||||
|
|
||||||
section, option = argument.split('_', 1)
|
|
||||||
if hasattr(self.args_passed, argument):
|
|
||||||
self.config[section][option] = getattr(self.args, argument)
|
|
||||||
else:
|
else:
|
||||||
if option not in self.config[section]:
|
section = self._entrypoint.__name__
|
||||||
self.config[section][option] = getattr(self.args, argument)
|
option = argument
|
||||||
|
|
||||||
|
if option not in self.arg_only:
|
||||||
|
if hasattr(self.args_passed, argument):
|
||||||
|
arg_value = getattr(self.args, argument)
|
||||||
|
if arg_value:
|
||||||
|
self.config[section][option] = arg_value
|
||||||
|
else:
|
||||||
|
if option not in self.config[section]:
|
||||||
|
self.config[section][option] = getattr(self.args, argument)
|
||||||
|
|
||||||
self.release_lock()
|
self.release_lock()
|
||||||
|
|
||||||
@@ -509,6 +530,8 @@ class MILC(object):
|
|||||||
self.acquire_lock()
|
self.acquire_lock()
|
||||||
|
|
||||||
config = RawConfigParser()
|
config = RawConfigParser()
|
||||||
|
config_dir = os.path.dirname(self.config_file)
|
||||||
|
|
||||||
for section_name, section in self.config._config.items():
|
for section_name, section in self.config._config.items():
|
||||||
config.add_section(section_name)
|
config.add_section(section_name)
|
||||||
for option_name, value in section.items():
|
for option_name, value in section.items():
|
||||||
@@ -517,7 +540,10 @@ class MILC(object):
|
|||||||
continue
|
continue
|
||||||
config.set(section_name, option_name, str(value))
|
config.set(section_name, option_name, str(value))
|
||||||
|
|
||||||
with NamedTemporaryFile(mode='w', dir=os.path.dirname(self.config_file), delete=False) as tmpfile:
|
if not os.path.exists(config_dir):
|
||||||
|
os.makedirs(config_dir)
|
||||||
|
|
||||||
|
with NamedTemporaryFile(mode='w', dir=config_dir, delete=False) as tmpfile:
|
||||||
config.write(tmpfile)
|
config.write(tmpfile)
|
||||||
|
|
||||||
# Move the new config file into place atomically
|
# Move the new config file into place atomically
|
||||||
@@ -527,6 +553,7 @@ class MILC(object):
|
|||||||
self.log.warning('Config file saving failed, not replacing %s with %s.', self.config_file, tmpfile.name)
|
self.log.warning('Config file saving failed, not replacing %s with %s.', self.config_file, tmpfile.name)
|
||||||
|
|
||||||
self.release_lock()
|
self.release_lock()
|
||||||
|
cli.log.info('Wrote configuration to %s', shlex.quote(self.config_file))
|
||||||
|
|
||||||
def __call__(self):
|
def __call__(self):
|
||||||
"""Execute the entrypoint function.
|
"""Execute the entrypoint function.
|
||||||
@@ -534,8 +561,7 @@ class MILC(object):
|
|||||||
if not self._inside_context_manager:
|
if not self._inside_context_manager:
|
||||||
# If they didn't use the context manager use it ourselves
|
# If they didn't use the context manager use it ourselves
|
||||||
with self:
|
with self:
|
||||||
self.__call__()
|
return self.__call__()
|
||||||
return
|
|
||||||
|
|
||||||
if not self._entrypoint:
|
if not self._entrypoint:
|
||||||
raise RuntimeError('No entrypoint provided!')
|
raise RuntimeError('No entrypoint provided!')
|
||||||
@@ -603,8 +629,8 @@ class MILC(object):
|
|||||||
"""Called by __enter__() to setup the logging configuration.
|
"""Called by __enter__() to setup the logging configuration.
|
||||||
"""
|
"""
|
||||||
if len(logging.root.handlers) != 0:
|
if len(logging.root.handlers) != 0:
|
||||||
# This is not a design decision. This is what I'm doing for now until I can examine and think about this situation in more detail.
|
# MILC is the only thing that should have root log handlers
|
||||||
raise RuntimeError('MILC should be the only system installing root log handlers!')
|
logging.root.handlers = []
|
||||||
|
|
||||||
self.acquire_lock()
|
self.acquire_lock()
|
||||||
|
|
||||||
@@ -649,8 +675,9 @@ class MILC(object):
|
|||||||
self.read_config()
|
self.read_config()
|
||||||
self.setup_logging()
|
self.setup_logging()
|
||||||
|
|
||||||
if self.config.general.save_config:
|
if 'save_config' in self.config.general and self.config.general.save_config:
|
||||||
self.save_config()
|
self.save_config()
|
||||||
|
exit(0)
|
||||||
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
@@ -713,4 +740,3 @@ if __name__ == '__main__':
|
|||||||
cli.goodbye.add_argument('-n', '--name', help='Name to bid farewell to', default='World')
|
cli.goodbye.add_argument('-n', '--name', help='Name to bid farewell to', default='World')
|
||||||
|
|
||||||
cli() # Automatically picks between main(), hello() and goodbye()
|
cli() # Automatically picks between main(), hello() and goodbye()
|
||||||
print(sorted(ansi_colors.keys()))
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
[build-system]
|
|
||||||
requires = ["flit"]
|
|
||||||
build-backend = "flit.buildapi"
|
|
||||||
|
|
||||||
[tool.flit.metadata]
|
|
||||||
dist-name = "qmk"
|
|
||||||
module = "qmk_cli"
|
|
||||||
author = "skullydazed"
|
|
||||||
author-email = "skullydazed@gmail.com"
|
|
||||||
description-file = "README.md"
|
|
||||||
home-page = "https://github.com/qmk/qmk_cli"
|
|
||||||
classifiers = [
|
|
||||||
'Development Status :: 3 - Alpha',
|
|
||||||
'Environment :: Console',
|
|
||||||
'Intended Audience :: Developers',
|
|
||||||
'Intended Audience :: System Administrators',
|
|
||||||
'Intended Audience :: End Users/Desktop',
|
|
||||||
'License :: OSI Approved :: MIT License',
|
|
||||||
'Natural Language :: English',
|
|
||||||
'Programming Language :: Python :: 3 :: Only',
|
|
||||||
'Topic :: Scientific/Engineering',
|
|
||||||
'Topic :: Software Development',
|
|
||||||
'Topic :: Utilities',
|
|
||||||
]
|
|
||||||
requires-python = ">=3.5"
|
|
||||||
requires = [
|
|
||||||
#"milc", #FIXME(skullydazed): Included in the repo for now.
|
|
||||||
"argcomplete",
|
|
||||||
"colorama",
|
|
||||||
#"halo"
|
|
||||||
]
|
|
||||||
|
|
||||||
[tool.flit.scripts]
|
|
||||||
qmk = "qmk_cli.script_qmk:main"
|
|
||||||
+1
-1
@@ -1,3 +1,3 @@
|
|||||||
"""A program to help you work with qmk_firmware."""
|
"""A program to help you work with qmk_firmware."""
|
||||||
|
|
||||||
__version__ = '0.0.3'
|
__version__ = '0.0.11'
|
||||||
|
|||||||
@@ -1,96 +0,0 @@
|
|||||||
"""QMK Python Doctor
|
|
||||||
|
|
||||||
Check up for QMK environment.
|
|
||||||
"""
|
|
||||||
import shutil
|
|
||||||
import platform
|
|
||||||
import os
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
from qmk_cli.milc import cli
|
|
||||||
|
|
||||||
|
|
||||||
def check_qmk_firmware():
|
|
||||||
"""Make sure qmk_firmware and the qmk cli are there.
|
|
||||||
"""
|
|
||||||
qmk_firmware = Path(os.environ['QMK_HOME'])
|
|
||||||
qmk_cli = qmk_firmware / 'bin' / 'qmk'
|
|
||||||
|
|
||||||
if qmk_firmware.exists() and not qmk_cli.exists():
|
|
||||||
cli.log.error("{fg_red}Can't find %s/bin/qmk! You need to `git pull`.", os.environ['QMK_HOME'])
|
|
||||||
return False
|
|
||||||
|
|
||||||
elif not qmk_firmware.exists():
|
|
||||||
cli.log.error("{fg_red}Can't find the qmk_firmware checkout! %s does not exist!", os.environ['QMK_HOME'])
|
|
||||||
return False
|
|
||||||
|
|
||||||
cli.log.info('Found qmk_firmware checkout in {fg_cyan}%s', str(qmk_firmware))
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def check_vital_programs():
|
|
||||||
"""Make sure the software we need has been installed.
|
|
||||||
|
|
||||||
TODO(unclaimed):
|
|
||||||
* [ ] Run the binaries to make sure they work
|
|
||||||
* [ ] Compile a trivial program with each compiler
|
|
||||||
"""
|
|
||||||
ok = True
|
|
||||||
binaries = ['dfu-programmer', 'avrdude', 'dfu-util', 'avr-gcc', 'arm-none-eabi-gcc']
|
|
||||||
|
|
||||||
for binary in binaries:
|
|
||||||
res = shutil.which(binary)
|
|
||||||
if res is None:
|
|
||||||
cli.log.error("{fg_red}QMK can't find %s in your path", binary)
|
|
||||||
ok = False
|
|
||||||
|
|
||||||
if ok:
|
|
||||||
cli.log.info("All necessary software is installed.")
|
|
||||||
|
|
||||||
return ok
|
|
||||||
|
|
||||||
|
|
||||||
def check_platform_tests():
|
|
||||||
"""Dispatch to platform specific tests.
|
|
||||||
"""
|
|
||||||
OS = platform.system()
|
|
||||||
|
|
||||||
if OS == "Darwin":
|
|
||||||
cli.log.info("Detected {fg_cyan}macOS")
|
|
||||||
return check_mac_os()
|
|
||||||
|
|
||||||
elif OS == "Linux":
|
|
||||||
cli.log.info("Detected {fg_cyan}linux")
|
|
||||||
return check_linux()
|
|
||||||
|
|
||||||
else:
|
|
||||||
cli.log.info("Assuming {fg_cyan}Windows")
|
|
||||||
return check_windows()
|
|
||||||
|
|
||||||
|
|
||||||
def check_mac_os():
|
|
||||||
"""Run macOS specific tests.
|
|
||||||
|
|
||||||
There aren't any yet.
|
|
||||||
"""
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def check_linux():
|
|
||||||
"""Run Linux specific tests.
|
|
||||||
|
|
||||||
TODO(unclaimed):
|
|
||||||
* [ ] Check for udev entries on linux
|
|
||||||
"""
|
|
||||||
test = 'systemctl list-unit-files | grep enabled | grep -i ModemManager'
|
|
||||||
if os.system(test) == 0:
|
|
||||||
cli.log.warn("{bg_yellow}Detected modem manager. Please disable it if you are using Pro Micros")
|
|
||||||
|
|
||||||
|
|
||||||
def check_windows():
|
|
||||||
"""Run Windows specific tests.
|
|
||||||
|
|
||||||
TODO(unclaimed):
|
|
||||||
* [ ] Check out the driver situation
|
|
||||||
"""
|
|
||||||
return True
|
|
||||||
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
"""
|
"""
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
from qmk_cli.milc import cli
|
from milc import cli
|
||||||
|
|
||||||
default_repo = 'qmk_firmware'
|
default_repo = 'qmk_firmware'
|
||||||
default_fork = 'qmk/' + default_repo
|
default_fork = 'qmk/' + default_repo
|
||||||
|
|||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
"""Useful helper functions.
|
"""Useful helper functions.
|
||||||
"""
|
"""
|
||||||
from qmk_cli.milc import cli
|
from milc import cli
|
||||||
|
|
||||||
|
|
||||||
def question(question, boolean=True, default=''):
|
def question(question, boolean=True, default=''):
|
||||||
@@ -8,7 +8,7 @@ def question(question, boolean=True, default=''):
|
|||||||
|
|
||||||
This keeps re-asking until it gets acceptible input.
|
This keeps re-asking until it gets acceptible input.
|
||||||
"""
|
"""
|
||||||
if cli.args.general_yes:
|
if cli.args.yes:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
if default and default.lower() == 'y':
|
if default and default.lower() == 'y':
|
||||||
|
|||||||
+23
-42
@@ -2,15 +2,28 @@
|
|||||||
"""CLI wrapper for running QMK commands.
|
"""CLI wrapper for running QMK commands.
|
||||||
|
|
||||||
This program can be run from anywhere, with or without a qmk_firmware checkout. It provides a small set of subcommands for working with QMK and otherwise dispatches to the `qmk_firmware/bin/qmk` script for the repo you are currently in, or your default repo if you are not currently in a qmk_firmware checkout.
|
This program can be run from anywhere, with or without a qmk_firmware checkout. It provides a small set of subcommands for working with QMK and otherwise dispatches to the `qmk_firmware/bin/qmk` script for the repo you are currently in, or your default repo if you are not currently in a qmk_firmware checkout.
|
||||||
|
|
||||||
|
FIXME(skullydazed/anyone): --help shows underscores where we want dashes in subcommands (EG json_keymap instead of json-keymap)
|
||||||
|
TODO(skullydazed/anyone): Need a way to filter some subcommands from --help (EG `qmk subcommands`)
|
||||||
"""
|
"""
|
||||||
import argparse
|
import argparse
|
||||||
import os
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
from functools import lru_cache
|
from functools import lru_cache
|
||||||
from importlib import import_module
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
import milc
|
||||||
|
|
||||||
|
milc.EMOJI_LOGLEVELS['INFO'] = '{fg_blue}Ψ{style_reset_all}'
|
||||||
|
|
||||||
|
|
||||||
|
@milc.cli.entrypoint('CLI wrapper for running QMK commands.')
|
||||||
|
def qmk_main(cli):
|
||||||
|
"""The function that gets run when there's no subcommand.
|
||||||
|
"""
|
||||||
|
cli.print_help()
|
||||||
|
|
||||||
|
|
||||||
@lru_cache(maxsize=2)
|
@lru_cache(maxsize=2)
|
||||||
def in_qmk_firmware():
|
def in_qmk_firmware():
|
||||||
@@ -21,7 +34,7 @@ def in_qmk_firmware():
|
|||||||
found_bin = cur_dir / 'bin' / 'qmk'
|
found_bin = cur_dir / 'bin' / 'qmk'
|
||||||
if found_bin.is_file():
|
if found_bin.is_file():
|
||||||
command = [found_bin, '--version']
|
command = [found_bin, '--version']
|
||||||
result = subprocess.run(command)
|
result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||||
|
|
||||||
if result.returncode == 0:
|
if result.returncode == 0:
|
||||||
return cur_dir
|
return cur_dir
|
||||||
@@ -47,21 +60,6 @@ def find_qmk_firmware():
|
|||||||
return Path.home() / 'qmk_firmware'
|
return Path.home() / 'qmk_firmware'
|
||||||
|
|
||||||
|
|
||||||
def parse_args():
|
|
||||||
"""Process arguments outside milc.
|
|
||||||
"""
|
|
||||||
parser = argparse.ArgumentParser(description='CLI wrapper for running QMK commands.')
|
|
||||||
parser.add_argument('-H', '--home', help='Path to the qmk_firmware directory.')
|
|
||||||
parser.add_argument('subcommand', help='Subcommand to run')
|
|
||||||
parser.add_argument('subcommand_args', nargs=argparse.REMAINDER, help='Arguments to pass to the subcommand.')
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
if args.home:
|
|
||||||
os.environ['QMK_HOME'] = args.home
|
|
||||||
|
|
||||||
return (args.subcommand, args.subcommand_args)
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
"""Dispatch the CLI subcommand to the proper place.
|
"""Dispatch the CLI subcommand to the proper place.
|
||||||
|
|
||||||
@@ -69,33 +67,16 @@ def main():
|
|||||||
|
|
||||||
All other subcommands are dispatched to the local `qmk`, either the one we are currently in or whatever the user's default qmk_firmware is.
|
All other subcommands are dispatched to the local `qmk`, either the one we are currently in or whatever the user's default qmk_firmware is.
|
||||||
"""
|
"""
|
||||||
subcommand, subcommand_args = parse_args()
|
# Environment setup
|
||||||
subcommand_module = 'qmk_cli.subcommands.' + subcommand
|
|
||||||
sys.argv = ['qmk-'+subcommand] + subcommand_args
|
|
||||||
qmk_firmware = find_qmk_firmware()
|
qmk_firmware = find_qmk_firmware()
|
||||||
qmk_bin = qmk_firmware / 'bin' / 'qmk'
|
|
||||||
os.environ['QMK_HOME'] = str(qmk_firmware)
|
os.environ['QMK_HOME'] = str(qmk_firmware)
|
||||||
|
|
||||||
try:
|
# Import the subcommand modules
|
||||||
# Attempt to import the subcommand from qmk_cli first
|
import qmk_cli.subcommands
|
||||||
import qmk_cli.milc
|
|
||||||
import_module(subcommand_module)
|
|
||||||
qmk_cli.milc.cli()
|
|
||||||
|
|
||||||
except ImportError as e:
|
if qmk_firmware.exists():
|
||||||
# Check to make sure there's not a bad import statement in qmk_cli
|
sys.path.append(str(qmk_firmware / 'lib' / 'python'))
|
||||||
if e.name != subcommand_module:
|
import qmk.cli
|
||||||
raise
|
|
||||||
|
|
||||||
# Dispatch to the underlying `qmk_firmware/bin/qmk`
|
# Call the entrypoint
|
||||||
if qmk_bin.is_file() and os.access(str(qmk_bin), os.X_OK):
|
milc.cli()
|
||||||
argv = ['python3', str(qmk_bin), subcommand] + subcommand_args
|
|
||||||
os.execvp('python3', argv)
|
|
||||||
|
|
||||||
# Tell the user we can't continue
|
|
||||||
print('*** Could not locate qmk_firmware directory!')
|
|
||||||
exit(255)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
"""QMK CLI Subcommands
|
||||||
|
|
||||||
|
We list each subcommand here explicitly because all the reliable ways of searching for modules are slow and delay startup.
|
||||||
|
"""
|
||||||
|
from . import clone
|
||||||
|
from . import setup
|
||||||
|
|||||||
@@ -3,8 +3,8 @@
|
|||||||
import os
|
import os
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from qmk_cli.milc import cli
|
from milc import cli
|
||||||
from qmk.cli.git import clone
|
from qmk_cli.git import clone
|
||||||
|
|
||||||
default_repo = 'qmk_firmware'
|
default_repo = 'qmk_firmware'
|
||||||
default_fork = 'qmk/' + default_repo
|
default_fork = 'qmk/' + default_repo
|
||||||
@@ -15,14 +15,14 @@ default_branch = 'master'
|
|||||||
@cli.argument('-b', '--branch', default=default_branch, help='The branch to clone.')
|
@cli.argument('-b', '--branch', default=default_branch, help='The branch to clone.')
|
||||||
@cli.argument('destination', default=os.environ['QMK_HOME'], nargs='?', help='The directory to clone to.')
|
@cli.argument('destination', default=os.environ['QMK_HOME'], nargs='?', help='The directory to clone to.')
|
||||||
@cli.argument('fork', default=default_fork, nargs='?', help='The qmk_firmware fork to clone')
|
@cli.argument('fork', default=default_fork, nargs='?', help='The qmk_firmware fork to clone')
|
||||||
@cli.entrypoint('Clone a qmk_firmware fork.')
|
@cli.subcommand('Clone a qmk_firmware fork.')
|
||||||
def main(cli):
|
def clone(cli):
|
||||||
qmk_firmware = Path(cli.args.destination)
|
qmk_firmware = Path(cli.args.destination)
|
||||||
git_url = '/'.join((cli.config.general.baseurl, cli.args.fork))
|
git_url = '/'.join((cli.config.clone.baseurl, cli.args.fork))
|
||||||
|
|
||||||
if qmk_firmware.exists():
|
if qmk_firmware.exists():
|
||||||
cli.log.error('Destination already exists: %s', cli.args.destination)
|
cli.log.error('Destination already exists: %s', cli.args.destination)
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
success = clone(git_url, cli.args.destination, cli.config.general.branch)
|
success = clone(git_url, cli.args.destination, cli.config.clone.branch)
|
||||||
exit(0 if success else 1)
|
exit(0 if success else 1)
|
||||||
|
|||||||
@@ -1,119 +0,0 @@
|
|||||||
"""QMK Python Doctor
|
|
||||||
|
|
||||||
Check up for QMK environment.
|
|
||||||
"""
|
|
||||||
import shutil
|
|
||||||
import platform
|
|
||||||
import os
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
from qmk_cli.milc import cli
|
|
||||||
|
|
||||||
|
|
||||||
def check_qmk_firmware():
|
|
||||||
"""Make sure qmk_firmware and the qmk cli are there.
|
|
||||||
"""
|
|
||||||
qmk_firmware = Path(os.environ['QMK_HOME'])
|
|
||||||
qmk_cli = qmk_firmware / 'bin' / 'qmk'
|
|
||||||
|
|
||||||
if qmk_firmware.exists() and not qmk_cli.exists():
|
|
||||||
cli.log.error("{fg_red}Can't find %s/bin/qmk! You need to `git pull`.", os.environ['QMK_HOME'])
|
|
||||||
return False
|
|
||||||
|
|
||||||
elif not qmk_firmware.exists():
|
|
||||||
cli.log.error("{fg_red}Can't find the qmk_firmware checkout! %s does not exist!", os.environ['QMK_HOME'])
|
|
||||||
return False
|
|
||||||
|
|
||||||
cli.log.info('Found qmk_firmware checkout in {fg_cyan}%s', str(qmk_firmware))
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def check_vital_programs():
|
|
||||||
"""Make sure the software we need has been installed.
|
|
||||||
|
|
||||||
TODO(unclaimed):
|
|
||||||
* [ ] Run the binaries to make sure they work
|
|
||||||
* [ ] Compile a trivial program with each compiler
|
|
||||||
"""
|
|
||||||
ok = True
|
|
||||||
binaries = ['dfu-programmer', 'avrdude', 'dfu-util', 'avr-gcc', 'arm-none-eabi-gcc']
|
|
||||||
|
|
||||||
for binary in binaries:
|
|
||||||
res = shutil.which(binary)
|
|
||||||
if res is None:
|
|
||||||
cli.log.error("{fg_red}QMK can't find %s in your path", binary)
|
|
||||||
ok = False
|
|
||||||
|
|
||||||
if ok:
|
|
||||||
cli.log.info("All necessary software is installed.")
|
|
||||||
|
|
||||||
return ok
|
|
||||||
|
|
||||||
|
|
||||||
def check_platform_tests():
|
|
||||||
"""Dispatch to platform specific tests.
|
|
||||||
"""
|
|
||||||
OS = platform.system()
|
|
||||||
|
|
||||||
if OS == "Darwin":
|
|
||||||
cli.log.info("Detected {fg_cyan}macOS")
|
|
||||||
return check_mac_os()
|
|
||||||
|
|
||||||
elif OS == "Linux":
|
|
||||||
cli.log.info("Detected {fg_cyan}linux")
|
|
||||||
return check_linux()
|
|
||||||
|
|
||||||
else:
|
|
||||||
cli.log.info("Assuming {fg_cyan}Windows")
|
|
||||||
return check_windows()
|
|
||||||
|
|
||||||
|
|
||||||
def check_mac_os():
|
|
||||||
"""Run macOS specific tests.
|
|
||||||
|
|
||||||
There aren't any yet.
|
|
||||||
"""
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def check_linux():
|
|
||||||
"""Run Linux specific tests.
|
|
||||||
|
|
||||||
TODO(unclaimed):
|
|
||||||
* [ ] Check for udev entries on linux
|
|
||||||
"""
|
|
||||||
test = 'systemctl list-unit-files | grep enabled | grep -i ModemManager'
|
|
||||||
if os.system(test) == 0:
|
|
||||||
cli.log.warn("{bg_yellow}Detected modem manager. Please disable it if you are using Pro Micros")
|
|
||||||
|
|
||||||
|
|
||||||
def check_windows():
|
|
||||||
"""Run Windows specific tests.
|
|
||||||
|
|
||||||
TODO(unclaimed):
|
|
||||||
* [ ] Check out the driver situation
|
|
||||||
"""
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
@cli.entrypoint('Basic QMK environment checks')
|
|
||||||
def main(cli):
|
|
||||||
"""Basic QMK environment checks.
|
|
||||||
|
|
||||||
This is currently very simple, it just checks that all the expected binaries are on your system.
|
|
||||||
"""
|
|
||||||
cli.log.info('QMK Doctor is checking your environment')
|
|
||||||
|
|
||||||
funcs = (
|
|
||||||
check_qmk_firmware,
|
|
||||||
check_vital_programs,
|
|
||||||
check_platform_tests,
|
|
||||||
)
|
|
||||||
|
|
||||||
ok = True
|
|
||||||
for func in funcs:
|
|
||||||
if not func():
|
|
||||||
ok = False
|
|
||||||
|
|
||||||
if ok:
|
|
||||||
cli.log.info('{fg_green}QMK is ready to go')
|
|
||||||
@@ -2,12 +2,12 @@
|
|||||||
"""
|
"""
|
||||||
import os
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from qmk_cli.doctor import check_vital_programs
|
from milc import cli
|
||||||
from qmk_cli.git import clone
|
from qmk_cli.git import clone
|
||||||
from qmk_cli.helpers import question
|
from qmk_cli.helpers import question
|
||||||
from qmk_cli.milc import cli
|
|
||||||
|
|
||||||
default_repo = 'qmk_firmware'
|
default_repo = 'qmk_firmware'
|
||||||
default_fork = 'qmk/' + default_repo
|
default_fork = 'qmk/' + default_repo
|
||||||
@@ -19,9 +19,8 @@ default_branch = 'master'
|
|||||||
@cli.argument('-b', '--branch', default=default_branch, help='The branch to clone')
|
@cli.argument('-b', '--branch', default=default_branch, help='The branch to clone')
|
||||||
@cli.argument('destination', default=os.environ['QMK_HOME'], nargs='?', help='The directory to clone to')
|
@cli.argument('destination', default=os.environ['QMK_HOME'], nargs='?', help='The directory to clone to')
|
||||||
@cli.argument('fork', default=default_fork, nargs='?', help='The qmk_firmware fork to clone')
|
@cli.argument('fork', default=default_fork, nargs='?', help='The qmk_firmware fork to clone')
|
||||||
@cli.entrypoint('Setup your computer for qmk_firmware.')
|
@cli.subcommand('Setup your computer for qmk_firmware.')
|
||||||
def main(cli):
|
def setup(cli):
|
||||||
setup_successful = False
|
|
||||||
qmk_firmware = Path(cli.args.destination)
|
qmk_firmware = Path(cli.args.destination)
|
||||||
|
|
||||||
# Check on qmk_firmware, and if it doesn't exist offer to check it out.
|
# Check on qmk_firmware, and if it doesn't exist offer to check it out.
|
||||||
@@ -33,20 +32,17 @@ def main(cli):
|
|||||||
git_url = '/'.join((cli.config.general.baseurl, cli.args.fork))
|
git_url = '/'.join((cli.config.general.baseurl, cli.args.fork))
|
||||||
clone(git_url, cli.args.destination, cli.config.general.branch)
|
clone(git_url, cli.args.destination, cli.config.general.branch)
|
||||||
|
|
||||||
# Check if the build environment is setup, and if not offer to set it up
|
# Run `qmk_firmware/bin/qmk doctor` to check the rest of the environment out
|
||||||
if check_vital_programs():
|
if qmk_firmware.exists():
|
||||||
cli.log.info('Your build environment is ready!')
|
qmk_bin = qmk_firmware / 'bin' / 'qmk'
|
||||||
else:
|
doctor = subprocess.run([sys.executable, qmk_bin, 'doctor'])
|
||||||
cli.log.error('Your build environment is not setup completely.')
|
if doctor.returncode != 0:
|
||||||
if qmk_firmware.exists() and question('Would you like to run util/qmk_install?'):
|
cli.log.error('Your build environment is not setup completely.')
|
||||||
curdir = os.getcwd()
|
|
||||||
os.chdir(str(qmk_firmware))
|
|
||||||
process = subprocess.run(['util/qmk_install.sh'])
|
|
||||||
os.chdir(curdir)
|
|
||||||
if process.returncode == 0:
|
|
||||||
setup_successful = True
|
|
||||||
|
|
||||||
|
if question('Would you like to run util/qmk_install?'):
|
||||||
# fin
|
curdir = os.getcwd()
|
||||||
if setup_successful:
|
os.chdir(str(qmk_firmware))
|
||||||
cli.log.info('QMK setup complete!')
|
process = subprocess.run(['util/qmk_install.sh'])
|
||||||
|
os.chdir(curdir)
|
||||||
|
if process.returncode == 0:
|
||||||
|
cli.log.info('QMK setup complete!')
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# Increment the verison number and release a new version of qmk_cli.
|
||||||
|
set -e
|
||||||
|
set -x
|
||||||
|
|
||||||
|
PYPI_USERNAME=${PYPI_USERNAME:=skully}
|
||||||
|
FLIT_USERNAME=$PYPI_USERNAME
|
||||||
|
TWINE_USERNAME=$PYPI_USERNAME
|
||||||
|
|
||||||
|
export FLIT_USERNAME TWINE_USERNAME
|
||||||
|
|
||||||
|
rm dist/*
|
||||||
|
bumpversion patch
|
||||||
|
git push origin master --tags
|
||||||
|
|
||||||
|
# For now we don't use flit
|
||||||
|
#flit publish
|
||||||
|
|
||||||
|
# Use setuptools until flit works with homebrew
|
||||||
|
python3 setup.py sdist bdist_wheel
|
||||||
|
twine upload dist/qmk-*
|
||||||
@@ -1,334 +1,83 @@
|
|||||||
|
[bumpversion]
|
||||||
|
current_version = 0.0.11
|
||||||
|
commit = True
|
||||||
|
tag = True
|
||||||
|
tag_name = {new_version}
|
||||||
|
message = New release: {current_version} → {new_version}
|
||||||
|
|
||||||
|
[bumpversion:file:qmk_cli/__init__.py]
|
||||||
|
|
||||||
|
[bumpversion:file:setup.cfg]
|
||||||
|
|
||||||
[bdist_wheel]
|
[bdist_wheel]
|
||||||
universal = 1
|
universal = 1
|
||||||
|
|
||||||
|
[flake8]
|
||||||
|
ignore = E501,E226
|
||||||
|
|
||||||
[metadata]
|
[metadata]
|
||||||
|
author = skullydazed
|
||||||
|
author-email = skullydazed@gmail.com
|
||||||
|
description-file = README.md
|
||||||
|
dist-name = qmk
|
||||||
license_file = LICENSE
|
license_file = LICENSE
|
||||||
|
module = qmk_cli
|
||||||
|
home-page = https://github.com/qmk/qmk_cli
|
||||||
|
requires-python = >=3.5
|
||||||
|
|
||||||
|
[entry_points]
|
||||||
|
qmk = qmk_cli.script_qmk:main
|
||||||
|
|
||||||
[yapf]
|
[yapf]
|
||||||
# Align closing bracket with visual indentation.
|
align_closing_bracket_with_visual_indent = True
|
||||||
align_closing_bracket_with_visual_indent=True
|
allow_multiline_dictionary_keys = False
|
||||||
|
allow_multiline_lambdas = False
|
||||||
# Allow dictionary keys to exist on multiple lines. For example:
|
allow_split_before_default_or_named_assigns = True
|
||||||
#
|
allow_split_before_dict_value = True
|
||||||
# x = {
|
arithmetic_precedence_indication = True
|
||||||
# ('this is the first element of a tuple',
|
blank_lines_around_top_level_definition = 2
|
||||||
# 'this is the second element of a tuple'):
|
blank_line_before_class_docstring = False
|
||||||
# value,
|
blank_line_before_module_docstring = False
|
||||||
# }
|
blank_line_before_nested_class_or_def = False
|
||||||
allow_multiline_dictionary_keys=False
|
coalesce_brackets = True
|
||||||
|
column_limit = 256
|
||||||
# Allow lambdas to be formatted on more than one line.
|
continuation_align_style = SPACE
|
||||||
allow_multiline_lambdas=False
|
continuation_indent_width = 4
|
||||||
|
dedent_closing_brackets = True
|
||||||
# Allow splitting before a default / named assignment in an argument list.
|
disable_ending_comma_heuristic = False
|
||||||
allow_split_before_default_or_named_assigns=True
|
each_dict_entry_on_separate_line = True
|
||||||
|
i18n_comment =
|
||||||
# Allow splits before the dictionary value.
|
i18n_function_call =
|
||||||
allow_split_before_dict_value=True
|
indent_blank_lines = False
|
||||||
|
indent_dictionary_value = True
|
||||||
# Let spacing indicate operator precedence. For example:
|
indent_width = 4
|
||||||
#
|
join_multiple_lines = False
|
||||||
# a = 1 * 2 + 3 / 4
|
no_spaces_around_selected_binary_operators =
|
||||||
# b = 1 / 2 - 3 * 4
|
spaces_around_default_or_named_assign = False
|
||||||
# c = (1 + 2) * (3 - 4)
|
spaces_around_power_operator = False
|
||||||
# d = (1 - 2) / (3 + 4)
|
spaces_before_comment = 2
|
||||||
# e = 1 * 2 - 3
|
space_between_ending_comma_and_closing_bracket = False
|
||||||
# f = 1 + 2 + 3 + 4
|
split_all_comma_separated_values = False
|
||||||
#
|
split_arguments_when_comma_terminated = True
|
||||||
# will be formatted as follows to indicate precedence:
|
split_before_arithmetic_operator = False
|
||||||
#
|
split_before_bitwise_operator = True
|
||||||
# a = 1*2 + 3/4
|
split_before_closing_bracket = True
|
||||||
# b = 1/2 - 3*4
|
split_before_dict_set_generator = True
|
||||||
# c = (1+2) * (3-4)
|
split_before_dot = False
|
||||||
# d = (1-2) / (3+4)
|
split_before_expression_after_opening_paren = False
|
||||||
# e = 1*2 - 3
|
split_before_first_argument = False
|
||||||
# f = 1 + 2 + 3 + 4
|
split_before_logical_operator = False
|
||||||
#
|
split_before_named_assigns = True
|
||||||
arithmetic_precedence_indication=True
|
split_complex_comprehension = True
|
||||||
|
split_penalty_after_opening_bracket = 300
|
||||||
# Number of blank lines surrounding top-level function and class
|
split_penalty_after_unary_operator = 10000
|
||||||
# definitions.
|
split_penalty_arithmetic_operator = 300
|
||||||
blank_lines_around_top_level_definition=2
|
split_penalty_before_if_expr = 0
|
||||||
|
split_penalty_bitwise_operator = 300
|
||||||
# Insert a blank line before a class-level docstring.
|
split_penalty_comprehension = 80
|
||||||
blank_line_before_class_docstring=False
|
split_penalty_excess_character = 7000
|
||||||
|
split_penalty_for_added_line_split = 30
|
||||||
# Insert a blank line before a module docstring.
|
split_penalty_import_names = 0
|
||||||
blank_line_before_module_docstring=False
|
split_penalty_logical_operator = 300
|
||||||
|
use_tabs = False
|
||||||
# Insert a blank line before a 'def' or 'class' immediately nested
|
|
||||||
# within another 'def' or 'class'. For example:
|
|
||||||
#
|
|
||||||
# class Foo:
|
|
||||||
# # <------ this blank line
|
|
||||||
# def method():
|
|
||||||
# ...
|
|
||||||
blank_line_before_nested_class_or_def=False
|
|
||||||
|
|
||||||
# Do not split consecutive brackets. Only relevant when
|
|
||||||
# dedent_closing_brackets is set. For example:
|
|
||||||
#
|
|
||||||
# call_func_that_takes_a_dict(
|
|
||||||
# {
|
|
||||||
# 'key1': 'value1',
|
|
||||||
# 'key2': 'value2',
|
|
||||||
# }
|
|
||||||
# )
|
|
||||||
#
|
|
||||||
# would reformat to:
|
|
||||||
#
|
|
||||||
# call_func_that_takes_a_dict({
|
|
||||||
# 'key1': 'value1',
|
|
||||||
# 'key2': 'value2',
|
|
||||||
# })
|
|
||||||
coalesce_brackets=True
|
|
||||||
|
|
||||||
# The column limit.
|
|
||||||
column_limit=256
|
|
||||||
|
|
||||||
# The style for continuation alignment. Possible values are:
|
|
||||||
#
|
|
||||||
# - SPACE: Use spaces for continuation alignment. This is default behavior.
|
|
||||||
# - FIXED: Use fixed number (CONTINUATION_INDENT_WIDTH) of columns
|
|
||||||
# (ie: CONTINUATION_INDENT_WIDTH/INDENT_WIDTH tabs) for continuation
|
|
||||||
# alignment.
|
|
||||||
# - VALIGN-RIGHT: Vertically align continuation lines with indent
|
|
||||||
# characters. Slightly right (one more indent character) if cannot
|
|
||||||
# vertically align continuation lines with indent characters.
|
|
||||||
#
|
|
||||||
# For options FIXED, and VALIGN-RIGHT are only available when USE_TABS is
|
|
||||||
# enabled.
|
|
||||||
continuation_align_style=SPACE
|
|
||||||
|
|
||||||
# Indent width used for line continuations.
|
|
||||||
continuation_indent_width=4
|
|
||||||
|
|
||||||
# Put closing brackets on a separate line, dedented, if the bracketed
|
|
||||||
# expression can't fit in a single line. Applies to all kinds of brackets,
|
|
||||||
# including function definitions and calls. For example:
|
|
||||||
#
|
|
||||||
# config = {
|
|
||||||
# 'key1': 'value1',
|
|
||||||
# 'key2': 'value2',
|
|
||||||
# } # <--- this bracket is dedented and on a separate line
|
|
||||||
#
|
|
||||||
# time_series = self.remote_client.query_entity_counters(
|
|
||||||
# entity='dev3246.region1',
|
|
||||||
# key='dns.query_latency_tcp',
|
|
||||||
# transform=Transformation.AVERAGE(window=timedelta(seconds=60)),
|
|
||||||
# start_ts=now()-timedelta(days=3),
|
|
||||||
# end_ts=now(),
|
|
||||||
# ) # <--- this bracket is dedented and on a separate line
|
|
||||||
dedent_closing_brackets=True
|
|
||||||
|
|
||||||
# Disable the heuristic which places each list element on a separate line
|
|
||||||
# if the list is comma-terminated.
|
|
||||||
disable_ending_comma_heuristic=False
|
|
||||||
|
|
||||||
# Place each dictionary entry onto its own line.
|
|
||||||
each_dict_entry_on_separate_line=True
|
|
||||||
|
|
||||||
# The regex for an i18n comment. The presence of this comment stops
|
|
||||||
# reformatting of that line, because the comments are required to be
|
|
||||||
# next to the string they translate.
|
|
||||||
i18n_comment=
|
|
||||||
|
|
||||||
# The i18n function call names. The presence of this function stops
|
|
||||||
# reformattting on that line, because the string it has cannot be moved
|
|
||||||
# away from the i18n comment.
|
|
||||||
i18n_function_call=
|
|
||||||
|
|
||||||
# Indent blank lines.
|
|
||||||
indent_blank_lines=False
|
|
||||||
|
|
||||||
# Indent the dictionary value if it cannot fit on the same line as the
|
|
||||||
# dictionary key. For example:
|
|
||||||
#
|
|
||||||
# config = {
|
|
||||||
# 'key1':
|
|
||||||
# 'value1',
|
|
||||||
# 'key2': value1 +
|
|
||||||
# value2,
|
|
||||||
# }
|
|
||||||
indent_dictionary_value=True
|
|
||||||
|
|
||||||
# The number of columns to use for indentation.
|
|
||||||
indent_width=4
|
|
||||||
|
|
||||||
# Join short lines into one line. E.g., single line 'if' statements.
|
|
||||||
join_multiple_lines=False
|
|
||||||
|
|
||||||
# Do not include spaces around selected binary operators. For example:
|
|
||||||
#
|
|
||||||
# 1 + 2 * 3 - 4 / 5
|
|
||||||
#
|
|
||||||
# will be formatted as follows when configured with "*,/":
|
|
||||||
#
|
|
||||||
# 1 + 2*3 - 4/5
|
|
||||||
no_spaces_around_selected_binary_operators=
|
|
||||||
|
|
||||||
# Use spaces around default or named assigns.
|
|
||||||
spaces_around_default_or_named_assign=False
|
|
||||||
|
|
||||||
# Use spaces around the power operator.
|
|
||||||
spaces_around_power_operator=False
|
|
||||||
|
|
||||||
# The number of spaces required before a trailing comment.
|
|
||||||
# This can be a single value (representing the number of spaces
|
|
||||||
# before each trailing comment) or list of values (representing
|
|
||||||
# alignment column values; trailing comments within a block will
|
|
||||||
# be aligned to the first column value that is greater than the maximum
|
|
||||||
# line length within the block). For example:
|
|
||||||
#
|
|
||||||
# With spaces_before_comment=5:
|
|
||||||
#
|
|
||||||
# 1 + 1 # Adding values
|
|
||||||
#
|
|
||||||
# will be formatted as:
|
|
||||||
#
|
|
||||||
# 1 + 1 # Adding values <-- 5 spaces between the end of the statement and comment
|
|
||||||
#
|
|
||||||
# With spaces_before_comment=15, 20:
|
|
||||||
#
|
|
||||||
# 1 + 1 # Adding values
|
|
||||||
# two + two # More adding
|
|
||||||
#
|
|
||||||
# longer_statement # This is a longer statement
|
|
||||||
# short # This is a shorter statement
|
|
||||||
#
|
|
||||||
# a_very_long_statement_that_extends_beyond_the_final_column # Comment
|
|
||||||
# short # This is a shorter statement
|
|
||||||
#
|
|
||||||
# will be formatted as:
|
|
||||||
#
|
|
||||||
# 1 + 1 # Adding values <-- end of line comments in block aligned to col 15
|
|
||||||
# two + two # More adding
|
|
||||||
#
|
|
||||||
# longer_statement # This is a longer statement <-- end of line comments in block aligned to col 20
|
|
||||||
# short # This is a shorter statement
|
|
||||||
#
|
|
||||||
# a_very_long_statement_that_extends_beyond_the_final_column # Comment <-- the end of line comments are aligned based on the line length
|
|
||||||
# short # This is a shorter statement
|
|
||||||
#
|
|
||||||
spaces_before_comment=2
|
|
||||||
|
|
||||||
# Insert a space between the ending comma and closing bracket of a list,
|
|
||||||
# etc.
|
|
||||||
space_between_ending_comma_and_closing_bracket=False
|
|
||||||
|
|
||||||
# Split before arguments
|
|
||||||
split_all_comma_separated_values=False
|
|
||||||
|
|
||||||
# Split before arguments if the argument list is terminated by a
|
|
||||||
# comma.
|
|
||||||
split_arguments_when_comma_terminated=True
|
|
||||||
|
|
||||||
# Set to True to prefer splitting before '+', '-', '*', '/', '//', or '@'
|
|
||||||
# rather than after.
|
|
||||||
split_before_arithmetic_operator=False
|
|
||||||
|
|
||||||
# Set to True to prefer splitting before '&', '|' or '^' rather than
|
|
||||||
# after.
|
|
||||||
split_before_bitwise_operator=True
|
|
||||||
|
|
||||||
# Split before the closing bracket if a list or dict literal doesn't fit on
|
|
||||||
# a single line.
|
|
||||||
split_before_closing_bracket=True
|
|
||||||
|
|
||||||
# Split before a dictionary or set generator (comp_for). For example, note
|
|
||||||
# the split before the 'for':
|
|
||||||
#
|
|
||||||
# foo = {
|
|
||||||
# variable: 'Hello world, have a nice day!'
|
|
||||||
# for variable in bar if variable != 42
|
|
||||||
# }
|
|
||||||
split_before_dict_set_generator=True
|
|
||||||
|
|
||||||
# Split before the '.' if we need to split a longer expression:
|
|
||||||
#
|
|
||||||
# foo = ('This is a really long string: {}, {}, {}, {}'.format(a, b, c, d))
|
|
||||||
#
|
|
||||||
# would reformat to something like:
|
|
||||||
#
|
|
||||||
# foo = ('This is a really long string: {}, {}, {}, {}'
|
|
||||||
# .format(a, b, c, d))
|
|
||||||
split_before_dot=False
|
|
||||||
|
|
||||||
# Split after the opening paren which surrounds an expression if it doesn't
|
|
||||||
# fit on a single line.
|
|
||||||
split_before_expression_after_opening_paren=False
|
|
||||||
|
|
||||||
# If an argument / parameter list is going to be split, then split before
|
|
||||||
# the first argument.
|
|
||||||
split_before_first_argument=False
|
|
||||||
|
|
||||||
# Set to True to prefer splitting before 'and' or 'or' rather than
|
|
||||||
# after.
|
|
||||||
split_before_logical_operator=False
|
|
||||||
|
|
||||||
# Split named assignments onto individual lines.
|
|
||||||
split_before_named_assigns=True
|
|
||||||
|
|
||||||
# Set to True to split list comprehensions and generators that have
|
|
||||||
# non-trivial expressions and multiple clauses before each of these
|
|
||||||
# clauses. For example:
|
|
||||||
#
|
|
||||||
# result = [
|
|
||||||
# a_long_var + 100 for a_long_var in xrange(1000)
|
|
||||||
# if a_long_var % 10]
|
|
||||||
#
|
|
||||||
# would reformat to something like:
|
|
||||||
#
|
|
||||||
# result = [
|
|
||||||
# a_long_var + 100
|
|
||||||
# for a_long_var in xrange(1000)
|
|
||||||
# if a_long_var % 10]
|
|
||||||
split_complex_comprehension=True
|
|
||||||
|
|
||||||
# The penalty for splitting right after the opening bracket.
|
|
||||||
split_penalty_after_opening_bracket=300
|
|
||||||
|
|
||||||
# The penalty for splitting the line after a unary operator.
|
|
||||||
split_penalty_after_unary_operator=10000
|
|
||||||
|
|
||||||
# The penalty of splitting the line around the '+', '-', '*', '/', '//',
|
|
||||||
# ``%``, and '@' operators.
|
|
||||||
split_penalty_arithmetic_operator=300
|
|
||||||
|
|
||||||
# The penalty for splitting right before an if expression.
|
|
||||||
split_penalty_before_if_expr=0
|
|
||||||
|
|
||||||
# The penalty of splitting the line around the '&', '|', and '^'
|
|
||||||
# operators.
|
|
||||||
split_penalty_bitwise_operator=300
|
|
||||||
|
|
||||||
# The penalty for splitting a list comprehension or generator
|
|
||||||
# expression.
|
|
||||||
split_penalty_comprehension=80
|
|
||||||
|
|
||||||
# The penalty for characters over the column limit.
|
|
||||||
split_penalty_excess_character=7000
|
|
||||||
|
|
||||||
# The penalty incurred by adding a line split to the unwrapped line. The
|
|
||||||
# more line splits added the higher the penalty.
|
|
||||||
split_penalty_for_added_line_split=30
|
|
||||||
|
|
||||||
# The penalty of splitting a list of "import as" names. For example:
|
|
||||||
#
|
|
||||||
# from a_very_long_or_indented_module_name_yada_yad import (long_argument_1,
|
|
||||||
# long_argument_2,
|
|
||||||
# long_argument_3)
|
|
||||||
#
|
|
||||||
# would reformat to something like:
|
|
||||||
#
|
|
||||||
# from a_very_long_or_indented_module_name_yada_yad import (
|
|
||||||
# long_argument_1, long_argument_2, long_argument_3)
|
|
||||||
split_penalty_import_names=0
|
|
||||||
|
|
||||||
# The penalty of splitting the line around the 'and' and 'or'
|
|
||||||
# operators.
|
|
||||||
split_penalty_logical_operator=300
|
|
||||||
|
|
||||||
# Use the Tab character for indentation.
|
|
||||||
use_tabs=False
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
from configparser import ConfigParser
|
||||||
|
from setuptools import setup, find_packages
|
||||||
|
|
||||||
|
setup_cfg = ConfigParser()
|
||||||
|
setup_cfg.read('setup.cfg')
|
||||||
|
metadata = setup_cfg['metadata']
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
setup(
|
||||||
|
name=metadata['dist-name'],
|
||||||
|
description='A program to help users work with QMK Firmware.',
|
||||||
|
entry_points={
|
||||||
|
'console_scripts': ['%s = %s' % l for l in setup_cfg['entry_points'].items()],
|
||||||
|
},
|
||||||
|
license='MIT License',
|
||||||
|
url=metadata['home-page'],
|
||||||
|
version=setup_cfg['bumpversion']['current_version'],
|
||||||
|
author=metadata['author'],
|
||||||
|
author_email=metadata['author-email'],
|
||||||
|
maintainer=metadata['author'],
|
||||||
|
maintainer_email=metadata['author-email'],
|
||||||
|
long_description=open('README.md').read(),
|
||||||
|
long_description_content_type="text/markdown",
|
||||||
|
packages=find_packages(),
|
||||||
|
classifiers=[
|
||||||
|
'Development Status :: 3 - Alpha',
|
||||||
|
'Environment :: Console',
|
||||||
|
'Intended Audience :: Developers',
|
||||||
|
'Intended Audience :: System Administrators',
|
||||||
|
'Intended Audience :: End Users/Desktop',
|
||||||
|
'License :: OSI Approved :: MIT License',
|
||||||
|
'Natural Language :: English',
|
||||||
|
'Programming Language :: Python :: 3 :: Only',
|
||||||
|
'Topic :: Scientific/Engineering',
|
||||||
|
'Topic :: Software Development',
|
||||||
|
'Topic :: Utilities',
|
||||||
|
],
|
||||||
|
requires_python=metadata['requires-python'],
|
||||||
|
install_requires=[
|
||||||
|
#"milc", #FIXME(skullydazed): Included in the repo for now.
|
||||||
|
"appdirs",
|
||||||
|
"argcomplete",
|
||||||
|
"colorama"
|
||||||
|
],
|
||||||
|
)
|
||||||
Reference in New Issue
Block a user