1
- import codecs
2
- import locale
3
1
from pathlib import Path
4
- from unittest .mock import create_autospec
5
2
6
3
import pytest
7
4
from databricks .sdk .service .workspace import Language
8
5
9
6
from databricks .labs .ucx .hive_metastore .table_migration_status import TableMigrationIndex
10
7
from databricks .labs .ucx .source_code .base import Deprecation , Failure
8
+ from databricks .labs .ucx .source_code .files import FileLoader
9
+ from databricks .labs .ucx .source_code .graph import Dependency
11
10
from databricks .labs .ucx .source_code .linters .context import LinterContext
12
11
from databricks .labs .ucx .source_code .linters .files import FileLinter , NotebookLinter
13
12
from databricks .labs .ucx .source_code .notebooks .sources import Notebook
14
13
from databricks .labs .ucx .source_code .path_lookup import PathLookup
15
14
16
15
17
- @pytest .mark .parametrize ("path, content" , [("xyz.py" , "a = 3" ), ("xyz.sql" , "select * from dual" )])
18
- def test_file_linter_lints_supported_language (path , content , migration_index , mock_path_lookup ) -> None :
19
- linter = FileLinter (LinterContext (migration_index ), mock_path_lookup , Path (path ), None , content )
16
+ def test_file_linter_lints_python (tmp_path , migration_index , mock_path_lookup ) -> None :
17
+ path = tmp_path / "xyz.py"
18
+ path .write_text ("a = 3" )
19
+ dependency = Dependency (FileLoader (), path )
20
+ linter = FileLinter (dependency , mock_path_lookup , LinterContext (migration_index ))
20
21
advices = list (linter .lint ())
21
22
assert not advices
22
23
23
24
24
- @pytest .mark .parametrize (
25
- "bom, encoding" ,
26
- [
27
- (codecs .BOM_UTF8 , "utf-8" ),
28
- (codecs .BOM_UTF16_LE , "utf-16-le" ),
29
- (codecs .BOM_UTF16_BE , "utf-16-be" ),
30
- (codecs .BOM_UTF32_LE , "utf-32-le" ),
31
- (codecs .BOM_UTF32_BE , "utf-32-be" ),
32
- ],
33
- )
34
- def test_file_linter_lints_supported_language_encoded_file_with_bom (
35
- tmp_path , migration_index , mock_path_lookup , bom , encoding
36
- ) -> None :
37
- path = tmp_path / "file.py"
38
- path .write_bytes (bom + "a = 12" .encode (encoding ))
39
- linter = FileLinter (LinterContext (migration_index ), mock_path_lookup , path , None )
40
-
25
+ def test_file_linter_lints_sql (tmp_path , migration_index , mock_path_lookup ) -> None :
26
+ path = tmp_path / "xyz.sql"
27
+ path .write_text ("SELECT * FROM dual" )
28
+ dependency = Dependency (FileLoader (), path )
29
+ linter = FileLinter (dependency , mock_path_lookup , LinterContext (migration_index ))
41
30
advices = list (linter .lint ())
42
-
43
31
assert not advices
44
32
45
33
46
34
@pytest .mark .parametrize ("path" , ["xyz.scala" , "xyz.r" , "xyz.sh" ])
47
35
def test_file_linter_lints_not_yet_supported_language (tmp_path , path , migration_index , mock_path_lookup ) -> None :
48
36
path = tmp_path / path
49
37
path .touch ()
50
- linter = FileLinter (LinterContext (migration_index ), mock_path_lookup , Path (path ), None , "" )
38
+ dependency = Dependency (FileLoader (), path )
39
+ linter = FileLinter (dependency , mock_path_lookup , LinterContext (migration_index ))
51
40
advices = list (linter .lint ())
52
41
assert [advice .code for advice in advices ] == ["unsupported-language" ]
53
42
@@ -74,49 +63,12 @@ def test_file_linter_lints_not_yet_supported_language(tmp_path, path, migration_
74
63
def test_file_linter_lints_ignorable_language (tmp_path , path , migration_index , mock_path_lookup ) -> None :
75
64
path = tmp_path / path
76
65
path .touch ()
77
- linter = FileLinter (LinterContext (migration_index ), mock_path_lookup , Path (path ), None )
66
+ dependency = Dependency (FileLoader (), path )
67
+ linter = FileLinter (dependency , mock_path_lookup , LinterContext (migration_index ))
78
68
advices = list (linter .lint ())
79
69
assert not advices
80
70
81
71
82
- def test_file_linter_lints_non_ascii_encoded_file (migration_index , mock_path_lookup ) -> None :
83
- preferred_encoding = locale .getpreferredencoding (False )
84
- non_ascii_encoded_file = Path (__file__ ).parent .parent / "samples" / "nonascii.py"
85
- linter = FileLinter (LinterContext (migration_index ), mock_path_lookup , non_ascii_encoded_file )
86
-
87
- advices = list (linter .lint ())
88
-
89
- assert len (advices ) == 1
90
- assert advices [0 ].code == "unsupported-file-encoding"
91
- assert advices [0 ].message == f"File without { preferred_encoding } encoding is not supported { non_ascii_encoded_file } "
92
-
93
-
94
- def test_file_linter_lints_file_with_missing_file (migration_index , mock_path_lookup ) -> None :
95
- path = create_autospec (Path )
96
- path .suffix = ".py"
97
- path .open .side_effect = FileNotFoundError ("No such file or directory: 'test.py'" )
98
- linter = FileLinter (LinterContext (migration_index ), mock_path_lookup , path )
99
-
100
- advices = list (linter .lint ())
101
-
102
- assert len (advices ) == 1
103
- assert advices [0 ].code == "file-not-found"
104
- assert advices [0 ].message == f"File not found: { path } "
105
-
106
-
107
- def test_file_linter_lints_file_with_missing_read_permission (migration_index , mock_path_lookup ) -> None :
108
- path = create_autospec (Path )
109
- path .suffix = ".py"
110
- path .open .side_effect = PermissionError ("Permission denied" )
111
- linter = FileLinter (LinterContext (migration_index ), mock_path_lookup , path )
112
-
113
- advices = list (linter .lint ())
114
-
115
- assert len (advices ) == 1
116
- assert advices [0 ].code == "file-permission"
117
- assert advices [0 ].message == f"Missing read permission for { path } "
118
-
119
-
120
72
class _NotebookLinter (NotebookLinter ):
121
73
"""A helper class to construct the notebook linter from source code for testing simplification."""
122
74
@@ -127,7 +79,7 @@ def from_source_code(
127
79
context = LinterContext (index )
128
80
notebook = Notebook .parse (Path ("" ), source , default_language )
129
81
assert notebook is not None
130
- return cls (context , path_lookup , notebook )
82
+ return cls (notebook , path_lookup , context )
131
83
132
84
133
85
def test_notebook_linter_lints_source_yielding_no_advices (migration_index , mock_path_lookup ) -> None :
@@ -198,7 +150,7 @@ def test_notebook_linter_lints_parent_child_context_from_grand_parent(migration_
198
150
"""Verify the NotebookLinter can resolve %run"""
199
151
path = Path (__file__ ).parent .parent / "samples" / "parent-child-context" / "grand_parent.py"
200
152
notebook = Notebook .parse (path , path .read_text (), Language .PYTHON )
201
- linter = NotebookLinter (LinterContext ( migration_index ) , mock_path_lookup .change_directory (path .parent ), notebook )
153
+ linter = NotebookLinter (notebook , mock_path_lookup .change_directory (path .parent ), LinterContext ( migration_index ) )
202
154
203
155
advices = list (linter .lint ())
204
156
0 commit comments