Skip to content

Stricter Argument Checking for Flatten Methods - #6823

Merged
seberg merged 1 commit into
numpy:masterfrom
gfyoung:order_arg_validate
Dec 18, 2015
Merged

Stricter Argument Checking for Flatten Methods#6823
seberg merged 1 commit into
numpy:masterfrom
gfyoung:order_arg_validate

Conversation

@gfyoung

@gfyoung gfyoung commented Dec 12, 2015

Copy link
Copy Markdown
Contributor

Addresses bug described in #6598 in which calls to the flatten method using nonsense arguments for the order parameter did not produce errors. This PR changes that behavior so that a TypeError is now thrown unless the order parameter is omitted OR takes on one of the following values (case-sensitive but unicode-insensitive): 'C', 'F', 'A', or 'K'. Here is what the behavior looks like now:

>>> import numpy as np
>>> x = np.zeros((3, 5))
>>>
>>> x.flatten(order=np.pi) # nonsense argument
Traceback (most recent call last):
   File "<stdin>", line 1, in <module>
TypeError: non-string object passed in for the ORDER parameter
>>>
>>> x.flatten(order='CAT') # invalid string argument
Traceback (most recent call last):
   File "<stdin>", line 1, in <module>
TypeError: order not understood

This is my first PR to the numpy library, so all comments regarding the proposed changes as well as the PR itself are welcome!

@njsmith

njsmith commented Dec 12, 2015

Copy link
Copy Markdown
Member

Mayyybe we should do a deprecation cycle for this, but personally I'd be
fine with waiting to see whether it actually bites anyone in the RC cycle
and only backing off if it does.
On Dec 11, 2015 8:05 PM, "gfyoung" [email protected] wrote:

Addresses bug described in #6598
#6598 in which calls to the flatten
method using nonsense arguments for the order parameter did not produce
errors. This PR changes that behavior so that a TypeError is now thrown
unless the order parameter is omitted OR takes on one of the following
values (case-sensitive but unicode-insensitive): 'C', 'F', 'A', or 'K'.
Here is what the behavior looks like now:

import numpy as np
x = np.zeros((3, 5))
x.flatten(order = np.pi) # nonsense argument
Traceback (most recent call last):
File "", line 1, in
TypeError: non-string object passed in for the ORDER parameter
x.flatten(order = 'CAT') # invalid string argument
Traceback (most recent call last):
File "", line 1, in
TypeError: order not understood

This is my first PR to the numpy library, so all comments regarding the

proposed changes as well as the PR itself are welcome!

You can view, comment on, or merge this pull request online at:

#6823
Commit Summary

File Changes

Patch Links:


Reply to this email directly or view it on GitHub
#6823.

@gfyoung

gfyoung commented Dec 12, 2015

Copy link
Copy Markdown
Contributor Author

Could you clarify what you mean by this (not a lot of experience with working on large open-source projects)? Even if the proposed coding changes are not merged in, I would certainly propose the changes to the documentation, as it seems misleading given the behind-the-scenes functionality

@gfyoung

gfyoung commented Dec 12, 2015

Copy link
Copy Markdown
Contributor Author

Yikes. Travis just blew up on me there. Seems like I may have been too strict on the order parameter, or perhaps some other code needs rewriting. Let me take a look.

@njsmith

njsmith commented Dec 12, 2015

Copy link
Copy Markdown
Member

Sure, to expand:

I think the change is a good one. But, it's possible that it will end up taking code that used to work, and make it start throwing exceptions. Of course that code was probably poorly written if it assumed that it was okay to do np.flatten(arr, order="XYZZY!") or whatever, but these things do sometimes happen, and it's somewhat disproportionate to take code that used to produce correct results and have it suddenly start blowing up without warning because numpy was upgraded.

How much code like this is out there? I don't know. Maybe ~none. Maybe a surprising amount. It's hard to say.

The usual way to handle this kind of issue is with a "deprecation cycle", which goes through several stages: first we change the code so that it still works the same way as before, but it calls warnings.warn("This is gonna break later!", category=DeprecationWarning). Then after some time has passed, we make the actual change we wanted to make in the first place. This has two advantages: (1) every once in a while people actually fix their code before we release the new version, so that users never see it break, and (2) when the code does break, we can say "look, you can't say we didn't warn you" ;-).

This is kinda a borderline case, because arguably anyone who was depending on the broken behavior was depending on a bug, and usually we don't do deprecation warnings for bug fixes. But if this turns out to break lots of code then we still probably want to do the deprecation thing, because making stuff work in this reality is more important that being able to claim that we have virtuously followed all the rules of the alternate reality where everyone does what they're supposed to...

So what I was saying was that: this is a borderline case where it isn't 100% obvious whether we need a deprecation cycle or not -- this is the sort of thing that has to be figured out when reviewing a change -- but I'd be fine with merging it, so long as we keep an eye out for it causing problems when we put out a Release Candidate for people to test, and possibly revert it then and take the slower route.

@gfyoung

gfyoung commented Dec 12, 2015

Copy link
Copy Markdown
Contributor Author

Okay, I understand. I have been starting to see what you mean already, as my subsequent commits have been catching errors in which booleans were passed in the order parameter, causing tests to break on Travis.

@njsmith

njsmith commented Dec 12, 2015

Copy link
Copy Markdown
Member

Oh yeah, that is a bad sign... if order=True and order=False used to be synonyms for "F" and "C" respectively (whyyyy??) then we might well be stuck doing a deprecation cycle on those at least.

@njsmith

njsmith commented Dec 12, 2015

Copy link
Copy Markdown
Member

A quick skim of scipy source finds lots of lowercase "c" and "f", but no bools, at least. (Though I might have missed them.)

@gfyoung

gfyoung commented Dec 12, 2015

Copy link
Copy Markdown
Contributor Author

Oh, that's right. I forgot that scipy code might also be broken if there are any major changes to numpy. Depending on how the builds are setup in scipy, I'm not entirely sure how the tests would indicate that changes in numpy broke scipy for example. However, I seem to have caught all of the immediate issues just in the numpy library, as Travis is finally happy with my changes.

@njsmith

njsmith commented Dec 12, 2015

Copy link
Copy Markdown
Member

It's not just scipy -- there are literally hundreds of libraries out there that use numpy, and any of them can (potentially) be broken by major changes in numpy.

scipy is a convenient one to check though, because it's especially widely used and because it's even older then numpy so has a special habit of using grotty old conventions from the bad old days :-)

@gfyoung

gfyoung commented Dec 12, 2015

Copy link
Copy Markdown
Contributor Author

Fair enough. Do you think it's best then that I role back some of the changes I made to the C code and replace them with PyErr_WarnEx(PyExc_DeprecationWarning, "BLAH BLAH BLAH", 1) or something of that sort?

@seberg

seberg commented Dec 12, 2015

Copy link
Copy Markdown
Member

Yes, we have a DEPRECATE macro to make it a bit shorter. And we need a test for it. There is a test_deprecations test file where it could go. Best, add a short date and numpy version comment to it (numpy version will be 1.11, just something like /* 2014-06-12, 1.9 */ before the DEPRECATE macro).

@jaimefrio

Copy link
Copy Markdown
Member

Do we really want to make lowercase versions an error? It seems a little too strict to me...

@gfyoung

gfyoung commented Dec 12, 2015

Copy link
Copy Markdown
Contributor Author

@seberg : I'm not entirely sure I follow what you were saying. Could you explain?

@jaimefrio : I guess it's an issue between what you are allowed to do and what the documentation says you can do. If I'm not mistaken, I don't think the documentation says you can use lowercase. Perhaps the documentation should then be expanded to state this?

@jaimefrio

Copy link
Copy Markdown
Member

Well, Nathaniel did mention there were tests in scipy using lowercase that would be broken by this change...

@gfyoung

gfyoung commented Dec 12, 2015

Copy link
Copy Markdown
Contributor Author

Right, but I guess the question is: should we allow people to pass in lowercase parameters? That will determine what sort of warning I could put in the C code instead of the changes I made.

@seberg

seberg commented Dec 13, 2015

Copy link
Copy Markdown
Member

@gfyoung, I mean that yes, please change the pull request to give deprecations. Just wanted to note where tests would go and DEPRECATE macro we have.
I agree with Jaime though, I don't think forbidding lower case will improve much at all, the meaning seems obvious in any case so it seems it would not achieve much except bugger people to change code that will seem fine to them.

@gfyoung

gfyoung commented Dec 13, 2015

Copy link
Copy Markdown
Contributor Author

Fair enough. I updated the PR to use the DEPRECATE macro instead. Is the implementation correct?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DEPRECATE needs to be

if (DEPRECATE("blabla") < 0) {
    cleanup_code/goto;
    return;
}

since it raises an error when the warnings are set accordingly. Also, please add a short test for both deprecations into the test_deprecations.py file, that would also find such an issue.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm sorry: could you clarify what you mean by this? I tried reading the documentation on Python Docs, but I'm not sure I quite follow still.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deprecate/PyErr_WarnEx can return an error value (specifically, this happens if you do for example import warnings; warnings.simplefilter("error", DeprecationWarning).
In that case you need to check for a negative return, do the typical cleanup (which might be nothing in this case) and return an error return (NULL or -1 normally).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you are curious: https://docs.python.org/2/c-api/exceptions.html#c.PyErr_WarnEx the second paragraph mentions it, the error handling code you can probably steal/infer from other error handling in the funciton.

@gfyoung

gfyoung commented Dec 14, 2015

Copy link
Copy Markdown
Contributor Author

@seberg : Ah, I think I understand. PR updated with a still happy Travis.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, this is right. Can you add a comment /* 2015-12-14, 1.11 */ above both? I think the \ might be unnecessary here, since it is all in brackets? Or was that different in C, don't remember?

Other nitpick, I think I prefer order in lower case maybe with backticks, but frankly I doubt we have a consistent style, so not unless someone else complains soon. I am not sure we should name ORDER explicitly, since in principle the argument could be called differently. Don't have a much nicer idea though, maybe someone else has ;).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe also just: "invalid order; only 'C', 'F', 'A', or 'K' can be converted to a memory/iteration order." (could add what was actually given, but it tends to be a bit annoying). Anyway, as I said, I am not good at writing these, just thinking loud :).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You only need \ in multiline defines.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@seberg : Regarding the comments, sure thing

Good point on the \. It's hard juggling multiple languages at once. :) I did a slight revision to the warning messages to not explicitly mention the order parameter

@seberg

seberg commented Dec 14, 2015

Copy link
Copy Markdown
Member

Anyway, thanks, almost there, will leave a bit in case someone has some nitpicks about the message or so.

@seberg

seberg commented Dec 14, 2015

Copy link
Copy Markdown
Member

Oh, sorry :(, one more thing, can wait a bit, but maybe you can add a short note to the release notes in doc/release/ for 1.11, we have most deprecations in there, and if we don't add it now, we would just forget about it later.

Comment thread numpy/core/tests/test_deprecations.py Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PEP8 prefers putting the whole string inside parenthesis, and getting rid of both the \s and the +s, relying on string literal concatenation.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good to know. Will do.

@gfyoung

gfyoung commented Dec 14, 2015

Copy link
Copy Markdown
Contributor Author

@seberg : No worries. I perfectly understand wanting to make sure the PR is just right. It's difficult to undo a bad merge. :) - I updated the release notes with the deprecation.

Comment thread doc/release/1.11.0-notes.rst Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is more general, this is used (almost) everywhere where an order argument is used. Also, I think you don't have to argue that it should not be allowed, it will not be allowed in the future ;). Anyway, the document will be revised again for the release.

When you are done with this, could you squash the commits together into one or two (rebase -i HEAD~7 or similar for example). Best give it a commit like "DEP: ..." and you can include "closes gh-6598" at the end in the message as well (the message will be closed automatically and we have the reference).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, and don't worry about the test failure, seems there is something else going on.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Regarding the release note, sure thing. Regarding the test failure, got it. I think several people (myself included) have brought this up in the discussion mailing list.

@charris

charris commented Dec 16, 2015

Copy link
Copy Markdown
Member

Close and reopen to restart tests.

@charris charris closed this Dec 16, 2015
@charris charris reopened this Dec 16, 2015
@gfyoung

gfyoung commented Dec 16, 2015

Copy link
Copy Markdown
Contributor Author

@charris : Thanks! If Travis is happy, and if everyone else is happy, this should be good to merge.

@seberg

seberg commented Dec 18, 2015

Copy link
Copy Markdown
Member

Sorry for the delay. If you got some patience left, the best form would be to have:

DEP: short description

longer description

closes gh-#

The DEPRECATE strings could be aligned more PEP8 like and the closing bracket on the same line ;). But I think I will merge this in a bit in any case. Lets hope it only annoys a moderate number of people ^^.

@gfyoung

gfyoung commented Dec 18, 2015

Copy link
Copy Markdown
Contributor Author

No worries. #6824 has been keeping me occupied :) This PR is good to merge I feel, so tidying it up a bit doesn't hurt at all. Will make those adjustments and make sure Travis is still happy.

The bug traces to the PyArray_OrderConverter
method in conversion_utils.c, where no errors
are thrown if the ORDER parameter passed in
is not of the string data-type or has a string
value of length greater than one. This commit
causes a DeprecationWarning to be raised, which
will later be turned into a TypeError or another
type of error in a future release.

Closes gh-6598.
@gfyoung

gfyoung commented Dec 18, 2015

Copy link
Copy Markdown
Contributor Author

32-bit build was hanging for no apparent reason. Closing and reopening PR to see if that was just an anomaly.

@gfyoung gfyoung closed this Dec 18, 2015
@gfyoung gfyoung reopened this Dec 18, 2015
@gfyoung

gfyoung commented Dec 18, 2015

Copy link
Copy Markdown
Contributor Author

@seberg : Travis is happy again! Style suggestions implemented successfully. Should be ready to merge.

@seberg

seberg commented Dec 18, 2015

Copy link
Copy Markdown
Member

OK, lets give it a shot. Thanks a lot!

seberg added a commit that referenced this pull request Dec 18, 2015
Stricter Argument Checking for Flatten Methods
@seberg
seberg merged commit 3af5f05 into numpy:master Dec 18, 2015
@gfyoung
gfyoung deleted the order_arg_validate branch December 18, 2015 12:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants