From 9c3c0712b8f6b33c8306c18f85e2e84fafadd925 Mon Sep 17 00:00:00 2001 From: zackees Date: Sun, 24 Mar 2024 16:53:45 -0700 Subject: [PATCH] updates --- .pylintrc | 2 +- README.md | 1 + pyproject.toml | 3 +- src/python_compile/cli.py | 37 ++++++++++++++++-------- src/python_compile/native_build.py | 30 +++++++++++++------ test | 2 +- tests/test_cli.py | 29 ------------------- tests/test_cli_native.py | 46 ++++++++++++++++++++++++++++++ tests/test_debian.py | 41 ++++++++++++++++++++++++++ tests/test_main.py | 45 +++++++++++++++++++++++++++++ tests/test_native.py | 43 ++++++++++++++++++++++++++++ 11 files changed, 227 insertions(+), 52 deletions(-) delete mode 100644 tests/test_cli.py create mode 100644 tests/test_cli_native.py create mode 100644 tests/test_debian.py create mode 100644 tests/test_main.py create mode 100644 tests/test_native.py diff --git a/.pylintrc b/.pylintrc index fc9b58a..b5866a0 100644 --- a/.pylintrc +++ b/.pylintrc @@ -1,2 +1,2 @@ [MESSAGES CONTROL] -disable=missing-module-docstring,missing-class-docstring,missing-function-docstring,line-too-long,raise-missing-from,too-few-public-methods,too-many-return-statements +disable=missing-module-docstring,missing-class-docstring,missing-function-docstring,line-too-long,raise-missing-from,too-few-public-methods,too-many-return-statements,duplicate-code diff --git a/README.md b/README.md index b1da606..2a55979 100644 --- a/README.md +++ b/README.md @@ -46,5 +46,6 @@ Run `./lint.sh` to find linting errors using `pylint`, `flake8` and `mypy`. # Releases + * 1.0.5: You may now omit the `--os` parameter and compile against the current system. * 1.0.4: Fix * 1.0.3: Native windows compile is available if the host is also windows. \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index be2e118..7ac2614 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,7 +12,8 @@ license = { text = "BSD 3-Clause License" } classifiers = ["Programming Language :: Python :: 3"] dependencies = [ "docker-run-cmd~=1.0.10", - "isolated-environment" + "isolated-environment", + "pytest-xdist" ] # Change this with the version number bump. version = "1.0.4" diff --git a/src/python_compile/cli.py b/src/python_compile/cli.py index 56ba4c2..6456086 100644 --- a/src/python_compile/cli.py +++ b/src/python_compile/cli.py @@ -5,12 +5,21 @@ import argparse import os import sys +from dataclasses import dataclass from pathlib import Path from docker_run_cmd.api import docker_run from python_compile.native_build import run_native_build + +@dataclass +class Args: + app_py: Path + requirements: Path | None = None + os: str | None = None + + HERE = Path(__file__).parent ASSETS = HERE / "assets" @@ -30,6 +39,7 @@ def parse_args() -> argparse.Namespace: type=str, help="Which os to use", choices=DOCKER_FILE_MAP.keys(), + required=False, ) parser.add_argument( "--platform", @@ -51,14 +61,10 @@ def parse_args() -> argparse.Namespace: return parser.parse_args() -def main() -> int: +def run(args: Args) -> int: """Main entry point for the template_python_cmd package.""" - args = parse_args() os_system = args.os - if not os_system: - print("You must provide an os") - return 1 - app_py = args.input + app_py = args.app_py requirements_txt = args.requirements if os_system == "windows": if os.name != "nt": @@ -67,8 +73,8 @@ def main() -> int: return 1 rtn = run_native_build(app_py=app_py, requirements_txt=requirements_txt) return rtn - if os_system == "native": - run_native_build(app_py=app_py, requirements_txt=requirements_txt) + if os_system is None or os_system == "native": + rtn = run_native_build(app_py=app_py, requirements_txt=requirements_txt) return rtn dockerpath: Path | None = DOCKER_FILE_MAP.get(os_system) @@ -76,10 +82,8 @@ def main() -> int: print(f"OS {os_system} is not supported") return 1 assert dockerpath.exists(), f"dockerpath {dockerpath} does not exist" - py_path = args.input - py_path = Path(py_path).as_posix() - - assert py_path, "You must provide a python path" + py_path = args.app_py + assert Path(py_path).as_posix(), "You must provide a python path" extra_files: dict[Path, Path] = {} if args.requirements: @@ -95,6 +99,15 @@ def main() -> int: return 0 +def main() -> int: + cli_args = parse_args() + os_system = cli_args.os + app_py = cli_args.input + requirements_txt = cli_args.requirements + args = Args(app_py=app_py, requirements=requirements_txt, os=os_system) + return run(args) + + if __name__ == "__main__": sys.argv.append("--os") sys.argv.append("debian") diff --git a/src/python_compile/native_build.py b/src/python_compile/native_build.py index 846dae7..b183b97 100644 --- a/src/python_compile/native_build.py +++ b/src/python_compile/native_build.py @@ -1,6 +1,10 @@ +import os +import shutil import subprocess import sys import tempfile +import warnings +from atexit import register from pathlib import Path from isolated_environment import IsolatedEnvironment, Requirements @@ -42,19 +46,29 @@ def generate_cmd_list(app_py: Path) -> list[str]: return cmd_list -def run_native_build(app_py: Path, requirements_txt: Path) -> int: - """Run the native windows build.""" +def clean_dir(path: str) -> None: + """Clean the directory.""" + try: + shutil.rmtree(path, ignore_errors=True) + except Exception as e: # pylint: disable=broad-except + warnings.warn(f"Failed to clean directory: {path} with error: {e}") + +def run_native_build(app_py: Path, requirements_txt: Path | None) -> int: + """Run the native windows build.""" print("Running native build") - with tempfile.TemporaryDirectory() as tmp_dir: + with tempfile.TemporaryDirectory(ignore_cleanup_errors=True) as tmp_dir: print(f"Creating temporary directory: {tmp_dir}") + full_tmp_path = os.path.abspath(tmp_dir) + register(clean_dir, full_tmp_path) env_path = Path(tmp_dir) / "nuitka_venv" iso_env = IsolatedEnvironment(env_path, REQS) - subprocess.run( - ["pip", "install", "-r", requirements_txt], - env=iso_env.environment(), - check=True, - ) + if requirements_txt: + subprocess.run( + ["pip", "install", "-r", requirements_txt], + env=iso_env.environment(), + check=True, + ) cmd_list: list[str] = generate_cmd_list(app_py) subprocess.run( cmd_list, diff --git a/test b/test index 3efbcfd..6ff1589 100644 --- a/test +++ b/test @@ -3,4 +3,4 @@ set -e . ./activate.sh echo "Running unittests" -python -m unittest discover -s tests -p "*test*.py" \ No newline at end of file +pytest -n auto -v tests \ No newline at end of file diff --git a/tests/test_cli.py b/tests/test_cli.py deleted file mode 100644 index 018d1df..0000000 --- a/tests/test_cli.py +++ /dev/null @@ -1,29 +0,0 @@ -""" -Unit test file. -""" - -import os -import unittest -from pathlib import Path - - -class MainTester(unittest.TestCase): - """Main tester class.""" - - def test_imports(self) -> None: - """Test command line interface (CLI).""" - cmd_list = [ - "python-compile", - "--os", - "debian", - "--input", - "src/python_compile/assets/demo_http_server.py", - ] - rtn = os.system(" ".join(cmd_list)) - self.assertEqual(0, rtn) - expected_path: Path = Path("demo_http_server.bin.gz") - self.assertTrue(expected_path.exists()) - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/test_cli_native.py b/tests/test_cli_native.py new file mode 100644 index 0000000..a1712de --- /dev/null +++ b/tests/test_cli_native.py @@ -0,0 +1,46 @@ +""" +Unit test file. +""" + +import os +import subprocess +import sys +import unittest +from pathlib import Path +from tempfile import TemporaryDirectory + +HERE = Path(__file__).parent +PROJECT_ROOT = HERE.parent +DEMO_PY = PROJECT_ROOT / "src/python_compile/assets/demo_http_server.py" + + +class CliNativetester(unittest.TestCase): + """Main tester class.""" + + def test_cli(self) -> None: + """Test command line interface (CLI).""" + with TemporaryDirectory() as tmp_dir: + os.chdir(tmp_dir) + try: + cmd_list = [ + "python-compile", + "--input", + str(DEMO_PY), + ] + cmd_str = subprocess.list2cmdline(cmd_list) + print(f"Running: {cmd_str}") + rtn = os.system(" ".join(cmd_list)) + self.assertEqual(0, rtn) + expected_path_gz: Path = Path("demo_http_server.bin.gz") + self.assertTrue(expected_path_gz.exists()) + if sys.platform == "win32": + expected_path = Path("demo_http_server.exe") + else: + expected_path = Path("demo_http_server.bin") + self.assertTrue(expected_path.exists()) + finally: + os.chdir(PROJECT_ROOT) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/test_debian.py b/tests/test_debian.py new file mode 100644 index 0000000..47d82e2 --- /dev/null +++ b/tests/test_debian.py @@ -0,0 +1,41 @@ +""" +Unit test file. +""" + +import os +import subprocess +import unittest +from pathlib import Path +from tempfile import TemporaryDirectory + + +class NativeTester(unittest.TestCase): + """Main tester class.""" + + def test_compile(self) -> None: + """Test command line interface (CLI).""" + with TemporaryDirectory() as tmp_dir: + prev_dir = os.getcwd() + os.chdir(tmp_dir) + try: + cmd_list = [ + "python-compile", + "--input", + "src/python_compile/assets/demo_http_server.py", + "--os", + "debian", + ] + cmd_str = subprocess.list2cmdline(cmd_list) + print(f"Running: {cmd_str}") + rtn = os.system(" ".join(cmd_list)) + self.assertEqual(0, rtn) + expected_path_gz: Path = Path("demo_http_server.bin.gz") + self.assertTrue(expected_path_gz.exists()) + expected_path = Path("demo_http_server.bin") + self.assertTrue(expected_path.exists()) + finally: + os.chdir(prev_dir) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/test_main.py b/tests/test_main.py new file mode 100644 index 0000000..0454f00 --- /dev/null +++ b/tests/test_main.py @@ -0,0 +1,45 @@ +""" +Unit test file. +""" + +import os +import sys +import unittest +from pathlib import Path +from pprint import pprint +from tempfile import TemporaryDirectory + +from python_compile.cli import Args, run + +HERE = Path(__file__).parent +PROJECT_ROOT = HERE.parent +DEMO_PY = PROJECT_ROOT / "src/python_compile/assets/demo_http_server.py" + + +class MainTester(unittest.TestCase): + """Main tester class.""" + + def test_run_main(self) -> None: + """Test command line interface (CLI).""" + with TemporaryDirectory(ignore_cleanup_errors=True) as tmp_dir: + prev_dir = os.getcwd() + os.chdir(tmp_dir) + if sys.platform == "win32": + expected_path = "demo_http_server.exe" + else: + expected_path = "demo_http_server.bin" + try: + args = Args(app_py=DEMO_PY) + rtn = run(args) + self.assertEqual(0, rtn) + files = os.listdir(tmp_dir) + pprint(files) + self.assertTrue( + expected_path in files, f"{expected_path} not found in {files}" + ) + finally: + os.chdir(prev_dir) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/test_native.py b/tests/test_native.py new file mode 100644 index 0000000..f533210 --- /dev/null +++ b/tests/test_native.py @@ -0,0 +1,43 @@ +""" +Unit test file. +""" + +import os +import subprocess +import sys +import unittest +from pathlib import Path +from tempfile import TemporaryDirectory + + +class NativeTester(unittest.TestCase): + """Main tester class.""" + + def test_compile(self) -> None: + """Test command line interface (CLI).""" + with TemporaryDirectory() as tmp_dir: + prev_dir = os.getcwd() + os.chdir(tmp_dir) + try: + cmd_list = [ + "python-compile", + "--input", + "src/python_compile/assets/demo_http_server.py", + ] + cmd_str = subprocess.list2cmdline(cmd_list) + print(f"Running: {cmd_str}") + rtn = os.system(" ".join(cmd_list)) + self.assertEqual(0, rtn) + expected_path_gz: Path = Path("demo_http_server.bin.gz") + self.assertTrue(expected_path_gz.exists()) + if sys.platform == "win32": + expected_path = Path("demo_http_server.exe") + else: + expected_path = Path("demo_http_server.bin") + self.assertTrue(expected_path.exists()) + finally: + os.chdir(prev_dir) + + +if __name__ == "__main__": + unittest.main()