#!/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, # ) 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()