diff --git a/interactive_runner/assignment.py b/interactive_runner/assignment.py index bffcc88..7020ca8 100644 --- a/interactive_runner/assignment.py +++ b/interactive_runner/assignment.py @@ -3,35 +3,33 @@ from __future__ import annotations __all__ = ("Assignment",) from functools import cached_property -from os import system as cmd +from .helpers import command from tomllib import loads as decode_toml from typing import TYPE_CHECKING, TypedDict if TYPE_CHECKING: + from pathlib import Path from .language import Language -AssignmentConfig = TypedDict( - "AssignmentConfig", - { - "name": str, - "given-date": str, - "main-file": str, - "description": str, - "directions": str, - } -) + +class AssignmentConfig(TypedDict): + name: str + date: str + main: str + description: str + directions: str class Assignment: __config: AssignmentConfig + __main: str language: Language name: str - given_date: str - __main_file: str + date: str description: str directions: str - def __init__(self, directory: Path, language: Language) -> None: + def __init__(self, directory: Path, language: Language, /) -> None: self.__directory = directory self.language = language @@ -40,19 +38,23 @@ class Assignment: return decode_toml((self.__directory / "assignment.toml").read_text()) def refresh(self) -> None: - del self.__config + """TODO""" @property def name(self) -> str: return self.__config["name"] @property - def given_date(self) -> str: - return self.__config["given-date"] + def date(self) -> str: + return self.__config["date"] @property - def __main_file(self) -> Path: - return self.__directory / self.__config["main-file"] + def __main(self) -> Path: + return (self.__directory / self.__config["main"]).absolute() + + @property + def __out(self) -> Path: + return (self.__directory / "compiled.out").absolute() @property def description(self) -> str: @@ -63,29 +65,29 @@ class Assignment: return self.__config["directions"] def compile(self, *, quiet: bool) -> int | None: - if missing := self.language.check_dependencies_installed(): - raise ValueError(f"Needed depencencies are not installed: {', '.join(missing)}") + if missing := self.language.check_if_dependencies_are_installed(): + raise ValueError( + f"Some dependencies that are needed are not installed: {', '.join(missing)}" + ) if not self.language.is_compiled: return None - return cmd( + return command( self.language._build_command.format( - **{ - "out-file": self.__directory / "compiled.out", - "main-file": self.__main_file.absolute(), - } - ) + - (QUIET_SUFFIX if quiet else "") - ) + out=self.__out, + main=self.__main + ), + quiet=quiet + ) def run(self) -> int: if self.language.is_compiled and not (self.__directory / "compiled.out").exists(): self.compile(quiet=True) - return cmd( + return command( self.language._run_command.format( - ( - self.__directory / "compiled.out").absolute() if self.language.is_compiled else self.__main_file.absolute() - ) - ) + self.__out if self.language.is_compiled else self.__main + ), + quiet=False + ) diff --git a/interactive_runner/constants.py b/interactive_runner/constants.py new file mode 100644 index 0000000..459d83e --- /dev/null +++ b/interactive_runner/constants.py @@ -0,0 +1,8 @@ +__all__ = ("ROOT", "OS") + +from os import name as os_name +from pathlib import Path +from typing import Literal + +ROOT = Path(__file__).parent.parent +OS: Literal["windows"] | Literal["unix"] = "windows" if os_name == "nt" else "unix" # type: ignore diff --git a/interactive_runner/consts.py b/interactive_runner/consts.py deleted file mode 100644 index 4fd9603..0000000 --- a/interactive_runner/consts.py +++ /dev/null @@ -1,10 +0,0 @@ -__all__ = ("ROOT", "IS_WINDOWS", "QUIET_SUFFIX", "CHECK_COMMAND_EXISTS", "OS_KEY") - -from os import name as os_name -from pathlib import Path - -ROOT = Path(__file__).parent.parent -IS_WINDOWS = os_name == "nt" -QUIET_SUFFIX = " | Out-Null" if IS_WINDOWS else " > /dev/null" -CHECK_COMMAND_EXISTS = ("Get-Command {}" if IS_WINDOWS else "command -v {}") + QUIET_SUFFIX -OS_KEY = "windows" if IS_WINDOWS else "unix" diff --git a/interactive_runner/helpers.py b/interactive_runner/helpers.py new file mode 100644 index 0000000..606a88b --- /dev/null +++ b/interactive_runner/helpers.py @@ -0,0 +1,30 @@ +__all__ = ("command", "chalk_from_int", "command_exists") + +from subprocess import DEVNULL as NULL, call as sys_command + +from chalky import Chalk, TrueColor + + +def command(s: str, /, *, quiet: bool) -> int: + return sys_command( + s.split(" "), **(dict(stdout=NULL, stderr=NULL, stdin=NULL) if quiet else {}) + ) + + +def __rgb_from_int(i: int, /) -> tuple[int, int, int]: + r = (i >> 16) & 255 + g = (i >> 8) & 255 + b = i & 255 + return r, g, b + + +def chalk_from_int(foreground: int, background: int = None, /) -> Chalk: + return Chalk( + foreground=TrueColor(*__rgb_from_int(foreground)), + background=TrueColor(*__rgb_from_int(background)) + ) + + +def command_exists(s: str, /) -> bool: + """TODO""" + return False diff --git a/interactive_runner/language.py b/interactive_runner/language.py index deabfa2..be96796 100644 --- a/interactive_runner/language.py +++ b/interactive_runner/language.py @@ -3,71 +3,65 @@ from __future__ import annotations __all__ = ("Language",) from functools import cached_property -from os import system as cmd from tomllib import loads as decode_toml from typing import TYPE_CHECKING, TypedDict -from chalky import hex - from .assignment import Assignment -from .consts import CHECK_COMMAND_EXISTS, OS_KEY +from .constants import OS +from .helpers import chalk_from_int, command_exists if TYPE_CHECKING: from pathlib import Path -LanguageConfig = TypedDict( - "LanguageConfig", - { - "name": str, - "descripton": str, - "colors": TypedDict( - "ColorConfig", - { - "foregroud": int, - "background": int, - } - ), - "build": None | TypedDict( - "BuildConfig", - { - "common": str | None, - "unix": str | None, - "windows": str | None, - } - ), - "run": TypedDict( - "RunConfig", - { - "common": str | None, - "unix": str | None, - "windows": str | None, - } - ), - "dependencies": None | TypedDict( - "DependencyConfig", - { - "common": list[str] | None, - "unix": list[str] | None, - "windows": list[str] | None, - } - ), - } -) + +class DependencyConfig(TypedDict): + """The list of commands that it needs.""" + common: list[str] | None + unix: list[str] | None + windows: list[str] | None + + +class RunConfig(TypedDict): + """command.format(main_file)""" + common: str | None + unix: str | None + windows: str | None + + +class BuildConfig(TypedDict): + """command.format(out=out_file, main=main_file)""" + common: str | None + unix: str | None + windows: str | None + + +class ColorConfig(TypedDict): + foreground: int + background: int + + +class LanguageConfig(TypedDict): + name: str + description: str + color: ColorConfig + build: BuildConfig | None + run: RunConfig + dependency: DependencyConfig | None class Language: __directory: Path __config: LanguageConfig + __dependencies: list[str] | None + _build_command: str | None + _run_command: str name: str styled_name: str description: str - __dependencies: list[str] | None - _build_command: str | None is_compiled: bool - _run_command: str assignments: dict[str, Assignment] - def __init__(self, directory: Path) -> None: + def __init__(self, directory: Path, /) -> None: self.__directory = directory @cached_property @@ -75,8 +69,7 @@ class Language: return decode_toml((self.__directory / "language.toml").read_text()) def refresh(self) -> None: - del self.__config - del self.assignments + """TODO""" @property def name(self) -> str: @@ -84,9 +77,8 @@ class Language: @property def styled_name(self) -> str: - colors = self.__config["colors"] - # hack - return hex(colors["foreground"]) & hex(colors["background"], background=True) | self.name + color = self.__config["color"] + return chalk_from_int(color["foreground"], color["background"]) | self.name @property def description(self) -> str: @@ -94,26 +86,20 @@ class Language: @property def __dependencies(self) -> list[str] | None: - dependencies = self.__config.get("dependencies", {}) - return dependencies.get("common", dependencies.get(OS_KEY)) + dependency = self.__config.get("dependency", {}) + return dependency.get("common") or dependency[OS] - def check_dependencies_installed(self) -> list[str]: + def check_if_dependencies_are_installed(self) -> list[str]: + """Return value is the not installed dependencies""" if self.__dependencies is None: return [] - not_installed = [] - for dependency in self.__dependencies: - exit = cmd(CHECK_COMMAND_EXISTS.format(dependency)) - - if exit: - not_installed.append(dependency) - - return not_installed + return [d for d in self.__dependencies if not command_exists(d)] @property def _build_command(self) -> str | None: build = self.__config.get("build", {}) - return build.get("common", build.get(OS_KEY)) + return build.get("common") or build.get(OS) @property def is_compiled(self) -> bool: @@ -122,7 +108,7 @@ class Language: @property def _run_command(self) -> str: run = self.__config["run"] - return run.get("common", run.get(OS_KEY)) + return run.get("common") or run[OS] @cached_property def assignments(self) -> dict[str, Assignment]: diff --git a/interactive_runner/sources.py b/interactive_runner/sources.py index 3c0972a..7fd9b40 100644 --- a/interactive_runner/sources.py +++ b/interactive_runner/sources.py @@ -5,7 +5,7 @@ __all__ = ("Sources",) from functools import cached_property from typing import TYPE_CHECKING -from .consts import ROOT +from .constants import ROOT from .language import Language if TYPE_CHECKING: @@ -32,4 +32,4 @@ class Sources: return languages def refresh(self) -> None: - del self.languages + """TODO""" diff --git a/sources/c/language.toml b/sources/c/language.toml index 829be9e..5d2b2ba 100644 --- a/sources/c/language.toml +++ b/sources/c/language.toml @@ -1,15 +1,15 @@ name = "C" description = "C (pronounced like the letter c) is a general-purpose computer programming language" -[colors] -foreground = "ffffff" -background = "00599d" +[color] +foreground = 0xffffff +background = 0x00599d [build] -common = "gcc -o {out-file} {main-file}" +common = "gcc -o {out} {main}" [run] common = "{}" -[dependencies] -common = ["gcc"] +[dependency] +common = [ "gcc" ] diff --git a/sources/cpython/example/assignment.toml b/sources/cpython/example/assignment.toml index 8813d70..ccf8b75 100644 --- a/sources/cpython/example/assignment.toml +++ b/sources/cpython/example/assignment.toml @@ -1,6 +1,6 @@ name = "example" -given-date = "3/11/2022" -main-file = "example.py" +date = "3/11/2022" +main = "example.py" description = "This is an example entry for CPython" directions = """ Print "Example" 10 times. diff --git a/sources/cpython/language.toml b/sources/cpython/language.toml index 62b68b0..e807834 100644 --- a/sources/cpython/language.toml +++ b/sources/cpython/language.toml @@ -2,13 +2,13 @@ name = "CPython" description = "Python is a high-level, general-purpose programming language" [colors] -foreground = "fed140" -background = "3670a0" +foreground = 0xfed140 +background = 0x3670a0 [run] unix = "python3 {}" windows = "py {}" [dependencies] -unix = ["python3"] -windows = ["py"] +unix = [ "python3" ] +windows = [ "py" ]