-
Notifications
You must be signed in to change notification settings - Fork 27
Foundation for detecting python version requirements from pyproject.toml #643
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
Merged
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
187c1d3
Foundation for detecting python version requirements from pyproject.toml
amol- 274fefc
Remove accidental uv changes
amol- 41863fd
Avoid complains from type checker
amol- 887fbad
Fix, toml wants a string, while tomllib wants bytes
amol- 5ed8e3c
Add more explicit comments to pyptoject tests
amol- File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| """ | ||
| Support for detecting various information from python projects metadata. | ||
|
|
||
| Metadata can only be loaded from static files (e.g. pyproject.toml, setup.cfg, etc.) | ||
| but not from setup.py due to its dynamic nature. | ||
| """ | ||
|
|
||
| import pathlib | ||
| import typing | ||
|
|
||
| try: | ||
| import tomllib | ||
| except ImportError: | ||
| # Python 3.11+ has tomllib in the standard library | ||
| import toml as tomllib # type: ignore[no-redef] | ||
|
|
||
|
|
||
| def lookup_metadata_file(directory: typing.Union[str, pathlib.Path]) -> typing.List[typing.Tuple[str, pathlib.Path]]: | ||
| """Given the directory of a project return the path of a usable metadata file. | ||
|
|
||
| The returned value is either a list of tuples [(filename, path)] or | ||
| an empty list [] if no metadata file was found. | ||
| """ | ||
| directory = pathlib.Path(directory) | ||
|
|
||
| def _generate(): | ||
| for filename in ("pyproject.toml", "setup.cfg", ".python-version"): | ||
| path = directory / filename | ||
| if path.is_file(): | ||
| yield (filename, path) | ||
|
|
||
| return list(_generate()) | ||
|
|
||
|
|
||
| def parse_pyproject_python_requires(pyproject_file: pathlib.Path) -> typing.Optional[str]: | ||
| """Parse the project.requires-python field from a pyproject.toml file. | ||
|
|
||
| Assumes that the pyproject.toml file exists, is accessible and well formatted. | ||
|
|
||
| Returns None if the field is not found. | ||
| """ | ||
| content = pyproject_file.read_text() | ||
| pyproject = tomllib.loads(content) | ||
|
|
||
| return pyproject.get("project", {}).get("requires-python", None) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,67 @@ | ||
| import os | ||
| import pathlib | ||
|
|
||
| from rsconnect.pyproject import lookup_metadata_file, parse_pyproject_python_requires | ||
|
|
||
| import pytest | ||
|
|
||
| HERE = os.path.dirname(__file__) | ||
| PROJECTS_DIRECTORY = os.path.abspath(os.path.join(HERE, "testdata", "python-project")) | ||
|
|
||
| # Most of this tests, verify against three fixture projects that are located in PROJECTS_DIRECTORY | ||
| # - using_pyproject: contains a pyproject.toml file with a project.requires-python field | ||
| # - using_setupcfg: contains a setup.cfg file with a options.python_requires field | ||
| # - using_pyversion: contains a .python-version file and a pyproject.toml file without any version constraint. | ||
| # - allofthem: contains all metadata files all with different version constraints. | ||
|
|
||
|
|
||
| @pytest.mark.parametrize( | ||
| "project_dir, expected", | ||
| [ | ||
| (os.path.join(PROJECTS_DIRECTORY, "using_pyproject"), ("pyproject.toml",)), | ||
| (os.path.join(PROJECTS_DIRECTORY, "using_setupcfg"), ("setup.cfg",)), | ||
| ( | ||
| os.path.join(PROJECTS_DIRECTORY, "using_pyversion"), | ||
| ( | ||
| "pyproject.toml", | ||
| ".python-version", | ||
| ), | ||
| ), | ||
| (os.path.join(PROJECTS_DIRECTORY, "allofthem"), ("pyproject.toml", "setup.cfg", ".python-version")), | ||
| ], | ||
| ids=["pyproject.toml", "setup.cfg", ".python-version", "allofthem"], | ||
| ) | ||
| def test_python_project_metadata_detect(project_dir, expected): | ||
| """Test that the metadata files are detected when they exist.""" | ||
| expectation = [(f, pathlib.Path(project_dir) / f) for f in expected] | ||
| assert lookup_metadata_file(project_dir) == expectation | ||
|
|
||
|
|
||
| @pytest.mark.parametrize( | ||
amol- marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| "project_dir", | ||
| [ | ||
| os.path.join(PROJECTS_DIRECTORY, "empty"), | ||
| os.path.join(PROJECTS_DIRECTORY, "missing"), | ||
| ], | ||
| ids=["empty", "missing"], | ||
| ) | ||
| def test_python_project_metadata_missing(project_dir): | ||
| """Test that lookup_metadata_file is able to deal with missing or empty directories.""" | ||
| assert lookup_metadata_file(project_dir) == [] | ||
|
|
||
|
|
||
| @pytest.mark.parametrize( | ||
amol- marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| "project_dir, expected", | ||
| [ | ||
| (os.path.join(PROJECTS_DIRECTORY, "using_pyproject"), ">=3.8"), | ||
| (os.path.join(PROJECTS_DIRECTORY, "using_pyversion"), None), | ||
| ], | ||
| ids=["option-exists", "option-missing"], | ||
| ) | ||
| def test_pyprojecttoml_python_requires(project_dir, expected): | ||
| """Test that the python_requires field is correctly parsed from pyproject.toml. | ||
|
|
||
| Both when the option exists or when it missing in the pyproject.toml file. | ||
| """ | ||
| pyproject_file = pathlib.Path(project_dir) / "pyproject.toml" | ||
| assert parse_pyproject_python_requires(pyproject_file) == expected | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| >=3.8, <3.12 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| def main(): | ||
| print("Hello from python-project!") | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| main() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| [project] | ||
| name = "python-project" | ||
| version = "0.1.0" | ||
| description = "Add your description here" | ||
| requires-python = ">=3.8" | ||
| dependencies = [] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| [metadata] | ||
| name = python-project | ||
| version = 0.1.0 | ||
| description = Add your description here | ||
|
|
||
| [options] | ||
| python_requires = >=3.8 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| def main(): | ||
| print("Hello from python-project!") | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| main() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| def main(): | ||
| print("Hello from python-project!") | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| main() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| [project] | ||
| name = "python-project" | ||
| version = "0.1.0" | ||
| description = "Add your description here" | ||
| requires-python = ">=3.8" | ||
| dependencies = [] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| >=3.8, <3.12 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| def main(): | ||
| print("Hello from python-project!") | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| main() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| [project] | ||
| name = "python-project" | ||
| version = "0.1.0" | ||
| description = "Add your description here" | ||
| dependencies = [] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| def main(): | ||
| print("Hello from python-project!") | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| main() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| [metadata] | ||
| name = python-project | ||
| version = 0.1.0 | ||
| description = Add your description here | ||
|
|
||
| [options] | ||
| python_requires = >=3.8 |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.