Skip to content
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

Merged
merged 3 commits into from
Feb 21, 2023

Conversation

achimgaedke
Copy link
Contributor

@achimgaedke achimgaedke commented Nov 19, 2022

This PR proposes fix the fix_mypy_strict branch:

The 3 commits contain:

  • Change of the github action to use a "matrix build", simply taken from the examples and adapted to the project
  • Complete the type annotations in the source code (fixes Re-implement mypy strict checking #27)
  • Add __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).

@achimgaedke achimgaedke changed the title Achims edits #27 - mypy fixup, adds to #24 - unblocks #31 Nov 19, 2022
if isinstance(c, _PathLike):
if (path / c).exists():
return True
else:
if callable(c):
Copy link
Contributor Author

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.

Copy link
Contributor

@jamesmyatt jamesmyatt Nov 20, 2022

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.

Copy link
Contributor

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.

Copy link
Contributor

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__ = [
Copy link
Contributor

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]
Copy link
Contributor

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,
Copy link
Contributor

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.

Copy link
Contributor

@jamesmyatt jamesmyatt Nov 20, 2022

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)
Copy link
Contributor

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],
Copy link
Contributor

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:
Copy link
Contributor

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]:
Copy link
Contributor

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:

Copy link
Contributor

@jamesmyatt jamesmyatt Nov 20, 2022

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
Copy link
Contributor

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
Copy link
Contributor

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]
Copy link
Contributor

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]
Copy link
Contributor

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.

@jamesmyatt
Copy link
Contributor

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.

@achimgaedke
Copy link
Contributor Author

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...

@chendaniely chendaniely merged commit 4b6ffd1 into chendaniely:fix_mypy_strict Feb 21, 2023
@chendaniely chendaniely mentioned this pull request Feb 21, 2023
2 tasks
@chendaniely
Copy link
Owner

tyty @achimgaedke for the PR merged!

I posted a quick reponse in #33 to continue the last bit of the conversatio in this PR. 🙇

@achimgaedke achimgaedke deleted the achims-edits branch February 21, 2023 22:22
@achimgaedke achimgaedke mentioned this pull request Feb 21, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants