serc-cli 0.1.2

An experimental CPython bytecode compiler, written in Rust.
mod common;

use serc::{CompileOptions, compile_bytes_with_options, compile_with_options};

#[test]
fn constant_condition_after_module_annotation_setup_matches_cpython() {
    let cases = [
        "if False:\n    pass\nx: int\n",
        "if True:\n    pass\nx: int\n",
        "if 0:\n    pass\nx: int\n",
        "if 1:\n    pass\nx: int\n",
        "if 1-1:\n    pass\nx: int\n",
        "if (\n    False\n):\n    pass\nx: int\n",
        "if False:\n    pass\nelse:\n    value = 1\nx: int\n",
        "if True:\n    value = 1\nelse:\n    pass\nx: int\n",
        "if False: pass\nx: int\n",
        "if True: pass\nx: int\n",
        "if False:\n    pass\nx: int = 1\n",
        "x: int\nif False:\n    pass\n",
        "\"example\"\nif False:\n    pass\nx: int\n",
        "from __future__ import annotations\nif False:\n    pass\nx: int\n",
        "if False:\n    pass\ndef f(x: int):\n    pass\n",
        "if False:\n    pass\nx = 1\n",
        "class C:\n    if False:\n        pass\n    x: int\n",
        "if True:\n    if False:\n        pass\nx: int\n",
        "if False:\n    pass\nif False:\n    pass\nx: int\n",
        "if False:\n    pass\ny = [x for x in (1, 2)]\nx: int\n",
        "if False:\n    pass\nobj.x: int\n",
        "if False:\n    pass\nfor i in (1, 2):\n    value = i\nx: int\n",
        "if False:\n    pass\ntry:\n    value = f()\nexcept Exception:\n    value = 1\nx: int\n",
        "if True:\n    value = 1\ntry:\n    f()\nfinally:\n    g()\nx: int\n",
        "if True:\n    for i in (1, 2):\n        value = i\nx: int\n",
        "if False:\n    pass\nvalue = [(x, y) for x in (1, 2) for y in (3, 4)]\nx: int\n",
        "if True:\n    pass\nvalue = (x for x in (1, 2))\nx: int\n",
        "if False:\n    value = (\"dead\", 123, (4, 5))\nelse:\n    value = \"live\"\nx: int\n",
        "if True: pass\nif True: pass\nx: int\n",
        "if False: pass\nif False: pass\nx: int\n",
        "if True:\n    try:\n        value = f()\n    finally:\n        value = 1\nx: int\n",
        "if False:\n    value: int\nvalue = 1\n",
        "if True:\n    try:\n        f()\n    except Exception:\n        g()\nx: int\n",
        "if False:\n    value = 0\nelse:\n    value = 1\n    other = 2\nx: int\n",
        "if True:\n    value = 1\n    other = 2\nelse:\n    value = 0\nx: int\n",
        "if False:\n    pass\ntry:\n    value = (x for x in (1, 2))\nexcept Exception:\n    value = None\nx: int\n",
        "if (\n    True\n):\n    value = 1\nx: int\n",
    ];
    let directory = tempfile::tempdir().unwrap();
    let source_path = directory.path().join("conditional_annotations.py");
    let filename = source_path.to_string_lossy();
    let mut mismatches = Vec::new();
    for version in common::PYTHON_VERSIONS {
        for source in cases {
            fs_err::write(&source_path, source).unwrap();
            let mut native = common::python_command(version);
            let native = native
                .args([
                    "-c",
                    "import marshal, pathlib, sys; p = pathlib.Path(sys.argv[1]); code = compile(p.read_bytes(), str(p), 'exec', dont_inherit=True, optimize=0); sys.stdout.buffer.write(marshal.dumps(code))",
                ])
                .arg(&source_path)
                .output()
                .unwrap();
            assert!(native.status.success(), "{version}: {source}: {native:?}");
            let options = CompileOptions {
                python_version: version,
                ..CompileOptions::default()
            };
            let text = compile_with_options(source, &filename, &options).unwrap();
            let bytes = compile_bytes_with_options(source.as_bytes(), &filename, &options).unwrap();
            if text.marshal() != native.stdout {
                mismatches.push(format!("text {version}: {source:?}"));
            }
            if bytes.marshal() != native.stdout {
                mismatches.push(format!("bytes {version}: {source:?}"));
            }
        }
    }
    assert!(mismatches.is_empty(), "{mismatches:#?}");
}