def test_unreachabale_file():
    files = {
        'root.file': """
from fo.o import aa
from bar import bb
""",
        'bar': '[ap] = 2'
    }

    # Failed to parse internal module.
    with pytest.raises(ImportLoaderError) as e:
        collect_imports('root.file', read_file_from_dict(files))
    assert f"""
{get_location_marks(files['root.file'], e.value.location)}
{e.value.message}
""".startswith("""
from fo.o import aa
     ^**^
Could not load module 'fo.o'.
Error: """)

    # Failed to parse root module.
    with pytest.raises(ImportLoaderError) as e:
        collect_imports('bad.root', read_file_from_dict(files))
    assert e.value.message.startswith("Could not load module 'bad.root'.")
Example #2
0
    def run(self, context: PassManagerContext):
        visited_modules = set()

        for additional_module in self.additional_modules:
            files = collect_imports(additional_module,
                                    read_file=self.read_module)
            for module_name, ast in files.items():
                if module_name in visited_modules:
                    continue
                visited_modules.add(module_name)
                scope = ScopedName.from_string(module_name)
                context.modules.append(
                    CairoModule(cairo_file=ast, module_name=scope))

        for code, filename in context.codes:
            # Function used to read files given module names.
            # The root module (filename) is handled separately, for this module code is returned.
            def read_file_fixed(name):
                return (
                    code,
                    filename) if name == filename else self.read_module(name)

            files = collect_imports(filename, read_file=read_file_fixed)
            for module_name, ast in files.items():
                # Check if the module is one of the files given in 'context.codes'.
                is_main_scope = module_name == filename
                if is_main_scope:
                    scope = context.main_scope
                else:
                    scope = ScopedName.from_string(module_name)
                    if module_name in visited_modules:
                        continue
                    visited_modules.add(module_name)
                context.modules.append(
                    CairoModule(cairo_file=ast, module_name=scope))
def verify_exception(files: Dict[str, str], main_file: str, error: str):
    """
    Verifies that parsing the code results in the given error.
    """
    with pytest.raises(LocationError) as e:
        collect_imports(main_file, read_file_from_dict(files))
    # Remove line and column information from the error using a regular expression.
    assert re.sub(':[0-9]+:[0-9]+: ', ':?:?: ', str(e.value)) == error.strip()
def test_unparsable_import():
    files = {
        'root.file': """
from foo import bar
""",
        'foo': 'this is not cairo code'
    }

    with pytest.raises(ParserError):
        collect_imports('root.file', read_file_from_dict(files))
def test_lang_directive():
    files = {
        'a': """
from c import x
""",
        'b': """
%lang other_lang
from c import x
""",
        'c': """
%lang lang
from d_lang import x
from d_no_lang import x
""",
        'd_lang': """
%lang lang
const x = 0
""",
        'd_no_lang': """
const x = 0
""",
        'e': """
%lang lang  # First line.
%lang lang  # Second line.
"""
    }

    # Make sure that starting from 'c' does not raise an exception.
    collect_imports('c', read_file_from_dict(files))

    verify_exception(
        files, 'a', """
a:?:?: Importing modules with %lang directive 'lang' must be from a module with the same directive.
from c import x
     ^
""")

    verify_exception(
        files, 'b', """
b:?:?: Importing modules with %lang directive 'lang' must be from a module with the same directive.
from c import x
     ^
""")

    verify_exception(
        files, 'e', """
e:?:?: Found two %lang directives
%lang lang  # Second line.
^********^
""")
def test_shallow_tree_graph():
    files = {
        'root.file': """
from a import aa
from b import bb
""",
        'a': '[ap] = 1',
        'b': '[ap] = 2'
    }

    expected_res = {name: parse_file(code) for name, code in files.items()}
    assert collect_imports('root.file',
                           read_file_from_dict(files)) == expected_res
    assert set(collect_imports('a',
                               read_file_from_dict(files)).keys()) == {'a'}
def test_circular_dep():
    # Singleton circle.
    with pytest.raises(UsingCycleError) as e:
        collect_imports('a', read_file_from_dict({'a': 'from a import b'}))
    assert str(e.value) == """\
Found circular imports dependency:
a imports
a"""

    # Big circle.
    with pytest.raises(UsingCycleError) as e:
        collect_imports(
            'a0',
            read_file_from_dict(
                {f'a{i}': f'from a{(i+1) % 9} import b'
                 for i in range(10)}))
    assert str(e.value) == """\
def test_dag():
    files = {
        'root.file': """
from a import aa
from b import bb
""",
        'a': """
from common.first import some1
from common.second import some2
""",
        'b': """
from common.first import some1
from common.second import some2
""",
        'common.first': '[ap] = 1',
        'common.second': '[ap] = 2',
    }

    expected_res = {name: parse_file(code) for name, code in files.items()}
    assert collect_imports('root.file',
                           read_file_from_dict(files)) == expected_res
def test_topologycal_order():
    """
    Build dependencies DAG over the vertices 0..99 and a list of files named 'a0'..'a99'
    such that a<i> imports a<j> directly if and only if i -> j in the dependencies DAG.
    The dependencies DAG is constructed by having every node pointing to 3 other nodes
    having higher indices.
    We test collect_imports on 'a0' returns the dictionary ordered correctly,
    by scanning it and ensuring that when we see some file, all it's dependencies
    where seen before.
    """

    N_VERTICES = 100
    N_NEIGHBORS = 3

    # Initialize the dependencies DAG. A list of int lists.
    # j is in the i-th list iff i->j in the dependencies DAG.
    dependencies = [[]] * N_VERTICES
    for i in range(N_VERTICES - N_NEIGHBORS):
        dependencies[i] = sample(range(i + 1, N_VERTICES), N_NEIGHBORS)

    # Construct files.
    files: Dict[str, str] = {}
    for i in range(N_VERTICES):
        # Build the i-th file.
        files[f'a{i}'] = '\n'.join(
            [f'from a{j} import nothing' for j in dependencies[i]])

    # Collect packages.
    packages = collect_imports('a0', read_file_from_dict(files))

    # Test order.
    seen = [False] * N_VERTICES
    for pkg in packages:
        curr_id = int(pkg[1:])
        for j in dependencies[i]:
            assert seen[j]
        seen[curr_id] = True
def test_long_path_grph():
    files = {f'a{i}': f'from a{i+1} import b' for i in range(10)}
    files['a9'] = '[ap] = 0'

    expected_res = {name: parse_file(code) for name, code in files.items()}
    assert collect_imports('a0', read_file_from_dict(files)) == expected_res