Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ Use the magic variable `lines` for a list of rstripped lines or `stdin` for `sys

```sh
# pyp like wc -c
cat /usr/share/dict/words | pyp 'len(stdin.read())'
cat /usr/share/dict/words | pyp 'len(stdin.buffer.read())'

# pyp like awk
seq 1 5 | pyp 'sum(map(int, lines))'
Expand Down
147 changes: 87 additions & 60 deletions tests/test_pyp.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,13 @@ def run_pyp(cmd: Union[str, List[str]], input: Optional[str] = None) -> str:
if cmd[0] == "pyp":
del cmd[0]

if input and isinstance(input, str):
input = input.encode()

output = io.StringIO()
with contextlib.redirect_stdout(output):
try:
old_stdin = sys.stdin
sys.stdin = io.StringIO(input)
pyp.run_pyp(pyp.parse_options(cmd))
finally:
sys.stdin = old_stdin
stdin = io.TextIOWrapper(io.BytesIO(input), encoding="utf-8")
with contextlib.redirect_stdout(output), patch("sys.stdin", stdin):
pyp.run_pyp(pyp.parse_options(cmd))
return output.getvalue()


Expand Down Expand Up @@ -80,61 +79,89 @@ def compare_scripts(explain_output: str, script: str) -> None:
# ====================


def test_examples():
"""Test approximately the examples in the README."""
def case(
example_cmd: str,
pyp_cmd: str,
input: Optional[str] = None,
use_subprocess: bool = False,
*,
marks=(),
):
return pytest.param(
example_cmd, pyp_cmd, input, use_subprocess, marks=marks, id=f"echo {input} | {pyp_cmd}"
)

example = "python\nsnake\nss\nmagpy\npiethon\nreadme.md\n"

compare_command(example_cmd="cut -c1-4", pyp_cmd="pyp 'x[:4]'", input=example)
compare_command(
example_cmd="wc -c | tr -d ' '", pyp_cmd="pyp 'len(stdin.read())'", input=example
)
compare_command(
example_cmd="awk '{s+=$1} END {print s}' ",
pyp_cmd="pyp 'sum(map(int, lines))'",
input="1\n2\n3\n4\n5\n",
)
compare_command(
example_cmd="sh",
pyp_cmd="pyp 'subprocess.run(lines[0], shell=True); pass'",
input="echo echo",
use_subprocess=True,
)
compare_command(
example_cmd="""jq -r '.[1]["lol"]'""",
pyp_cmd="""pyp 'json.load(stdin)[1]["lol"]'""",
input='[0, {"lol": "hmm"}, 0]',
)
compare_command(
example_cmd="grep -E '(py|md)'",
pyp_cmd="""pyp 'x if re.search("(py|md)", x) else None'""",
input=example,
)
compare_command(example_cmd="echo 'sqrt(9.0)' | bc", pyp_cmd="pyp 'sqrt(9)'")
compare_command(
example_cmd="x=README.md; echo ${x##*.}", pyp_cmd='''pyp "Path('README.md').suffix[1:]"'''
)
compare_command(
example_cmd="echo ' 1 a\n 2 b'", pyp_cmd="""pyp 'f"{idx+1: >3} {x}"'""", input="a\nb"
)
compare_command(
example_cmd="grep py", pyp_cmd="""pyp 'x if "py" in x else None'""", input=example
)
compare_command(example_cmd="tail -n 3", pyp_cmd="pyp 'lines[-3:]'", input=example)
compare_command(example_cmd="sort", pyp_cmd="pyp 'sorted(lines)'", input=example)
compare_command(
example_cmd="echo 'Sorting 2 lines\n1\n2'",
pyp_cmd="""pyp 'print(f"Sorting {len(lines)} lines"); pypprint(sorted(lines))'""",
input="2\n1\n",
)
compare_command(
example_cmd="sort | uniq", pyp_cmd="pyp 'sorted(set(lines))'", input="2\n1\n1\n1\n3"
)
compare_command(
example_cmd='''echo "a: ['1', '3']\nb: ['2']"''',
pyp_cmd="pyp -b 'd = defaultdict(list)' 'k, v = x.split(); d[k].append(v)' -a 'd'",
input="a 1\nb 2\na 3",
)
example = "🐍\npython\nsnake\nss\nmagpy\npiethon\nreadme.md\n"


@pytest.mark.parametrize(
"example_cmd, pyp_cmd, input, use_subprocess",
(
case(example_cmd="cut -c1-4", pyp_cmd="pyp 'x[:4]'", input=example),
case(
example_cmd="wc -c | tr -d ' '", pyp_cmd="pyp 'len(stdin.buffer.read())'", input=example
),
case(
example_cmd="awk '{s+=$1} END {print s}' ",
pyp_cmd="pyp 'sum(map(int, lines))'",
input="1\n2\n3\n4\n5\n",
),
case(
example_cmd="sh",
pyp_cmd="pyp 'subprocess.run(lines[0], shell=True); pass'",
input="echo echo",
use_subprocess=True,
),
case(
example_cmd="""jq -r '.[1]["lol"]'""",
pyp_cmd="""pyp 'json.load(stdin)[1]["lol"]'""",
input='[0, {"lol": "hmm"}, 0]',
),
case(
example_cmd="grep -E '(py|md)'",
pyp_cmd="""pyp 'x if re.search("(py|md)", x) else None'""",
input=example,
),
case(example_cmd="echo 'sqrt(9.0)' | bc", pyp_cmd="pyp 'sqrt(9)'"),
case(
example_cmd="x=README.md; echo ${x##*.}",
pyp_cmd='''pyp "Path('README.md').suffix[1:]"''',
),
case(
example_cmd="echo ' 1 a\n 2 b'", pyp_cmd="""pyp 'f"{idx+1: >3} {x}"'""", input="a\nb"
),
case(example_cmd="grep py", pyp_cmd="""pyp 'x if "py" in x else None'""", input=example),
case(example_cmd="tail -n 3", pyp_cmd="pyp 'lines[-3:]'", input=example),
case(
example_cmd="sort",
pyp_cmd="pyp 'sorted(lines)'",
input=example,
marks=pytest.mark.xfail(
condition=sys.platform != "darwin", # should still pass on macOS
reason="sort deals with UTF-8 differently on different platforms, "
"see https://github.com/hauntsaninja/pyp/pull/27#issuecomment-1200105035",
raises=AssertionError,
),
),
# same as above, but without emoji, should pass everywhere
case(example_cmd="sort", pyp_cmd="pyp 'sorted(lines)'", input=example[2:]),
case(
example_cmd="echo 'Sorting 2 lines\n1\n2'",
pyp_cmd="""pyp 'print(f"Sorting {len(lines)} lines"); pypprint(sorted(lines))'""",
input="2\n1\n",
),
case(example_cmd="sort | uniq", pyp_cmd="pyp 'sorted(set(lines))'", input="2\n1\n1\n1\n3"),
case(
example_cmd='''echo "a: ['1', '3']\nb: ['2']"''',
pyp_cmd="pyp -b 'd = defaultdict(list)' 'k, v = x.split(); d[k].append(v)' -a 'd'",
input="a 1\nb 2\na 3",
),
),
)
def test_examples(example_cmd, pyp_cmd, input, use_subprocess):
"""Test approximately the examples in the README."""
compare_command(example_cmd, pyp_cmd, input, use_subprocess)


def test_magic_variable_failures():
Expand Down