From 6422d99cec0b2c9367c945e344507dc7cc87b107 Mon Sep 17 00:00:00 2001 From: Chris Rink Date: Tue, 3 Sep 2024 21:54:13 -0400 Subject: [PATCH 01/12] Allow adding additional `sys.path` entries from the CLI --- CHANGELOG.md | 3 +++ src/basilisp/cli.py | 30 ++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c59d525..0db00a47 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Added + * Added the `-p`/`--include-path` CLI command to add entries to the `sys.path` (#???) + ### Changed * The compiler will no longer require `Var` indirection for top-level `do` forms unless those forms specify `^:use-var-indirection` metadata (which currently is only used in the `ns` macro) (#1034) * nREPL server no longer sends ANSI color escape sequences in exception messages to clients (#1039) diff --git a/src/basilisp/cli.py b/src/basilisp/cli.py index 94d69873..67b2f1df 100644 --- a/src/basilisp/cli.py +++ b/src/basilisp/cli.py @@ -2,6 +2,7 @@ import importlib.metadata import io import os +import pathlib import sys import textwrap import types @@ -78,6 +79,19 @@ def bootstrap_repl(ctx: compiler.CompilerContext, which_ns: str) -> types.Module return importlib.import_module(REPL_NS) +def init_path(args: argparse.Namespace) -> None: + def append_once(path: str) -> None: + if path in sys.path: + return + sys.path.insert(0, path) + + for path in args.include_path or []: + p = pathlib.Path(path).resolve() + append_once(str(p)) + + append_once("") + + def _to_bool(v: Optional[str]) -> Optional[bool]: """Coerce a string argument to a boolean value, if possible.""" if v is None: @@ -265,6 +279,16 @@ def _add_debug_arg_group(parser: argparse.ArgumentParser) -> None: ) +def _add_import_arg_group(parser: argparse.ArgumentParser) -> None: + group = parser.add_argument_group("path options") + group.add_argument( + "-p", + "--include-path", + action="append", + help="path to add to `sys.path`; maybe specified more than once", + ) + + def _add_runtime_arg_group(parser: argparse.ArgumentParser) -> None: group = parser.add_argument_group( "runtime arguments", @@ -386,6 +410,7 @@ def nrepl_server( args: argparse.Namespace, ) -> None: basilisp.init(_compiler_opts(args)) + init_path(args) nrepl_server_mod = importlib.import_module(munge(NREPL_SERVER_NS)) nrepl_server_mod.start_server__BANG__( lmap.map( @@ -422,6 +447,7 @@ def _add_nrepl_server_subcommand(parser: argparse.ArgumentParser) -> None: help='the file path where the server port number is output to, defaults to ".nrepl-port".', ) _add_compiler_arg_group(parser) + _add_import_arg_group(parser) _add_runtime_arg_group(parser) _add_debug_arg_group(parser) @@ -432,6 +458,7 @@ def repl( ) -> None: opts = _compiler_opts(args) basilisp.init(opts) + init_path(args) ctx = compiler.CompilerContext(filename=REPL_INPUT_FILE_PATH, opts=opts) prompter = get_prompter() eof = object() @@ -512,6 +539,7 @@ def _add_repl_subcommand(parser: argparse.ArgumentParser) -> None: help="default namespace to use for the REPL", ) _add_compiler_arg_group(parser) + _add_import_arg_group(parser) _add_runtime_arg_group(parser) _add_debug_arg_group(parser) @@ -532,6 +560,7 @@ def run( opts = _compiler_opts(args) basilisp.init(opts) + init_path(args) ctx = compiler.CompilerContext( filename=( CLI_INPUT_FILE_PATH @@ -617,6 +646,7 @@ def _add_run_subcommand(parser: argparse.ArgumentParser) -> None: help="command line args made accessible to the script as basilisp.core/*command-line-args*", ) _add_compiler_arg_group(parser) + _add_import_arg_group(parser) _add_runtime_arg_group(parser) _add_debug_arg_group(parser) From 946ad0e9434b8b5144b744f85b92585f549f15fa Mon Sep 17 00:00:00 2001 From: Chris Rink Date: Wed, 4 Sep 2024 16:58:42 -0400 Subject: [PATCH 02/12] More --- CHANGELOG.md | 5 ++-- src/basilisp/cli.py | 55 +++++++++++++++++++++++++++++--------- tests/basilisp/cli_test.py | 12 +++++++++ 3 files changed, 58 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0db00a47..8aca6f05 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Added - * Added the `-p`/`--include-path` CLI command to add entries to the `sys.path` (#???) + * Added the `-p`/`--include-path` CLI command to prepend entries to the `sys.path` as an alternative to `PYTHONPATH` (#1027) + * Added an empty entry to `sys.path` for all CLI entrypoints (`basilisp run`, `basilisp repl`, etc.) (#1027) ### Changed * The compiler will no longer require `Var` indirection for top-level `do` forms unless those forms specify `^:use-var-indirection` metadata (which currently is only used in the `ns` macro) (#1034) @@ -14,7 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed * Fix a bug where the compiler would always generate inline function definitions even if the `inline-functions` compiler option is disabled (#1023) - * Fix a bug where `defrecord`/`deftype` constructors could not be used in the type's methods. (#1025) + * Fix a bug where `defrecord`/`deftype` constructors could not be used in the type's methods (#1025) * Fix a bug where `keys` and `vals` would fail for records (#1030) * Fix a bug where operations on records created by `defrecord` failed for fields whose Python-safe names were mangled by the Python compiler (#1029) * Fix incorrect line numbers for compiler exceptions in nREPL when evaluating forms in loaded files (#1037) diff --git a/src/basilisp/cli.py b/src/basilisp/cli.py index 67b2f1df..16c563fd 100644 --- a/src/basilisp/cli.py +++ b/src/basilisp/cli.py @@ -79,17 +79,22 @@ def bootstrap_repl(ctx: compiler.CompilerContext, which_ns: str) -> types.Module return importlib.import_module(REPL_NS) -def init_path(args: argparse.Namespace) -> None: - def append_once(path: str) -> None: - if path in sys.path: - return - sys.path.insert(0, path) +def _sys_path_prepend_once(path: str) -> None: + """Prepend an entry to `sys.path` if it is not already in `sys.path`.""" + if path in sys.path: + return + sys.path.insert(0, path) + +def init_path(args: argparse.Namespace, unsafe_path: str = "") -> None: + """Prepend any import group arguments to `sys.path`, including `unsafe_path` (which + defaults to the empty string) if --include-unsafe-path is specified.""" for path in args.include_path or []: p = pathlib.Path(path).resolve() - append_once(str(p)) + _sys_path_prepend_once(str(p)) - append_once("") + if args.include_unsafe_path: + _sys_path_prepend_once(unsafe_path) def _to_bool(v: Optional[str]) -> Optional[bool]: @@ -280,12 +285,35 @@ def _add_debug_arg_group(parser: argparse.ArgumentParser) -> None: def _add_import_arg_group(parser: argparse.ArgumentParser) -> None: - group = parser.add_argument_group("path options") + group = parser.add_argument_group( + "path options", + description=( + "The path options below can be used to control how Basilisp (and Python) " + "find your code." + ), + ) + group.add_argument( + "--include-unsafe-path", + action=_set_envvar_action( + "BASILISP_INCLUDE_UNSAFE_PATH", parent=argparse._StoreAction + ), + nargs="?", + const=True, + type=_to_bool, + help=( + "if true, automatically prepend a potentially unsafe path to `sys.path`; " + "this is the Basilisp equivalent to the PYTHONSAFEPATH environment variable " + "(env: BASILISP_INCLUDE_UNSAFE_PATH; default: true)" + ), + ) group.add_argument( "-p", "--include-path", action="append", - help="path to add to `sys.path`; maybe specified more than once", + help=( + "path to prepend to `sys.path`; may be specified more than once to " + "include multiple paths (env: PYTHONPATH)" + ), ) @@ -305,8 +333,8 @@ def _add_runtime_arg_group(parser: argparse.ArgumentParser) -> None: const=_to_bool(os.getenv("BASILISP_USE_DATA_READERS_ENTRY_POINT", "true")), type=_to_bool, help=( - "If true, Load data readers from importlib entry points in the " - '"basilisp_data_readers" group. (env: ' + "if true, Load data readers from importlib entry points in the " + '"basilisp_data_readers" group (env: ' "BASILISP_USE_DATA_READERS_ENTRY_POINT; default: true)" ), ) @@ -560,7 +588,6 @@ def run( opts = _compiler_opts(args) basilisp.init(opts) - init_path(args) ctx = compiler.CompilerContext( filename=( CLI_INPUT_FILE_PATH @@ -583,6 +610,7 @@ def run( cli_args_var.bind_root(vec.vector(args.args)) if args.code: + init_path(args) eval_str(target, ctx, ns, eof) elif args.load_namespace: # Set the requested namespace as the *main-ns* @@ -590,10 +618,13 @@ def run( assert main_ns_var is not None main_ns_var.bind_root(sym.symbol(target)) + init_path(args) importlib.import_module(munge(target)) elif target == STDIN_FILE_NAME: + init_path(args) eval_stream(io.TextIOWrapper(sys.stdin.buffer, encoding="utf-8"), ctx, ns) else: + init_path(args, unsafe_path=str(pathlib.Path(target).resolve())) eval_file(target, ctx, ns) diff --git a/tests/basilisp/cli_test.py b/tests/basilisp/cli_test.py index 4e480005..c87f1657 100644 --- a/tests/basilisp/cli_test.py +++ b/tests/basilisp/cli_test.py @@ -1,3 +1,4 @@ +import importlib import io import os import pathlib @@ -6,6 +7,7 @@ import secrets import stat import subprocess +import sys import tempfile import time from threading import Thread @@ -30,6 +32,16 @@ def env_vars(): os.environ[var] = val +@pytest.fixture(autouse=True) +def sys_path(): + sys_path = list(sys.path) + try: + yield + finally: + sys.path = sys_path + importlib.invalidate_caches() + + @pytest.fixture def isolated_filesystem(): with tempfile.TemporaryDirectory() as d: From 717318baaae3eddcd420f28e0cb2c0e723190e92 Mon Sep 17 00:00:00 2001 From: Chris Rink Date: Wed, 4 Sep 2024 17:19:32 -0400 Subject: [PATCH 03/12] How about that --- src/basilisp/cli.py | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/src/basilisp/cli.py b/src/basilisp/cli.py index 16c563fd..7f5169be 100644 --- a/src/basilisp/cli.py +++ b/src/basilisp/cli.py @@ -79,22 +79,21 @@ def bootstrap_repl(ctx: compiler.CompilerContext, which_ns: str) -> types.Module return importlib.import_module(REPL_NS) -def _sys_path_prepend_once(path: str) -> None: - """Prepend an entry to `sys.path` if it is not already in `sys.path`.""" - if path in sys.path: - return - sys.path.insert(0, path) - - def init_path(args: argparse.Namespace, unsafe_path: str = "") -> None: """Prepend any import group arguments to `sys.path`, including `unsafe_path` (which defaults to the empty string) if --include-unsafe-path is specified.""" - for path in args.include_path or []: - p = pathlib.Path(path).resolve() - _sys_path_prepend_once(str(p)) + + def prepend_once(path: str) -> None: + if path in sys.path: + return + sys.path.insert(0, path) + + for pth in args.include_path or []: + p = pathlib.Path(pth).resolve() + prepend_once(str(p)) if args.include_unsafe_path: - _sys_path_prepend_once(unsafe_path) + prepend_once(unsafe_path) def _to_bool(v: Optional[str]) -> Optional[bool]: @@ -294,11 +293,10 @@ def _add_import_arg_group(parser: argparse.ArgumentParser) -> None: ) group.add_argument( "--include-unsafe-path", - action=_set_envvar_action( - "BASILISP_INCLUDE_UNSAFE_PATH", parent=argparse._StoreAction - ), + action="store", nargs="?", const=True, + default=os.getenv("BASILISP_INCLUDE_UNSAFE_PATH", "true"), type=_to_bool, help=( "if true, automatically prepend a potentially unsafe path to `sys.path`; " From 37194b61f0d3c31b44d11a7dd8d74ea3d0641229 Mon Sep 17 00:00:00 2001 From: Chris Rink Date: Fri, 6 Sep 2024 16:03:37 -0400 Subject: [PATCH 04/12] Tests --- src/basilisp/cli.py | 5 +- tests/basilisp/cli_test.py | 533 +++++++++++++++++++++++++++---------- 2 files changed, 398 insertions(+), 140 deletions(-) diff --git a/src/basilisp/cli.py b/src/basilisp/cli.py index 7f5169be..48bfb858 100644 --- a/src/basilisp/cli.py +++ b/src/basilisp/cli.py @@ -300,7 +300,8 @@ def _add_import_arg_group(parser: argparse.ArgumentParser) -> None: type=_to_bool, help=( "if true, automatically prepend a potentially unsafe path to `sys.path`; " - "this is the Basilisp equivalent to the PYTHONSAFEPATH environment variable " + "setting `--include-unsafe-path=false` is the Basilisp equivalent to " + "setting PYTHONSAFEPATH to a non-empty string for CPython's REPL " "(env: BASILISP_INCLUDE_UNSAFE_PATH; default: true)" ), ) @@ -622,7 +623,7 @@ def run( init_path(args) eval_stream(io.TextIOWrapper(sys.stdin.buffer, encoding="utf-8"), ctx, ns) else: - init_path(args, unsafe_path=str(pathlib.Path(target).resolve())) + init_path(args, unsafe_path=str(pathlib.Path(target).resolve().parent)) eval_file(target, ctx, ns) diff --git a/tests/basilisp/cli_test.py b/tests/basilisp/cli_test.py index c87f1657..3f21a2ea 100644 --- a/tests/basilisp/cli_test.py +++ b/tests/basilisp/cli_test.py @@ -33,22 +33,39 @@ def env_vars(): @pytest.fixture(autouse=True) -def sys_path(): +def sys_path() -> List[str]: sys_path = list(sys.path) try: - yield + yield sys_path finally: sys.path = sys_path importlib.invalidate_caches() +@pytest.fixture() +def temp_paths(tmp_path_factory) -> List[pathlib.Path]: + paths = [] + for i in range(3): + path = tmp_path_factory.mktemp(f"dir{i}") + paths.append(path) + return paths + + +@pytest.fixture +def temp_path_args(temp_paths: List[pathlib.Path]) -> List[str]: + args = [] + for p in temp_paths: + args.extend(("-p", str(p))) + return args + + @pytest.fixture def isolated_filesystem(): with tempfile.TemporaryDirectory() as d: wd = os.getcwd() os.chdir(d) try: - yield + yield d finally: os.chdir(wd) @@ -239,161 +256,401 @@ def test_other_exception(self, run_cli): assert "basilisp.user=> basilisp.user=> " == result.out assert "Exception: CLI test" in result.err + class TestPathConfig: + @pytest.mark.parametrize( + "args", + [ + (), + ("--include-unsafe-path",), + ("--include-unsafe-path=true",), + ("--include-unsafe-path", "true"), + ], + ) + def test_repl_include_unsafe_path(self, run_cli, args): + result = run_cli( + ["repl", *args], input="(import sys) (prn (first sys/path))" + ) + assert '""\n' == result.lisp_out + assert "" == sys.path[0] -class TestRun: - cli_args_params = [ - ([], f"0{os.linesep}"), - (["--"], f"0{os.linesep}"), - (["1", "2", "3"], f"6{os.linesep}"), - (["--", "1", "2", "3"], f"6{os.linesep}"), - ] - cli_args_code = "(println (apply + (map int *command-line-args*)))" + @pytest.mark.parametrize( + "args", + [ + ("--include-unsafe-path=false",), + ("--include-unsafe-path", "false"), + ], + ) + def test_repl_do_not_include_unsafe_path(self, run_cli, args, sys_path): + result = run_cli( + ["repl", *args], input="(import sys) (prn (first sys/path))" + ) + assert '""\n' != result.lisp_out + assert sys_path[0] == sys.path[0] + + def test_repl_include_extra_path( + self, run_cli, temp_paths: List[pathlib.Path], temp_path_args: List[str] + ): + result = run_cli( + ["repl", *temp_path_args], + input="(import sys) (doseq [path sys/path] (prn path))", + ) + out_lines = set(result.lisp_out.splitlines()) + assert {'""', *map(lambda p: f'"{p}"', temp_paths)}.issubset(out_lines) + +cli_run_args_params = [ + ([], f"0{os.linesep}"), + (["--"], f"0{os.linesep}"), + (["1", "2", "3"], f"6{os.linesep}"), + (["--", "1", "2", "3"], f"6{os.linesep}"), +] +cli_run_args_code = "(println (apply + (map int *command-line-args*)))" + + +class TestRun: def test_run_ns_and_code_mutually_exclusive(self, run_cli): with pytest.raises(SystemExit): run_cli(["run", "-c", "-n"]) - def test_run_code(self, run_cli): - result = run_cli(["run", "-c", "(println (+ 1 2))"]) - assert f"3{os.linesep}" == result.lisp_out - - def test_run_code_main_ns(self, run_cli): - result = run_cli(["run", "-c", "(println *main-ns*)"]) - assert f"nil{os.linesep}" == result.lisp_out - - @pytest.mark.parametrize("args,ret", cli_args_params) - def test_run_code_with_args(self, run_cli, args: List[str], ret: str): - result = run_cli(["run", "-c", self.cli_args_code, *args]) - assert ret == result.lisp_out + class TestRunCode: + def test_run_code(self, run_cli): + result = run_cli(["run", "-c", "(println (+ 1 2))"]) + assert f"3{os.linesep}" == result.lisp_out - def test_run_file_rel(self, isolated_filesystem, run_cli): - with open("test.lpy", mode="w") as f: - f.write("(println (+ 1 2))") - result = run_cli(["run", "test.lpy"]) - assert f"3{os.linesep}" == result.lisp_out + def test_run_code_main_ns(self, run_cli): + result = run_cli(["run", "-c", "(println *main-ns*)"]) + assert f"nil{os.linesep}" == result.lisp_out - def test_run_file_abs(self, isolated_filesystem, run_cli): - with open("test.lpy", mode="w") as f: - f.write("(println (+ 1 3))") - full_path = os.path.abspath("test.lpy") - result = run_cli(["run", full_path]) - assert f"4{os.linesep}" == result.lisp_out - - def test_run_file_not_found(self, isolated_filesystem, run_cli): - with pytest.raises(FileNotFoundError): - run_cli(["run", "xyz.lpy"]) - - def test_run_file_main_ns(self, isolated_filesystem, run_cli): - with open("test.lpy", mode="w") as f: - f.write("(println *main-ns*)") - result = run_cli(["run", "test.lpy"]) - assert f"nil{os.linesep}" == result.lisp_out - - @pytest.mark.parametrize("args,ret", cli_args_params) - def test_run_file_with_args( - self, isolated_filesystem, run_cli, args: List[str], ret: str - ): - with open("test.lpy", mode="w") as f: - f.write(self.cli_args_code) - result = run_cli(["run", "test.lpy", *args]) - assert ret == result.lisp_out - - @pytest.fixture - def namespace_name(self) -> str: - return f"package.core{secrets.token_hex(4)}" - - @pytest.fixture - def namespace_file( - self, monkeypatch, tmp_path: pathlib.Path, namespace_name: str - ) -> pathlib.Path: - parent_ns, child_ns = namespace_name.split(".", maxsplit=1) - parent = tmp_path / parent_ns - parent.mkdir() - nsfile = parent / f"{child_ns}.lpy" - nsfile.touch() - monkeypatch.syspath_prepend(str(tmp_path)) - yield nsfile - - def test_cannot_run_namespace_with_in_ns_arg( - self, run_cli, namespace_name: str, namespace_file: pathlib.Path - ): - namespace_file.write_text("(println (+ 1 2))") - with pytest.raises(SystemExit): - run_cli(["run", "--in-ns", "otherpackage.core", "-n", namespace_name]) + @pytest.mark.parametrize("args,ret", cli_run_args_params) + def test_run_code_with_args(self, run_cli, args: List[str], ret: str): + result = run_cli(["run", "-c", cli_run_args_code, *args]) + assert ret == result.lisp_out - def test_run_namespace( - self, run_cli, namespace_name: str, namespace_file: pathlib.Path - ): - namespace_file.write_text(f"(ns {namespace_name}) (println (+ 1 2))") - result = run_cli(["run", "-n", namespace_name]) - assert f"3{os.linesep}" == result.lisp_out + @pytest.mark.parametrize( + "args", + [ + (), + ("--include-unsafe-path",), + ("--include-unsafe-path=true",), + ("--include-unsafe-path", "true"), + ], + ) + def test_run_code_include_unsafe_path(self, run_cli, args): + result = run_cli( + ["run", *args, "-c", "(import sys) (prn (first sys/path))"] + ) + assert '""\n' == result.lisp_out + assert "" == sys.path[0] - def test_run_namespace_main_ns( - self, run_cli, namespace_name: str, namespace_file: pathlib.Path - ): - namespace_file.write_text( - f"(ns {namespace_name}) (println (name *ns*)) (println *main-ns*)" + @pytest.mark.parametrize( + "args", + [ + ("--include-unsafe-path=false",), + ("--include-unsafe-path", "false"), + ], ) - result = run_cli(["run", "-n", namespace_name]) - assert ( - f"{namespace_name}{os.linesep}{namespace_name}{os.linesep}" - == result.lisp_out + def test_run_code_do_not_include_unsafe_path(self, run_cli, args, sys_path): + result = run_cli( + ["run", *args, "-c", "(import sys) (prn (first sys/path))"] + ) + assert '""\n' != result.lisp_out + assert sys_path[0] == sys.path[0] + + def test_run_code_include_extra_path( + self, run_cli, temp_paths: List[pathlib.Path], temp_path_args: List[str] + ): + result = run_cli( + [ + "run", + *temp_path_args, + "-c", + "(import sys) (doseq [path sys/path] (prn path))", + ] + ) + out_lines = set(result.lisp_out.splitlines()) + assert {'""', *map(lambda p: f'"{p}"', temp_paths)}.issubset(out_lines) + + class TestRunFile: + def test_run_file_rel(self, isolated_filesystem, run_cli): + with open("test.lpy", mode="w") as f: + f.write("(println (+ 1 2))") + result = run_cli(["run", "test.lpy"]) + assert f"3{os.linesep}" == result.lisp_out + + def test_run_file_abs(self, isolated_filesystem, run_cli): + with open("test.lpy", mode="w") as f: + f.write("(println (+ 1 3))") + full_path = os.path.abspath("test.lpy") + result = run_cli(["run", full_path]) + assert f"4{os.linesep}" == result.lisp_out + + def test_run_file_not_found(self, isolated_filesystem, run_cli): + with pytest.raises(FileNotFoundError): + run_cli(["run", "xyz.lpy"]) + + def test_run_file_main_ns(self, isolated_filesystem, run_cli): + with open("test.lpy", mode="w") as f: + f.write("(println *main-ns*)") + result = run_cli(["run", "test.lpy"]) + assert f"nil{os.linesep}" == result.lisp_out + + @pytest.mark.parametrize("args,ret", cli_run_args_params) + def test_run_file_with_args( + self, isolated_filesystem, run_cli, args: List[str], ret: str + ): + with open("test.lpy", mode="w") as f: + f.write(cli_run_args_code) + result = run_cli(["run", "test.lpy", *args]) + assert ret == result.lisp_out + + @pytest.mark.parametrize( + "args", + [ + (), + ("--include-unsafe-path=true",), + ("--include-unsafe-path", "true"), + ], ) - - @pytest.mark.parametrize("args,ret", cli_args_params) - def test_run_namespace_with_args( - self, - run_cli, - namespace_name: str, - namespace_file: pathlib.Path, - args: List[str], - ret: str, - ): - namespace_file.write_text(f"(ns {namespace_name}) {self.cli_args_code}") - result = run_cli(["run", "-n", namespace_name, *args]) - assert ret == result.lisp_out - - def test_run_namespace_with_subnamespace( - self, run_cli, monkeypatch, tmp_path: pathlib.Path - ): - ns_name = f"parent{secrets.token_hex(4)}" - child = "child" - - parent_ns_dir = tmp_path / ns_name - parent_ns_dir.mkdir() - - parent_ns_file = tmp_path / f"{ns_name}.lpy" - parent_ns_file.touch() - parent_ns_file.write_text( - f'(ns {ns_name} (:require [{ns_name}.{child}])) (python/print "loading:" *ns*)' + def test_run_file_include_unsafe_path(self, isolated_filesystem, run_cli, args): + with open("test.lpy", mode="w") as f: + f.write("(import sys) (prn (first sys/path))") + result = run_cli(["run", *args, "test.lpy"]) + resolved_path = pathlib.Path(isolated_filesystem).resolve() + assert f'"{resolved_path}"\n' == result.lisp_out + assert str(resolved_path) == sys.path[0] + + @pytest.mark.parametrize( + "args", + [ + ("--include-unsafe-path=false",), + ("--include-unsafe-path", "false"), + ], ) + def test_run_file_do_not_include_unsafe_path( + self, isolated_filesystem, run_cli, args, sys_path + ): + with open("test.lpy", mode="w") as f: + f.write("(import sys) (prn (first sys/path))") + result = run_cli(["run", *args, "test.lpy"]) + resolved_path = pathlib.Path(isolated_filesystem).resolve() + assert f'"{resolved_path}"\n' != result.lisp_out + assert sys_path[0] == sys.path[0] + + def test_run_file_include_extra_path( + self, + isolated_filesystem, + run_cli, + temp_paths: List[pathlib.Path], + temp_path_args: List[str], + ): + with open("test.lpy", mode="w") as f: + f.write("(import sys) (doseq [path sys/path] (prn path))") + result = run_cli(["run", *temp_path_args, "test.lpy"]) + resolved_path = pathlib.Path(isolated_filesystem).resolve() + out_lines = set(result.lisp_out.splitlines()) + assert { + f'"{resolved_path}"', + *map(lambda p: f'"{p}"', temp_paths), + }.issubset(out_lines) + + class TestRunNamespace: + @pytest.fixture + def namespace_name(self) -> str: + return f"package.core{secrets.token_hex(4)}" + + @pytest.fixture + def namespace_file( + self, monkeypatch, tmp_path: pathlib.Path, namespace_name: str + ) -> pathlib.Path: + parent_ns, child_ns = namespace_name.split(".", maxsplit=1) + parent = tmp_path / parent_ns + parent.mkdir() + nsfile = parent / f"{child_ns}.lpy" + nsfile.touch() + monkeypatch.syspath_prepend(str(tmp_path)) + yield nsfile + + def test_cannot_run_namespace_with_in_ns_arg( + self, run_cli, namespace_name: str, namespace_file: pathlib.Path + ): + namespace_file.write_text("(println (+ 1 2))") + with pytest.raises(SystemExit): + run_cli(["run", "--in-ns", "otherpackage.core", "-n", namespace_name]) + + def test_run_namespace( + self, run_cli, namespace_name: str, namespace_file: pathlib.Path + ): + namespace_file.write_text(f"(ns {namespace_name}) (println (+ 1 2))") + result = run_cli(["run", "-n", namespace_name]) + assert f"3{os.linesep}" == result.lisp_out + + def test_run_namespace_main_ns( + self, run_cli, namespace_name: str, namespace_file: pathlib.Path + ): + namespace_file.write_text( + f"(ns {namespace_name}) (println (name *ns*)) (println *main-ns*)" + ) + result = run_cli(["run", "-n", namespace_name]) + assert ( + f"{namespace_name}{os.linesep}{namespace_name}{os.linesep}" + == result.lisp_out + ) - child_ns_file = parent_ns_dir / f"{child}.lpy" - child_ns_file.touch() - child_ns_file.write_text( - f'(ns {ns_name}.{child}) (python/print "loading:" *ns*)' - ) + @pytest.mark.parametrize("args,ret", cli_run_args_params) + def test_run_namespace_with_args( + self, + run_cli, + namespace_name: str, + namespace_file: pathlib.Path, + args: List[str], + ret: str, + ): + namespace_file.write_text(f"(ns {namespace_name}) {cli_run_args_code}") + result = run_cli(["run", "-n", namespace_name, *args]) + assert ret == result.lisp_out + + def test_run_namespace_with_subnamespace( + self, run_cli, monkeypatch, tmp_path: pathlib.Path + ): + ns_name = f"parent{secrets.token_hex(4)}" + child = "child" + + parent_ns_dir = tmp_path / ns_name + parent_ns_dir.mkdir() + + parent_ns_file = tmp_path / f"{ns_name}.lpy" + parent_ns_file.touch() + parent_ns_file.write_text( + f'(ns {ns_name} (:require [{ns_name}.{child}])) (python/print "loading:" *ns*)' + ) - monkeypatch.syspath_prepend(str(tmp_path)) + child_ns_file = parent_ns_dir / f"{child}.lpy" + child_ns_file.touch() + child_ns_file.write_text( + f'(ns {ns_name}.{child}) (python/print "loading:" *ns*)' + ) - result = run_cli(["run", "-n", ns_name]) - assert f"loading: {ns_name}.{child}\nloading: {ns_name}\n" == result.out + monkeypatch.syspath_prepend(str(tmp_path)) - def test_run_stdin(self, run_cli): - result = run_cli(["run", "-"], input="(println (+ 1 2))") - assert f"3{os.linesep}" == result.lisp_out + result = run_cli(["run", "-n", ns_name]) + assert f"loading: {ns_name}.{child}\nloading: {ns_name}\n" == result.out - def test_run_stdin_main_ns(self, run_cli): - result = run_cli(["run", "-"], input="(println *main-ns*)") - assert f"nil{os.linesep}" == result.lisp_out + @pytest.mark.parametrize( + "args", + [ + (), + ("--include-unsafe-path",), + ("--include-unsafe-path=true",), + ("--include-unsafe-path", "true"), + ], + ) + def test_run_namespace_include_unsafe_path( + self, run_cli, namespace_name: str, namespace_file: pathlib.Path, args + ): + namespace_file.write_text( + f"(ns {namespace_name} (:import sys)) (prn (first sys/path))" + ) + result = run_cli(["run", *args, "-n", namespace_name]) + assert '""\n' == result.lisp_out + assert "" == sys.path[0] - @pytest.mark.parametrize("args,ret", cli_args_params) - def test_run_stdin_with_args(self, run_cli, args: List[str], ret: str): - result = run_cli( - ["run", "-", *args], - input=self.cli_args_code, + @pytest.mark.parametrize( + "args", + [ + ("--include-unsafe-path=false",), + ("--include-unsafe-path", "false"), + ], + ) + def test_run_namespace_do_not_include_unsafe_path( + self, + run_cli, + namespace_name: str, + namespace_file: pathlib.Path, + args, + sys_path, + ): + namespace_file.write_text( + f"(ns {namespace_name} (:import sys)) (prn (first sys/path))" + ) + result = run_cli(["run", *args, "-n", namespace_name]) + assert '""\n' != result.lisp_out + + def test_run_namespace_include_extra_path( + self, + run_cli, + namespace_name: str, + namespace_file: pathlib.Path, + temp_paths: List[pathlib.Path], + temp_path_args: List[str], + ): + namespace_file.write_text( + f"(ns {namespace_name} (:import sys)) (doseq [path sys/path] (prn path))" + ) + result = run_cli(["run", *temp_path_args, "-n", namespace_name]) + out_lines = set(result.lisp_out.splitlines()) + assert {'""', *map(lambda p: f'"{p}"', temp_paths)}.issubset(out_lines) + + class TestRunStdin: + def test_run_stdin(self, run_cli): + result = run_cli(["run", "-"], input="(println (+ 1 2))") + assert f"3{os.linesep}" == result.lisp_out + + def test_run_stdin_main_ns(self, run_cli): + result = run_cli(["run", "-"], input="(println *main-ns*)") + assert f"nil{os.linesep}" == result.lisp_out + + @pytest.mark.parametrize("args,ret", cli_run_args_params) + def test_run_stdin_with_args(self, run_cli, args: List[str], ret: str): + result = run_cli( + ["run", "-", *args], + input=cli_run_args_code, + ) + assert ret == result.lisp_out + + @pytest.mark.parametrize( + "args", + [ + (), + ("--include-unsafe-path=true",), + ("--include-unsafe-path", "true"), + ], ) - assert ret == result.lisp_out + def test_run_stdin_include_unsafe_path(self, run_cli, args): + result = run_cli( + ["run", *args, "-"], input="(import sys) (prn (first sys/path))" + ) + assert '""\n' == result.lisp_out + assert "" == sys.path[0] + + @pytest.mark.parametrize( + "args", + [ + ("--include-unsafe-path=false",), + ("--include-unsafe-path", "false"), + ], + ) + def test_run_stdin_do_not_include_unsafe_path(self, run_cli, args, sys_path): + result = run_cli( + ["run", *args, "-"], input="(import sys) (prn (first sys/path))" + ) + assert '""\n' != result.lisp_out + assert sys_path[0] == sys.path[0] + + def test_run_stdin_include_extra_path( + self, + run_cli, + temp_paths: List[pathlib.Path], + temp_path_args: List[str], + ): + result = run_cli( + ["run", *temp_path_args, "-"], + input="(import sys) (doseq [path sys/path] (prn path))", + ) + out_lines = set(result.lisp_out.splitlines()) + assert { + '""', + *map(lambda p: f'"{p}"', temp_paths), + }.issubset(out_lines) def test_version(run_cli): From e06cb77b92b864fc5b48da98cf2794b2f3390fdf Mon Sep 17 00:00:00 2001 From: Chris Rink Date: Fri, 6 Sep 2024 17:07:19 -0400 Subject: [PATCH 05/12] Documentation --- docs/cli.rst | 22 ++++++++++++++++++++++ docs/reader.rst | 3 ++- docs/runtime.rst | 10 ++++++++++ 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/docs/cli.rst b/docs/cli.rst index 1ff84ef6..17483752 100644 --- a/docs/cli.rst +++ b/docs/cli.rst @@ -14,6 +14,28 @@ Basilisp exposes all of it's available configuration options as CLI flags and en All Basilisp CLI subcommands which include configuration note the available configuration options when the ``-h`` and ``--help`` flags are given. Generally the Basilisp CLI configuration options are simple passthroughs that correspond to :ref:`configuration options for the compiler `. +.. _cli_path_configuration: + +``PYTHONPATH`` Configuration +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Basilisp uses the ``PYTHONPATH`` environment variable and :external:py:data:`sys.path` to determine where to look for Basilisp code when :ref:`requiring namespaces `. +Additional values may be set using the ``-p`` (or ``--include-path``) CLI flags. +Depending on how Basilisp is invoked from the CLI, an additional entry will automatically be added unless explicitly disabled using ``--include-unsafe-path=false``: + +* An empty string (which implies the current working directory) will be prepended to the ``sys.path`` in the following cases: + + * Starting a REPL + * Running a string of code directly (using ``run -c``) + * Running code directly from ``stdin`` (using ``run -``) + * Running a namespace directly (using ``run -n``) + +* When running a script directly (as by ``run /path/to/script.lpy``), the parent directory of the script will be prepended to ``sys.path`` + +.. seealso:: + + :ref:`pythonpath_configuration` + .. _start_a_repl_session: Start a REPL Session diff --git a/docs/reader.rst b/docs/reader.rst index 16dc6062..921826b6 100644 --- a/docs/reader.rst +++ b/docs/reader.rst @@ -474,9 +474,10 @@ Custom Data Readers When Basilisp starts it can load data readers from multiple sources. -It will search in :external:py:attr:`sys.path` for files named ``data_readers.lpy`` or else ``data_readers.cljc``; each which must contain a mapping of qualified symbol tags to qualified symbols of function vars. +It will search in :external:py:data:`sys.path` for files named ``data_readers.lpy`` or else ``data_readers.cljc``; each which must contain a mapping of qualified symbol tags to qualified symbols of function vars. .. code-block:: clojure + {my/tag my.namespace/tag-handler} It will also search for any :external:py:class:`importlib.metadata.EntryPoint` in the group ``basilisp_data_readers`` group. diff --git a/docs/runtime.rst b/docs/runtime.rst index 9e60e3c3..e640ff33 100644 --- a/docs/runtime.rst +++ b/docs/runtime.rst @@ -101,6 +101,16 @@ See the documentation for :lpy:fn:`require` for more details. :lpy:fn:`ns-aliases`, :lpy:fn:`ns-interns`, :lpy:fn:`ns-map`, :lpy:fn:`ns-publics`, :lpy:fn:`ns-refers`, :lpy:fn:`ns-unalias`, :lpy:fn:`ns-unmap`, :lpy:fn:`refer`, :lpy:fn:`require`, :lpy:fn:`use` +.. _pythonpath_configuration: + +``PYTHONPATH``, ``sys.path``, and Finding Basilisp Namespaces +############################################################# + +Basilisp uses the ``PYTHONPATH`` environment variable and :external:py:data:`sys.path` to determine where to look for Basilisp code when requiring namespaces. +This is roughly analogous to the Java classpath in Clojure. +These values may be set manually, but are more often configured by some project management tool such as Poetry or defined in your Python virtualenv. +These values may also be set via :ref:`cli` arguments. + .. _vars: Vars From 98fb59f1ce5691f491389c1f48cbafbb8e0b53f2 Mon Sep 17 00:00:00 2001 From: Chris Rink Date: Fri, 6 Sep 2024 17:17:59 -0400 Subject: [PATCH 06/12] Maybe --- tests/basilisp/cli_test.py | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/tests/basilisp/cli_test.py b/tests/basilisp/cli_test.py index 3f21a2ea..c59999bf 100644 --- a/tests/basilisp/cli_test.py +++ b/tests/basilisp/cli_test.py @@ -270,7 +270,7 @@ def test_repl_include_unsafe_path(self, run_cli, args): result = run_cli( ["repl", *args], input="(import sys) (prn (first sys/path))" ) - assert '""\n' == result.lisp_out + assert '""' == result.lisp_out.rstrip() assert "" == sys.path[0] @pytest.mark.parametrize( @@ -295,7 +295,9 @@ def test_repl_include_extra_path( input="(import sys) (doseq [path sys/path] (prn path))", ) out_lines = set(result.lisp_out.splitlines()) - assert {'""', *map(lambda p: f'"{p}"', temp_paths)}.issubset(out_lines) + assert {'""', *map(lambda p: f'"{p.as_posix()}"', temp_paths)}.issubset( + out_lines + ) cli_run_args_params = [ @@ -339,7 +341,7 @@ def test_run_code_include_unsafe_path(self, run_cli, args): result = run_cli( ["run", *args, "-c", "(import sys) (prn (first sys/path))"] ) - assert '""\n' == result.lisp_out + assert '""' == result.lisp_out.rstrip() assert "" == sys.path[0] @pytest.mark.parametrize( @@ -368,7 +370,9 @@ def test_run_code_include_extra_path( ] ) out_lines = set(result.lisp_out.splitlines()) - assert {'""', *map(lambda p: f'"{p}"', temp_paths)}.issubset(out_lines) + assert {'""', *map(lambda p: f'"{p.as_posix()}"', temp_paths)}.issubset( + out_lines + ) class TestRunFile: def test_run_file_rel(self, isolated_filesystem, run_cli): @@ -450,7 +454,7 @@ def test_run_file_include_extra_path( out_lines = set(result.lisp_out.splitlines()) assert { f'"{resolved_path}"', - *map(lambda p: f'"{p}"', temp_paths), + *map(lambda p: f'"{p.as_posix()}"', temp_paths), }.issubset(out_lines) class TestRunNamespace: @@ -551,7 +555,7 @@ def test_run_namespace_include_unsafe_path( f"(ns {namespace_name} (:import sys)) (prn (first sys/path))" ) result = run_cli(["run", *args, "-n", namespace_name]) - assert '""\n' == result.lisp_out + assert '""' == result.lisp_out.rstrip() assert "" == sys.path[0] @pytest.mark.parametrize( @@ -588,7 +592,9 @@ def test_run_namespace_include_extra_path( ) result = run_cli(["run", *temp_path_args, "-n", namespace_name]) out_lines = set(result.lisp_out.splitlines()) - assert {'""', *map(lambda p: f'"{p}"', temp_paths)}.issubset(out_lines) + assert {'""', *map(lambda p: f'"{p.as_posix()}"', temp_paths)}.issubset( + out_lines + ) class TestRunStdin: def test_run_stdin(self, run_cli): @@ -619,7 +625,7 @@ def test_run_stdin_include_unsafe_path(self, run_cli, args): result = run_cli( ["run", *args, "-"], input="(import sys) (prn (first sys/path))" ) - assert '""\n' == result.lisp_out + assert '""' == result.lisp_out.rstrip() assert "" == sys.path[0] @pytest.mark.parametrize( @@ -649,7 +655,7 @@ def test_run_stdin_include_extra_path( out_lines = set(result.lisp_out.splitlines()) assert { '""', - *map(lambda p: f'"{p}"', temp_paths), + *map(lambda p: f'"{p.as_posix()}"', temp_paths), }.issubset(out_lines) From 84e7f2da096d0d5970af9aed4a231d6d8ef96eb1 Mon Sep 17 00:00:00 2001 From: Chris Rink Date: Sat, 7 Sep 2024 12:12:30 -0400 Subject: [PATCH 07/12] Paths --- tests/basilisp/cli_test.py | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/tests/basilisp/cli_test.py b/tests/basilisp/cli_test.py index c59999bf..3499a45e 100644 --- a/tests/basilisp/cli_test.py +++ b/tests/basilisp/cli_test.py @@ -417,10 +417,17 @@ def test_run_file_with_args( ) def test_run_file_include_unsafe_path(self, isolated_filesystem, run_cli, args): with open("test.lpy", mode="w") as f: - f.write("(import sys) (prn (first sys/path))") + f.write( + os.linesep.join( + [ + "(import pathlib sys)", + "(prn (.as-posix (pathlib/Path (first sys/path))))", + ] + ) + ) result = run_cli(["run", *args, "test.lpy"]) resolved_path = pathlib.Path(isolated_filesystem).resolve() - assert f'"{resolved_path}"\n' == result.lisp_out + assert f'"{resolved_path}"' == result.lisp_out.rstrip() assert str(resolved_path) == sys.path[0] @pytest.mark.parametrize( @@ -434,9 +441,16 @@ def test_run_file_do_not_include_unsafe_path( self, isolated_filesystem, run_cli, args, sys_path ): with open("test.lpy", mode="w") as f: - f.write("(import sys) (prn (first sys/path))") + f.write( + os.linesep.join( + [ + "(import pathlib sys)", + "(prn (.as-posix (pathlib/Path (first sys/path))))", + ] + ) + ) result = run_cli(["run", *args, "test.lpy"]) - resolved_path = pathlib.Path(isolated_filesystem).resolve() + resolved_path = pathlib.Path(isolated_filesystem).resolve().as_posix() assert f'"{resolved_path}"\n' != result.lisp_out assert sys_path[0] == sys.path[0] @@ -448,9 +462,17 @@ def test_run_file_include_extra_path( temp_path_args: List[str], ): with open("test.lpy", mode="w") as f: - f.write("(import sys) (doseq [path sys/path] (prn path))") + f.write( + os.linesep.join( + [ + "(import pathlib sys)", + "(doseq [path sys/path]", + " (prn (.as-posix (pathlib/Path path))))", + ] + ) + ) result = run_cli(["run", *temp_path_args, "test.lpy"]) - resolved_path = pathlib.Path(isolated_filesystem).resolve() + resolved_path = pathlib.Path(isolated_filesystem).resolve().as_posix() out_lines = set(result.lisp_out.splitlines()) assert { f'"{resolved_path}"', From 81fc5cce0ef8024ba1e4f3fe83261424eee82c71 Mon Sep 17 00:00:00 2001 From: Chris Rink Date: Sat, 7 Sep 2024 12:37:57 -0400 Subject: [PATCH 08/12] Show my your secrets --- tests/basilisp/cli_test.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/basilisp/cli_test.py b/tests/basilisp/cli_test.py index 3499a45e..cda85670 100644 --- a/tests/basilisp/cli_test.py +++ b/tests/basilisp/cli_test.py @@ -427,6 +427,7 @@ def test_run_file_include_unsafe_path(self, isolated_filesystem, run_cli, args): ) result = run_cli(["run", *args, "test.lpy"]) resolved_path = pathlib.Path(isolated_filesystem).resolve() + print(result.lisp_out.rstrip()) assert f'"{resolved_path}"' == result.lisp_out.rstrip() assert str(resolved_path) == sys.path[0] @@ -451,7 +452,8 @@ def test_run_file_do_not_include_unsafe_path( ) result = run_cli(["run", *args, "test.lpy"]) resolved_path = pathlib.Path(isolated_filesystem).resolve().as_posix() - assert f'"{resolved_path}"\n' != result.lisp_out + print(result.lisp_out.rstrip()) + assert f'"{resolved_path}"' != result.lisp_out.rstrip() assert sys_path[0] == sys.path[0] def test_run_file_include_extra_path( @@ -474,6 +476,7 @@ def test_run_file_include_extra_path( result = run_cli(["run", *temp_path_args, "test.lpy"]) resolved_path = pathlib.Path(isolated_filesystem).resolve().as_posix() out_lines = set(result.lisp_out.splitlines()) + print(out_lines) assert { f'"{resolved_path}"', *map(lambda p: f'"{p.as_posix()}"', temp_paths), From 25ff8fd10a72500391a187631243b111b2b86951 Mon Sep 17 00:00:00 2001 From: Chris Rink Date: Sat, 7 Sep 2024 12:51:13 -0400 Subject: [PATCH 09/12] Windows paths are hell --- tests/basilisp/cli_test.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/basilisp/cli_test.py b/tests/basilisp/cli_test.py index cda85670..595ceecf 100644 --- a/tests/basilisp/cli_test.py +++ b/tests/basilisp/cli_test.py @@ -427,7 +427,9 @@ def test_run_file_include_unsafe_path(self, isolated_filesystem, run_cli, args): ) result = run_cli(["run", *args, "test.lpy"]) resolved_path = pathlib.Path(isolated_filesystem).resolve() + print(resolved_path) print(result.lisp_out.rstrip()) + print(sys.path[0]) assert f'"{resolved_path}"' == result.lisp_out.rstrip() assert str(resolved_path) == sys.path[0] @@ -452,7 +454,10 @@ def test_run_file_do_not_include_unsafe_path( ) result = run_cli(["run", *args, "test.lpy"]) resolved_path = pathlib.Path(isolated_filesystem).resolve().as_posix() + print(resolved_path) print(result.lisp_out.rstrip()) + print(sys_path[0]) + print(sys.path[0]) assert f'"{resolved_path}"' != result.lisp_out.rstrip() assert sys_path[0] == sys.path[0] @@ -475,6 +480,7 @@ def test_run_file_include_extra_path( ) result = run_cli(["run", *temp_path_args, "test.lpy"]) resolved_path = pathlib.Path(isolated_filesystem).resolve().as_posix() + print(resolved_path) out_lines = set(result.lisp_out.splitlines()) print(out_lines) assert { From bdab70fcac3b0154139bfb3b81061d0fa3af49e6 Mon Sep 17 00:00:00 2001 From: Chris Rink Date: Sat, 7 Sep 2024 13:13:19 -0400 Subject: [PATCH 10/12] Maybe this --- tests/basilisp/cli_test.py | 50 ++++++++++++++++++++++++++------------ 1 file changed, 35 insertions(+), 15 deletions(-) diff --git a/tests/basilisp/cli_test.py b/tests/basilisp/cli_test.py index 595ceecf..969779c3 100644 --- a/tests/basilisp/cli_test.py +++ b/tests/basilisp/cli_test.py @@ -292,14 +292,22 @@ def test_repl_include_extra_path( ): result = run_cli( ["repl", *temp_path_args], - input="(import sys) (doseq [path sys/path] (prn path))", + input=" ".join( + [ + f"(import pathlib sys)", + "(doseq [path sys/path]", + " (prn (.as-posix (pathlib/Path path))))", + ] + ), ) out_lines = set(result.lisp_out.splitlines()) - assert {'""', *map(lambda p: f'"{p.as_posix()}"', temp_paths)}.issubset( + assert {'"."', *map(lambda p: f'"{p.as_posix()}"', temp_paths)}.issubset( out_lines ) +m + cli_run_args_params = [ ([], f"0{os.linesep}"), (["--"], f"0{os.linesep}"), @@ -366,11 +374,17 @@ def test_run_code_include_extra_path( "run", *temp_path_args, "-c", - "(import sys) (doseq [path sys/path] (prn path))", + os.linesep.join( + [ + f"(import pathlib sys)", + "(doseq [path sys/path]", + " (prn (.as-posix (pathlib/Path path))))", + ] + ), ] ) out_lines = set(result.lisp_out.splitlines()) - assert {'""', *map(lambda p: f'"{p.as_posix()}"', temp_paths)}.issubset( + assert {'"."', *map(lambda p: f'"{p.as_posix()}"', temp_paths)}.issubset( out_lines ) @@ -430,7 +444,7 @@ def test_run_file_include_unsafe_path(self, isolated_filesystem, run_cli, args): print(resolved_path) print(result.lisp_out.rstrip()) print(sys.path[0]) - assert f'"{resolved_path}"' == result.lisp_out.rstrip() + assert f'"{resolved_path.as_posix()}"' == result.lisp_out.rstrip() assert str(resolved_path) == sys.path[0] @pytest.mark.parametrize( @@ -454,10 +468,6 @@ def test_run_file_do_not_include_unsafe_path( ) result = run_cli(["run", *args, "test.lpy"]) resolved_path = pathlib.Path(isolated_filesystem).resolve().as_posix() - print(resolved_path) - print(result.lisp_out.rstrip()) - print(sys_path[0]) - print(sys.path[0]) assert f'"{resolved_path}"' != result.lisp_out.rstrip() assert sys_path[0] == sys.path[0] @@ -480,9 +490,7 @@ def test_run_file_include_extra_path( ) result = run_cli(["run", *temp_path_args, "test.lpy"]) resolved_path = pathlib.Path(isolated_filesystem).resolve().as_posix() - print(resolved_path) out_lines = set(result.lisp_out.splitlines()) - print(out_lines) assert { f'"{resolved_path}"', *map(lambda p: f'"{p.as_posix()}"', temp_paths), @@ -619,11 +627,17 @@ def test_run_namespace_include_extra_path( temp_path_args: List[str], ): namespace_file.write_text( - f"(ns {namespace_name} (:import sys)) (doseq [path sys/path] (prn path))" + os.linesep.join( + [ + f"(ns {namespace_name} (:import pathlib sys))", + "(doseq [path sys/path]", + " (prn (.as-posix (pathlib/Path path))))", + ] + ) ) result = run_cli(["run", *temp_path_args, "-n", namespace_name]) out_lines = set(result.lisp_out.splitlines()) - assert {'""', *map(lambda p: f'"{p.as_posix()}"', temp_paths)}.issubset( + assert {'"."', *map(lambda p: f'"{p.as_posix()}"', temp_paths)}.issubset( out_lines ) @@ -681,11 +695,17 @@ def test_run_stdin_include_extra_path( ): result = run_cli( ["run", *temp_path_args, "-"], - input="(import sys) (doseq [path sys/path] (prn path))", + input=os.linesep.join( + [ + f"(import pathlib sys)", + "(doseq [path sys/path]", + " (prn (.as-posix (pathlib/Path path))))", + ] + ), ) out_lines = set(result.lisp_out.splitlines()) assert { - '""', + '"."', *map(lambda p: f'"{p.as_posix()}"', temp_paths), }.issubset(out_lines) From 63ff21f3989094ca15d414eec686416096af1271 Mon Sep 17 00:00:00 2001 From: Chris Rink Date: Sat, 7 Sep 2024 13:28:05 -0400 Subject: [PATCH 11/12] Good waste of everyone's time --- tests/basilisp/cli_test.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/basilisp/cli_test.py b/tests/basilisp/cli_test.py index 969779c3..33424a1c 100644 --- a/tests/basilisp/cli_test.py +++ b/tests/basilisp/cli_test.py @@ -306,8 +306,6 @@ def test_repl_include_extra_path( ) -m - cli_run_args_params = [ ([], f"0{os.linesep}"), (["--"], f"0{os.linesep}"), From 6f087b834247dd755483d8fec6bf322abbe5646b Mon Sep 17 00:00:00 2001 From: Chris Rink Date: Sat, 7 Sep 2024 13:39:43 -0400 Subject: [PATCH 12/12] Remove the unnecessary prints --- tests/basilisp/cli_test.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/basilisp/cli_test.py b/tests/basilisp/cli_test.py index 33424a1c..e8db7bf5 100644 --- a/tests/basilisp/cli_test.py +++ b/tests/basilisp/cli_test.py @@ -439,9 +439,6 @@ def test_run_file_include_unsafe_path(self, isolated_filesystem, run_cli, args): ) result = run_cli(["run", *args, "test.lpy"]) resolved_path = pathlib.Path(isolated_filesystem).resolve() - print(resolved_path) - print(result.lisp_out.rstrip()) - print(sys.path[0]) assert f'"{resolved_path.as_posix()}"' == result.lisp_out.rstrip() assert str(resolved_path) == sys.path[0]