-
-
Notifications
You must be signed in to change notification settings - Fork 31.3k
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 fnmatch.filterfalse function #74598
Comments
There has been some discussion on Python-Ideas about adding fnmatch.filter_false to complement filter, for when you want to ignore matching files rather than non-matching ones. https://mail.python.org/pipermail/python-ideas/2017-May/045694.html I don't believe there have been any strong objections, and work-arounds have a considerable performance penalty (according to the OP, I have not confirmed that). Here's a patch which refactors filter and adds filter_false. Sorry I haven't got my head around the brave new world on Github, so I'm going old school here with a diff :-) |
In general LGTM. I left few minor comments on Rietveld. Needed the versionadded directive, Misc/NEWS and What's New entries. But shouldn't the new function have name "filterfalse" for parallel with itertools.filterfalse? And this would better match the style of the fnmatch module, fnmatchcase doesn't contain an underscore. Tests for fnmatch.filter are too poor. I think we should add more tests (this is other issue). |
Hi, Another thing I noted: fnmatch takes a rather unusual approach to determine whether normcase is NOP or not. It imports posixpath only to see if it is the same as os.path. That looks pretty weird and wasteful to me (especially if you are running this on Windows and are effectively importing posixpath just for fun then). I think it would be better to just check os.name instead (like pathlib does for example). I moved the corresponding check to the top of the module to make this easier to address, should that be of interest. I'm also using the result of the check in fnmatch.fnmatch because I don't see any reason why you wouldn't want to benefit from it there as well. |
Your patch makes filter() returning normalized names. This may be not what is expected. |
Does it? I thought it does so only if normalize_case is True. |
Yes, it does so only if normalize_case is True. Currently fnmatch.filter() returns non-normalized names. |
I'm happy for you to change the name to filterfalse. |
@serhiy: my bad! I just hadn't realized this behavior of the original. Some other questions though to everyone involved:
isn't normcase in both posixpath and ntpath doing isinstance(str, bytes) checks that are redundant with os.fspath? Is this oversight or am I missing something again? |
Currently os.path is posixpath if and only if os.name == 'posix'. But this can be changed in future. I think it is safer to the current check. The posixpath module doesn't contain classes, enums or namedtuples, it's import is fast. And it would be safer to keep the check in the function rather than move it at module level.
This is a different issue. fnmatch.filter() was added for speeding up glob.glob() (see bpo-409973). I don't know whether there is a good use case for filtercase().
Good catch! It seems to me that they are redundant. Please open a new issue for this. |
done: bpo-30427 |
Yet another thing I just realized (sorry for being so annoying): With os.normcase calling os.fspath in 3.6+ it is not really a NOP anymore even on posix. As a consequence, you can now do some weird things with fnmatch: in all cases, and only in these, where the module *does* call normcase you can pass in Path-like objects as the names. Also note that you can also pass a Path-like object as pat everywhere except in fnmatchcase. |
I don't think os.normcase() calling os.fspath() needs to be a worry. Pragmatically it's just getting a str/bytes from an object that knows how to return a str/bytes for a path. IOW it's no different than os.normcase(str(path)) and so embedding the call such that it's not a flat-out no-op shouldn't be a general concern. |
Was this abandoned? Is there a reason not to accept this? |
Rather than adding a new function, why not adding a parameter like sort(key=func, reverse=True)? Something like fnmatch.filterfalse(pat, invert=True)? The Unix grep command has a --invert-match option:
The Unix find and test commands use "!" to invert a condition. Example: find ! -name "*.py" # list files which doesn't match ".py" |
On Wed, Aug 21, 2019 at 10:51:02AM +0000, STINNER Victor wrote:
Guido argues that as a general rule of thumb, we should avoid "constant
If we typically call the function with a bool constant: filter(pat, invert=True) rather than a variable or expression filter(pat, invert=condition or default) that's a hint that the two cases probably should be seperate functions. Martin Fowler agrees: https://martinfowler.com/bliki/FlagArgument.html as does Raymond Chen: https://devblogs.microsoft.com/oldnewthing/20060828-18/?p=29953 (Of course there are cases where it is impractical to avoid bool flags |
Guido, the reason iteratools has a separate filter() and filterfalse() tools is because of your API guidance to prefer separate functions rather than having flags in most cases. Do you still feel that way and does it apply here? Personally, I would want to have a separate function for fnmatch.filter_false() rather than adding complexity to the API for fnmatch. |
Yes, I still feel like this and I find it applies here. (Note that the module already has two variants of fnmatch(), so it's nothing new in this context.) |
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
bugs.python.org fields:
Linked PRs
fnmatch.filterfalse
for excluding names #121185The text was updated successfully, but these errors were encountered: