-
-
Notifications
You must be signed in to change notification settings - Fork 26.5k
TST add tests for pyproject minimum dependency checks #32826
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
| ): | ||
| # tomllib is available in Python 3.11 | ||
| tomllib = pytest.importorskip("tomllib") | ||
| def extract_packages_and_pyproject_tags(dependencies): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the PR! This seems to go in the direction I had in mind, I'll try to have a closer look at one point.
General comment: the more you can reduce the diff, the more this will make the reviewer's life (my life in this particular case) easier 😉. For example, it seems like you move global variables min_dependencies_tag_to_packages_without_version and pyproject_section_to_min_dependencies_tag to a function but this function is called once.
Maybe you have a good reason to do it and I would be curious to hear about it, but as a reviewer, I am expecting an "easy" PR to review and I have to take 5-10 minutes to understand just this part and that doesn't help keeping this PR in the "easy to review" category 😅.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I moved the global variables into a function in order to create them either from the min_dependencies or the test_dependencies, meaning the function is only called once, but the parameter passed from test_min_dependencies_pyproject_toml and test_check_pyproject_section_with_upper_bounds differs.
One could integrate the code into check_pyproject_sections, but since it is sort of a pre-processing step, I decided to keep it as an own function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried to move the function to the same place as the global variables to make it easier to compare, but github still aligned the wrong parts, so I'll try to mark the changes below instead.
|
Thank you for the feedback! I had to fiddle around with the code a bit to make it easy to switch between real dependencies and a minimal test (because the code does not only check if the versions are the same, but also if the specified packages are the same (which makes sense), so I had to create matching minimal test samples for both I tried to reduce the differences, and comparing this version to main in VSCode (using GitLens) actually doesn't look too bad, but here on GitHub it tries to align the wrong overlapping parts... 😞 |
| tomllib = pytest.importorskip("tomllib") | ||
| def extract_packages_and_pyproject_tags(dependencies): | ||
| min_depencies_tag_to_packages_without_version = defaultdict(list) | ||
| for package, (min_version, tags) in dependencies.items(): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here, it now iterates over the argument dependencies instead of the global imported dependent_packages
(I took the liberty of also renaming extras to tags, because it took my a while to figure out what "extras" are.)
| def check_pyproject_sections(pyproject_toml, min_dependencies): | ||
| packages, pyproject_tags = extract_packages_and_pyproject_tags(min_dependencies) | ||
|
|
||
| for pyproject_section, min_dependencies_tag in pyproject_tags.items(): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I replaced the parametrization in the existing test with a for-loop here, to enable iterating over either the whole actual pyproject.toml or the given test subset.
| for pyproject_section, min_dependencies_tag in pyproject_tags.items(): | ||
| # NumPy is more complex because build-time (>=1.25) and run-time (>=1.19.5) | ||
| # requirement currently don't match | ||
| skip_version_check_for = ["numpy"] if min_dependencies_tag == "build" else [] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because the tags are only available at this stage, the definition of the skip packages has to happen here now.
|
|
||
| pyproject_section_keys = pyproject_section.split(".") | ||
| info = pyproject_toml | ||
| # iterate through nested keys to get packages and version |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added a comment here because it took me a while to understand the following loop and what info actually contains at the end.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The remainder of this function is almost unchanged except for one extra indentation because of the loop above and one other change marked below.
|
|
||
| @pytest.mark.parametrize( | ||
| "pyproject_section, min_dependencies_tag", | ||
| pyproject_section_to_min_dependencies_tag.items(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This parametrization is replaced by the for-loop in check_pyproject_sections(
| def test_min_dependencies_pyproject_toml(): | ||
| """Check versions in pyproject.toml is consistent with _min_dependencies.""" | ||
|
|
||
| root_directory = Path(sklearn.__file__).parent.parent |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reading in the actual pyproject.toml file is now happening here, and the parsed version is passed to check_pyproject_sections (to enable passing a test version in the new test)
|
|
||
| for package, version in pyproject_build_min_versions.items(): | ||
| version = parse_version(version) | ||
| expected_min_version = parse_version(min_dependencies[package][0]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here, the version is compared to the given min_dependencies, not the global imported dependent_packages (as in line 119 in the old version)
ogrisel
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please find a pass of review below.
| pyproject_section_to_min_dependencies_tag = { | ||
| "build-system.requires": "build", | ||
| "project.dependencies": "install", | ||
| MIN_DEPENDENT_PACKAGES = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we could use more explicit variable names to convey that those are not expected to match the currently used version numbers.
| MIN_DEPENDENT_PACKAGES = { | |
| EXAMPLE_MIN_DEPENDENT_PACKAGES = { |
| section = f"project.optional-dependencies.{tag}" | ||
| pyproject_section_to_min_dependencies_tag[section] = tag | ||
|
|
||
| MATCHING_PYPROJECT_SECTIONS_WITH_UPPER_BOUND = """ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| MATCHING_PYPROJECT_SECTIONS_WITH_UPPER_BOUND = """ | |
| EXAMPLE_EXPECTED_PYPROJECT_SECTIONS_WITH_UPPER_BOUND = """ |
| install = ["joblib>=1.3.0,<2.0", "scipy==1.10.0"] | ||
| [build-system] | ||
| requires = ["scipy>=1.10.0,<1.19.0"] | ||
| """ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe add a similar example pyproject without the upper bounds to also test the check function against it?
| min_dependencies_tag, | ||
| skip_version_check_for=skip_version_check_for, | ||
| ) | ||
| check_pyproject_sections(pyproject_toml, MIN_DEPENDENT_PACKAGES) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please also expand the test to also checks that a meaningful exception is raised when inconsistent arguments are passed to check_pyproject_sections.
Reference Issues/PRs
Related to #32790 (comment)
What does this implement/fix? Explain your changes.
This adds a test using a minimal pyproject sample, to test that the tests actually work with upper bounds.
I had to restructure the code a bit in order to use a test string instead of the toml file.
Let me know if this is what you had in mind @lesteve.
Any other comments?
I noticed that there are also no tests testing the mismatch conditions of package versions. Adding this would be fairly straight forward now, but I'm not sure if these files are actually touched manually, and if this would make sense?