-
Notifications
You must be signed in to change notification settings - Fork 265
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
Add type hints #576
base: master
Are you sure you want to change the base?
Add type hints #576
Conversation
if package == 'tlz': | ||
return ModuleSpec(fullname, self) | ||
return None |
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.
Missing return
if package == 'tlz': | ||
return self | ||
return None |
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.
missing return
|
||
class TlzLoader: | ||
|
||
class TlzLoader(Loader): |
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.
Make it an explicit subclass of Loader
if sigspec is None: | ||
return -1 |
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.
account for sigspec=None
""" Return the names of position-only arguments if func has **kwargs""" | ||
if num_pos_only == 0: | ||
if num_pos_only == 0 or sigspec is None: |
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.
account for sigspec=None
@@ -641,7 +673,7 @@ def expand_sig(sig): | |||
if isinstance(sig, tuple): | |||
if len(sig) == 3: | |||
num_pos_only, func, keyword_only = sig | |||
assert isinstance(sig[-1], tuple) | |||
# assert isinstance(sig[-1], tuple) |
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.
remove assert
, replace with nothing here
@@ -36,6 +35,7 @@ | |||
count, | |||
curry, | |||
diff, | |||
excepts, |
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 the tests to pass
from .exceptions import merge, merge_with | ||
|
||
accumulate = toolz.curry(toolz.accumulate) | ||
apply = toolz.curry(toolz.apply) |
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 the tests to pass, because the signature of apply
changed.
def merge(*dicts, **kwargs): | ||
def merge( | ||
*dicts: Mapping[_S, _T], | ||
factory: type[_DictType] = dict, |
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.
explicitly add a factory
keyword argument
wow, nice! Thanks @getzze! I'm in the process of moving so it may take me some time to properly review this, but know that this is appreciated and likely to go in :) |
def merge_with( | ||
func: Callable[[Sequence[_T]], _U], | ||
*dicts: Mapping[_S, _T], | ||
factory: type[_DictType] = dict, |
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.
explicitly add a factory
keyword argument
|
||
values = collections.defaultdict(lambda: [].append) | ||
values = defaultdict(list) |
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.
use defaultdict(list)
instead of a lambda.
def dissoc( | ||
d: Mapping[_S, _T], | ||
*keys: _S, | ||
factory: type[_DictType] = dict, |
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.
explicitly add a factory
keyword argument
if self.key == self._default_hashkey: | ||
val = self.key | ||
def __hash__(self) -> int: | ||
if not callable(self.key): |
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.
check if the key is callable instead of a specific string.
@@ -50,7 +70,8 @@ def fold(binop, seq, default=no_default, map=map, chunksize=128, combine=None): | |||
>>> fold(add, [1, 2, 3, 4], chunksize=2, map=map) | |||
10 | |||
""" | |||
assert chunksize > 1 | |||
# assert chunksize > 1 | |||
chunksize = max(chunksize, 1) |
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.
update the variable instead of using an assert
class NoDefaultType(Enum): | ||
no_default = '__no_default__' | ||
|
||
|
||
no_default = NoDefaultType.no_default | ||
|
||
# no_default = '__no__default__' | ||
# NoDefaultType = Literal['__no__default__'] | ||
|
||
|
||
class NoPadType(Enum): | ||
no_pad = '__no_pad__' | ||
|
||
|
||
no_pad = NoPadType.no_pad | ||
|
||
# no_pad = '__no__pad__' | ||
# NoPadType = Literal['__no__pad__'] |
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.
use an Enum
for no_default
and no_pad
instead of a string
d: dict[_S, list[_T]] = collections.defaultdict(list) | ||
for item in seq: | ||
d[key(item)](item) | ||
rv = {} | ||
for k, v in d.items(): | ||
rv[k] = v.__self__ | ||
return rv | ||
vals = d[key(item)] | ||
vals.append(item) | ||
return d |
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.
use defaultdict(list)
instead of a lambda and loop only once over items
def merge_sorted(*seqs, **kwargs): | ||
def merge_sorted( | ||
*seqs: Iterable[_CT], | ||
key: UnaryOp[_CT] | None = None, |
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.
explicitly add a key
keyword argument
@@ -259,21 +305,20 @@ def unique(seq, key=None): | |||
('cat', 'mouse') | |||
""" | |||
seen = set() | |||
seen_add = seen.add |
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.
do not cache the set.add
function
seen_add(item) | ||
return True | ||
else: | ||
if isinstance(seq, Sequence): |
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.
use isinstance(seq, Sequence)
instead of not (iter(seq) is seq)
default: _T | NoDefaultType = no_default, | ||
key: TransformOp[_T, Any] | None = None, |
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.
explicitly add a default
and key
keyword arguments
@@ -26,7 +68,7 @@ def identity(x): | |||
return x | |||
|
|||
|
|||
def apply(*func_and_args, **kwargs): | |||
def apply(func: Callable[..., _T], /, *args: Any, **kwargs: Any) -> _T: |
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.
explicitly add func
first positional only argument.
args = (val,) + args | ||
return func(*args) | ||
return reduce(evalform_front, forms, val) | ||
# if isinstance(form, tuple): |
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.
according to the types, no need to check for tuple
type, but it would be good to check anyway.
) -> _T: | ||
if callable(form): | ||
return form(val) | ||
# if isinstance(form, tuple): |
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.
according to the types, no need to check for tuple
type, but it would be good to check anyway.
def __init__( | ||
self, | ||
# TODO: type hint that the returned value of a `partial` is _T | ||
func: curry[_T] | partial | Callable[..., _T], |
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.
add an explicit func
first position only argument.
func = obj.func | ||
obj = cls(func, *args, **(kwargs or {})) | ||
# TODO: check that func is callable |
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.
TODO: check that func
is really callable
What's preventing this from going through? Would love better typehints in toolz |
@getzze have you lost interest in this? If so, I wouldn't mind continuing your work |
I can do it but there is nothing more to do, no? |
I thought it had become stale due to lack of activity. In that case, what is preventing this from being merged? Maybe we can @ some maintainer to take a look :) |
Agreed, this PR seems very cool! But what's up with the deleted versioneer and move to the new pyproject.toml configuration? Don't really think that it fits the PR's goal, does it? Would create a separate PR for that really. |
This is a huge PR, but hopefully it is not too hard to review.
To type check the code I had to setup
mypy
and I found it was simpler to also add apyproject.toml
file and bump the minimal python version to 3.8 (3.7 is not supported anymore anyway).I also set up a
ruff
workflow in thepyproject.toml
file, I find it very useful to lint and format the code.All tests are passing,
mypy
andruff
give no errors.The problem to review this PR is that I modified the python code sometimes, to improve type checking. Maybe I can add comments where the code was modified to make it easier to review. Some examples of things that I modified:
no_default
anEnum
sentinel: using the string "no_default" do not work anymoreno_pad
.def some_func(*args, ...)
indef some_func(func, /, *args, ...)
whenargs
needed to be a function followed by its arguments.**kwargs
in function definitions to be able to type check the arguments.assert
toif
blocks.Also, I added a dependency to
typing_extensions
, that is a backport oftyping
improvements to older python versions. It can be removed, losing a bit of type checking forfunctoolz.excepts
andfunctoolz.juxt
.Related PR: #516 #502
Issue: #496