-
Notifications
You must be signed in to change notification settings - Fork 16
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
#27 - mypy fixup, adds to #24 - unblocks #31 #32
#27 - mypy fixup, adds to #24 - unblocks #31 #32
Conversation
if isinstance(c, _PathLike): | ||
if (path / c).exists(): | ||
return True | ||
else: | ||
if callable(c): |
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 should revert this to else
again. Tried to get the type inference of mypy onto the right track.
I ended up giving up here - see the ignore instruction.
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, ideally, this logic would all be encapsulated with classes. But I think this was just a simple version that works. But making something that's not confusing for mypy shouldn't be the first objective.
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.
Can we leave the else
, please? It should throw an error (ideally TypeError but that's not super important) if c isn't path-like or callable, rather than silently ignoring it.
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.
Perhaps it needs a type hint for the criterion_collection
variable. Something like List[Union[PathType, Callable[[PathType], bool]]
.
from .root import find_root, find_root_with_reason | ||
from .here import here | ||
|
||
__all__ = [ |
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 don't think you need this since everything you want to export is explicitly imported.
@@ -9,35 +9,50 @@ | |||
import typing | |||
from os import PathLike as _PathLike | |||
|
|||
|
|||
_PathType = typing.Union[_PathLike, str] |
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 (but I'm not sure) that "PathLike" includes "str". Can you check this?
# TODO: It would be nice to have a class that encapsulates these checks, | ||
# so that we can implement methods like |, !, &, ^ operators | ||
|
||
# TODO: Refactor in a way that allows creation of reasons | ||
|
||
|
||
def as_root_criterion(criterion) -> typing.Callable: | ||
def as_root_criterion( | ||
criterion: _CriterionType, |
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 type hint is quite complicated. Maybe it would be better replaced with something using functools.singledispatch: https://docs.python.org/3/library/functools.html#functools.singledispatch. That's probably a lot for this PR though.
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.
But I don't know if it's worth making an alias for this, since it's only used once. The purpose of this function is to take various inputs and standardise them as a callable version.
""" | ||
Find directory matching root criterion. | ||
|
||
Recursively search parents of start path for directory | ||
matching root criterion. | ||
""" | ||
try: | ||
root, _ = find_root_with_reason(criterion, start=start, **kwargs) | ||
root, _ = find_root_with_reason(criterion, start=start) |
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'd prefer it if the extra kwargs
we still passed through, since it ensures future compatibility. What was the mypy error?
_PathType = typing.Union[_PathLike, str] | ||
_CriterionType = typing.Union[ | ||
typing.Callable[[_PathType], bool], | ||
typing.Callable[[_pathlib.Path], bool], |
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 don't think you need to duplicate these for _pathlib.Path
, since that's also "PathLike"
|
||
|
||
def as_start_path(start: _PathLike) -> _pathlib.Path: | ||
def as_start_path(start: _typing.Union[None, _PathType]) -> _pathlib.Path: |
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 it's better to write _typing.Optional[_PathType]
rather than _typing.Union[None, _PathType]
. Although in later python versions, _PathType | None
would be preferred.
def as_root_criterion(criterion) -> typing.Callable: | ||
def as_root_criterion( | ||
criterion: _CriterionType, | ||
) -> typing.Callable[[_pathlib.Path], bool]: |
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.
Probably worth making an alias for this output type, since it's reused.
criterion = list(criterion) | ||
criterion_collection = [criterion] | ||
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.
I would remove the extra blank line here: it's unnecessary.
black --check --diff src/pyprojroot tests | ||
- name: Test with pytest | ||
run: | | ||
PYTHONPATH=src pytest |
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 correct thing to do is to install the package as editable:
python -m pip install -e .
- name: Install dependencies | ||
run: | | ||
python -m pip install --upgrade pip | ||
pip install flake8 pytest mypy black |
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.
Would be better if this were synchronised with environment-dev.yml
if callable(criterion): | ||
return criterion | ||
|
||
# criterion must be a Collection, rather than just Iterable | ||
if isinstance(criterion, _PathLike): | ||
criterion = [criterion] | ||
criterion = list(criterion) | ||
criterion_collection = [criterion] |
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 it's better to just return the right function directly here and then handle the iterable input separately.
def f(path: _pathlib.Path) -> bool:
return (path / criterion).exists()
criterion_collection = [criterion] | ||
else: | ||
|
||
criterion_collection = list(criterion) # type: ignore[arg-type] |
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.
For anyone that's interested, this needs to be converted into a list here from a generic iterable since we need to be able to restart the iterator.
Thanks for your work @achimgaedke . I've put quite a few comments, but remember that I'm not a maintainer: @chendaniely is. So my comments are just opinions. |
Thanks @jamesmyatt for the comments, I will work through them. I had a long fight with mypy, some comments are going right down to where I lost the battle... "I think this is required, mypy doesn't think it is sufficient". Will see... |
tyty @achimgaedke for the PR merged! I posted a quick reponse in #33 to continue the last bit of the conversatio in this PR. 🙇 |
This PR proposes fix the
fix_mypy_strict
branch:The 3 commits contain:
mypy strict
checking #27)__all__
to the__init__.py
to define the public interface of this package.The next steps would be to merge the result into master and release it (#31).