示例#1
0
def test_invoke_compiler_success():
    "Test that warnings are propagated"
    warn_file = Path('tests/pkg1/warn.nim').resolve()
    out_file = Path('tests/pkg1/warn' + NimCompiler.EXT).resolve()

    try:
        # Since we are full-on invoking the compiler for a working build here,
        # we gotta make sure it has the same flags as normal (for win32, etc.)
        # This also means Nimpy has to be installed prior to building.
        NimCompiler.ensure_nimpy()

        out, err, war, hin = NimCompiler.invoke_compiler([
            'nim', 'c', *NimCompiler.NIM_CLI_ARGS, f'--out:{out_file}',
            str(warn_file)
        ])

        assert out_file.exists(), err
        assert any("Warning: imported and not used: 'asyncstreams'" in i
                   for i in war)
        assert any('Hint' in i for i in hin)

    finally:
        if out_file.exists():
            out_file.unlink()
            if sys.platform == 'win32':
                warn_exp = Path('tests/pkg1/warn.exp')
                warn_lib = Path('tests/pkg1/warn.lib')

                if warn_exp.exists():
                    warn_exp.unlink()

                if warn_lib.exists():
                    warn_lib.unlink()
示例#2
0
def test_custom_build_switches():
    "Test to make sure custom build switches can be used"

    switch_file = Path('tests/lib2/switches.py')
    scope = dict(
        MODULE_PATH=Path('foo/bar/baz.nim'),
        BUILD_ARTIFACT=Path('foo/bar/__pycache__/baz.' + NimCompiler.EXT),
        BUILD_DIR=None,
        IS_LIBRARY=False
    )
    switches = NimCompiler.get_switches(switch_file, **scope)
    old_import = switches['import'][:]
    old_bundle = switches['bundle'][:]

    assert switches
    assert old_import
    assert old_bundle

    # Make sure different platforms are handled correctly
    old_platform = sys.platform + ''
    sys.platform = 'darwin' if sys.platform == 'win32' else 'win32'

    try:
        switches = NimCompiler.get_switches(switch_file, **scope)
        
        assert switches
        assert old_import
        assert old_bundle
        assert old_import != switches['import']
        assert old_bundle != switches['bundle']
    finally:
        sys.platform = old_platform
示例#3
0
def test_get_import_prefix():
    "Make sure that the right namespace is returned for a given module path."
    with nimporter.cd('tests') as tmpdir:
        module_path1 = Path('pkg1/mod1.nim')
        module_path2 = Path('pkg1/pkg2/mod2.nim')
        gold1 = 'pkg1', 'mod1.nim'
        gold2 = 'pkg1', 'pkg2', 'mod2.nim'
        assert NimCompiler.get_import_prefix(module_path1, Path()) == gold1
        assert NimCompiler.get_import_prefix(module_path2, Path()) == gold2
示例#4
0
def test_build_library():
    "Test that a given Nim module can produce a Python extension library."
    with nimporter.cd('tests'):
        module = Path('lib1')
        output = NimCompiler.build_artifact(module)
        artifact = NimCompiler.compile_nim_code(module, output, library=True)

        assert artifact.exists()
        assert artifact.parent == output.parent
        assert artifact.suffix == output.suffix
示例#5
0
def main(args=None):
    parser = argparse.ArgumentParser(description='Nimporter CLI')
    subs = parser.add_subparsers(dest='cmd', required=True)

    subs.add_parser('clean')
    build = subs.add_parser('build')
    build.add_argument('source',
                       type=pathlib.Path,
                       help='the Nim module/library to compile')
    build.add_argument('--dest',
                       type=pathlib.Path,
                       help='the folder to store the build artifact')

    args = parser.parse_args(args or sys.argv[1:])

    if args.cmd == 'clean':
        cwd = pathlib.Path()
        print('Cleaning Directory:', cwd.resolve())
        clean(cwd)

    elif args.cmd == 'build':
        args.source = args.source.absolute()

        if not args.dest:
            args.dest = NimCompiler.build_artifact(args.source).parent

        else:
            assert args.dest.is_dir(), (
                'Cannot specify output filename since extensions change per '
                'platform. Please specify an output directory such as ".".')

        args.dest.mkdir(exist_ok=True)

        module = args.source

        if args.source.is_dir():
            is_library = bool([*module.glob('*.nimble')])
            assert is_library, 'Library dir must contain <libname>.nimble file'

        elif args.source.is_file():
            is_library = bool([*module.parent.glob('*.nimble')])
            if is_library: module = module.parent

        temp_build_dir = pathlib.Path('build').absolute()
        temp_build_dir.mkdir(exist_ok=True)
        artifact = temp_build_dir / (args.source.stem + NimCompiler.EXT)

        try:
            NimCompiler.compile_nim_code(module, artifact, library=is_library)
            shutil.copy(artifact, args.dest)
            module_name = args.source.stem + '.nim'
            Nimporter.update_hash(args.dest.parent / module_name)
        finally:
            shutil.rmtree(temp_build_dir)
示例#6
0
def test_build_module():
    "Test that a given Nim module can produce a Python extension module."
    with nimporter.cd('tests'):
        module = Path('mod_a.nim')
        output = NimCompiler.build_artifact(module)

        f = io.StringIO()
        with redirect_stdout(f):
            artifact = NimCompiler.compile_nim_code(module, output, library=False)

        assert artifact.exists()
        assert artifact.parent == output.parent
        assert 'Warning:' in f.getvalue()
示例#7
0
def test_find_nim_std_lib():
    "Make sure that Nim's standard library can be found."
    assert shutil.which('nim'), 'Nim compiler is not installed or not on path'
    assert NimCompiler.find_nim_std_lib(), (
        "Can't find Nim stdlib even though it is installed")

    # Now test failure condition by making Nim disappear
    environ = os.environ.copy()
    try:
        os.environ['PATH'] = ''
        assert not shutil.which('nim')
        assert not NimCompiler.find_nim_std_lib()
    finally:
        os.environ.clear()
        os.environ.update(environ)
示例#8
0
def test_build_artifact_location():
    "Make sure that the expected location to the build artifact is returned."
    module_path = Path('tests/pkg1/mod1.nim').absolute()
    ext = NimCompiler.EXT
    expected_path = Path('tests/pkg1/__pycache__/mod1' + ext).absolute()

    assert NimCompiler.build_artifact(module_path).absolute() == expected_path
示例#9
0
def test_build_library_fails():
    "Test NimInvokeException"

    # Build library using Nim module
    try:
        NimCompiler.compile_nim_code(Path('tests/mod_b.nim'),
                                     None,
                                     library=True)
        assert False, 'Should throw exception.'
    except NimporterException:
        "Expected result"

    # Build a library that has an error
    try:
        module = Path('tests/lib4')
        output = NimCompiler.build_artifact(module)
        NimCompiler.compile_nim_code(module, output, library=True)
        assert False, 'Should throw exception.'
    except NimInvokeException as e:
        assert str(e)
        assert e.get_output()

    # Build a library that doesn't have a Nimble file
    try:
        NimCompiler.compile_nim_code(Path('tests/lib3'), None, library=True)
        assert False, 'Should throw exception.'
    except NimporterException:
        "Expected result"
示例#10
0
def test_build_module_fails():
    "Test NimCompileException"

    # Build nonexistent file
    try:
        fake = Path('nonesense.nim')
        NimCompiler.compile_nim_code(fake, None, library=True)
        assert False, "Should throw exception. File doesn't exist: " + str(
            fake)
    except NimporterException:
        "Expected result"

    # Build module using library path
    try:
        NimCompiler.compile_nim_code(Path('tests/lib1'), None, library=False)
        assert False, 'Should throw exception.'
    except NimporterException:
        "Expected result"

    # Build a module that has an error
    try:
        module = Path('tests/pkg1/error.nim')
        output = NimCompiler.build_artifact(module)
        NimCompiler.compile_nim_code(module, output, library=False)
        assert False, 'Should throw exception.'
    except NimCompileException as e:
        assert str(e)
示例#11
0
def test_should_compile():
    "Make sure that modules should be (re)compiled or not."
    filename = Path('tests/pkg4/mod4.nim')

    assert not Nimporter.is_hashed(filename)
    assert Nimporter.hash_changed(filename)
    assert not Nimporter.is_built(filename)
    assert not Nimporter.is_cache(filename)
    assert not NimCompiler.pycache_dir(filename).exists()
    assert not Nimporter.IGNORE_CACHE
    assert Nimporter.should_compile(filename)
示例#12
0
def test_invoke_compiler():
    "Make sure that you can invoke the compiler as well as any executable."

    # NOTE: When ran in PowerShell, echo is a Cmdlet, not an executable ... :|
    if not shutil.which('echo'):
        return

    # Test that any program can be called
    gold_out = 'Hello World!' + ('\r\n' if sys.platform == 'win32' else '\n')
    out, err, war, hin = NimCompiler.invoke_compiler(['echo', gold_out.strip()])
    assert out == gold_out
示例#13
0
def test_compile_switches():
    "Make sure that an extension can still be compiled when using a switchfile."
    ext = NimCompiler.compile_nim_extension(Path('tests/lib2'),
                                            Path('tests'),
                                            library=True)

    assert isinstance(ext, Extension)
    assert ext.name == 'lib2'

    includes = set(Path(i) for i in ext.include_dirs)

    for source in ext.sources:
        src = Path(source).absolute()
        assert src.suffix == '.c'
示例#14
0
def test_compile_nim_configs(lib):
    """
    Make sure that an extension can still be compiled when nim configuration files are detected.

    """
    ext = NimCompiler.compile_nim_extension(
        Path(f'tests/{lib}'), Path('tests'), library=True
    )

    assert isinstance(ext, Extension)
    assert ext.name == f'{lib}'

    for source in ext.sources:
        src = Path(source).absolute()
        assert src.suffix == '.c'
示例#15
0
def test_invoke_compiler_failure():
    "Make sure that the compiler fails on bad input."
    err_file = Path('tests/pkg1/error.nim').resolve()
    ext = '.exe' if sys.platform == 'win32' else ''
    out_file = Path('tests/pkg1/error' + ext).resolve()

    try:
        out, err, war, hin = NimCompiler.invoke_compiler(
            ['nim', 'c', str(err_file)])

        assert not out_file.exists()
        assert any('Error: cannot open file: fallacy' in i for i in err)
        assert any('Hint: system [Processing]' in i for i in hin)

    finally:
        if out_file.exists(): out_file.unlink()
示例#16
0
def test_invoke_compiler_success():
    "Test that warnings are propagated"
    warn_file = Path('tests/pkg1/warn.nim').resolve()
    ext = '.exe' if sys.platform == 'win32' else ''
    out_file = Path('tests/pkg1/warn' + ext).resolve()

    try:
        out, err, war, hin = NimCompiler.invoke_compiler(
            ['nim', 'c', str(warn_file)])

        assert out_file.exists()
        assert any("Warning: imported and not used: 'tables'" in i
                   for i in war)
        assert any('Hint: system [Processing]' in i for i in hin)

    finally:
        if out_file.exists(): out_file.unlink()
示例#17
0
def test_invoke_compiler_failure():
    "Make sure that the compiler fails on bad input."
    err_file = Path('tests/pkg1/error.nim').resolve()
    ext = '.exe' if sys.platform == 'win32' else ''
    out_file = Path('tests/pkg1/error' + ext).resolve()

    try:
        # Since this will fail at the Nim layer, no need to ensure all the same
        # flags are set per platform (since it won't ever get that far).
        out, err, war, hin = NimCompiler.invoke_compiler(
            ['nim', 'c', *NimCompiler.NIM_CLI_ARGS,
             str(err_file)])

        assert not out_file.exists(), err
        assert any('Error: cannot open file: fallacy' in i for i in err)
        assert any('Hint' in i for i in hin)

    finally:
        if out_file.exists(): out_file.unlink()
示例#18
0
def test_compilation_failures():
    "Make sure that all expected errors are thrown."

    # Doesn't exist
    try:
        NimCompiler.compile_nim_extension(Path('bar/baz'), None, library=False)
        assert False, 'Should throw exception.'
    except NimporterException:
        "Expected result"

    # No Nimble file
    try:
        NimCompiler.compile_nim_extension(
            Path('tests/lib3'), Path('tests'), library=True
        )
        assert False, 'Should throw exception.'
    except NimporterException:
        "Expected result"

    # Errors with Library
    try:
        NimCompiler.compile_nim_extension(
            Path('tests/lib4'), Path('tests'), library=True
        )
        assert False, 'Should throw exception.'
    except NimInvokeException:
        "Expected result"

    # Errors with Module
    try:
        NimCompiler.compile_nim_extension(
            Path('tests/pkg1/error.nim'), Path('tests'), library=False
        )
        assert False, 'Should throw exception.'
    except NimCompileException:
        "Expected result"
示例#19
0
def test_ensure_nimpy():
    "Make sure that the Nimpy library can be installed."
    assert shutil.which('nim'), 'Nim compiler is not installed or not on path'

    # Make sure it is not installed
    out = NimCompiler.invoke_compiler('nimble path nimpy'.split())

    # Remove it
    if out[1]:
        out = NimCompiler.invoke_compiler(
            'nimble uninstall nimpy --accept'.split())

    # Install/verify it is already installed
    NimCompiler.ensure_nimpy()

    # Check installation code path
    NimCompiler.ensure_nimpy()

    # Verify that it is actuall installed according to Nimble
    out = NimCompiler.invoke_compiler('nimble path nimpy'.split())
    assert out[0] and not out[1]
示例#20
0
def main(cli_args=None):
    parser = argparse.ArgumentParser(description='Nimporter CLI')
    subs = parser.add_subparsers(dest='cmd', required=True)

    subs.add_parser('clean')
    build = subs.add_parser('build')
    build.add_argument(
        'source',
        type=pathlib.Path,
        help='the Nim module/library to compile'
    )
    build.add_argument(
        '--dest',
        type=pathlib.Path,
        help='the folder to store the build artifact'
    )

    bundle_parser = subs.add_parser('bundle')
    bundle = bundle_parser.add_subparsers(dest='exp', required=True)
    bin_ = bundle.add_parser('bin')
    src = bundle.add_parser('src')
    args = parser.parse_args(cli_args or sys.argv[1:])

    if args.cmd == 'clean':
        cwd = pathlib.Path()
        print('Cleaning Directory:', cwd.resolve())
        clean(cwd)

    elif args.cmd == 'build':
        args.source = args.source.absolute()

        if not args.dest:
            args.dest = NimCompiler.build_artifact(args.source).parent

        else:
            assert args.dest.is_dir(), (
                'Cannot specify output filename since extensions change per '
                'platform. Please specify an output directory such as ".".'
            )

        args.dest.mkdir(exist_ok=True)

        module = args.source

        if args.source.is_dir():
            is_library = bool([*module.glob('*.nimble')])
            assert is_library, 'Library dir must contain <libname>.nimble file'

        elif args.source.is_file():
            is_library = bool([*module.parent.glob('*.nimble')])
            if is_library: module = module.parent

        temp_build_dir = pathlib.Path('build').absolute()
        temp_build_dir.mkdir(exist_ok=True)
        artifact = temp_build_dir / (args.source.stem + NimCompiler.EXT)

        try:
            NimCompiler.compile_nim_code(
                module, artifact, library=is_library
            )
            shutil.copy(artifact, args.dest)
            module_name = args.source.stem + '.nim'
            Nimporter.update_hash(args.dest.parent / module_name)
        finally:
            shutil.rmtree(temp_build_dir)

    elif args.cmd == 'bundle':
        setup = pathlib.Path('setup.py')

        if not setup.exists():
            print('No setup.py found in dir, would you like to generate one?')

            answer = 'a'
            while answer not in 'YN':
                answer = input('  Y/N: ').upper() or 'a'

            if answer == 'Y':
                setup.write_text(
                    f'# Setup.py tutorial:\n'
                    f'# https://github.com/navdeep-G/setup.py\n'
                    f'# Edit `packages=` to fit your requirements\n'
                    f'import setuptools, nimporter\n\n'
                    f'setuptools.setup(\n'
                    f'    name="{pathlib.Path().absolute().name}",\n'
                    f'    packages=[..],  # Please read the above tutorial\n'
                    f'    ext_modules=nimporter.build_nim_extensions()\n'
                    f')\n'
                )

                print('Generated reference setup.py')
                print('Modify setup.py to point to your modules/packages.')

                bundle_type = 'source' if args.exp == 'src' else 'binary'

                print(
                    f'Once you have finished, run `{" ".join(cli_args)}` again '
                    f'to create a {bundle_type} distribution package.'
                )
        else:
            pyexe = 'python' if sys.platform == 'win32' else 'python3'

            if args.exp == 'bin':
                subprocess.Popen(f'{pyexe} setup.py bdist_wheel'.split()).wait()

            elif args.exp == 'src':
                subprocess.Popen(f'{pyexe} setup.py sdist'.split()).wait()

    return 0
示例#21
0
def main(cli_args=None):
    parser = argparse.ArgumentParser(description='Nimporter CLI')
    subs = parser.add_subparsers(dest='cmd', required=True)

    # Clean command
    subs.add_parser(
        'clean',
        help=(
            'Run in project root to recursively remove all Nimporter-specific '
            'build artifacts and hash files'))

    # Build command
    build = subs.add_parser(
        'build',
        help=(
            'Builds a Nim module/library into an importable Python extension'))
    build.add_argument('source',
                       type=pathlib.Path,
                       help='the Nim module/library to compile')
    build.add_argument('--dest',
                       type=pathlib.Path,
                       help='the folder to store the build artifact')

    # Bundle command
    bundle_parser = subs.add_parser(
        'bundle',
        help=(
            'Convenience command for running: python setup.py sdist/bdist_wheel'
        ))
    bundle = bundle_parser.add_subparsers(dest='exp', required=True)
    bin_ = bundle.add_parser('bin')
    src = bundle.add_parser('src')

    # Compile command
    compile_ = subs.add_parser(
        'compile',
        help=('Clean project and then recurse through and build all Nim '
              'modules/libraries'))

    args = parser.parse_args(cli_args or sys.argv[1:])

    if args.cmd == 'clean':
        cwd = pathlib.Path()
        print('Cleaning Directory:', cwd.resolve())
        clean(cwd)

    elif args.cmd == 'build':
        args.source = args.source.absolute()

        if not args.dest:
            args.dest = NimCompiler.build_artifact(args.source).parent

        else:
            assert args.dest.is_dir(), (
                'Cannot specify output filename since extensions change per '
                'platform. Please specify an output directory such as ".".')

        args.dest.mkdir(exist_ok=True)

        module = args.source

        if args.source.is_dir():
            is_library = bool([*module.glob('*.nimble')])
            assert is_library, 'Library dir must contain <libname>.nimble file'

        elif args.source.is_file():
            is_library = bool([*module.parent.glob('*.nimble')])
            if is_library: module = module.parent

        temp_build_dir = pathlib.Path('build').absolute()
        temp_build_dir.mkdir(exist_ok=True)
        artifact = temp_build_dir / (args.source.stem + NimCompiler.EXT)

        try:
            NimCompiler.compile_nim_code(module, artifact, library=is_library)
            shutil.copy(artifact, args.dest)
            module_name = args.source.stem + '.nim'
            Nimporter.update_hash(args.dest.parent / module_name)
        finally:
            shutil.rmtree(temp_build_dir)

    elif args.cmd == 'bundle':
        setup = pathlib.Path('setup.py')

        if not setup.exists():
            print('No setup.py found in dir, would you like to generate one?')

            answer = 'a'
            while answer not in 'YN':
                answer = input('  Y/N: ').upper() or 'a'

            if answer == 'Y':
                setup.write_text(SETUPPY_TEMPLATE)

                print('Generated reference setup.py')
                print('Modify setup.py to point to your modules/packages.')

                bundle_type = 'source' if args.exp == 'src' else 'binary'

                print(
                    f'Once you have finished, run `{" ".join(cli_args)}` again '
                    f'to create a {bundle_type} distribution package.')
        else:
            pyexe = 'python' if sys.platform == 'win32' else 'python3'

            if args.exp == 'bin':
                subprocess.Popen(
                    f'{pyexe} setup.py bdist_wheel'.split()).wait()

            elif args.exp == 'src':
                subprocess.Popen(f'{pyexe} setup.py sdist'.split()).wait()

    elif args.cmd == 'compile':
        clean()

        CTM = lambda: round(time.time() * 1000)
        start = CTM()
        extensions = Nimporter._find_extensions(pathlib.Path())

        for extension in extensions:
            is_lib = extension.is_dir()

            print(f'Building Extension {"Lib" if is_lib else "Mod"}: '
                  f'{extension.name}')

            NimCompiler.compile_nim_code(extension.absolute(),
                                         NimCompiler.build_artifact(
                                             extension.absolute()),
                                         library=is_lib)

            if is_lib:
                Nimporter.update_hash(extension / (extension.name + '.nim'))
            else:
                Nimporter.update_hash(extension)

        print('Done.')
        print(f'Built {len(extensions)} Extensions In '
              f'{(CTM() - start) / 1000.0} secs')

    return 0
示例#22
0
def test_pycache_dir():
    "Make sure that the correct path to the __pycache__ dir is returned."
    module_path = Path('tests/pkg1/mod1.nim').absolute()
    expected_pycache = Path('tests/pkg1/__pycache__').absolute()

    assert NimCompiler.pycache_dir(module_path).absolute() == expected_pycache