-
Notifications
You must be signed in to change notification settings - Fork 15
add support for pyproject.toml #84
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
Open
cgahr
wants to merge
48
commits into
bwhmather:master
Choose a base branch
from
cgahr:fix-72
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
48 commits
Select commit
Hold shift + click to select a range
1a671bb
exclude test_data/samples/* from black, isort, mypy
dbce624
remove black, isort, mypy config from tox.ini
056f6e2
remove black, isort, mypy config from github ci
ac2840c
remove testing of setup.py in tox.ini
5b8776c
remove setup.py
d50431a
move config from setup.cfg to pyproject.toml
0c8cf68
remove mention of setup.py in readme
556e674
update tox.ini for PEP517 compatibility
f4c9aae
Merge branch 'master' into pyproject-toml
8c4190a
update release ci to not use setup.py
aa7f40b
remove setup.py from readme
e1088e0
Merge branch 'master' into fix-72
b5456a8
introduce Config class, that parses pyproject.toml and iterates over …
f6439b9
remove redundant find_python_files code, add find_project_root
fa2b99f
use new config to iterate over python files
193af08
remove unnecessary error handling, both are handled by config.files()
5080f51
add config for ssort
3955ad8
update tox.ini for PEP517 compatibility
ce73311
Merge branch 'pyproject-toml' into fix-72
3d55df1
update tests for _files.py
3b91f35
fix bug find_project_root
6a659df
use tomli for python < 3.11, tomlib for python >= 3.11
797424b
add tomli dependency
cafa5bb
remove pathspec mypy block since pathspec is not longer needed
674ef12
remove test to check for not existent file, this cannot happen anymore
91d8317
mock find_project_root so that pytest does not search for pyproject.t…
192d9ec
ssort only sorts the inteded files, not all files under root
be890a4
Merge branch 'master' into fix-72
f18cee6
Merge branch 'master' into fix-72
27fee83
Revert "remove unnecessary error handling, both are handled by config…
7a44e8a
Revert "remove test to check for not existent file, this cannot happe…
64ae5b6
non existent and no py files are now handled by the config class and …
7d57482
FileNotFoundError case handled by config
7e41274
path is directory is handled by dir iterator of config
31381d9
add tests for config
de951ec
update tox.ini to use new config
be59549
Merge branch 'master' into fix-72
9415c06
define pytest test path in pyproject.toml
f8e49de
sync tox.ini and ci.yaml
d65d304
export only get_config_from_root using __all__
e9689b5
fix import statement to mirror the others
08cb4d7
add current working dir to find_project_root to find the expected pyp…
6bbf597
add skip_glob configuration key to filter files with glob pattern
ad3ff91
add tests for Config.is_invalid
8c049d1
fix a bug where glob pattern was incorrectly applied if path was not …
ee33e99
create current_working_dir to make testing easier, add more tests
77e6705
remove __pycache__ and .pytest_cache to match blacks defaults
4440ec6
ignore __pycache__ and .pytest_cache folders
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
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,100 @@ | ||
| from __future__ import annotations | ||
|
|
||
| import sys | ||
| from dataclasses import dataclass, field | ||
| from pathlib import Path | ||
|
|
||
| if sys.version_info >= (3, 11): | ||
| from tomllib import load | ||
| else: | ||
| from tomli import load | ||
|
|
||
|
|
||
| __all__ = ["get_config_from_root"] | ||
|
|
||
|
|
||
| DEFAULT_SKIP = frozenset( | ||
| { | ||
| ".bzr", | ||
| ".direnv", | ||
| ".eggs", | ||
| ".git", | ||
| ".hg", | ||
| ".mypy_cache", | ||
| ".nox", | ||
| ".pants.d", | ||
| ".pytype", | ||
| ".ruff_cache", | ||
| ".svn", | ||
| ".tox", | ||
| ".venv", | ||
| "__pypackages__", | ||
| "_build", | ||
| "buck-out", | ||
| "build", | ||
| "dist", | ||
| "node_modules", | ||
| "venv", | ||
| } | ||
| ) | ||
|
|
||
|
|
||
| def iter_valid_python_files_recursive(folder, *, is_invalid): | ||
| for child in folder.iterdir(): | ||
| if is_invalid(child): | ||
| continue | ||
|
|
||
| elif child.is_file() and child.suffix == ".py": | ||
| yield child | ||
|
|
||
| elif child.is_dir(): | ||
| yield from iter_valid_python_files_recursive( | ||
| child, is_invalid=is_invalid | ||
| ) | ||
|
|
||
|
|
||
| @dataclass(frozen=True) | ||
| class Config: | ||
| skip: frozenset | list = DEFAULT_SKIP | ||
| skip_glob: list = field(default_factory=list) | ||
| extend_skip: list = field(default_factory=list) | ||
|
|
||
| def is_invalid(self, path): | ||
| if path.name in (set(self.skip) | set(self.extend_skip)): | ||
| return True | ||
|
|
||
| for pat in self.skip_glob: | ||
| if path.is_file() and path.match(pat): | ||
| return True | ||
|
|
||
| return False | ||
|
|
||
| def iterate_files_matching_patterns(self, pattern): | ||
| for pat in pattern: | ||
| path = Path(pat).resolve() | ||
|
|
||
| if path.is_file() and path.suffix == ".py": | ||
| yield path | ||
|
|
||
| elif path.is_dir(): | ||
| yield from iter_valid_python_files_recursive( | ||
| path, is_invalid=self.is_invalid | ||
| ) | ||
|
|
||
|
|
||
| def parse_pyproject_toml(path): | ||
| with open(path, "rb") as fh: | ||
| pyproject_toml = load(fh) | ||
|
|
||
| return pyproject_toml.get("tool", {}).get("ssort", {}) | ||
|
|
||
|
|
||
| def get_config_from_root(root): | ||
| path_pyproject_toml = root / "pyproject.toml" | ||
|
|
||
| if path_pyproject_toml.exists(): | ||
| config_dict = parse_pyproject_toml(path_pyproject_toml) | ||
| else: | ||
| config_dict = {} | ||
|
|
||
| return Config(**config_dict) |
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 |
|---|---|---|
| @@ -1,72 +1,33 @@ | ||
| from __future__ import annotations | ||
| from pathlib import Path | ||
|
|
||
| import os | ||
| import pathlib | ||
| from typing import Iterable | ||
| __all__ = ["find_project_root"] | ||
|
|
||
| import pathspec | ||
|
|
||
| from ssort._utils import memoize | ||
| def current_working_dir(): | ||
| return Path(".").resolve() | ||
|
|
||
| _EMPTY_PATH_SPEC = pathspec.PathSpec([]) | ||
|
|
||
| def find_project_root(patterns): | ||
| all_patterns = [current_working_dir()] | ||
|
|
||
| @memoize | ||
| def _is_project_root(path: pathlib.Path) -> bool: | ||
| if path == path.root or path == path.parent: | ||
| return True | ||
| if patterns: | ||
| all_patterns.extend(patterns) | ||
|
|
||
| if (path / ".git").is_dir(): | ||
| return True | ||
| paths = [Path(p).resolve() for p in all_patterns] | ||
| parents_and_self = [ | ||
| list(reversed(p.parents)) + ([p] if p.is_dir() else []) for p in paths | ||
| ] | ||
|
|
||
| return False | ||
| *_, (common_base, *_) = ( | ||
| common_parent | ||
| for same_lvl_parent in zip(*parents_and_self) | ||
| if len(common_parent := set(same_lvl_parent)) == 1 | ||
| ) | ||
|
|
||
| for directory in (common_base, *common_base.parents): | ||
| if (directory / ".git").exists() or ( | ||
| directory / "pyproject.toml" | ||
| ).is_file(): | ||
| return directory | ||
|
|
||
| @memoize | ||
| def _get_ignore_patterns(path: pathlib.Path) -> pathspec.PathSpec: | ||
| git_ignore = path / ".gitignore" | ||
| if git_ignore.is_file(): | ||
| with git_ignore.open() as f: | ||
| return pathspec.PathSpec.from_lines("gitwildmatch", f) | ||
|
|
||
| return _EMPTY_PATH_SPEC | ||
|
|
||
|
|
||
| def is_ignored(path: str | os.PathLike) -> bool: | ||
| # Can't use pathlib.Path.resolve() here because we want to maintain | ||
| # symbolic links. | ||
| path = pathlib.Path(os.path.abspath(path)) | ||
|
|
||
| for part in (path, *path.parents): | ||
| patterns = _get_ignore_patterns(part) | ||
| if patterns.match_file(path.relative_to(part)): | ||
| return True | ||
|
|
||
| if _is_project_root(part): | ||
| return False | ||
|
|
||
| return False | ||
|
|
||
|
|
||
| def find_python_files( | ||
| patterns: Iterable[str | os.PathLike[str]], | ||
| ) -> Iterable[pathlib.Path]: | ||
| if not patterns: | ||
| patterns = ["."] | ||
|
|
||
| paths_set = set() | ||
| for pattern in patterns: | ||
| path = pathlib.Path(pattern) | ||
| if not path.is_dir(): | ||
| subpaths = [path] | ||
| else: | ||
| subpaths = [ | ||
| subpath | ||
| for subpath in path.glob("**/*.py") | ||
| if not is_ignored(subpath) and subpath.is_file() | ||
| ] | ||
|
|
||
| for subpath in sorted(subpaths): | ||
| if subpath not in paths_set: | ||
| paths_set.add(subpath) | ||
| yield subpath | ||
| return common_base | ||
|
Owner
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. I think it's important for the git root to be per-path. If it isn't then the results of running |
||
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
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.
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 need to track the
.gitroot and thepyproject.tomlroot separately.We want
.gitigorefiles outside thepyproject.tomlroot to be respected but inside the.gitroot, but we wantproject.tomlfiles outside the.gitroot to be ignored.