Skip to content

Commit 5a6a250

Browse files
radoeringneersighted
authored andcommitted
performance: cache validation result to avoid unnecessary file system access
1 parent 76d0984 commit 5a6a250

File tree

5 files changed

+57
-40
lines changed

5 files changed

+57
-40
lines changed

src/poetry/core/packages/directory_dependency.py

+8-17
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
from __future__ import annotations
22

33
import functools
4-
import logging
54

65
from typing import TYPE_CHECKING
76
from typing import Iterable
@@ -14,8 +13,6 @@
1413
if TYPE_CHECKING:
1514
from pathlib import Path
1615

17-
logger = logging.getLogger(__name__)
18-
1916

2017
class DirectoryDependency(PathDependency):
2118
def __init__(
@@ -46,28 +43,22 @@ def __init__(
4643
def develop(self) -> bool:
4744
return self._develop
4845

49-
def validate(self, *, raise_error: bool) -> bool:
50-
if not super().validate(raise_error=raise_error):
51-
return False
46+
def _validate(self) -> str:
47+
message = super()._validate()
48+
if message:
49+
return message
5250

53-
message = ""
5451
if self._full_path.is_file():
55-
message = (
52+
return (
5653
f"{self._full_path} for {self.pretty_name} is a file,"
5754
" expected a directory"
5855
)
59-
elif not is_python_project(self._full_path):
60-
message = (
56+
if not is_python_project(self._full_path):
57+
return (
6158
f"Directory {self._full_path} for {self.pretty_name} does not seem"
6259
" to be a Python package"
6360
)
64-
65-
if message:
66-
if raise_error:
67-
raise ValueError(message)
68-
logger.warning(message)
69-
return False
70-
return True
61+
return ""
7162

7263
def _supports_poetry(self) -> bool:
7364
return PyProjectTOML(self._full_path / "pyproject.toml").is_poetry_project()

src/poetry/core/packages/file_dependency.py

+6-12
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import hashlib
44
import io
5-
import logging
65
import warnings
76

87
from typing import TYPE_CHECKING
@@ -14,8 +13,6 @@
1413
if TYPE_CHECKING:
1514
from pathlib import Path
1615

17-
logger = logging.getLogger(__name__)
18-
1916

2017
class FileDependency(PathDependency):
2118
def __init__(
@@ -37,20 +34,17 @@ def __init__(
3734
extras=extras,
3835
)
3936

40-
def validate(self, *, raise_error: bool) -> bool:
41-
if not super().validate(raise_error=raise_error):
42-
return False
37+
def _validate(self) -> str:
38+
message = super()._validate()
39+
if message:
40+
return message
4341

4442
if self._full_path.is_dir():
45-
message = (
43+
return (
4644
f"{self._full_path} for {self.pretty_name} is a directory,"
4745
" expected a file"
4846
)
49-
if raise_error:
50-
raise ValueError(message)
51-
logger.warning(message)
52-
return False
53-
return True
47+
return ""
5448

5549
def hash(self, hash_name: str = "sha256") -> str:
5650
warnings.warn(

src/poetry/core/packages/path_dependency.py

+13-7
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ def __init__(
4545
source_url=self._full_path.as_posix(),
4646
extras=extras,
4747
)
48+
# cache validation result to avoid unnecessary file system access
49+
self._validation_error = self._validate()
4850
self.validate(raise_error=False)
4951

5052
@property
@@ -66,13 +68,12 @@ def is_directory(self) -> bool:
6668
return self._source_type == "directory"
6769

6870
def validate(self, *, raise_error: bool) -> bool:
69-
if not self._full_path.exists():
70-
message = f"Path {self._full_path} for {self.pretty_name} does not exist"
71-
if raise_error:
72-
raise ValueError(message)
73-
logger.warning(message)
74-
return False
75-
return True
71+
if not self._validation_error:
72+
return True
73+
if raise_error:
74+
raise ValueError(self._validation_error)
75+
logger.warning(self._validation_error)
76+
return False
7677

7778
@property
7879
def base_pep_508_name(self) -> str:
@@ -86,3 +87,8 @@ def base_pep_508_name(self) -> str:
8687
requirement += f" @ {path}"
8788

8889
return requirement
90+
91+
def _validate(self) -> str:
92+
if not self._full_path.exists():
93+
return f"Path {self._full_path} for {self.pretty_name} does not exist"
94+
return ""

tests/packages/test_directory_dependency.py

+16-2
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,18 @@
1212

1313
if TYPE_CHECKING:
1414
from _pytest.logging import LogCaptureFixture
15+
from pytest_mock import MockerFixture
16+
1517

1618
DIST_PATH = Path(__file__).parent.parent / "fixtures" / "distributions"
1719
SAMPLE_PROJECT = Path(__file__).parent.parent / "fixtures" / "sample_project"
1820

1921

20-
def test_directory_dependency_does_not_exist(caplog: LogCaptureFixture) -> None:
22+
def test_directory_dependency_does_not_exist(
23+
caplog: LogCaptureFixture, mocker: MockerFixture
24+
) -> None:
25+
mock_exists = mocker.patch.object(Path, "exists")
26+
mock_exists.return_value = False
2127
dep = DirectoryDependency("demo", DIST_PATH / "invalid")
2228
assert len(caplog.records) == 1
2329
record = caplog.records[0]
@@ -27,8 +33,14 @@ def test_directory_dependency_does_not_exist(caplog: LogCaptureFixture) -> None:
2733
with pytest.raises(ValueError, match="does not exist"):
2834
dep.validate(raise_error=True)
2935

36+
mock_exists.assert_called_once()
37+
3038

31-
def test_directory_dependency_is_file(caplog: LogCaptureFixture) -> None:
39+
def test_directory_dependency_is_file(
40+
caplog: LogCaptureFixture, mocker: MockerFixture
41+
) -> None:
42+
mock_is_file = mocker.patch.object(Path, "is_file")
43+
mock_is_file.return_value = True
3244
dep = DirectoryDependency("demo", DIST_PATH / "demo-0.1.0.tar.gz")
3345
assert len(caplog.records) == 1
3446
record = caplog.records[0]
@@ -38,6 +50,8 @@ def test_directory_dependency_is_file(caplog: LogCaptureFixture) -> None:
3850
with pytest.raises(ValueError, match="is a file"):
3951
dep.validate(raise_error=True)
4052

53+
mock_is_file.assert_called_once()
54+
4155

4256
def test_directory_dependency_is_not_a_python_project(
4357
caplog: LogCaptureFixture,

tests/packages/test_file_dependency.py

+14-2
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,11 @@
2121
TEST_FILE = "demo-0.1.0.tar.gz"
2222

2323

24-
def test_file_dependency_does_not_exist(caplog: LogCaptureFixture) -> None:
24+
def test_file_dependency_does_not_exist(
25+
caplog: LogCaptureFixture, mocker: MockerFixture
26+
) -> None:
27+
mock_exists = mocker.patch.object(Path, "exists")
28+
mock_exists.return_value = False
2529
dep = FileDependency("demo", DIST_PATH / "demo-0.2.0.tar.gz")
2630
assert len(caplog.records) == 1
2731
record = caplog.records[0]
@@ -31,8 +35,14 @@ def test_file_dependency_does_not_exist(caplog: LogCaptureFixture) -> None:
3135
with pytest.raises(ValueError, match="does not exist"):
3236
dep.validate(raise_error=True)
3337

38+
mock_exists.assert_called_once()
39+
3440

35-
def test_file_dependency_is_directory(caplog: LogCaptureFixture) -> None:
41+
def test_file_dependency_is_directory(
42+
caplog: LogCaptureFixture, mocker: MockerFixture
43+
) -> None:
44+
mock_is_directory = mocker.patch.object(Path, "is_dir")
45+
mock_is_directory.return_value = True
3646
dep = FileDependency("demo", DIST_PATH)
3747
assert len(caplog.records) == 1
3848
record = caplog.records[0]
@@ -42,6 +52,8 @@ def test_file_dependency_is_directory(caplog: LogCaptureFixture) -> None:
4252
with pytest.raises(ValueError, match="is a directory"):
4353
dep.validate(raise_error=True)
4454

55+
mock_is_directory.assert_called_once()
56+
4557

4658
def test_default_hash() -> None:
4759
with pytest.warns(DeprecationWarning):

0 commit comments

Comments
 (0)