/ tests / dev / test_check_init_py.py
test_check_init_py.py
  1  import subprocess
  2  import sys
  3  from pathlib import Path
  4  
  5  import pytest
  6  
  7  
  8  def get_check_init_py_script() -> Path:
  9      return Path(__file__).resolve().parents[2] / "dev" / "check_init_py.py"
 10  
 11  
 12  @pytest.fixture
 13  def temp_git_repo(tmp_path: Path) -> Path:
 14      subprocess.check_call(["git", "init"], cwd=tmp_path)
 15      subprocess.check_call(["git", "config", "user.email", "test@example.com"], cwd=tmp_path)
 16      subprocess.check_call(["git", "config", "user.name", "Test User"], cwd=tmp_path)
 17      return tmp_path
 18  
 19  
 20  def test_exits_with_0_when_all_directories_have_init_py(temp_git_repo: Path) -> None:
 21      mlflow_dir = temp_git_repo / "mlflow"
 22      test_package_dir = mlflow_dir / "test_package"
 23      test_package_dir.mkdir(parents=True)
 24  
 25      (mlflow_dir / "__init__.py").touch()
 26      (test_package_dir / "__init__.py").touch()
 27      (test_package_dir / "test_module.py").touch()
 28  
 29      subprocess.check_call(["git", "add", "."], cwd=temp_git_repo)
 30      subprocess.check_call(["git", "commit", "-m", "Initial commit"], cwd=temp_git_repo)
 31  
 32      result = subprocess.run(
 33          [sys.executable, get_check_init_py_script()],
 34          capture_output=True,
 35          text=True,
 36          cwd=temp_git_repo,
 37      )
 38  
 39      assert result.returncode == 0
 40      assert result.stdout == ""
 41  
 42  
 43  def test_exits_with_1_when_directories_missing_init_py(temp_git_repo: Path) -> None:
 44      mlflow_dir = temp_git_repo / "mlflow"
 45      test_package_dir = mlflow_dir / "test_package"
 46      test_package_dir.mkdir(parents=True)
 47  
 48      (test_package_dir / "test_module.py").touch()
 49  
 50      subprocess.check_call(["git", "add", "."], cwd=temp_git_repo)
 51      subprocess.check_call(["git", "commit", "-m", "Initial commit"], cwd=temp_git_repo)
 52  
 53      result = subprocess.run(
 54          [sys.executable, get_check_init_py_script()],
 55          capture_output=True,
 56          text=True,
 57          cwd=temp_git_repo,
 58      )
 59  
 60      assert result.returncode == 1
 61      assert (
 62          "Error: The following directories contain Python files but lack __init__.py:"
 63          in result.stdout
 64      )
 65      assert "mlflow" in result.stdout
 66      assert "mlflow/test_package" in result.stdout
 67  
 68  
 69  def test_exits_with_0_when_no_python_files_exist(temp_git_repo: Path) -> None:
 70      mlflow_dir = temp_git_repo / "mlflow"
 71      js_dir = mlflow_dir / "server" / "js"
 72      js_dir.mkdir(parents=True)
 73  
 74      (js_dir / "main.js").touch()
 75  
 76      subprocess.check_call(["git", "add", "."], cwd=temp_git_repo)
 77      subprocess.check_call(["git", "commit", "-m", "Initial commit"], cwd=temp_git_repo)
 78  
 79      result = subprocess.run(
 80          [sys.executable, get_check_init_py_script()],
 81          capture_output=True,
 82          text=True,
 83          cwd=temp_git_repo,
 84      )
 85  
 86      assert result.returncode == 0
 87      assert result.stdout == ""
 88  
 89  
 90  def test_identifies_only_directories_missing_init_py(temp_git_repo: Path) -> None:
 91      mlflow_dir = temp_git_repo / "mlflow"
 92      package1_dir = mlflow_dir / "package1"
 93      package2_dir = mlflow_dir / "package2"
 94      package1_dir.mkdir(parents=True)
 95      package2_dir.mkdir(parents=True)
 96  
 97      (mlflow_dir / "__init__.py").touch()
 98      (package1_dir / "__init__.py").touch()
 99  
100      (package1_dir / "module1.py").touch()
101      (package2_dir / "module2.py").touch()
102  
103      subprocess.check_call(["git", "add", "."], cwd=temp_git_repo)
104      subprocess.check_call(["git", "commit", "-m", "Initial commit"], cwd=temp_git_repo)
105  
106      result = subprocess.run(
107          [sys.executable, get_check_init_py_script()],
108          capture_output=True,
109          text=True,
110          cwd=temp_git_repo,
111      )
112  
113      assert result.returncode == 1
114      assert (
115          "Error: The following directories contain Python files but lack __init__.py:"
116          in result.stdout
117      )
118      assert "mlflow/package2" in result.stdout
119      assert "mlflow/package1" not in result.stdout
120  
121  
122  def test_checks_tests_directory_for_missing_init_py(temp_git_repo: Path) -> None:
123      tests_dir = temp_git_repo / "tests"
124      test_package_dir = tests_dir / "test_package"
125      test_package_dir.mkdir(parents=True)
126  
127      # Only test files (starting with test_) are checked
128      (test_package_dir / "test_module.py").touch()
129  
130      subprocess.check_call(["git", "add", "."], cwd=temp_git_repo)
131      subprocess.check_call(["git", "commit", "-m", "Initial commit"], cwd=temp_git_repo)
132  
133      result = subprocess.run(
134          [sys.executable, get_check_init_py_script()],
135          capture_output=True,
136          text=True,
137          cwd=temp_git_repo,
138      )
139  
140      assert result.returncode == 1
141      assert (
142          "Error: The following directories contain Python files but lack __init__.py:"
143          in result.stdout
144      )
145      assert "tests/test_package" in result.stdout
146      assert "tests" in result.stdout  # Parent directory also needs __init__.py
147  
148  
149  def test_exits_with_0_when_tests_directories_have_init_py(temp_git_repo: Path) -> None:
150      tests_dir = temp_git_repo / "tests"
151      test_package_dir = tests_dir / "test_package"
152      test_package_dir.mkdir(parents=True)
153  
154      (tests_dir / "__init__.py").touch()
155      (test_package_dir / "__init__.py").touch()
156      (test_package_dir / "test_module.py").touch()
157  
158      subprocess.check_call(["git", "add", "."], cwd=temp_git_repo)
159      subprocess.check_call(["git", "commit", "-m", "Initial commit"], cwd=temp_git_repo)
160  
161      result = subprocess.run(
162          [sys.executable, get_check_init_py_script()],
163          capture_output=True,
164          text=True,
165          cwd=temp_git_repo,
166      )
167  
168      assert result.returncode == 0
169      assert result.stdout == ""
170  
171  
172  def test_ignores_non_test_files_in_tests_directory(temp_git_repo: Path) -> None:
173      tests_dir = temp_git_repo / "tests"
174      test_package_dir = tests_dir / "test_package"
175      test_package_dir.mkdir(parents=True)
176  
177      # Non-test file (doesn't start with test_) should be ignored
178      (test_package_dir / "helper.py").touch()
179      (test_package_dir / "utils.py").touch()
180  
181      subprocess.check_call(["git", "add", "."], cwd=temp_git_repo)
182      subprocess.check_call(["git", "commit", "-m", "Initial commit"], cwd=temp_git_repo)
183  
184      result = subprocess.run(
185          [sys.executable, get_check_init_py_script()],
186          capture_output=True,
187          text=True,
188          cwd=temp_git_repo,
189      )
190  
191      # Should pass since no test files exist
192      assert result.returncode == 0
193      assert result.stdout == ""
194  
195  
196  def test_checks_all_parent_directories(temp_git_repo: Path) -> None:
197      mlflow_dir = temp_git_repo / "mlflow"
198      deep_dir = mlflow_dir / "level1" / "level2" / "level3"
199      deep_dir.mkdir(parents=True)
200  
201      # Create a Python file deep in the hierarchy
202      (deep_dir / "module.py").touch()
203  
204      # Only add __init__.py to some directories
205      (mlflow_dir / "__init__.py").touch()
206      (mlflow_dir / "level1" / "__init__.py").touch()
207      # Missing: level2 and level3 __init__.py files
208  
209      subprocess.check_call(["git", "add", "."], cwd=temp_git_repo)
210      subprocess.check_call(["git", "commit", "-m", "Initial commit"], cwd=temp_git_repo)
211  
212      result = subprocess.run(
213          [sys.executable, get_check_init_py_script()],
214          capture_output=True,
215          text=True,
216          cwd=temp_git_repo,
217      )
218  
219      assert result.returncode == 1
220      assert (
221          "Error: The following directories contain Python files but lack __init__.py:"
222          in result.stdout
223      )
224      # Both level2 and level3 should be reported as missing __init__.py
225      assert "mlflow/level1/level2" in result.stdout
226      assert "mlflow/level1/level2/level3" in result.stdout