mirror of
https://github.com/RYDE-WORK/ballistica.git
synced 2026-01-25 00:13:27 +08:00
156 lines
4.9 KiB
Python
156 lines
4.9 KiB
Python
# Released under the MIT License. See LICENSE for details.
|
|
#
|
|
"""Functionality for wrangling tool config files.
|
|
|
|
This lets us auto-populate values such as python-paths or versions
|
|
into tool config files automatically instead of having to update
|
|
everything everywhere manually. It also provides a centralized location
|
|
for some tool defaults across all my projects.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import TYPE_CHECKING
|
|
|
|
from efro.terminal import Clr
|
|
|
|
if TYPE_CHECKING:
|
|
from pathlib import Path
|
|
|
|
|
|
def install_tool_config(projroot: Path, src: Path, dst: Path) -> None:
|
|
"""Install a config."""
|
|
print(f'Creating tool config: {Clr.BLD}{dst}{Clr.RST}')
|
|
|
|
with src.open(encoding='utf-8') as infile:
|
|
cfg = infile.read()
|
|
|
|
# Some substitutions, etc.
|
|
cfg = _filter_tool_config(projroot, cfg)
|
|
|
|
# Add an auto-generated notice.
|
|
comment = None
|
|
if dst.name in ['.dir-locals.el']:
|
|
comment = ';;'
|
|
elif dst.name in [
|
|
'.mypy.ini',
|
|
'.pycheckers',
|
|
'.pylintrc',
|
|
'.style.yapf',
|
|
'.clang-format',
|
|
'.editorconfig',
|
|
]:
|
|
comment = '#'
|
|
if comment is not None:
|
|
cfg = (
|
|
f'{comment} THIS FILE WAS AUTOGENERATED; DO NOT EDIT.\n'
|
|
f'{comment} Source: {src}.\n\n' + cfg
|
|
)
|
|
|
|
with dst.open('w', encoding='utf-8') as outfile:
|
|
outfile.write(cfg)
|
|
|
|
|
|
def _filter_tool_config(projroot: Path, cfg: str) -> str:
|
|
# pylint: disable=too-many-locals
|
|
import textwrap
|
|
|
|
from efrotools import getprojectconfig, PYVER
|
|
|
|
# Stick project-root wherever they want.
|
|
cfg = cfg.replace('__EFRO_PROJECT_ROOT__', str(projroot))
|
|
|
|
# Project Python version; '3.11', etc.
|
|
name = '__EFRO_PY_VER__'
|
|
if name in cfg:
|
|
cfg = cfg.replace(name, PYVER)
|
|
|
|
# Project Python version as a binary name; 'python3.11', etc.
|
|
name = '__EFRO_PY_BIN__'
|
|
if name in cfg:
|
|
cfg = cfg.replace(name, f'python{PYVER}')
|
|
|
|
# Colon-separated list of project Python paths.
|
|
name = '__EFRO_PYTHON_PATHS__'
|
|
if name in cfg:
|
|
pypaths = getprojectconfig(projroot).get('python_paths')
|
|
if pypaths is None:
|
|
raise RuntimeError('python_paths not set in project config')
|
|
assert not any(' ' in p for p in pypaths)
|
|
cfg = cfg.replace(name, ':'.join(f'{projroot}/{p}' for p in pypaths))
|
|
|
|
# Quoted relative space-separated list of project Python paths.
|
|
name = '__EFRO_PYTHON_PATHS_Q_REL_STR__'
|
|
if name in cfg:
|
|
pypaths = getprojectconfig(projroot).get('python_paths')
|
|
if pypaths is None:
|
|
raise RuntimeError('python_paths not set in project config')
|
|
assert not any(' ' in p for p in pypaths)
|
|
cfg = cfg.replace(name, ' '.join(f'"{p}"' for p in pypaths))
|
|
|
|
# Short project name.
|
|
short_names = {
|
|
'ballistica-internal': 'ba-i',
|
|
'ballistica': 'ba',
|
|
'ballistica-master-server': 'bmas',
|
|
'ballistica-master-server-legacy': 'bmasl',
|
|
'ballistica-server-node': 'basn',
|
|
}
|
|
shortname = short_names.get(projroot.name, projroot.name)
|
|
cfg = cfg.replace('__EFRO_PROJECT_SHORTNAME__', shortname)
|
|
|
|
mypy_standard_settings = textwrap.dedent(
|
|
"""
|
|
# We don't want all of our plain scripts complaining
|
|
# about __main__ being redefined.
|
|
scripts_are_modules = True
|
|
|
|
# Try to be as strict as we can about using types everywhere.
|
|
no_implicit_optional = True
|
|
warn_unused_ignores = True
|
|
warn_no_return = True
|
|
warn_return_any = True
|
|
warn_redundant_casts = True
|
|
warn_unreachable = True
|
|
warn_unused_configs = True
|
|
disallow_incomplete_defs = True
|
|
disallow_untyped_defs = True
|
|
disallow_untyped_decorators = True
|
|
disallow_untyped_calls = True
|
|
disallow_any_unimported = True
|
|
disallow_subclassing_any = True
|
|
strict_equality = True
|
|
local_partial_types = True
|
|
no_implicit_reexport = True
|
|
|
|
enable_error_code = redundant-expr, truthy-bool, \
|
|
truthy-function, unused-awaitable
|
|
"""
|
|
).strip()
|
|
|
|
cfg = cfg.replace('__EFRO_MYPY_STANDARD_SETTINGS__', mypy_standard_settings)
|
|
|
|
name = '__PYTHON_BLACK_EXTRA_ARGS__'
|
|
if name in cfg:
|
|
from efrotools.code import black_base_args
|
|
|
|
bargs = black_base_args()
|
|
assert bargs[2] == 'black'
|
|
cfg = cfg.replace(
|
|
name, '(' + ' '.join(f'"{b}"' for b in bargs[3:]) + ')'
|
|
)
|
|
|
|
# Gen a pylint init hook that sets up our Python paths.
|
|
pylint_init_tag = '__EFRO_PYLINT_INIT__'
|
|
if pylint_init_tag in cfg:
|
|
pypaths = getprojectconfig(projroot).get('python_paths')
|
|
if pypaths is None:
|
|
raise RuntimeError('python_paths not set in project config')
|
|
cstr = 'init-hook=import sys;'
|
|
# Stuff our paths in at the beginning in the order they appear
|
|
# in our projectconfig.
|
|
for i, path in enumerate(pypaths):
|
|
cstr += f" sys.path.insert({i}, '{projroot}/{path}');"
|
|
cfg = cfg.replace(pylint_init_tag, cstr)
|
|
return cfg
|