-
Notifications
You must be signed in to change notification settings - Fork 2.4k
Validate user input against project-schema.json #10433
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
Changes from 4 commits
32228a6
e647315
ac37d72
19260a2
de85d79
4744ec6
4b146b2
f558d4b
7fc3293
8850d66
dc17053
7167171
5623e28
71b4152
fd5a8ca
07bd96a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -16,6 +16,7 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from poetry.console.commands.command import Command | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from poetry.console.commands.env_command import EnvCommand | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from poetry.factory import Factory | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from poetry.utils.dependency_specification import RequirementsParser | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from poetry.utils.env.python import Python | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -126,15 +127,13 @@ def _init_pyproject( | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self.line("") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| name = self.option("name") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if not name: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| name = project_path.name.lower() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| name = self.option("name") or project_path.name.lower() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if is_interactive: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| question = self.create_question( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| f"Package name [<comment>{name}</comment>]: ", default=name | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| name = self.ask(question) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if is_interactive: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| question = self.create_question( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| f"Package name [<comment>{name}</comment>]: ", default=name | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| name = self.ask(question) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| version = "0.1.0" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -265,6 +264,12 @@ def _init_pyproject( | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return 1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # validate fields before creating pyproject.toml file. If any validations fail, throw error. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| validation_results = self._validate(pyproject.data) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if validation_results.get("errors"): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self.line_error(f"<error>Validation failed: {validation_results}</error>") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return 1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+274
to
+276
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion: Consider formatting validation errors for readability Displaying only relevant error messages or a summary instead of the full dictionary will make the output clearer for users.
Suggested change
Comment on lines
+274
to
+276
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🚨 suggestion (security): Printing the full Extract and display only the relevant error messages to prevent leaking internal or sensitive information.
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pyproject.save() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if create_layout: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -533,3 +538,10 @@ def _get_pool(self) -> RepositoryPool: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self._pool.add_repository(PyPiRepository(pool_size=pool_size)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return self._pool | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @staticmethod | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def _validate(pyproject_data: dict) -> dict: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Validates the given pyproject data and returns the validation results. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return Factory.validate(pyproject_data) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1143,3 +1143,76 @@ def test_get_pool(mocker: MockerFixture, source_dir: Path) -> None: | |
| assert isinstance(command, InitCommand) | ||
| pool = command._get_pool() | ||
| assert pool.repositories | ||
|
|
||
|
|
||
| def build_pyproject_data(project_name: str, description: str = "A project") -> dict: | ||
| return { | ||
| "project": { | ||
| "name": project_name, | ||
| "version": "0.1.0", | ||
| "description": description, | ||
| "authors": [{"name": "Author Name", "email": "author@example.com"}], | ||
| "readme": "README.md", | ||
| "requires-python": ">=3.13", | ||
| "dependencies": [], | ||
| }, | ||
| "tool": {}, | ||
| "build-system": { | ||
| "requires": ["poetry-core>=2.0.0,<3.0.0"], | ||
| "build-backend": "poetry.core.masonry.api", | ||
| }, | ||
| } | ||
|
|
||
|
|
||
| @pytest.mark.parametrize( | ||
| "valid_project_name", | ||
| [ | ||
| "newproject", | ||
| "new_project", | ||
| "new-project", | ||
| "new.project", | ||
| "newproject123", | ||
| ], | ||
| ) | ||
| def test_valid_project_name(valid_project_name): | ||
| pyproject_data = build_pyproject_data(valid_project_name) | ||
| result = InitCommand._validate(pyproject_data) | ||
| assert result["errors"] == [] | ||
|
|
||
|
|
||
| @pytest.mark.parametrize( | ||
| "invalid_project_name, reason", | ||
| [ | ||
| ("new+project", "plus sign"), | ||
| ("new/project", "slash"), | ||
| ("new@project", "at sign"), | ||
| ("new project", "space"), | ||
| ("", "empty string"), | ||
| (" newproject", "leading space"), | ||
| ("newproject ", "trailing space"), | ||
| ("new#project", "hash (#)"), | ||
| ("new%project", "percent (%)"), | ||
| ("new*project", "asterisk (*)"), | ||
| ("new(project)", "parentheses"), | ||
| ("-newproject", "leading hyphen"), | ||
| ("newproject-", "trailing hyphen"), | ||
| (".newproject", "leading dot"), | ||
| ("newproject.", "trailing dot"), | ||
| ( | ||
| "_newproject", | ||
| "leading underscore (PEP 621 allows, stricter validators may reject)", | ||
| ), | ||
| ( | ||
| "newproject_", | ||
| "trailing underscore (PEP 621 allows, stricter validators may reject)", | ||
| ), | ||
| ("1newproject!", "starts with digit, ends with exclamation"), | ||
| (".", "just dot"), | ||
| ], | ||
| ) | ||
| def test_invalid_project_name(invalid_project_name, reason): | ||
| pyproject_data = build_pyproject_data(invalid_project_name) | ||
| result = InitCommand._validate(pyproject_data) | ||
|
|
||
| assert "errors" in result, f"Expected error for: {reason}" | ||
| assert any("project.name must match pattern" in err for err in result["errors"]) | ||
|
||
Uh oh!
There was an error while loading. Please reload this page.