Skip to content

Commit 58d4261

Browse files
authored
feat(langchain): add ruff rules PTH (#32008)
See https://docs.astral.sh/ruff/rules/#flake8-use-pathlib-pth
1 parent fd168e1 commit 58d4261

File tree

10 files changed

+41
-47
lines changed

10 files changed

+41
-47
lines changed

libs/langchain/langchain/agents/agent.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -209,10 +209,10 @@ def save(self, file_path: Union[Path, str]) -> None:
209209
raise NotImplementedError(msg)
210210

211211
if save_path.suffix == ".json":
212-
with open(file_path, "w") as f:
212+
with save_path.open("w") as f:
213213
json.dump(agent_dict, f, indent=4)
214214
elif save_path.suffix.endswith((".yaml", ".yml")):
215-
with open(file_path, "w") as f:
215+
with save_path.open("w") as f:
216216
yaml.dump(agent_dict, f, default_flow_style=False)
217217
else:
218218
msg = f"{save_path} must be json or yaml"
@@ -352,10 +352,10 @@ def save(self, file_path: Union[Path, str]) -> None:
352352
directory_path.mkdir(parents=True, exist_ok=True)
353353

354354
if save_path.suffix == ".json":
355-
with open(file_path, "w") as f:
355+
with save_path.open("w") as f:
356356
json.dump(agent_dict, f, indent=4)
357357
elif save_path.suffix.endswith((".yaml", ".yml")):
358-
with open(file_path, "w") as f:
358+
with save_path.open("w") as f:
359359
yaml.dump(agent_dict, f, default_flow_style=False)
360360
else:
361361
msg = f"{save_path} must be json or yaml"

libs/langchain/langchain/agents/loading.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,10 +136,10 @@ def _load_agent_from_file(
136136
file_path = Path(file) if isinstance(file, str) else file
137137
# Load from either json or yaml.
138138
if file_path.suffix[1:] == "json":
139-
with open(file_path) as f:
139+
with file_path.open() as f:
140140
config = json.load(f)
141141
elif file_path.suffix[1:] == "yaml":
142-
with open(file_path) as f:
142+
with file_path.open() as f:
143143
config = yaml.safe_load(f)
144144
else:
145145
msg = f"Unsupported file type, must be one of {valid_suffixes}."

libs/langchain/langchain/chains/base.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -781,10 +781,10 @@ def save(self, file_path: Union[Path, str]) -> None:
781781
directory_path.mkdir(parents=True, exist_ok=True)
782782

783783
if save_path.suffix == ".json":
784-
with open(file_path, "w") as f:
784+
with save_path.open("w") as f:
785785
json.dump(chain_dict, f, indent=4)
786786
elif save_path.suffix.endswith((".yaml", ".yml")):
787-
with open(file_path, "w") as f:
787+
with save_path.open("w") as f:
788788
yaml.dump(chain_dict, f, default_flow_style=False)
789789
else:
790790
msg = f"{save_path} must be json or yaml"

libs/langchain/langchain/chains/loading.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -713,10 +713,10 @@ def _load_chain_from_file(file: Union[str, Path], **kwargs: Any) -> Chain:
713713
file_path = Path(file) if isinstance(file, str) else file
714714
# Load from either json or yaml.
715715
if file_path.suffix == ".json":
716-
with open(file_path) as f:
716+
with file_path.open() as f:
717717
config = json.load(f)
718718
elif file_path.suffix.endswith((".yaml", ".yml")):
719-
with open(file_path) as f:
719+
with file_path.open() as f:
720720
config = yaml.safe_load(f)
721721
else:
722722
msg = "File type must be json or yaml"

libs/langchain/langchain/storage/file_system.py

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -77,16 +77,17 @@ def _get_full_path(self, key: str) -> Path:
7777
if not re.match(r"^[a-zA-Z0-9_.\-/]+$", key):
7878
msg = f"Invalid characters in key: {key}"
7979
raise InvalidKeyException(msg)
80-
full_path = os.path.abspath(self.root_path / key)
81-
common_path = os.path.commonpath([str(self.root_path), full_path])
82-
if common_path != str(self.root_path):
80+
full_path = (self.root_path / key).resolve()
81+
root_path = self.root_path.resolve()
82+
common_path = os.path.commonpath([root_path, full_path])
83+
if common_path != str(root_path):
8384
msg = (
84-
f"Invalid key: {key}. Key should be relative to the full path."
85-
f"{self.root_path} vs. {common_path} and full path of {full_path}"
85+
f"Invalid key: {key}. Key should be relative to the full path. "
86+
f"{root_path} vs. {common_path} and full path of {full_path}"
8687
)
8788
raise InvalidKeyException(msg)
8889

89-
return Path(full_path)
90+
return full_path
9091

9192
def _mkdir_for_store(self, dir_path: Path) -> None:
9293
"""Makes a store directory path (including parents) with specified permissions
@@ -104,7 +105,7 @@ def _mkdir_for_store(self, dir_path: Path) -> None:
104105
self._mkdir_for_store(dir_path.parent)
105106
dir_path.mkdir(exist_ok=True)
106107
if self.chmod_dir is not None:
107-
os.chmod(dir_path, self.chmod_dir)
108+
dir_path.chmod(self.chmod_dir)
108109

109110
def mget(self, keys: Sequence[str]) -> list[Optional[bytes]]:
110111
"""Get the values associated with the given keys.
@@ -124,7 +125,7 @@ def mget(self, keys: Sequence[str]) -> list[Optional[bytes]]:
124125
values.append(value)
125126
if self.update_atime:
126127
# update access time only; preserve modified time
127-
os.utime(full_path, (time.time(), os.stat(full_path).st_mtime))
128+
os.utime(full_path, (time.time(), full_path.stat().st_mtime))
128129
else:
129130
values.append(None)
130131
return values
@@ -143,7 +144,7 @@ def mset(self, key_value_pairs: Sequence[tuple[str, bytes]]) -> None:
143144
self._mkdir_for_store(full_path.parent)
144145
full_path.write_bytes(value)
145146
if self.chmod_file is not None:
146-
os.chmod(full_path, self.chmod_file)
147+
full_path.chmod(self.chmod_file)
147148

148149
def mdelete(self, keys: Sequence[str]) -> None:
149150
"""Delete the given keys and their associated values.

libs/langchain/pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ select = [
165165
"PGH", # pygrep-hooks
166166
"PIE", # flake8-pie
167167
"PERF", # flake8-perf
168+
"PTH", # flake8-use-pathlib
168169
"PYI", # flake8-pyi
169170
"Q", # flake8-quotes
170171
"RET", # flake8-return
Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,18 @@
1-
import os
21
from pathlib import Path
32

43
import pytest
54

65
# Getting the absolute path of the current file's directory
7-
ABS_PATH = os.path.dirname(os.path.abspath(__file__))
6+
ABS_PATH = Path(__file__).resolve().parent
87

98
# Getting the absolute path of the project's root directory
10-
PROJECT_DIR = os.path.abspath(os.path.join(ABS_PATH, os.pardir, os.pardir))
9+
PROJECT_DIR = ABS_PATH.parent.parent
1110

1211

1312
# Loading the .env file if it exists
1413
def _load_env() -> None:
15-
dotenv_path = os.path.join(PROJECT_DIR, "tests", "integration_tests", ".env")
16-
if os.path.exists(dotenv_path):
14+
dotenv_path = PROJECT_DIR / "tests" / "integration_tests" / ".env"
15+
if dotenv_path.exists():
1716
from dotenv import load_dotenv
1817

1918
load_dotenv(dotenv_path)
@@ -24,15 +23,12 @@ def _load_env() -> None:
2423

2524
@pytest.fixture(scope="module")
2625
def test_dir() -> Path:
27-
return Path(os.path.join(PROJECT_DIR, "tests", "integration_tests"))
26+
return PROJECT_DIR / "tests" / "integration_tests"
2827

2928

3029
# This fixture returns a string containing the path to the cassette directory for the
3130
# current module
3231
@pytest.fixture(scope="module")
3332
def vcr_cassette_dir(request: pytest.FixtureRequest) -> str:
34-
return os.path.join(
35-
os.path.dirname(request.module.__file__),
36-
"cassettes",
37-
os.path.basename(request.module.__file__).replace(".py", ""),
38-
)
33+
module = Path(request.module.__file__)
34+
return str(module.parent / "cassettes" / module.stem)

libs/langchain/tests/unit_tests/storage/test_filesystem.py

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import os
21
import tempfile
32
from collections.abc import Generator
3+
from pathlib import Path
44

55
import pytest
66
from langchain_core.stores import InvalidKeyException
@@ -41,9 +41,8 @@ def test_mset_chmod(chmod_dir_s: str, chmod_file_s: str) -> None:
4141
with tempfile.TemporaryDirectory() as temp_dir:
4242
# Instantiate the LocalFileStore with a directory inside the temporary directory
4343
# as the root path
44-
temp_dir = os.path.join(temp_dir, "store_dir")
4544
file_store = LocalFileStore(
46-
temp_dir,
45+
Path(temp_dir) / "store_dir",
4746
chmod_dir=chmod_dir,
4847
chmod_file=chmod_file,
4948
)
@@ -54,34 +53,32 @@ def test_mset_chmod(chmod_dir_s: str, chmod_file_s: str) -> None:
5453

5554
# verify the permissions are set correctly
5655
# (test only the standard user/group/other bits)
57-
dir_path = str(file_store.root_path)
58-
file_path = os.path.join(dir_path, "key1")
59-
assert (os.stat(dir_path).st_mode & 0o777) == chmod_dir
60-
assert (os.stat(file_path).st_mode & 0o777) == chmod_file
56+
dir_path = file_store.root_path
57+
file_path = file_store.root_path / "key1"
58+
assert (dir_path.stat().st_mode & 0o777) == chmod_dir
59+
assert (file_path.stat().st_mode & 0o777) == chmod_file
6160

6261

6362
def test_mget_update_atime() -> None:
6463
# Create a temporary directory for testing
6564
with tempfile.TemporaryDirectory() as temp_dir:
6665
# Instantiate the LocalFileStore with a directory inside the temporary directory
6766
# as the root path
68-
temp_dir = os.path.join(temp_dir, "store_dir")
69-
file_store = LocalFileStore(temp_dir, update_atime=True)
67+
file_store = LocalFileStore(Path(temp_dir) / "store_dir", update_atime=True)
7068

7169
# Set values for keys
7270
key_value_pairs = [("key1", b"value1"), ("key2", b"value2")]
7371
file_store.mset(key_value_pairs)
7472

7573
# Get original access time
76-
dir_path = str(file_store.root_path)
77-
file_path = os.path.join(dir_path, "key1")
78-
atime1 = os.stat(file_path).st_atime
74+
file_path = file_store.root_path / "key1"
75+
atime1 = file_path.stat().st_atime
7976

8077
# Get values for keys
8178
_ = file_store.mget(["key1", "key2"])
8279

8380
# Make sure the filesystem access time has been updated
84-
atime2 = os.stat(file_path).st_atime
81+
atime2 = file_path.stat().st_atime
8582
assert atime2 != atime1
8683

8784

@@ -131,7 +128,7 @@ def test_yield_keys(file_store: LocalFileStore) -> None:
131128
keys = list(file_store.yield_keys())
132129

133130
# Assert that the yielded keys match the expected keys
134-
expected_keys = ["key1", os.path.join("subdir", "key2")]
131+
expected_keys = ["key1", str(Path("subdir") / "key2")]
135132
assert keys == expected_keys
136133

137134

libs/langchain/tests/unit_tests/test_dependencies.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
@pytest.fixture
1717
def uv_conf() -> dict[str, Any]:
1818
"""Load the pyproject.toml file."""
19-
with open(PYPROJECT_TOML) as f:
19+
with PYPROJECT_TOML.open() as f:
2020
return toml.load(f)
2121

2222

libs/langchain/tests/unit_tests/test_imports.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,8 +118,7 @@ def extract_deprecated_lookup(file_path: str) -> Optional[dict[str, Any]]:
118118
Returns:
119119
dict or None: The value of DEPRECATED_LOOKUP if it exists, None otherwise.
120120
"""
121-
with open(file_path) as file:
122-
tree = ast.parse(file.read(), filename=file_path)
121+
tree = ast.parse(Path(file_path).read_text(), filename=file_path)
123122

124123
for node in ast.walk(tree):
125124
if isinstance(node, ast.Assign):

0 commit comments

Comments
 (0)