# pyflyby/test_py.py

# License for THIS FILE ONLY: CC0 Public Domain Dedication
# http://creativecommons.org/publicdomain/zero/1.0/

from __future__ import (absolute_import, division, print_function,
                        with_statement)

import os
import pytest
from   shutil                   import rmtree
import six
import subprocess
import sys
import tempfile
from   tempfile                 import NamedTemporaryFile, mkdtemp
from   textwrap                 import dedent

from   six                      import PY2, PY3, string_types

import pyflyby
from   pyflyby._file            import Filename
from   pyflyby._util            import cached_attribute


PYFLYBY_HOME = Filename(__file__).real.dir.dir
BIN_DIR = PYFLYBY_HOME / "bin"
PYFLYBY_PATH = PYFLYBY_HOME / "etc/pyflyby"


def flatten(args):
    result = []
    for arg in args:
        if isinstance(arg, six.string_types):
            result.append(arg)
        else:
            try:
                it = iter(arg)
            except TypeError:
                result.append(arg)
            else:
                result.extend(flatten(it))
    return result


python = sys.executable

def pipe(command, stdin="", env=None, retretcode="automatic"):
    if command[0] != python:
        command = (python,) + command
    proc = subprocess.Popen(
        command,
        stdin=subprocess.PIPE,
        stdout=subprocess.PIPE,
        stderr=subprocess.STDOUT,
        env=env
    )
    stdout = proc.communicate(stdin)[0].strip().decode('utf-8')
    retcode = proc.returncode
    assert retcode >= 0
    if retretcode == True:
        return stdout, retcode
    elif retretcode == False:
        return stdout
    elif retretcode == 'automatic':
        if retcode:
            return stdout, retcode
        else:
            return stdout
    else:
        raise ValueError("invalid retretcode=%r" % (retretcode,))


@pytest.fixture
def tmp(request):
    return _TmpFixture(request)


class _TmpFixture(object):
    def __init__(self, request):
        self._request = request

    @cached_attribute
    def dir(self):
        """
        Single memoized new_tempdir()
        """
        return self.new_tempdir()

    def new_tempdir(self):
        d = mkdtemp(prefix="pyflyby_test_", suffix=".tmp")
        self._request.addfinalizer(lambda: rmtree(d))
        return Filename(d).real


def _build_pythonpath(PYTHONPATH):
    """
    Build PYTHONPATH value to use.

    :rtype:
      ``str``
    """
    pypath = [os.path.dirname(os.path.dirname(pyflyby.__file__))]
    if isinstance(PYTHONPATH, (Filename,) + string_types):
        PYTHONPATH = [PYTHONPATH]
    PYTHONPATH = [str(Filename(d)) for d in PYTHONPATH]
    pypath += PYTHONPATH
    pypath += os.environ["PYTHONPATH"].split(":")
    return ":".join(pypath)


def _py_internal_1(args, stdin="",
                   PYTHONPATH=[],
                   PYFLYBY_PATH=PYFLYBY_PATH):
    env = dict(os.environ)
    env["PYFLYBY_PATH" ] = str(Filename(PYFLYBY_PATH))
    env["PYTHONPATH"   ] = _build_pythonpath(PYTHONPATH)
    env["PYTHONSTARTUP"] = ""
    prog = str(BIN_DIR/"py")
    return pipe((prog,) + args, stdin=stdin, env=env)


def py(*args, **kwargs):
    """
    Run ``py`` and return stdout.
    """
    if len(args) == 1 and isinstance(args[0], (tuple, list)):
        args = tuple(args[0])
    return _py_internal_1(args, **kwargs)


def writetext(filename, text, mode='w'):
    text = dedent(text)
    filename = Filename(filename)
    with open(str(filename), mode) as f:
        f.write(text)
    return filename


def test_0prefix_raw_1():
    # Verify that we're testing the virtualenv we think we are.
    result = pipe([python, '-c', 'import sys; print(sys.prefix)'])
    expected = sys.prefix
    assert expected == result


def test_0version_1():
    # Verify that we're testing the version we think we are.
    result = py('-q', '--print', 'sys.version').strip()
    expected = sys.version
    assert expected == result


def test_0prefix_1():
    # Verify that we're testing the virtualenv we think we are.
    result = py('-q', '--print', 'sys.prefix').strip()
    expected = sys.prefix
    assert expected == result


def test_eval_1():
    result = py("--eval", "b64decode('VGhvbXBzb24=')")
    expected = dedent("""
        [PYFLYBY] from base64 import b64decode
        [PYFLYBY] b64decode('VGhvbXBzb24=')
        b'Thompson'
    """).strip()
    if PY2:
        expected = expected.replace("b'Thompson'", "'Thompson'")
    assert expected == result


def test_eval_single_dash_1():
    result = py("-eval", "b64decode('V29vc3Rlcg==')")
    expected = dedent("""
        [PYFLYBY] from base64 import b64decode
        [PYFLYBY] b64decode('V29vc3Rlcg==')
        b'Wooster'
    """).strip()
    if PY2:
        expected = expected.replace("b'Wooster'", "'Wooster'")
    assert expected == result


def test_eval_equals_1():
    result = py("--eval=b64decode('TWVyY2Vy')")
    expected = dedent("""
        [PYFLYBY] from base64 import b64decode
        [PYFLYBY] b64decode('TWVyY2Vy')
        b'Mercer'
    """).strip()
    if PY2:
        expected = expected.replace("b'Mercer'", "'Mercer'")
    assert expected == result


def test_eval_single_dash_equals_1():
    result = py("-eval=b64decode('VW5pdmVyc2l0eQ==')")
    expected = dedent("""
        [PYFLYBY] from base64 import b64decode
        [PYFLYBY] b64decode('VW5pdmVyc2l0eQ==')
        b'University'
    """).strip()
    if PY2:
        expected = expected.replace("b'University'", "'University'")
    assert expected == result


def test_eval_c_1():
    result = py("-c", "b64decode('QmxlZWNrZXI=')")
    expected = dedent("""
        [PYFLYBY] from base64 import b64decode
        [PYFLYBY] b64decode('QmxlZWNrZXI=')
        b'Bleecker'
    """).strip()
    if PY2:
        expected = expected.replace("b'Bleecker'", "'Bleecker'")
    assert expected == result


def test_eval_quiet_1():
    result = py("-q", "-c", "b64decode('U3VsbGl2YW4=')")
    if PY2:
        expected = "'Sullivan'"
    else:
        expected = "b'Sullivan'"
    assert expected == result


def test_eval_expression_1():
    result = py("--eval", "calendar.WEDNESDAY")
    expected = dedent("""
        [PYFLYBY] import calendar
        [PYFLYBY] calendar.WEDNESDAY
        2
    """).strip()
    assert expected == result


def test_eval_expression_quiet_1():
    result = py("--quiet", "--eval", "calendar.WEDNESDAY")
    expected = "2"
    assert expected == result


def test_exec_1():
    result = py("-c", "if 1: print(b64decode('UHJpbmNl'))")
    expected = dedent("""
        [PYFLYBY] from base64 import b64decode
        [PYFLYBY] if 1: print(b64decode('UHJpbmNl'))
        b'Prince'
    """).strip()
    if PY2:
        expected = expected.replace("b'Prince'", "Prince")
    assert expected == result


def test_argv_1():
    result = py("-c", "sys.argv", "x", "y")
    expected = dedent("""
        [PYFLYBY] import sys
        [PYFLYBY] sys.argv
        ['-c', 'x', 'y']
    """).strip()
    assert expected == result


def test_argv_2():
    result = py("-c", "sys.argv", "--debug", "-x  x")
    expected = dedent("""
        [PYFLYBY] import sys
        [PYFLYBY] sys.argv
        ['-c', '--debug', '-x  x']
    """).strip()
    assert expected == result


def test_file_1():
    with tempfile.NamedTemporaryFile(suffix=".py", mode='w+') as f:
        f.write('print(("Boone", sys.argv))\n')
        f.flush()
        result = py(f.name, "a", "b")
    expected = dedent("""
        [PYFLYBY] import sys
        ('Boone', [%r, 'a', 'b'])
    """).strip() % (f.name,)
    assert expected == result


@pytest.mark.parametrize("arg", [
    "--file", "-file", "file", "%file",
    "--f", "-f", "f", "%f",
    "--execfile", "-execfile", "execfile", "%execfile",
    "--execf", "-execf", "execf", "%execf",
    "--runfile", "-runfile", "runfile", "%runfile",
    "--run", "-run", "run", "%run",
])
def test_file_variants_1(arg):
    with tempfile.NamedTemporaryFile(suffix=".py", mode='w+') as f:
        f.write('print(("Longfellow", sys.argv))\n')
        f.write("print(__file__)\n")
        f.flush()
        result = py(f.name, "a", "b")
    expected = dedent("""
        [PYFLYBY] import sys
        ('Longfellow', [%r, 'a', 'b'])
        %s
    """).strip() % (f.name, f.name)
    assert expected == result


def test_apply_1():
    result = py("--apply", "str.upper", "'Roosevelt'")
    expected = dedent("""
        [PYFLYBY] str.upper('Roosevelt')
        'ROOSEVELT'
    """).strip()
    assert expected == result


def test_apply_stdin_1():
    result = py("--apply", "str.upper", "-", stdin=b"Eagle")
    expected = dedent("""
        [PYFLYBY] str.upper('Eagle')
        'EAGLE'
    """).strip()
    assert expected == result


def test_apply_stdin_more_args_1():
    result = py("--apply", "str.find", "-", "'k'", stdin=b"Jackson\n")
    expected = dedent(r"""
        [PYFLYBY] str.find('Jackson\n', 'k')
        3
    """).strip()
    assert expected == result


def test_apply_lambda_1():
    result = py("--apply", "lambda a,b:a*b", "6", "7")
    expected = dedent("""
        [PYFLYBY] (lambda a,b:a*b)(6, 7)
        42
    """).strip()
    assert expected == result


def test_apply_lambda_nested_1():
    result = py("--apply", "(lambda a,b: lambda c,d: a*b*c*d)(2,3)", "5", "11")
    expected = dedent("""
        [PYFLYBY] (lambda a,b: lambda c,d: a*b*c*d)(2,3)(5, 11)
        330
    """).strip()
    assert expected == result


def test_apply_args_1():
    result = py("--apply", "round", "2.984375", "3")
    expected = dedent("""
        [PYFLYBY] round(2.984375, 3)
        2.984
    """).strip()
    assert expected == result


def test_apply_kwargs_1():
    result = py("--apply", "round", "2.984375", "--ndigits=3")
    expected = dedent("""
        [PYFLYBY] round(2.984375, ndigits=3)
        2.984
    """).strip()
    assert expected == result


@pytest.mark.skipif(
    sys.version_info[0] != 3, reason="keyword-only args require Python 3"
)
def test_apply_kwonlyargs_1():
    result = py("--apply", "lambda x, *, y: x + y", "2", "--y=3")
    expected = dedent("""
        [PYFLYBY] (lambda x, *, y: x + y)(2, y=3)
        5
    """).strip()
    assert expected == result


@pytest.mark.skipif(
    sys.version_info[0] != 3, reason="keyword-only args require Python 3"
)
def test_apply_kwonlyargs_2():
    result = py("--apply", "lambda x, *, y, z=1: x + y + z", "2", "--y=3")
    expected = dedent("""
        [PYFLYBY] (lambda x, *, y, z=1: x + y + z)(2, y=3, z=1)
        6
    """).strip()
    assert expected == result


@pytest.mark.skipif(
    sys.version_info[0] != 3, reason="keyword-only args require Python 3"
)
def test_apply_kwonlyargs_3():
    result = py("--apply", "lambda x, *, y, z=1: x + y + z", "2", "--y=3", "--z=4")
    expected = dedent("""
        [PYFLYBY] (lambda x, *, y, z=1: x + y + z)(2, y=3, z=4)
        9
    """).strip()
    assert expected == result


@pytest.mark.skipif(
    sys.version_info[0] != 3, reason="keyword-only args require Python 3"
)
def test_apply_kwonlyargs_4():
    result = py("--apply", "lambda x, *, y, z=1: x + y + z", "2", "3")
    expected = dedent("""
        [PYFLYBY] missing required keyword argument y

        Python signature:
          >>> (lambda x, *, y, z=1: x + y + z)(x, *, y, z=1)

        Command-line signature:
          $ py 'lambda x, *, y, z=1: x + y + z' x --y=... [--z=...]
          $ py 'lambda x, *, y, z=1: x + y + z' --x=... --y=... [--z=...]

        Filename:
          <unknown>

        Docstring:
          (No docstring)
    """).strip(), 1
    assert expected == result


@pytest.mark.skipif(
    sys.version_info[0] != 3, reason="keyword-only args require Python 3"
)
def test_apply_kwonlyargs_5():
    result = py("--apply", "lambda x, *, y, z=1: x + y + z", "2", "--z=3")
    expected = dedent("""
        [PYFLYBY] missing required keyword argument y

        Python signature:
          >>> (lambda x, *, y, z=1: x + y + z)(x, *, y, z=1)

        Command-line signature:
          $ py 'lambda x, *, y, z=1: x + y + z' x --y=... [--z=...]
          $ py 'lambda x, *, y, z=1: x + y + z' --x=... --y=... [--z=...]

        Filename:
          <unknown>

        Docstring:
          (No docstring)
    """).strip(), 1
    assert expected == result


@pytest.mark.skipif(
    sys.version_info[0] != 3, reason="keyword-only args require Python 3"
)
def test_apply_unknown_option():
    result = py("--apply", "lambda a, *, b=10, c: a + b", "2", "--b=15", "-c=1", "-d=6")
    expected = dedent("""
        [PYFLYBY] Unknown option name d

        Python signature:
          >>> (lambda a, *, b=10, c: a + b)(a, *, b=10, c)

        Command-line signature:
          $ py 'lambda a, *, b=10, c: a + b' a [--b=...] --c=...
          $ py 'lambda a, *, b=10, c: a + b' --a=... [--b=...] --c=...

        Filename:
          <unknown>

        Docstring:
          (No docstring)
    """).strip(), 1
    assert expected == result


def test_apply_print_function_1():
    result = py("--apply", "print", "50810461")
    expected = dedent("""
        [PYFLYBY] print(50810461)
        50810461
    """).strip()
    assert expected == result


def test_apply_print_function_string_1():
    result = py("--apply", "print", "'Bedford'")
    expected = dedent("""
        [PYFLYBY] print('Bedford')
        Bedford
    """).strip()
    assert expected == result


def test_apply_print_function_expression_1():
    result = py("--apply", "print", "Bedford")
    expected = dedent("""
        [PYFLYBY] print('Bedford')
        Bedford
    """).strip()
    assert expected == result


def test_apply_variant_1():
    result = py("--call", "round", "2.984375", "3")
    expected = dedent("""
        [PYFLYBY] round(2.984375, 3)
        2.984
    """).strip()
    assert expected == result


def test_apply_expression_1():
    result = py("--call", "3.0.is_integer")
    expected = dedent("""
        [PYFLYBY] 3.0.is_integer()
        True
    """).strip()
    assert expected == result


def test_argmode_string_1():
    result = py("--args=string", "--apply", "print", "Barrow")
    expected = dedent("""
        [PYFLYBY] print('Barrow')
        Barrow
    """).strip()
    assert expected == result


def test_argmode_string_donteval_module_1():
    result = py("--args=string", "--apply", "print", "sys")
    expected = dedent("""
        [PYFLYBY] print('sys')
        sys
    """).strip()
    assert expected == result


def test_argmode_string_donteval_expression_1():
    result = py("--args=string", "--apply", "print", "1+2")
    expected = dedent("""
        [PYFLYBY] print('1+2')
        1+2
    """).strip()
    assert expected == result


@pytest.mark.parametrize("args", [
    "--args=string",
    "--args string",
    "--arguments=strings",
    "--arguments string",
    "--argmode strs",
    "--arg_mode=str",
    "--arg-mode=s",
])
def test_argmode_string_variants_1(args):
    result = py((args+" --apply print sys").split())
    expected = dedent("""
        [PYFLYBY] print('sys')
        sys
    """).strip()
    assert expected == result


def test_argmode_string_quoted_1():
    result = py("--args=string", "--apply", "print", "'Jones'")
    expected = dedent("""
        [PYFLYBY] print("'Jones'")
        'Jones'
    """).strip()
    assert expected == result


def test_argmode_eval_1():
    result = py("--args=eval", "--apply", "print", "'Vandam'")
    expected = dedent("""
        [PYFLYBY] print('Vandam')
        Vandam
    """).strip()
    assert expected == result


def test_argmode_eval_expression_1():
    result = py("--args=eval", "--apply", "print", "1+2")
    expected = dedent("""
        [PYFLYBY] print(3)
        3
    """).strip()
    assert expected == result


def test_argmode_eval_unparsable_1():
    result, retcode = py("--args=eval", "--apply", "print", "29033611+")
    assert retcode == 1
    assert "29033611+: SyntaxError: invalid syntax" in result


def test_argmode_eval_modname_1():
    result = py("--args=eval", "--apply", "print", "sys")
    expected = dedent("""
        [PYFLYBY] import sys
        [PYFLYBY] print(<module 'sys' (built-in)>)
        <module 'sys' (built-in)>
    """).strip()
    assert expected == result


def test_argmode_eval_badname_1():
    result, retcode = py("--args=eval", "--apply", "print", "foo67475309")
    assert retcode == 1
    assert "NameError: name 'foo67475309' is not defined" in result


@pytest.mark.parametrize("args", [
    "--args=eval",
    "--args eval",
    "--arguments=evaluate",
    "--arguments expression",
    "--argmode exprs",
    "--arg_mode=expr",
    "--arg-mode=e",
])
def test_argmode_eval_variants_1(args):
    result = py((args+" --apply print 5+2").split())
    expected = dedent("""
        [PYFLYBY] print(7)
        7
    """).strip()
    assert expected == result


def test_argmode_auto_unparsable_1():
    result = py("--args=auto", "--apply", "print", "1+")
    expected = dedent("""
        [PYFLYBY] print('1+')
        1+
    """).strip()
    assert expected == result


def test_argmode_auto_expression_1():
    result = py("--args=auto", "--apply", "print", "1+2")
    expected = dedent("""
        [PYFLYBY] print(3)
        3
    """).strip()
    assert expected == result


def test_argmode_auto_goodname_1():
    result = py("--args=auto", "--apply", "print", "sys")
    expected = dedent("""
        [PYFLYBY] import sys
        [PYFLYBY] print(<module 'sys' (built-in)>)
        <module 'sys' (built-in)>
    """).strip()
    assert expected == result


def test_argmode_auto_badname_1():
    result = py("--args=auto", "--apply", "print", "foo71398671")
    expected = dedent("""
        [PYFLYBY] print('foo71398671')
        foo71398671
    """).strip()
    assert expected == result


def test_argmode_auto_each_1():
    result = py("--args=auto", "--apply", "print", "7_", "7+1")
    expected = dedent("""
        [PYFLYBY] print('7_', 8)
        7_ 8
    """).strip()
    assert expected == result


@pytest.mark.parametrize("args", [
    "--args=auto",
    "--args auto",
    "--arguments=automatic",
    "--argmode a",
    "",
])
def test_argmode_auto_variants_1(args):
    result = py((args+" --apply print 7_ 7+1").split())
    expected = dedent("""
        [PYFLYBY] print('7_', 8)
        7_ 8
    """).strip()
    assert expected == result


def test_heuristic_eval_1():
    result = py("1+2")
    expected = dedent("""
        [PYFLYBY] 1+2
        3
    """).strip()
    assert expected == result


def test_heuristic_eval_concat_1():
    result = py("5 + 7")
    expected = dedent("""
        [PYFLYBY] 5 + 7
        12
    """).strip()
    assert expected == result


def test_heuristic_eval_complex_1():
    result = py("5 + 7j")
    expected = dedent("""
        [PYFLYBY] 5 + 7j
        (5+7j)
    """).strip()
    assert expected == result


def test_heuristic_eval_complex_2():
    result = py("(5+7j) ** 12")
    expected = dedent("""
        [PYFLYBY] (5+7j) ** 12
        (65602966976-150532462080j)
    """).strip()
    assert expected == result


def test_heuristic_eval_exponentiation_1():
    result = py("123**4")
    expected = dedent("""
        [PYFLYBY] 123**4
        228886641
    """).strip()
    assert expected == result


def test_heuristic_eval_with_argv_1():
    result = py('for x in sys.argv[1:]: print(x.capitalize())',
                'canal', 'grand')
    expected = dedent("""
        [PYFLYBY] import sys
        [PYFLYBY] for x in sys.argv[1:]: print(x.capitalize())
        Canal
        Grand
    """).strip()
    assert expected == result


def test_heuristic_exec_statement_1():
    result = py('''if 1: print("Mulberry")''')
    expected = dedent("""
        [PYFLYBY] if 1: print("Mulberry")
        Mulberry
    """).strip()
    assert expected == result


def test_heuristic_exec_multiline_statement_1():
    result = py('''if 1:\n  print("Mott")''')
    expected = dedent("""
        [PYFLYBY] if 1:
        [PYFLYBY]   print("Mott")
        Mott
    """).strip()
    assert expected == result


def test_heuristic_apply_1():
    result = py("str.upper", "'Ditmars'")
    expected = dedent("""
        [PYFLYBY] str.upper('Ditmars')
        'DITMARS'
    """).strip()
    assert expected == result


def test_heuristic_apply_stdin_1():
    result = py("str.upper", "-", stdin=b"Nassau")
    expected = dedent("""
        [PYFLYBY] str.upper('Nassau')
        'NASSAU'
    """).strip()
    assert expected == result


def test_heuristic_apply_stdin_2():
    result = py("--output=silent", "sys.stdout.write", "-", stdin=b"Downing")
    expected = dedent("""
        [PYFLYBY] import sys
        [PYFLYBY] sys.stdout.write('Downing')
        Downing
    """).strip()
    assert expected == result


def test_heuristic_apply_stdin_no_eval_1():
    result = py("--output=silent", "sys.stdout.write", "-", stdin=b"3+4")
    expected = dedent("""
        [PYFLYBY] import sys
        [PYFLYBY] sys.stdout.write('3+4')
        3+4
    """).strip()
    assert expected == result


def test_heuristic_apply_stdin_quiet_1():
    result = py("--output=silent", "-q", "sys.stdout.write", "-", stdin=b"Houston")
    expected = "Houston"
    assert expected == result


def test_heuristic_apply_lambda_1():
    result = py("lambda a,b:a*b", "6", "7")
    expected = dedent("""
        [PYFLYBY] lambda a,b:a*b
        [PYFLYBY] (lambda a,b:a*b)(6, 7)
        42
    """).strip()
    assert expected == result


def test_heuristic_apply_lambda_nested_1():
    result = py("(lambda a,b: lambda c,d: a*b*c*d)(2,3)", "5", "7")
    expected = dedent("""
        [PYFLYBY] (lambda a,b: lambda c,d: a*b*c*d)(2,3)
        [PYFLYBY] (lambda a,b: lambda c,d: a*b*c*d)(2,3)(5, 7)
        210
    """).strip()
    assert expected == result


def test_heuristic_apply_builtin_args_1():
    result = py("round", "2.984375", "3")
    expected = dedent("""
        [PYFLYBY] round(2.984375, 3)
        2.984
    """).strip()
    assert expected == result


def test_heuristic_apply_builtin_args_2():
    result = py("round", "2.984375")
    if PY2:
        expected = dedent("""
            [PYFLYBY] round(2.984375)
            3.0
        """).strip()
    else:
        expected = dedent("""
            [PYFLYBY] round(2.984375)
            3
        """).strip()
    assert expected == result


def test_heuristic_apply_builtin_kwargs_1():
    result = py("round", "2.984375", "--ndigits=3")
    expected = dedent("""
        [PYFLYBY] round(2.984375, ndigits=3)
        2.984
    """).strip()
    assert expected == result


def test_heuristic_apply_builtin_kwargs_separate_arg_1():
    result = py("round", "2.984375", "--ndigits", "3")
    expected = dedent("""
        [PYFLYBY] round(2.984375, ndigits=3)
        2.984
    """).strip()
    assert expected == result


def test_heuristic_print_1():
    result = py("print", "4", "5")
    expected = dedent("""
        [PYFLYBY] print(4, 5)
        4 5
    """).strip()
    assert expected == result


def test_heuristic_apply_expression_1():
    result = py("3.0.is_integer")
    expected = dedent("""
        [PYFLYBY] 3.0.is_integer()
        True
    """).strip()
    assert expected == result


def test_heuristic_apply_expression_2():
    result = py("sys.stdout.flush")
    expected = dedent("""
        [PYFLYBY] import sys
        [PYFLYBY] sys.stdout.flush()
    """).strip()
    assert expected == result


def test_heuristic_eval_expression_1():
    result = py("os.path.sep")
    expected = dedent("""
        [PYFLYBY] import os.path
        [PYFLYBY] os.path.sep
        '/'
    """).strip()
    assert expected == result


def test_heuristic_eval_expression_nonmodule_1():
    result = py("os.getcwd.__name__")
    expected = dedent("""
        [PYFLYBY] import os
        [PYFLYBY] os.getcwd.__name__
        'getcwd'
    """).strip()
    assert expected == result


@pytest.mark.skipif(
    sys.version_info[0] == 3, reason="xml.dom.minidom also need import on py3"
)
def test_heuristic_eval_symbol_submodule_1():
    # Verify that heuristic eval of an expression in a module in a package
    # works, and also verify that we log the submodule import.
    result = py("xml.dom.minidom.XMLNS_NAMESPACE")
    expected = dedent("""
        [PYFLYBY] import xml.dom
        [PYFLYBY] xml.dom.minidom.XMLNS_NAMESPACE
        'http://www.w3.org/2000/xmlns/'
    """).strip()
    assert expected == result


def test_heuristic_apply_method_arg_1():
    result = py("float.is_integer", "3.0")
    expected = dedent("""
        [PYFLYBY] float.is_integer(3.0)
        True
    """).strip()
    assert expected == result
    result = py("float.is_integer", "3.5")
    expected = dedent("""
        [PYFLYBY] float.is_integer(3.5)
        False
    """).strip()
    assert expected == result


def test_apply_builtin_too_few_args_1():
    result, retcode = py("round")
    assert retcode == 1
    if PY2:
        assert "TypeError: Required argument 'number' (pos 1) not found" in result
    else:
        assert "TypeError: round() missing required argument 'number' (pos 1)" in result


def test_apply_builtin_too_many_args_1():
    result, retcode = py("round", "6", "7", "8")
    assert retcode == 1
    assert "TypeError: round() takes at most 2 arguments (3 given)" in result


def test_apply_builtin_bad_kwarg_1():
    result, retcode = py("round", "2.7182", "--foo=5")
    assert retcode == 1
    assert "TypeError: 'foo' is an invalid keyword argument" in result


def test_apply_pyfunc_posargs_1():
    result = py("calendar.weekday 2014 7 18".split())
    expected = dedent("""
        [PYFLYBY] import calendar
        [PYFLYBY] calendar.weekday(2014, 7, 18)
        4
    """).strip()
    assert expected == result


def test_apply_pyfunc_kwarg_1():
    result = py("calendar.weekday --year=2014 --month=7 --day=17".split())
    expected = dedent("""
        [PYFLYBY] import calendar
        [PYFLYBY] calendar.weekday(2014, 7, 17)
        3
    """).strip()
    assert expected == result


def test_apply_pyfunc_kwarg_disorder_1():
    result = py("calendar.weekday --day=16 --month=7 --year=2014".split())
    expected = dedent("""
        [PYFLYBY] import calendar
        [PYFLYBY] calendar.weekday(2014, 7, 16)
        2
    """).strip()
    assert expected == result


def test_apply_pyfunc_kwarg_short_1():
    result = py("calendar.weekday -m 7 -d 15 -y 2014".split())
    expected = dedent("""
        [PYFLYBY] import calendar
        [PYFLYBY] calendar.weekday(2014, 7, 15)
        1
    """).strip()
    assert expected == result


def test_apply_pyfunc_hybrid_args_disorder_1():
    result = py("calendar.weekday 2014 -day 15 -month 7".split())
    expected = dedent("""
        [PYFLYBY] import calendar
        [PYFLYBY] calendar.weekday(2014, 7, 15)
        1
    """).strip()
    assert expected == result


def test_apply_argspec_too_few_args_1():
    result, retcode = py("base64.b64decode")
    assert retcode == 1
    assert "[PYFLYBY] missing required argument s" in result
    if PY2:
        assert "$ py base64.b64decode s [altchars]" in result, result
    else:
        assert "$ py base64.b64decode s [altchars [validate]]" in result


def test_apply_argspec_too_few_args_2():
    result, retcode = py("calendar.weekday")
    assert retcode == 1
    assert "[PYFLYBY] missing required argument year" in result
    assert "$ py calendar.weekday year month day" in result


def test_apply_argspec_too_many_args_1():
    result, retcode = py("base64.b64decode", "a", "b", "c", "d")
    assert retcode == 1
    if PY2:
        assert ("[PYFLYBY] Too many positional arguments.  "
                "Expected 1-2 positional argument(s): s, altchars.  "
                "Got 4 args: a b c d") in result, result
        assert "$ py base64.b64decode s [altchars]" in result
    else:
        assert ("[PYFLYBY] Too many positional arguments.  "
        "Expected 1-3 positional argument(s): s, altchars, validate.  "
        "Got 4 args: a b c d") in result, result
        assert "$ py base64.b64decode s [altchars [validate]]" in result


def test_apply_argspec_too_many_args_2():
    result, retcode = py("calendar.weekday", "a", "b", "c", "d")
    assert retcode == 1
    assert ("[PYFLYBY] Too many positional arguments.  "
            "Expected 3 positional argument(s): year, month, day.  "
            "Got 4 args: a b c d") in result
    assert "$ py calendar.weekday year month day" in result


def test_apply_argspec_bad_kwarg_1():
    result, retcode = py("base64.b64decode", "x", "--christopher=sheridan")
    assert retcode == 1
    assert "[PYFLYBY] Unknown option name christopher" in result
    if PY2:
        assert "$ py base64.b64decode s [altchars]" in result
    else:
        assert "$ py base64.b64decode s [altchars [validate]]" in result


def test_apply_dashdash_1():
    result = py('--apply', 'print', '4.000', '--', '--help', '5.000')
    expected = dedent("""
        [PYFLYBY] print(4.0, '--help', '5.000')
        4.0 --help 5.000
    """).strip()
    assert expected == result


def test_apply_namedtuple_1():
    result = py('namedtuple("ab", "aa bb")', "3", "4")
    expected = dedent("""
        [PYFLYBY] from collections import namedtuple
        [PYFLYBY] namedtuple("ab", "aa bb")
        [PYFLYBY] namedtuple("ab", "aa bb")(3, 4)
        ab(aa=3, bb=4)
    """).strip()
    assert expected == result


def test_repr_str_1():
    result = py("'Astor'")
    expected = dedent("""
        [PYFLYBY] 'Astor'
        'Astor'
    """).strip()
    assert expected == result


@pytest.mark.skipif(
    PY3,
    reason="Long integers are not valid syntax in Python 3.")
def test_repr_long_1():
    result = py("5L")
    expected = dedent("""
        [PYFLYBY] 5L
        5L
    """).strip()
    assert expected == result


def test_future_division_1():
    result = py("1/2")
    expected = dedent("""
        [PYFLYBY] 1/2
        0.5
    """).strip()
    assert expected == result


def test_integer_division_1():
    result = py("7//3")
    expected = dedent("""
        [PYFLYBY] 7//3
        2
    """).strip()
    assert expected == result


def test_print_statement_1():
    result = py("print(42)")
    expected = dedent("""
        [PYFLYBY] print(42)
        42
    """).strip()
    assert expected == result


def test_print_statement_sep_1():
    result = py("print", "43")
    if PY2:
        expected = dedent("""
            [PYFLYBY] print 43
            43
        """).strip()
    else:
        expected = dedent("""
            [PYFLYBY] print(43)
            43
        """).strip()
    assert expected == result


def test_print_function_1():
    result = py("print(44, file=sys.stdout)")
    expected = dedent("""
        [PYFLYBY] import sys
        [PYFLYBY] print(44, file=sys.stdout)
        44
    """).strip()
    assert expected == result


def test_print_function_tuple_1():
    result = py("print(5,6)")
    expected = dedent("""
        [PYFLYBY] print(5,6)
        5 6
    """).strip()
    assert expected == result


def test_write_1():
    with NamedTemporaryFile(mode='w+') as f:
        output = py("--output=silent", "-q", "open(%r,'w').write"%f.name, "-", stdin=b"Greenwich")
        assert output == ""
        result = f.read()
    expected = "Greenwich"
    assert expected == result


def test_print_args_1():
    with NamedTemporaryFile(mode='w+') as f:
        output = py("-q", "print", "-", "--file=open(%r,'w')"%f.name,
                    stdin=b"Spring")
        assert output == ""
        result = f.read()
    expected = "Spring\n"
    assert expected == result


def test_program_help_1():
    output = py("--help")
    assert "--version" in output


def test_program_help_full_1():
    for arg in ["--help", "-help", "help", "--h", "-h", "--?", "-?", "?"]:
        output = py(arg)
        assert "--version" in output


def test_function_help_1():
    output = py("base64.b64encode", "--help")
    assert "s [altchars]" in output
    assert ">>> base64.b64encode(s, altchars=None)" in output
    assert "$ py base64.b64encode s [altchars]" in output
    assert "$ py base64.b64encode --s=... [--altchars=...]" in output
    assert "--version" not in output
    assert "[PYFLYBY] import base64" in output
    if PY2:
        assert "\n  Encode a string using Base64." in output
    else:
        assert "\n  Encode the bytes-like object s using Base64 and return a bytes object." in output
    assert "binascii.b2a_base64" not in output


def test_function_help_autoimport_1():
    output = py("b64encode", "--help")
    assert "[PYFLYBY] from base64 import b64encode" in output
    assert "s [altchars]" in output
    assert "$ py b64encode s [altchars]" in output
    assert "binascii.b2a_base64" not in output


def test_function_help_expression_1():
    output = py("sys.stdout.write", "--help")
    assert '>>> sys.stdout.write(' in output
    assert "$ py sys.stdout.write " in output
    if PY2:
        assert "Write string str to file." in output, output
    else:
        assert "Write string to stream." in output, output


def test_function_help_quote_need_parens_1():
    output = py('lambda a,b: a*b', '-h')
    assert ">>> (lambda a,b: a*b)(a, b)" in output
    assert "$ py 'lambda a,b: a*b' a b" in output


def test_function_help_quote_already_have_parens_1():
    output = py('(lambda a,b: a*b)', '-h')
    assert ">>> (lambda a,b: a*b)(a, b)" in output
    assert "$ py '(lambda a,b: a*b)' a b" in output


def test_function_help_quote_nested_lambdas_1():
    output = py('(lambda a,b: lambda c,d: a*b*c*d)(2,3)', '-h')
    assert ">>> (lambda a,b: lambda c,d: a*b*c*d)(2,3)(c, d)" in output
    assert "$ py '(lambda a,b: lambda c,d: a*b*c*d)(2,3)' c d" in output
    assert "$ py '(lambda a,b: lambda c,d: a*b*c*d)(2,3)' --c=... --d=..." in output


def test_help_class_init_1(tmp):
    writetext(tmp.dir/"f80166304.py", """
        class Greenpoint(object):
            def __init__(self66770013,  Milton,  * Noble,  ** India):
                pass
    """)
    writetext(tmp.dir/"p", """
        from f80166304 import Greenpoint
    """)
    output = py("Greenpoint?", PYTHONPATH=tmp.dir, PYFLYBY_PATH=tmp.dir/"p")
    assert ">>> Greenpoint(Milton, *Noble, **India)" in output


def test_help_class_init_oldclass_1(tmp):
    writetext(tmp.dir/"f20579393.py", """
        class Williamsburg:
            def __init__(self42828936, Metropolitan, * Morgan, ** Bogart):
                pass
    """)
    writetext(tmp.dir/"p", """
        from f20579393 import Williamsburg
    """)
    output = py("Williamsburg??", PYTHONPATH=tmp.dir, PYFLYBY_PATH=tmp.dir/"p")
    assert ">>> Williamsburg(Metropolitan, *Morgan, **Bogart)" in output


def test_help_class_new_1(tmp):
    writetext(tmp.dir/"f56365338.py", """
        class Knickerbocker(object):
            def __init__(cls15515092, Wilson, * Madison, ** Linden):
                pass
    """)
    writetext(tmp.dir/"p", """
        from f56365338 import Knickerbocker
    """)
    output = py("?Knickerbocker", PYTHONPATH=tmp.dir, PYFLYBY_PATH=tmp.dir/"p")
    assert ">>> Knickerbocker(Wilson, *Madison, **Linden)" in output


@pytest.mark.parametrize("cmdline", [
    "b64encode --help",
    "b64encode -help",
    "b64encode --h",
    "b64encode -h",
    "b64encode --?",
    "b64encode -?",
    "b64encode ?",
    "--help b64encode",
    "-help b64encode",
    "help b64encode",
    "--h b64encode",
    "-h b64encode",
    "--? b64encode",
    "-? b64encode",
    "? b64encode",
    "b64encode?",
    "?b64encode",
    "base64.b64encode --help",
    "base64.b64encode -help",
    "base64.b64encode --h",
    "base64.b64encode -h",
    "base64.b64encode --?",
    "base64.b64encode -?",
    "base64.b64encode ?",
    "--help base64.b64encode",
    "-help base64.b64encode",
    "help base64.b64encode",
    "--? base64.b64encode",
    "-? base64.b64encode",
    "? base64.b64encode",
    "base64.b64encode?",
    "?base64.b64encode",
])
def test_function_help_variants_1(cmdline):
    output = py(cmdline.split())
    assert "s [altchars]" in output
    assert "--version" not in output
    assert "binascii.b2a_base64" not in output


def test_function_source_1():
    output = py("base64.b64encode", "--source")
    assert "[PYFLYBY] import base64" in output
    assert ">>> base64.b64encode(s, altchars=None)" in output
    assert "$ py base64.b64encode s [altchars]" in output
    assert "$ py base64.b64encode --s=... [--altchars=...]" in output
    assert "binascii.b2a_base64" in output # from source code
    if PY2:
        assert output.count("Encode a string using Base64") == 1, output
    else:
        assert output.count("Encode the bytes-like object s using Base64 and return a bytes object.") == 1
    assert "--version" not in output


def test_function_source_autoimport_1():
    output = py("b64encode", "--source")
    assert "[PYFLYBY] from base64 import b64encode" in output
    assert ">>> b64encode(s, altchars=None)" in output
    assert "$ py b64encode s [altchars]" in output
    assert "$ py b64encode --s=... [--altchars=...]" in output
    assert "binascii.b2a_base64" in output # from source code
    if PY2:
        assert output.count("Encode a string using Base64") == 1, output
    else:
        assert output.count("Encode the bytes-like object s using Base64 and return a bytes object.") == 1


@pytest.mark.parametrize("cmdline", [
    "b64encode --source",
    "b64encode -source",
    "b64encode --??",
    "b64encode -??",
    "b64encode ??",
    "--source b64encode",
    "-source b64encode",
    "source b64encode",
    "--?? b64encode",
    "-?? b64encode",
    "?? b64encode",
    "b64encode??",
    "??b64encode",
    "base64.b64encode --source",
    "base64.b64encode -source",
    "base64.b64encode --??",
    "base64.b64encode -??",
    "base64.b64encode ??",
    "--source base64.b64encode",
    "-source base64.b64encode",
    "source base64.b64encode",
    "--?? base64.b64encode",
    "-?? base64.b64encode",
    "?? base64.b64encode",
    "base64.b64encode??",
    "??base64.b64encode"
])
def test_function_source_variants_1(cmdline):
    output = py(cmdline.split())
    assert "s [altchars]" in output
    assert "binascii.b2a_base64" in output


def test_module_help_1():
    output = py("base64?")
    assert "RFC 3548" in output
    assert "import binascii" not in output


@pytest.mark.parametrize("args", [
    "base64 --help",
    "base64 -help",
    "base64 --h",
    "base64 -h",
    "base64 --?",
    "base64 -?",
    "base64 ?",
    "--help base64",
    "-help base64",
    "help base64",
    "--h base64",
    "-h base64",
    "--? base64",
    "-? base64",
    "? base64",
    "base64?",
    "?base64",
])
def test_module_help_variants_1(args):
    output = py(args.split())
    assert "RFC 3548" in output, output
    assert "import binascii" not in output


def test_module_source_1():
    output = py("base64??")
    assert "RFC 3548" in output
    assert "import binascii" in output


def test_module_no_help_1():
    output, retcode = py("-m", "base64", "--help")
    assert retcode == 2
    assert "option --help not recognized" in output
    assert "RFC 3548" not in output


@pytest.mark.parametrize("args", [
    "base64 --source",
    "base64 -source",
    "base64 --??",
    "base64 -??",
    "base64 ??",
    "--source base64",
    "-source base64",
    "source base64",
    "--?? base64",
    "-?? base64",
    "?? base64",
    "base64??",
    "??base64",
])
def test_module_source_variants_1(args):
    output = py(args.split())
    assert "RFC 3548" in output
    assert "import binascii" in output


@pytest.mark.parametrize("args", [
    "--help=3",
    "-help=3",
    "--help 3",
    "-help 3",
    "--hel=3",
    "-hel=3",
    "--hel 3",
    "-hel 3",
    "--he=3",
    "-he=3",
    "--he 3",
    "-he 3",
    "--h=3",
    "-h=3",
    "--h 3",
    "-h 3",
])
def test_function_arg_help_1(args):
    result = py('lambda help: help*4', *(args.split()))
    expected = dedent("""
        [PYFLYBY] lambda help: help*4
        [PYFLYBY] (lambda help: help*4)(3)
        12
    """).strip()
    assert expected == result


@pytest.mark.parametrize("args", [
    "--hello=3",
    "-hello=3",
    "--hello 3",
    "-hello 3",
    "--hel=3",
    "-hel=3",
    "--hel 3",
    "-hel 3",
    "--he=3",
    "-he=3",
    "--he 3",
    "-he 3",
    "--h=3",
    "-h=3",
    "--h 3",
    "-h 3",
])
def test_function_arg_hello_1(args):
    result = py('lambda hello: hello*7', *(args.split()))
    expected = dedent("""
        [PYFLYBY] lambda hello: hello*7
        [PYFLYBY] (lambda hello: hello*7)(3)
        21
    """).strip()
    assert expected == result


def test_function_arg_help_qmark_1():
    output = py('lambda help: help*4', '-?')
    assert "$ py 'lambda help: help*4' help" in output


def test_function_arg_help_help_1():
    output, retcode = py('lambda help: help*4', '--help')
    assert retcode == 1
    assert "Missing argument to --help" in output


def test_function_arg_help_h_1():
    output, retcode = py('lambda help: help*4', '-h')
    assert retcode == 1
    assert "Missing argument to -h" in output


def test_object_method_help_1():
    output = py('email.message.Message().get', '--help')
    assert "$ py 'email.message.Message().get' name [failobj]" in output
    assert "Get a header value." in output


@pytest.mark.parametrize("args", [
    "email.message.Message().get --help",
    "email.message.Message().get -help",
    "email.message.Message().get --h",
    "email.message.Message().get -h",
    "email.message.Message().get --?",
    "email.message.Message().get -?",
    "email.message.Message().get ?",
    "email.message.Message().get?",
    "--help email.message.Message().get",
    "-help email.message.Message().get",
    "help email.message.Message().get",
    "--h email.message.Message().get",
    "-h email.message.Message().get",
    "--? email.message.Message().get",
    "-? email.message.Message().get",
    "? email.message.Message().get",
    "?email.message.Message().get",
])
def test_object_method_help_variants_1(args):
    output = py(args.split())
    assert "$ py 'email.message.Message().get' name [failobj]" in output
    assert "Get a header value." in output


def test_object_method_source_1():
    output = py('email.message.Message().get', '--source')
    assert "$ py 'email.message.Message().get' name [failobj]" in output
    assert "Get a header value." in output
    assert "name.lower()" in output


@pytest.mark.parametrize("args", [
    "email.message.Message().get --source",
    "email.message.Message().get -source",
    "email.message.Message().get --??",
    "email.message.Message().get -??",
    "email.message.Message().get ??",
    "email.message.Message().get??",
    "--source email.message.Message().get",
    "-source email.message.Message().get",
    "source email.message.Message().get",
    "--?? email.message.Message().get",
    "-?? email.message.Message().get",
    "?? email.message.Message().get",
    "??email.message.Message().get",
])
def test_object_method_source_variants1(args):
    output = py(args.split())
    assert "$ py 'email.message.Message().get' name [failobj]" in output
    assert "Get a header value." in output
    assert "name.lower()" in output


def test_arg_nodashdash_1():
    result = py('print', '42.0000', 'sys')
    expected = dedent("""
        [PYFLYBY] import sys
        [PYFLYBY] print(42.0, <module 'sys' (built-in)>)
        42.0 <module 'sys' (built-in)>
    """).strip()
    assert expected == result


def test_arg_dashdash_1():
    result = py('print', '--', '42.0000', 'sys')
    expected = dedent("""
        [PYFLYBY] print('42.0000', 'sys')
    42.0000 sys
    """).strip()
    assert expected == result


def test_arg_dashdash_2():
    result = py('print', '42.0000', '--', 'sys')
    expected = dedent("""
        [PYFLYBY] print(42.0, 'sys')
    42.0 sys
    """).strip()
    assert expected == result


def test_arg_dashdash_3():
    result = py('print', '42.0000', 'sys', '--')
    expected = dedent("""
        [PYFLYBY] import sys
        [PYFLYBY] print(42.0, <module 'sys' (built-in)>)
        42.0 <module 'sys' (built-in)>
    """).strip()
    assert expected == result


def test_arg_dashdash_4():
    result = py('print', '42.0000', 'sys', '--', '--')
    expected = dedent("""
        [PYFLYBY] import sys
        [PYFLYBY] print(42.0, <module 'sys' (built-in)>, '--')
        42.0 <module 'sys' (built-in)> --
    """).strip()
    assert expected == result


def test_arg_dashdash_help_1():
    result = py('print', '--', '--help')
    expected = dedent("""
        [PYFLYBY] print('--help')
        --help
    """).strip()
    assert expected == result


def test_arg_dashdash_dashdash_1():
    result = py('print', '--', '--', '42.000')
    expected = dedent("""
        [PYFLYBY] print('--', '42.000')
        -- 42.000
    """).strip()
    assert expected == result


def test_kwargs_no_dashdash_1():
    result = py("lambda *a,**k: (a,k)", "3.500", "--foo", "7.500")
    expected = dedent("""
        [PYFLYBY] lambda *a,**k: (a,k)
        [PYFLYBY] (lambda *a,**k: (a,k))(3.5, foo=7.5)
        ((3.5,), {'foo': 7.5})
    """).strip()
    assert expected == result, result


def test_kwargs_dashdash_1():
    result = py("lambda *a,**k: (a,k)", "--", "3.500", "--foo", "7.500")
    expected = dedent("""
        [PYFLYBY] lambda *a,**k: (a,k)
        [PYFLYBY] (lambda *a,**k: (a,k))('3.500', '--foo', '7.500')
        (('3.500', '--foo', '7.500'), {})
    """).strip()
    assert expected == result


def test_joinstr_1():
    result = py("3", "+", "5")
    expected = dedent("""
        [PYFLYBY] 3 + 5
        8
    """).strip()
    assert expected == result


def test_print_joinstr_1():
    result = py("print", "3", "+", "5")
    if PY2:
        expected = dedent("""
            [PYFLYBY] print 3 + 5
            8
        """).strip()
    else:
        expected = dedent("""
            [PYFLYBY] print(3, '+', 5)
            3 + 5
        """).strip()
    assert expected == result

def test_print_joinstr_2():
    result = py("print", "3 + 5")
    if PY2:
        expected = dedent("""
            [PYFLYBY] print 3 + 5
            8
        """).strip()
    else:
        expected = dedent("""
            [PYFLYBY] print(8)
            8
        """).strip()
    assert expected == result


def test_join_single_arg_1():
    result = py("print", "sys")
    if PY2:
        expected = dedent("""
            [PYFLYBY] import sys
            [PYFLYBY] print sys
            <module 'sys' (built-in)>
        """).strip()
    else:
        # In autocall mode, the arguments are evaluated and the repr() is
        # printed for the [PYFLYBY] line
        expected = dedent("""
            [PYFLYBY] import sys
            [PYFLYBY] print(<module 'sys' (built-in)>)
            <module 'sys' (built-in)>
        """).strip()

    assert expected == result


def test_join_single_arg_fallback_1():
    # Verify that when heuristically joining, we fall back to heuristic apply.
    # Even though "print foo" is parsable as a joined string, if ``foo`` is not
    # importable, then fallback to "print('foo')".
    result = py("print", "ardmore23653526")
    expected = dedent("""
        [PYFLYBY] print('ardmore23653526')
        ardmore23653526
    """).strip()
    assert expected == result


def test_no_ipython_for_eval_1():
    result = py("-q", "'os' in sys.modules, 'IPython' in sys.modules")
    expected = "(True, False)"
    assert expected == result


def test_outputmode_interactive_pprint_1():
    result = py('dict(zip("abcdefghij","ABCDEFGHIJ"))')
    expected = dedent("""
        [PYFLYBY] dict(zip("abcdefghij","ABCDEFGHIJ"))
        {'a': 'A',
         'b': 'B',
         'c': 'C',
         'd': 'D',
         'e': 'E',
         'f': 'F',
         'g': 'G',
         'h': 'H',
         'i': 'I',
         'j': 'J'}
    """).strip()
    assert expected == result


def test_outputmode_interactive_pprint_str_1():
    result = py('--output=interactive', '"Grove"')
    expected = dedent("""
        [PYFLYBY] "Grove"
        'Grove'
    """).strip()
    assert expected == result


def test_outputmode_interactive_none_1():
    result = py('--output=interactive', 'None')
    expected = dedent("""
        [PYFLYBY] None
    """).strip()
    assert expected == result


@pytest.mark.parametrize("args", [
    "--output=inTeraCtive",
    "--output iNteracTive",
    "--output i",
    "--output_mode=I",
    "--output-mode=i",
    "--outmode i",
    "--out i",
    "-out I",
    "-o i",
    "-o=i",
    "--o i",
])
def test_outputmode_interactive_variants_1(args):
    result = py((args + ' "Perry"').split())
    expected = dedent("""
        [PYFLYBY] "Perry"
        'Perry'
    """).strip()
    assert expected == result
    result = py((args + " None").split())
    expected = dedent("""
        [PYFLYBY] None
    """).strip()
    assert expected == result


def test_outputmode_str_1():
    result = py('--output=str', '"Morton"')
    expected = dedent("""
        [PYFLYBY] "Morton"
        Morton
    """).strip()
    assert expected == result


def test_outputmode_str_date_1():
    result = py('--output=str', 'datetime.date(2014,7,18)')
    expected = dedent("""
        [PYFLYBY] import datetime
        [PYFLYBY] datetime.date(2014,7,18)
        2014-07-18
    """).strip()
    assert expected == result


def test_outputmode_str_none_1():
    result = py('--output=str', 'None')
    expected = dedent("""
        [PYFLYBY] None
        None
    """).strip()
    assert expected == result


def test_outputmode_str_str_none_1():
    result = py('--output=str', '"None"')
    expected = dedent("""
        [PYFLYBY] "None"
        None
    """).strip()
    assert expected == result


@pytest.mark.parametrize("args", [
    "--output=StriNg",
    "--output sTr",
    "--output prInt",
    "-o p",
    "--print",
    "-print",
])
def test_outputmode_str_variants_1(args):
    result = py((args + ' "Greene"').split())
    expected = dedent("""
        [PYFLYBY] "Greene"
        Greene
    """).strip()
    assert expected == result


def test_outputmode_silent_1():
    result = py('--output=silent', '"Bethune"')
    expected = dedent("""
        [PYFLYBY] "Bethune"
    """).strip()
    assert expected == result


def test_outputmode_silent_outputonly_1():
    result = py('--output=silent', 'sys.stdout.write("Gansevoort"), 78844525')
    expected = dedent("""
        [PYFLYBY] import sys
        [PYFLYBY] sys.stdout.write("Gansevoort"), 78844525
        Gansevoort
    """).strip()
    assert expected == result


@pytest.mark.parametrize("args", [
    "--output=nO",
    "--output NoNe",
    "--output n",
    "-o SilEnt",
    "--silent",
    "-silent",
])
def test_outputmode_silent_variants_1(args):
    result = py((args + ' "Clarkson"').split())
    expected = '[PYFLYBY] "Clarkson"'
    assert expected == result


def test_outputmode_repr_str_1():
    result = py('--output=repr', '"Moore"')
    expected = dedent("""
        [PYFLYBY] "Moore"
        'Moore'
    """).strip()
    assert expected == result


def test_outputmode_repr_date_1():
    result = py('--output=repr', 'datetime.date(2014,7,18)')
    expected = dedent("""
        [PYFLYBY] import datetime
        [PYFLYBY] datetime.date(2014,7,18)
        datetime.date(2014, 7, 18)
    """).strip()
    assert expected == result


def test_outputmode_repr_none_1():
    result = py('--output=repr', 'None')
    expected = dedent("""
        [PYFLYBY] None
        None
    """).strip()
    assert expected == result


def test_outputmode_repr_str_none_1():
    result = py('--output=repr', '"None"')
    expected = dedent("""
        [PYFLYBY] "None"
        'None'
    """).strip()
    assert expected == result


@pytest.mark.parametrize("args", [
    "--output=Repr",
    "--output repR",
    "--output r",
    "-o r",
    "--repr",
    "-repr",
])
def test_outputmode_repr_variants_1(args):
    result = py((args + ' "Norfolk"').split())
    expected = dedent("""
        [PYFLYBY] "Norfolk"
        'Norfolk'
    """).strip()
    assert expected == result
    result = py((args + ' None').split())
    expected = dedent("""
        [PYFLYBY] None
        None
    """).strip()
    assert expected == result


def test_outputmode_reprifnotnone_date_1():
    result = py('--output=repr-if-not-none', 'datetime.date(2014,7,18)')
    expected = dedent("""
        [PYFLYBY] import datetime
        [PYFLYBY] datetime.date(2014,7,18)
        datetime.date(2014, 7, 18)
    """).strip()
    assert expected == result


def test_outputmode_reprifnotnone_none_1():
    result = py('--output=repr-if-not-none', 'None')
    expected = dedent("""
        [PYFLYBY] None
    """).strip()
    assert expected == result


@pytest.mark.parametrize("args", [
    "--output=REPR-if-NOT-none",
    "--output reprifnotnone",
    "--output reprunlessnone",
    "-o rn",
])
def test_outputmode_reprifnotnone_variants_1(args):
    result = py((args + ' "Suffolk"').split())
    expected = dedent("""
        [PYFLYBY] "Suffolk"
        'Suffolk'
    """).strip()
    assert expected == result
    result = py((args + ' None').split())
    expected = dedent("""
        [PYFLYBY] None
    """).strip()
    assert expected == result


def test_outputmode_pprint_1():
    result = py('--output=pprint', 'dict(zip("abcdefghij","ABCDEFGHIJ"))')
    expected = dedent("""
        [PYFLYBY] dict(zip("abcdefghij","ABCDEFGHIJ"))
        {'a': 'A',
         'b': 'B',
         'c': 'C',
         'd': 'D',
         'e': 'E',
         'f': 'F',
         'g': 'G',
         'h': 'H',
         'i': 'I',
         'j': 'J'}
    """).strip()
    assert expected == result


def test_outputmode_pprint_str_1():
    result = py('--output=pprint', '"Willett"')
    expected = dedent("""
        [PYFLYBY] "Willett"
        'Willett'
    """).strip()
    assert expected == result


def test_outputmode_pprint_none_1():
    result = py('--output=pprint', 'None')
    expected = dedent("""
        [PYFLYBY] None
        None
    """).strip()
    assert expected == result


@pytest.mark.parametrize("args", [
    "--output=pPrInT",
    "--output pprint",
    "--output pp",
    "-o PP",
    "--pprint",
    "-pprint",
])
def test_outputmode_pprint_variants_1(args):
    result = py((args + ' "Delancey"').split())
    expected = dedent("""
        [PYFLYBY] "Delancey"
        'Delancey'
    """).strip()
    assert expected == result
    result = py((args + ' None').split())
    expected = dedent("""
        [PYFLYBY] None
        None
    """).strip()
    assert expected == result
    result = py((args + ' dict(zip("abcdefghij","ABCDEFGHIJ"))').split())
    expected = dedent("""
        [PYFLYBY] dict(zip("abcdefghij","ABCDEFGHIJ"))
        {'a': 'A',
         'b': 'B',
         'c': 'C',
         'd': 'D',
         'e': 'E',
         'f': 'F',
         'g': 'G',
         'h': 'H',
         'i': 'I',
         'j': 'J'}
    """).strip()
    assert expected == result


def test_outputmode_pprintifnotnone_1():
    result = py('--output=pprint-if-not-none', '"Baruch"')
    expected = dedent("""
        [PYFLYBY] "Baruch"
        'Baruch'
    """).strip()
    assert expected == result


def test_outputmode_pprintifnotnone_none_1():
    result = py('--output=pprint-if-not-none', 'None')
    expected = dedent("""
        [PYFLYBY] None
    """).strip()
    assert expected == result
    result = py('--quiet', '--output=pprint-if-not-none', 'None')
    assert "" == result


@pytest.mark.parametrize("args", [
    "--output=pprint-if-not-none",
    "--output PprintIfNotNone",
    "--output pprintUNLESSnone",
    "-o ppn",
])
def test_outputmode_pprintifnotnone_variants_1(args):
    result = py((args + ' "Rivington"').split())
    expected = dedent("""
        [PYFLYBY] "Rivington"
        'Rivington'
    """).strip()
    assert expected == result
    result = py((args + ' None').split())
    expected = dedent("""
        [PYFLYBY] None
    """).strip()
    assert expected == result
    result = py((args + ' dict(zip("abcdefghij","ABCDEFGHIJ"))').split())
    expected = dedent("""
        [PYFLYBY] dict(zip("abcdefghij","ABCDEFGHIJ"))
        {'a': 'A',
         'b': 'B',
         'c': 'C',
         'd': 'D',
         'e': 'E',
         'f': 'F',
         'g': 'G',
         'h': 'H',
         'i': 'I',
         'j': 'J'}
    """).strip()
    assert expected == result


def test_outputmode_bad_1():
    result, retcode = py("--output=foo81576743", "5")
    assert retcode == 1
    assert "Invalid output='foo81576743'" in result


def test_run_module_1():
    result = py("-m", "base64", "-d", "-", stdin=b"VHJpbml0eQ==")
    expected = dedent("""
        [PYFLYBY] python -m base64 -d -
        Trinity
    """).strip()
    assert expected == result


@pytest.mark.parametrize("args", [
    "--module base64",
    "-module base64",
    "module base64",
    "--m base64",
    "-m base64",
    "-mbase64",
])
def test_run_module_variants_1(args):
    result = py((args + " -d -").split(), stdin=b"VHJvdXRtYW4=")
    expected = dedent("""
        [PYFLYBY] python -m base64 -d -
        Troutman
    """).strip()
    assert expected == result


def test_run_module_argstr_1(tmp):
    # runmodule.  Verify that arguments are strings and not evaluated, and
    # verify that argv works correctly.
    writetext(tmp.dir/"odule12786636.py", """
        from __future__ import print_function
        if __name__ == "__main__":
            import sys
            print("Rector", sys.argv)
    """)
    result = py("-module12786636", "22524739.000", "math",
                PYTHONPATH=tmp.dir)
    expected = dedent("""
        [PYFLYBY] python -m odule12786636 22524739.000 math
        Rector ['%s/odule12786636.py', '22524739.000', 'math']
    """).strip() % tmp.dir
    assert expected == result


def test_run_module_under_package_1(tmp):
    result = py("encodings.rot_13", stdin=b"Tenavgr")
    expected = dedent("""
        [PYFLYBY] import encodings
        [PYFLYBY] python -m encodings.rot_13
        Granite
    """).strip()
    assert expected == result


def test_heuristic_run_module_1():
    result = py("base64", "-d", "-", stdin=b"U2VuZWNh")
    expected = dedent("""
        [PYFLYBY] python -m base64 -d -
        Seneca
    """).strip()
    assert expected == result


def test_heuristic_run_module_under_package_1(tmp):
    result = py("encodings.rot_13", stdin=b"Qrpxre")
    expected = dedent("""
        [PYFLYBY] import encodings
        [PYFLYBY] python -m encodings.rot_13
        Decker
    """).strip()
    assert expected == result


def test_heuristic_run_module_under_package_2(tmp):
    os.mkdir("%s/bard22402805" % tmp.dir)
    os.mkdir("%s/bard22402805/douglas" % tmp.dir)
    writetext(tmp.dir/"bard22402805/__init__.py", """
        from __future__ import print_function
        print('Davis', __name__)
    """)
    writetext(tmp.dir/"bard22402805/douglas/__init__.py", """
        from __future__ import print_function
        print('Clove', __name__)
    """)
    writetext(tmp.dir/"bard22402805/douglas/thames.py", """
        from __future__ import print_function
        print('Huron', __name__)
    """)
    result = py("bard22402805.douglas.thames", PYTHONPATH=tmp.dir)
    expected = dedent("""
        [PYFLYBY] import bard22402805
        Davis bard22402805
        [PYFLYBY] import bard22402805.douglas
        Clove bard22402805.douglas
        [PYFLYBY] python -m bard22402805.douglas.thames
        Huron __main__
    """).strip()
    assert expected == result


def test_heuristic_run_module_no_auto_import_1(tmp):
    # Heuristic runmodule.  Verify that we don't auto-import anything.
    writetext(tmp.dir/"vernon84909775.py", """
        if __name__ == "__main__":
            print('Basin')
            os # expect NameError
    """)
    result, retcode = py("vernon84909775", PYTHONPATH=tmp.dir)
    assert retcode == 1
    assert "Basin" in result
    assert "NameError: name 'os' is not defined" in result


def test_heuristic_run_module_nameerror_1(tmp):
    # Heuristic runmodule.  Verify that NameError in module doesn't confuse us.
    writetext(tmp.dir/"blaine15479940.py", """
        glenwood75406634
    """)
    result, retcode = py("blaine15479940", PYTHONPATH=tmp.dir)
    assert retcode == 1
    assert result.endswith("NameError: name 'glenwood75406634' is not defined")


def test_heuristic_run_module_importerror_1(tmp):
    # Heuristic runmodule.  Verify that ImportError in module doesn't confuse
    # us.
    writetext(tmp.dir/"griswold73262001.py", """
        import whitewood62047754
    """)
    result, retcode = py("griswold73262001", PYTHONPATH=tmp.dir)
    assert retcode == 1
    if PY2:
        assert result.endswith("ImportError: No module named whitewood62047754")
    else:
        assert result.endswith("ModuleNotFoundError: No module named 'whitewood62047754'")


def test_heuristic_run_module_argstr_1(tmp):
    # Heuristic runmodule.  Verify that arguments are strings and not
    # evaluated, and verify that argv works correctly.
    writetext(tmp.dir/"gantry20720070.py", """
        from __future__ import print_function
        if __name__ == "__main__":
            import sys
            print("Belmont", sys.argv)
    """)
    result = py("gantry20720070", "26792622.000", "math",
                PYTHONPATH=tmp.dir)
    expected = dedent("""
        [PYFLYBY] python -m gantry20720070 26792622.000 math
        Belmont ['%s/gantry20720070.py', '26792622.000', 'math']
    """).strip() % tmp.dir
    assert expected == result


def test_builtin_no_run_module_1(tmp):
    # Verify that builtins take precedence over modules.
    writetext(tmp.dir/"round.py", """
        print('bad morrison56321353')
    """)
    result = py("round", "17534159.5", PYTHONPATH=tmp.dir)
    if PY2:
        expected = dedent("""
            [PYFLYBY] round(17534159.5)
            17534160.0
        """).strip()
    else:
        expected = dedent("""
            [PYFLYBY] round(17534159.5)
            17534160
        """).strip()
    assert expected == result


def test_run_module_no_superfluous_import_1(tmp):
    # Verify that 'py -m foomodule' doesn't cause a superfluous import of
    # foomodule.
    writetext(tmp.dir/"delafield47227231.py", """
        from __future__ import print_function
        print('Oakwood', __name__)
    """)
    result = py("-m", "delafield47227231", PYTHONPATH=tmp.dir)
    expected = dedent("""
        [PYFLYBY] python -m delafield47227231
        Oakwood __main__
    """).strip()
    assert expected == result


def test_heuristic_run_module_no_superfluous_import_1(tmp):
    # Verify that 'py foomodule' doesn't cause a superfluous import of
    # foomodule.
    writetext(tmp.dir/"pelton58495419.py", """
        from __future__ import print_function
        print('Bement', __name__)
    """)
    result = py("pelton58495419", PYTHONPATH=tmp.dir)
    expected = dedent("""
        [PYFLYBY] python -m pelton58495419
        Bement __main__
    """).strip()
    assert expected == result


def test_unsafe_pyflyby_path_1(tmp):
    writetext(tmp.dir/"f62242229.py", """
        Walton = 3058692
    """)
    writetext(tmp.dir/"p", """
        from f62242229 import Walton
    """)
    result = py("Walton", PYTHONPATH=tmp.dir, PYFLYBY_PATH=tmp.dir/"p")
    expected = dedent("""
        [PYFLYBY] from f62242229 import Walton
        [PYFLYBY] Walton
        3058692
    """).strip()
    assert expected == result


def test_safe_no_pyflyby_path_1(tmp):
    writetext(tmp.dir/"f86964994.py", """
        Fordham = 85883909
    """)
    writetext(tmp.dir/"p", """
        from f86964994 import Fordham
    """)
    result, retcode = py("--safe", "Fordham",
                         PYTHONPATH=tmp.dir, PYFLYBY_PATH=tmp.dir/"p")
    assert retcode == 1
    assert "name 'Fordham' is not defined" in result


def test_safe_fully_qualified_module_1(tmp):
    writetext(tmp.dir/"f47762194.py", """
        Montefiore = 85883909
    """)
    result = py("--safe", "f47762194.Montefiore", PYTHONPATH=tmp.dir)
    expected = dedent("""
        [PYFLYBY] import f47762194
        [PYFLYBY] f47762194.Montefiore
        85883909
    """).strip()
    assert expected == result


def test_unsafe_args_1():
    result = py("type", "sys")
    if PY2:
        expected = dedent("""
            [PYFLYBY] import sys
            [PYFLYBY] type(<module 'sys' (built-in)>)
            <type 'module'>
        """).strip()
    else:
        expected = dedent("""
            [PYFLYBY] import sys
            [PYFLYBY] type(<module 'sys' (built-in)>)
            <class 'module'>
        """).strip()
    assert expected == result


def test_safe_args_1():
    result = py("--safe", "type", "sys")
    if PY2:
        expected = dedent("""
            [PYFLYBY] type('sys')
            <type 'str'>
        """).strip()
    else:
        expected = dedent("""
            [PYFLYBY] type('sys')
            <class 'str'>
        """).strip()
    assert expected == result


def test_argmode_string_dashdash_1():
    result = py("--args=string", "print", "Beekman/", "--", "Spruce/", "--")
    expected = dedent("""
        [PYFLYBY] print('Beekman/', 'Spruce/', '--')
        Beekman/ Spruce/ --
    """).strip()
    assert expected == result


def test_safe_dashdash_1():
    result = py("--safe", "print", "Oliver/", "--", "Catherine/", "--")
    expected = dedent("""
        [PYFLYBY] print('Oliver/', 'Catherine/', '--')
        Oliver/ Catherine/ --
    """).strip()
    assert expected == result


def test_safe_no_concat_1():
    result = py("--safe", "print", "sys")
    expected = dedent("""
        [PYFLYBY] print('sys')
        sys
    """).strip()
    assert expected == result


def test_argmode_str_no_concat_1():
    result = py("--args=str", "print", "sys")
    expected = dedent("""
        [PYFLYBY] print('sys')
        sys
    """).strip()
    assert expected == result


def test_argmode_eval_no_concat_1():
    result = py("--args=eval", "print", "sys")
    expected = dedent("""
        [PYFLYBY] import sys
        [PYFLYBY] print(<module 'sys' (built-in)>)
        <module 'sys' (built-in)>
    """).strip()
    assert expected == result


def test_argmode_auto_no_concat_1():
    result = py("--args=auto", "print", "sys")
    expected = dedent("""
        [PYFLYBY] import sys
        [PYFLYBY] print(<module 'sys' (built-in)>)
        <module 'sys' (built-in)>
    """).strip()
    assert expected == result


def test_exec_stdin_print_statement_1():
    # This is still a print statement in Python 2 (with redundant parentheses)
    result = py(stdin=b"print('Carnegie')")
    expected = "Carnegie"
    assert expected == result


def test_exec_stdin_print_function_1():
    result = py(stdin=b"print('Sinai', file=sys.stdout)")
    expected = dedent("""
        [PYFLYBY] import sys
        Sinai
    """).strip()
    assert expected == result


def test_exec_stdin_noresult_1():
    result = py(stdin=b"42")
    assert "" == result


def test_argv_stdin_noarg_1():
    result = py(stdin=b"print(sys.argv)")
    expected = dedent("""
        [PYFLYBY] import sys
        ['']
    """).strip()
    assert expected == result


def test_argv_stdin_dash_1():
    result = py("-", stdin=b"print(sys.argv)")
    expected = dedent("""
        [PYFLYBY] import sys
        ['-']
    """).strip()
    assert expected == result


def test_argv_stdin_dash_args_1():
    result = py("-", "sys", stdin=b"print(sys.argv)")
    expected = dedent("""
        [PYFLYBY] import sys
        ['-', 'sys']
    """).strip()
    assert expected == result


def test_map_1():
    result = py("-q", "--map", "print", "2", "4")
    expected = dedent("""
        2
        4
    """).strip()
    assert expected == result


def test_map_2():
    result = py("--map", "str.capitalize", "hello", "world")
    expected = dedent("""
        [PYFLYBY] str.capitalize('hello')
        'Hello'
        [PYFLYBY] str.capitalize('world')
        'World'
    """).strip()
    assert expected == result


def test_map_3():
    result = py("--map", "float.as_integer_ratio", "2.5", "3.5")
    expected = dedent("""
        [PYFLYBY] float.as_integer_ratio(2.5)
        (5, 2)
        [PYFLYBY] float.as_integer_ratio(3.5)
        (7, 2)
    """).strip()
    assert expected == result


def test_map_eval_1():
    result = py("-q", "--map", "print", "1/2", "1/4")
    expected = dedent("""
        0.5
        0.25
    """).strip()
    assert expected == result


def test_map_stringarg_1():
    result = py("-q", "--args=str", "--map", "print", "1/2", "1/4")
    expected = dedent("""
        1/2
        1/4
    """).strip()
    assert expected == result


def test_map_safe_1():
    result = py("-q", "--safe", "--map", "print", "1/2", "1/4")
    expected = dedent("""
        1/2
        1/4
    """).strip()
    assert expected == result


def test_map_safe_dashdash_1():
    result = py("-q", "--safe", "--map", "print", "--", "--", "1/2", "--")
    expected = dedent("""
        --
        1/2
        --
    """).strip()
    assert expected == result


def test_map_dashdash_1():
    result = py("-q", "--map", "print", "--", "1/2", "1/4")
    expected = dedent("""
        1/2
        1/4
    """).strip()
    assert expected == result


def test_map_dashdash_dashdash_1():
    result = py("-q", "--map", "print", "--", "--", "1/2", "--", "1/4")
    expected = dedent("""
        --
        1/2
        --
        1/4
    """).strip()
    assert expected == result


def test_map_lambda_1():
    result = py("--map", "lambda x: x**2", "1", "2", "3", "4", "5")
    expected = dedent("""
        [PYFLYBY] (lambda x: x**2)(1)
        1
        [PYFLYBY] (lambda x: x**2)(2)
        4
        [PYFLYBY] (lambda x: x**2)(3)
        9
        [PYFLYBY] (lambda x: x**2)(4)
        16
        [PYFLYBY] (lambda x: x**2)(5)
        25
    """).strip()
    assert expected == result


def test_map_empty_1():
    result = py("-q", "--map", "print")
    assert "" == result


def test_map_dashdash_empty_1():
    result = py("-q", "--map", "print", "--")
    assert "" == result


def test_map_missing_function_1():
    result, retcode = py("-q", "--map")
    assert retcode == 1
    assert "expected argument to --map" in result


def test_output_exit_1():
    result = py("--output=exit", "5+7")
    expected = ("[PYFLYBY] 5+7", 12)
    assert expected == result


@pytest.mark.parametrize("args", [
    "--output=exit 5+7",
    "--output=exit 5 + 7",
    "--output=eXiT 5+ 7",
    "--output=systemEXIT 4+8",
    "--output=raise 3+8+1",
])
def test_output_exit_variants_1(args):
    result = py(("-q " + args).split())
    expected = ("", 12)
    assert expected == result


def test_info_function_simple_1():
    result = py('--output=silent', 'sys.stdout.write', 'Franklin')
    expected = dedent("""
        [PYFLYBY] import sys
        [PYFLYBY] sys.stdout.write('Franklin')
        Franklin
    """).strip()
    assert expected == result


def test_info_function_lambda_1():
    result = py('--output=silent', '(lambda: sys.stdout.write)()', 'Chambers')
    expected = dedent("""
        [PYFLYBY] import sys
        [PYFLYBY] (lambda: sys.stdout.write)()
        [PYFLYBY] (lambda: sys.stdout.write)()('Chambers')
        Chambers
    """).strip()
    assert expected == result


@pytest.mark.skipif(
    (os.uname()[0] not in ["Linux", "Darwin"]) or (sys.version_info[0] == 3),
    reason="unsupported OS",
)
def test_ctypes_1():
    uname = os.uname()[0]
    if uname == 'Linux':
        libname = "libc.so.6"
    elif uname == 'Darwin':
        libname = "libc.dylib"
    else:
        raise AssertionError
    result = py('--output=silent', 'ctypes.CDLL("%s").printf'%libname, "b'%03d'", "7")
    if PY2:
        expected = dedent("""
            [PYFLYBY] import ctypes
            [PYFLYBY] ctypes.CDLL("{libname}").printf
            [PYFLYBY] ctypes.CDLL("{libname}").printf('%03d', 7)
            007
        """).strip().format(libname=libname)
    else:
        expected = dedent("""
            [PYFLYBY] import ctypes
            [PYFLYBY] ctypes.CDLL("{libname}").printf
            [PYFLYBY] ctypes.CDLL("{libname}").printf(b'%03d', 7)
            007
        """).strip().format(libname=libname)
    assert expected == result, repr(result)


def test_name_eval_1():
    result = py("-c", "__name__")
    expected = dedent("""
        [PYFLYBY] __name__
        '__main__'
    """).strip()
    assert expected == result


def test_name_heuristic_eval_1():
    result = py("__name__")
    expected = dedent("""
        [PYFLYBY] __name__
        '__main__'
    """).strip()
    assert expected == result


def test_name_heuristic_apply_eval_1():
    result = py("--output=silent", "sys.stdout.write", "__name__")
    expected = dedent("""
        [PYFLYBY] import sys
        [PYFLYBY] sys.stdout.write('__main__')
        __main__
    """).strip()
    assert expected == result


def test_name_heuristic_join_eval_1():
    result = py("print", "'Castle'", ",", "__name__")
    if PY2:
        expected = dedent("""
            [PYFLYBY] print 'Castle' , __name__
            Castle __main__
        """).strip()
    else:
        expected = dedent("""
            [PYFLYBY] print('Castle', ',', '__main__')
            Castle , __main__
        """).strip()
    assert expected == result


def test_name_stdin_1():
    result = py(stdin=b"print(('Winter', __name__))")
    expected = dedent("""
        ('Winter', '__main__')
    """).strip()
    assert expected == result


def test_name_dash_stdin_1():
    result = py("-", stdin=b"print(('Victory', __name__))")
    expected = dedent("""
        ('Victory', '__main__')
    """).strip()
    assert expected == result


def test_name_file_1():
    with tempfile.NamedTemporaryFile(suffix=".py", mode='w+') as f:
        f.write('from __future__ import print_function\nprint("Forest", __name__)\n')
        f.flush()
        result = py("--file", f.name)
    expected = dedent("""
        Forest __main__
    """).strip()
    assert expected == result


def test_name_heuristic_file_1():
    with tempfile.NamedTemporaryFile(suffix=".py", mode='w+') as f:
        f.write('from __future__ import print_function\nprint("Oakland", __name__)\n')
        f.flush()
        result = py(f.name)
    expected = dedent("""
        Oakland __main__
    """).strip()
    assert expected == result


def test_name_module_1(tmp):
    # Verify that 'py -m modulename' works.  Also verify that we don't import the
    # module by name before run_module.
    writetext(tmp.dir/"swan80274886.py", """
        from __future__ import print_function
        print('Lafayette', __name__)
    """)
    result = py("-m", "swan80274886", PYTHONPATH=tmp.dir)
    expected = dedent("""
        [PYFLYBY] python -m swan80274886
        Lafayette __main__
    """).strip()
    assert expected == result


def test_name_heuristic_module_1(tmp):
    # Verify that 'py modulename' works.  Also verify that we don't import the
    # module by name before run_module.
    writetext(tmp.dir/"arnold17339681.py", """
        from __future__ import print_function
        print('Hendricks', __name__)
    """)
    result = py("arnold17339681", PYTHONPATH=tmp.dir)
    expected = dedent("""
        [PYFLYBY] python -m arnold17339681
        Hendricks __main__
    """).strip()
    assert expected == result


def test_unknown_option_1():
    result, retcode = py("--Charlotte", "--module", "profile")
    assert retcode == 1
    assert "[PYFLYBY] Unknown option --Charlotte" in result
    assert "For usage, see:" in result


@pytest.mark.parametrize("args", flatten([
    "--tremont", "--Elsmere", "-Mohegan",
    [ ["--%s"%x, "-%s"%x] for x in "abgjklnprstuvwxyz"],
    [ ["--%s"%x, "-%s"%x] for x in "ABCDEFGHIJKLMNOPQRSTUVWXYZ"],
]))
def test_unknown_option_variants_1(args):
    result, retcode = py(args.split())
    assert retcode == 1
    assert "[PYFLYBY] Unknown option "+args.split()[0] in result
    assert "For usage, see:" in result


@pytest.mark.parametrize("m", list(
    "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"))
def test_single_char_arg0_1(tmp, m):
    # Verify that single characters without dashes aren't treated as options,
    # i.e. verify that we don't treat e.g. "py c 42" as "py -c 42".
    writetext(tmp.dir/("%s.py"%m), """
        from __future__ import print_function
        import sys
        print('Hayward', sys.argv[1])
    """)
    result = py(m, "42", PYTHONPATH=tmp.dir)
    expected = dedent("""
        [PYFLYBY] python -m %s 42
        Hayward 42
    """).strip() % m
    assert expected == result


def test_auto_arg_goodname_1():
    result = py("--output=silent", "sys.stdout.write", "os.path.sep")
    expected = dedent("""
        [PYFLYBY] import sys
        [PYFLYBY] import os.path
        [PYFLYBY] sys.stdout.write('/')
        /
    """).strip()
    assert expected == result


def test_auto_arg_badname_1():
    result = py("--output=silent", "sys.stdout.write", "Burnside55731946.Valentine")
    expected = dedent("""
        [PYFLYBY] import sys
        [PYFLYBY] sys.stdout.write('Burnside55731946.Valentine')
        Burnside55731946.Valentine
    """).strip()
    assert expected == result


def test_auto_arg_goodname_property_1(tmp):
    writetext(tmp.dir/"quarry47946518.py", """
        class Creston(object):
            @property
            def sedgwick(self):
                return 'arden'
        creston = Creston()
    """)
    result = py("--output=silent", "sys.stdout.write", "quarry47946518.creston.sedgwick",
                PYTHONPATH=tmp.dir)
    expected = dedent("""
        [PYFLYBY] import sys
        [PYFLYBY] import quarry47946518
        [PYFLYBY] sys.stdout.write('arden')
        arden
    """).strip()
    assert expected == result


def test_auto_arg_broken_name_property_1(tmp):
    # Verify that a NameError in user code doesn't confuse us into
    # using an argument as a string.
    writetext(tmp.dir/"kingsbridge90850275.py", """
        class Mosholu(object):
            @property
            def cortlandt(self):
                return Woodlawn # bad
        mosholu = Mosholu()
    """)
    result, retcode = py(
        "sys.stdout.write", "kingsbridge90850275.mosholu.cortlandt",
        PYTHONPATH=tmp.dir)
    assert retcode == 1
    assert "[PYFLYBY] import kingsbridge90850275\n" in result
    if PY2:
        assert "NameError: global name 'Woodlawn' is not defined" in result
    else:
        assert "NameError: name 'Woodlawn' is not defined" in result


@pytest.mark.xfail # TODO FIXME
def test_auto_arg_broken_import_1(tmp):
    # Verify that an ImportError in user code doesn't confuse us into
    # using an argument as a string.
    writetext(tmp.dir/"mclean76253083.py", """
        import martha8602542
    """)
    result, retcode = py(
        "sys.stdout.write", "mclean76253083.winfred",
        PYTHONPATH=tmp.dir)
    assert retcode == 1
    assert "[PYFLYBY] import mclean76253083\n" in result
    assert "ImportError:XXX" in result


def test_first_arg_empty_string_1():
    result, retcode = py("", "5")
    assert retcode == 1
    assert "got empty string as first argument" in result


def test_first_arg_spaces_1():
    result, retcode = py("\t  \t ", "5")
    assert retcode == 1
    assert "got empty string as first argument" in result


def test_function_defaults_1(tmp):
    # Verify that default values get passed through without being disturbed,
    # without being round-tripped through strings, etc.
    writetext(tmp.dir / "dobbin69118865.py", """
        from __future__ import print_function

        class X(object):
            _ctr = 0
            def __init__(self):
                X._ctr += 1
                self._i = X._ctr
            def __repr__(self):
                return "<X %d>" % (self._i)
            def __str__(self):
                return "<<X %d>>" % (self._i)
        def meserole(a, b, c=X(), d="77610270.000", e=None, f=X()):
            print(a, b, c, d, e, f)
    """)
    result = py("dobbin69118865.meserole", "java", "'kent'", "--e=paidge",
                PYTHONPATH=tmp.dir)
    expected = dedent("""
        [PYFLYBY] import dobbin69118865
        [PYFLYBY] dobbin69118865.meserole('java', 'kent', c=<X 1>, d='77610270.000', e='paidge', f=<X 2>)
        java kent <<X 1>> 77610270.000 paidge <<X 2>>
    """).strip()
    assert expected == result


def test_apply_not_a_function():
    result, retcode = py("--call", "75650517")
    assert retcode == 1
    assert "NotAFunctionError: ('Not a function', 75650517)" in result


# TODO: test timeit, time
# TODO: test --attach
# TODO: test postmortem debugging
# TODO: test SystemExit
# TODO: test SIGQUIT
# TODO: test faulthandler
# TODO: test globals e.g. breakpoint
# TODO: test py python foo
# TODO: test py program-on-$PATH
# TODO: test py --debug 'code...'
# TODO: test py --debug PID
# TODO: test repeated attach to the same PID.
# TODO: test py -i filename.pya
# TODO: test py -i 'code ...'
# TODO: test 'py -i' == 'py' (no double shell)
# TODO: exiting debugger with EOF (control-D)
