#!/usr/bin/env python3 #vim: set filetype=python import logging import json import enum import pathlib import sys import argparse #import optparse import dataclasses import subprocess import os from typing import ( Optional, Any, TypeAlias, Literal, cast, BinaryIO, Generator, ClassVar, Self, ) logger = logging.getLogger() @dataclasses.dataclass class Settings: project_root : pathlib.Path = pathlib.Path.cwd() env_path : pathlib.Path = project_root / 'tmp' / 'env3' _settings : ClassVar[Optional['Settings']] = None @classmethod def settings(cls) -> Self: if cls._settings is None: cls._settings = cls() return cls._settings def js(argv: list[str]) -> int: return subprocess.check_call([ 'sudo', 'docker-compose', '--project-directory', Settings.settings().project_root, '-f', Settings.settings().project_root / 'docker' / 'js' / 'docker-compose.yml', *argv, ]) def env( argv: Optional[list[str]] = None, mode: Literal['exec', 'subprocess'] = 'subprocess', **kwargs: Any, ) -> Optional[subprocess.CompletedProcess[bytes]]: env_path = Settings.settings().env_path if not env_path.exists(): subprocess.check_call([ sys.executable, '-m', 'venv', '--system-site-packages', str(env_path) ]) subprocess.check_call([ env_path / 'bin' / 'python3', '-m', 'pip', 'install', '-r', 'requirements.txt', ]) if not argv is None: python_path = str(env_path / 'bin' / 'python3') if mode == 'exec': os.execv( python_path, [ python_path, *argv, ], ) return None elif mode == 'subprocess': return subprocess.run([ python_path, *argv, ], **kwargs) else: raise NotImplementedError return None def ruff(argv: list[str]) -> None: parser = argparse.ArgumentParser() parser.add_argument( '-i', dest='paths', help='specify paths to check', default=[], action='append', ) parser.add_argument( '-e', dest='exclude', help='rules to ignore', default=[], action='append', ) options, args = parser.parse_known_args(argv) if len(options.paths) == 0: options.paths.extend([ '.', 'dotfiles/.local/bin/commands', ]) if len(options.exclude) == 0: options.exclude.extend([ 'E731', 'E713', 'E714', 'E703', ]) res = env([ '-m', 'ruff', 'check', *args, '--output-format', 'json', '--ignore', ','.join(options.exclude), *options.paths, ], stdout=subprocess.PIPE, stderr=subprocess.PIPE) assert not res is None errors = json.loads(res.stdout.decode('utf-8')) g: dict[str, Any] = dict() for o in errors: if not o['filename'] in g: g[o['filename']] = [] g[o['filename']].append(o) h = { k : len(v) for k, v in g.items() } logger.info(json.dumps(errors, indent=4)) logger.info(json.dumps(h, indent=4)) def inside_env() -> bool: try: import numpy return True except Exception: return False #class Commands(enum.StrEnum): # js = 'js' # mypy = 'mypy' # env = 'env' # ruff = 'ruff' # m2 = 'm2' def mypy(argv: list[str]) -> None: import online.fxreader.pr34.commands_typed.mypy as _mypy _mypy.run( argv, settings=_mypy.MypySettings( paths=[ #Settings.settings().project_root / 'dotfiles/.local/bin/commands', Settings.settings().project_root / 'python' / 'm.py', Settings.settings().project_root / 'python' / '_m.py', Settings.settings().project_root / 'python' / 'online', # Settings.settings().project_root / 'deps/com.github.aiortc.aiortc/src', #Settings.settings().project_root / 'm.py', ], max_errors={ 'python/online/fxreader/pr34/commands_typed': 0, 'deps/com.github.aiortc.aiortc/src/online_fxreader': 0, 'deps/com.github.aiortc.aiortc/src/aiortc/contrib/signaling': 0 } ), ) def host_deps(argv: list[str]) -> None: if sys.platform in ['linux']: subprocess.check_call(r''' exec yay -S $(cat requirements-archlinux.txt) ''', shell=True,) else: raise NotImplementedError Command_args = ['js', 'mypy', 'env', 'ruff', 'm2', 'host_deps',] Command : TypeAlias = Literal['js', 'mypy', 'env', 'ruff', 'm2', 'host_deps',] def run(argv: Optional[list[str]] = None) -> None: logging.basicConfig( level=logging.INFO, format=( '%(levelname)s:%(name)s:%(message)s' ':%(process)d' ':%(asctime)s' ':%(pathname)s:%(funcName)s:%(lineno)s' ), ) if argv is None: argv = sys.argv[:] parser = argparse.ArgumentParser() parser.add_argument( 'command', #'_command', choices=[ o for o in Command_args ], #required=True, ) options, args = parser.parse_known_args(argv[1:]) assert options.command in Command_args if len(args) > 0 and args[0] == '--': del args[0] #options.command = Commands(options._command) if options.command == 'js': js(args) elif options.command == 'host_deps': host_deps(args) elif options.command == 'env': env(args, mode='exec',) elif options.command == 'mypy': if not inside_env(): env( [ pathlib.Path(__file__).parent / 'm.py', *argv[1:], ], mode='exec' ) else: mypy(args) elif options.command == 'ruff': ruff(args) elif options.command == 'm2': if not inside_env(): env(['--', '_m.py', 'm2', *args]) return import python.tasks.cython python.tasks.cython.mypyc_build( pathlib.Path('_m.py') ) else: raise NotImplementedError if __name__ == '__main__': run()