Skip to content

Commit c2a3021

Browse files
Erick Friiseyurtsevbaskaryandanodonovantomdgr
authored
multiple: pydantic 2 compatibility, v0.3 (#26443)
Signed-off-by: ChengZi <[email protected]> Co-authored-by: Eugene Yurtsev <[email protected]> Co-authored-by: Bagatur <[email protected]> Co-authored-by: Dan O'Donovan <[email protected]> Co-authored-by: Tom Daniel Grande <[email protected]> Co-authored-by: Grande <[email protected]> Co-authored-by: Bagatur <[email protected]> Co-authored-by: ccurme <[email protected]> Co-authored-by: Harrison Chase <[email protected]> Co-authored-by: Tomaz Bratanic <[email protected]> Co-authored-by: ZhangShenao <[email protected]> Co-authored-by: Friso H. Kingma <[email protected]> Co-authored-by: ChengZi <[email protected]> Co-authored-by: Nuno Campos <[email protected]> Co-authored-by: Morgante Pell <[email protected]>
1 parent d9813bd commit c2a3021

File tree

1,402 files changed

+38823
-30915
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

1,402 files changed

+38823
-30915
lines changed

.github/scripts/check_diff.py

Lines changed: 85 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22
import json
33
import os
44
import sys
5-
import tomllib
65
from collections import defaultdict
76
from typing import Dict, List, Set
87
from pathlib import Path
8+
import tomllib
9+
10+
from get_min_versions import get_min_version_from_toml
911

1012

1113
LANGCHAIN_DIRS = [
@@ -16,6 +18,12 @@
1618
"libs/experimental",
1719
]
1820

21+
# when set to True, we are ignoring core dependents
22+
# in order to be able to get CI to pass for each individual
23+
# package that depends on core
24+
# e.g. if you touch core, we don't then add textsplitters/etc to CI
25+
IGNORE_CORE_DEPENDENTS = False
26+
1927
# ignored partners are removed from dependents
2028
# but still run if directly edited
2129
IGNORED_PARTNERS = [
@@ -99,44 +107,96 @@ def add_dependents(dirs_to_eval: Set[str], dependents: dict) -> List[str]:
99107

100108

101109
def _get_configs_for_single_dir(job: str, dir_: str) -> List[Dict[str, str]]:
102-
if dir_ == "libs/core":
103-
return [
104-
{"working-directory": dir_, "python-version": f"3.{v}"}
105-
for v in range(8, 13)
106-
]
107-
min_python = "3.8"
108-
max_python = "3.12"
110+
if job == "test-pydantic":
111+
return _get_pydantic_test_configs(dir_)
109112

113+
if dir_ == "libs/core":
114+
py_versions = ["3.9", "3.10", "3.11", "3.12"]
110115
# custom logic for specific directories
111-
if dir_ == "libs/partners/milvus":
116+
elif dir_ == "libs/partners/milvus":
112117
# milvus poetry doesn't allow 3.12 because they
113118
# declare deps in funny way
114-
max_python = "3.11"
119+
py_versions = ["3.9", "3.11"]
115120

116-
if dir_ in ["libs/community", "libs/langchain"] and job == "extended-tests":
121+
elif dir_ in ["libs/community", "libs/langchain"] and job == "extended-tests":
117122
# community extended test resolution in 3.12 is slow
118123
# even in uv
119-
max_python = "3.11"
124+
py_versions = ["3.9", "3.11"]
120125

121-
if dir_ == "libs/community" and job == "compile-integration-tests":
126+
elif dir_ == "libs/community" and job == "compile-integration-tests":
122127
# community integration deps are slow in 3.12
123-
max_python = "3.11"
128+
py_versions = ["3.9", "3.11"]
129+
else:
130+
py_versions = ["3.9", "3.12"]
124131

125-
return [
126-
{"working-directory": dir_, "python-version": min_python},
127-
{"working-directory": dir_, "python-version": max_python},
132+
return [{"working-directory": dir_, "python-version": py_v} for py_v in py_versions]
133+
134+
135+
def _get_pydantic_test_configs(
136+
dir_: str, *, python_version: str = "3.11"
137+
) -> List[Dict[str, str]]:
138+
with open("./libs/core/poetry.lock", "rb") as f:
139+
core_poetry_lock_data = tomllib.load(f)
140+
for package in core_poetry_lock_data["package"]:
141+
if package["name"] == "pydantic":
142+
core_max_pydantic_minor = package["version"].split(".")[1]
143+
break
144+
145+
with open(f"./{dir_}/poetry.lock", "rb") as f:
146+
dir_poetry_lock_data = tomllib.load(f)
147+
148+
for package in dir_poetry_lock_data["package"]:
149+
if package["name"] == "pydantic":
150+
dir_max_pydantic_minor = package["version"].split(".")[1]
151+
break
152+
153+
core_min_pydantic_version = get_min_version_from_toml(
154+
"./libs/core/pyproject.toml", "release", python_version, include=["pydantic"]
155+
)["pydantic"]
156+
core_min_pydantic_minor = core_min_pydantic_version.split(".")[1] if "." in core_min_pydantic_version else "0"
157+
dir_min_pydantic_version = (
158+
get_min_version_from_toml(
159+
f"./{dir_}/pyproject.toml", "release", python_version, include=["pydantic"]
160+
)
161+
.get("pydantic", "0.0.0")
162+
)
163+
dir_min_pydantic_minor = dir_min_pydantic_version.split(".")[1] if "." in dir_min_pydantic_version else "0"
164+
165+
custom_mins = {
166+
# depends on pydantic-settings 2.4 which requires pydantic 2.7
167+
"libs/community": 7,
168+
}
169+
170+
max_pydantic_minor = min(
171+
int(dir_max_pydantic_minor),
172+
int(core_max_pydantic_minor),
173+
)
174+
min_pydantic_minor = max(
175+
int(dir_min_pydantic_minor),
176+
int(core_min_pydantic_minor),
177+
custom_mins.get(dir_, 0),
178+
)
179+
180+
configs = [
181+
{
182+
"working-directory": dir_,
183+
"pydantic-version": f"2.{v}.0",
184+
"python-version": python_version,
185+
}
186+
for v in range(min_pydantic_minor, max_pydantic_minor + 1)
128187
]
188+
return configs
129189

130190

131191
def _get_configs_for_multi_dirs(
132-
job: str, dirs_to_run: List[str], dependents: dict
192+
job: str, dirs_to_run: Dict[str, Set[str]], dependents: dict
133193
) -> List[Dict[str, str]]:
134194
if job == "lint":
135195
dirs = add_dependents(
136196
dirs_to_run["lint"] | dirs_to_run["test"] | dirs_to_run["extended-test"],
137197
dependents,
138198
)
139-
elif job in ["test", "compile-integration-tests", "dependencies"]:
199+
elif job in ["test", "compile-integration-tests", "dependencies", "test-pydantic"]:
140200
dirs = add_dependents(
141201
dirs_to_run["test"] | dirs_to_run["extended-test"], dependents
142202
)
@@ -165,6 +225,7 @@ def _get_configs_for_multi_dirs(
165225
dirs_to_run["lint"] = all_package_dirs()
166226
dirs_to_run["test"] = all_package_dirs()
167227
dirs_to_run["extended-test"] = set(LANGCHAIN_DIRS)
228+
168229
for file in files:
169230
if any(
170231
file.startswith(dir_)
@@ -182,8 +243,12 @@ def _get_configs_for_multi_dirs(
182243
if any(file.startswith(dir_) for dir_ in LANGCHAIN_DIRS):
183244
# add that dir and all dirs after in LANGCHAIN_DIRS
184245
# for extended testing
246+
185247
found = False
186248
for dir_ in LANGCHAIN_DIRS:
249+
if dir_ == "libs/core" and IGNORE_CORE_DEPENDENTS:
250+
dirs_to_run["extended-test"].add(dir_)
251+
continue
187252
if file.startswith(dir_):
188253
found = True
189254
if found:
@@ -224,7 +289,6 @@ def _get_configs_for_multi_dirs(
224289

225290
# we now have dirs_by_job
226291
# todo: clean this up
227-
228292
map_job_to_configs = {
229293
job: _get_configs_for_multi_dirs(job, dirs_to_run, dependents)
230294
for job in [
@@ -233,6 +297,7 @@ def _get_configs_for_multi_dirs(
233297
"extended-tests",
234298
"compile-integration-tests",
235299
"dependencies",
300+
"test-pydantic",
236301
]
237302
}
238303
map_job_to_configs["test-doc-imports"] = (

.github/scripts/check_prerelease_dependencies.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
# see if we're releasing an rc
1313
version = toml_data["tool"]["poetry"]["version"]
14-
releasing_rc = "rc" in version
14+
releasing_rc = "rc" in version or "dev" in version
1515

1616
# if not, iterate through dependencies and make sure none allow prereleases
1717
if not releasing_rc:

.github/scripts/get_min_versions.py

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import sys
2+
from typing import Optional
23

34
if sys.version_info >= (3, 11):
45
import tomllib
@@ -7,6 +8,9 @@
78
import tomli as tomllib
89

910
from packaging.version import parse as parse_version
11+
from packaging.specifiers import SpecifierSet
12+
from packaging.version import Version
13+
1014
import re
1115

1216
MIN_VERSION_LIBS = [
@@ -15,6 +19,7 @@
1519
"langchain",
1620
"langchain-text-splitters",
1721
"SQLAlchemy",
22+
"pydantic",
1823
]
1924

2025
SKIP_IF_PULL_REQUEST = ["langchain-core"]
@@ -45,7 +50,13 @@ def get_min_version(version: str) -> str:
4550
raise ValueError(f"Unrecognized version format: {version}")
4651

4752

48-
def get_min_version_from_toml(toml_path: str, versions_for: str):
53+
def get_min_version_from_toml(
54+
toml_path: str,
55+
versions_for: str,
56+
python_version: str,
57+
*,
58+
include: Optional[list] = None,
59+
):
4960
# Parse the TOML file
5061
with open(toml_path, "rb") as file:
5162
toml_data = tomllib.load(file)
@@ -64,11 +75,20 @@ def get_min_version_from_toml(toml_path: str, versions_for: str):
6475
continue
6576
# Check if the lib is present in the dependencies
6677
if lib in dependencies:
78+
if include and lib not in include:
79+
continue
6780
# Get the version string
6881
version_string = dependencies[lib]
6982

7083
if isinstance(version_string, dict):
7184
version_string = version_string["version"]
85+
if isinstance(version_string, list):
86+
version_string = [
87+
vs
88+
for vs in version_string
89+
if check_python_version(python_version, vs["python"])
90+
][0]["version"]
91+
7292

7393
# Use parse_version to get the minimum supported version from version_string
7494
min_version = get_min_version(version_string)
@@ -79,13 +99,31 @@ def get_min_version_from_toml(toml_path: str, versions_for: str):
7999
return min_versions
80100

81101

102+
def check_python_version(version_string, constraint_string):
103+
"""
104+
Check if the given Python version matches the given constraints.
105+
106+
:param version_string: A string representing the Python version (e.g. "3.8.5").
107+
:param constraint_string: A string representing the package's Python version constraints (e.g. ">=3.6, <4.0").
108+
:return: True if the version matches the constraints, False otherwise.
109+
"""
110+
try:
111+
version = Version(version_string)
112+
constraints = SpecifierSet(constraint_string)
113+
return version in constraints
114+
except Exception as e:
115+
print(f"Error: {e}")
116+
return False
117+
118+
82119
if __name__ == "__main__":
83120
# Get the TOML file path from the command line argument
84121
toml_file = sys.argv[1]
85122
versions_for = sys.argv[2]
123+
python_version = sys.argv[3]
86124
assert versions_for in ["release", "pull_request"]
87125

88126
# Call the function to get the minimum versions
89-
min_versions = get_min_version_from_toml(toml_file, versions_for)
127+
min_versions = get_min_version_from_toml(toml_file, versions_for, python_version)
90128

91129
print(" ".join([f"{lib}=={version}" for lib, version in min_versions.items()]))

.github/workflows/_dependencies.yml

Lines changed: 0 additions & 114 deletions
This file was deleted.

0 commit comments

Comments
 (0)