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

BUG: ndarrays pickled by 1.16 cannot be loaded by 1.15.4 and lower #12842

Merged
merged 1 commit into from Jan 27, 2019

Conversation

mattip
Copy link
Member

@mattip mattip commented Jan 24, 2019

Fixes #12837
When pickling ndarrays, the pickle protocol adds the reconstructing function's modulename to the pickled string so it can be called. The function (which is_reconstruct) now lives in numpy.core._multiarray_umath, but in pre-1.16 numpy it was in numpy.core.multiarray. Also for scalar objects, the function scalar` moved.

The fix is very simple, changing the __module__ attribute of _reconstruct and scalar

In array_reduce_ex, used when the pickle protocol is 5, the function will be numpy.core.numeric._frombuffer, which will not exist on pre-1.16 numpy (added in commit 64a855f, Oct 2018). I don't think we can work around that. People who do not have that function will have to modify their numpy if they wish to import protocol-5 pickled ndarrays.

@mattip mattip added this to the 1.16.1 release milestone Jan 24, 2019
@charris
Copy link
Member

charris commented Jan 24, 2019

Lots of test failures.

@charris
Copy link
Member

charris commented Jan 24, 2019

There is a dedent function that does that, look at test_arrayprint.py for some examples. It might be better to separate the shell code from the call in any case.

@mattip
Copy link
Member Author

mattip commented Jan 24, 2019

3.5 fails because I used fstrings. Since the test is marked slow, and we only run slow on 3.6, I will skip 3.5 altogether.

3.6 is failing tests that check stdout, since the output is getting clobbered with "Coverage.py warning: --include is ignored because --source is set (include-ignored)". Did something change in coverage? Is this just on this PR or elsewhere too?

@charris
Copy link
Member

charris commented Jan 24, 2019

Cannot backport if it uses fstrings.

@mattip
Copy link
Member Author

mattip commented Jan 25, 2019

Removed fstrings, rebased off master, but the test still causes problems on travis. It seems creating a venv via pytohn3 -venv and installing things into it somehow messes up the current environment on travis, azure seems OK with it.

I am not sure why the numpy/distutils/tests/test_exec_command.py test is failing, see PR #12849

@pytest.mark.skipif(not sys.platform.startswith('linux'),
reason="linux only")
@pytest.mark.skipif(sys.version_info < (3,4), reason="lacks venv")
@pytest.mark.skip("venv on travis is flakey")
Copy link
Member Author

Choose a reason for hiding this comment

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

Skip the test entirely for now, maybe someday re-enable. It passes when run alone (--k test_pickle_across_versions) which gives me confidence that the actual fix works

Copy link
Member

Choose a reason for hiding this comment

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

How about just look at the pickle header?

Copy link
Member

Choose a reason for hiding this comment

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

Hmm, OK, looks like pickle itself determines the module, but it must be in the header somewhere. I wonder if the pickle can simply be searched for the module name? For instance

In [30]: a = array(1)

In [31]: pickle.dumps(a, protocol=4)
Out[31]: b'\x80\x04\x95\x96\x00\x00\x00\x00\x00\x00\x00\x8c\x1cnumpy.core._multiarray_umath\x94\x8c\x0c_reconstruct\x94\x93\x94\x8c\x05numpy\x94\x8c\x07ndarray\x94\x93\x94K\x00\x85\x94C\x01b\x94\x87\x94R\x94(K\x01)h\x03\x8c\x05dtype\x94\x93\x94\x8c\x02i8\x94K\x00K\x01\x87\x94R\x94(K\x03\x8c\x01<\x94NNNJ\xff\xff\xff\xffJ\xff\xff\xff\xffK\x00t\x94b\x89C\x08\x01\x00\x00\x00\x00\x00\x00\x00\x94t\x94b.'

Copy link
Member

Choose a reason for hiding this comment

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

Looking at that, I wonder why the umath module is there? Maybe from the ndarray __reduce__ method? AFAICT that method should not be called, being replaced by __reduce_ex__. I think we should look at both those methods.

Copy link
Member

Choose a reason for hiding this comment

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

We should also test the different protocols.

Copy link
Member

Choose a reason for hiding this comment

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

If you really want to test across versions, could also pickle some ndarray in an earlier version, get the hash of the result, and check that it agrees with the hash of the same data pickled with a latter version. The "correct" hashes can be stored in the test.

Copy link
Member

Choose a reason for hiding this comment

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

For instance with current master and previous array

In [43]: hashlib.sha256(s).hexdigest()
Out[43]: 'cce278984d68606f0ceb4d23c298ebd8abe944adba1bc6b161f16c180e785fc3'

Copy link
Member

Choose a reason for hiding this comment

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

The array_reduce function does look buggy, but it is unlikely it gets called unless some ndarray subclass does it explicitly.

Copy link
Member Author

Choose a reason for hiding this comment

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

I wonder why the umath module is there

The module is _multiarray_umath.

I will write a test that checks the bytestring for the correct module name in all protocols. Tieing ourselves to the exact pickle bytestring or hash seems too restrictive going forward.

@charris
Copy link
Member

charris commented Jan 25, 2019

There is also some ufunc pickling stuff in numpy/core/__init__.py that might could have problems.

@charris
Copy link
Member

charris commented Jan 25, 2019

The ufunc pickles look OK.

@charris
Copy link
Member

charris commented Jan 26, 2019

Is _reconstruct the only problem?

@mattip
Copy link
Member Author

mattip commented Jan 26, 2019

_reconstruct for ndarrays and scalar for scalars both need their __module__ adjusted

@@ -14,7 +14,7 @@ jobs:
docker pull i386/ubuntu:bionic
docker run -v $(pwd):/numpy i386/ubuntu:bionic /bin/bash -c "cd numpy && \
apt-get -y update && \
apt-get -y install python3.6-dev python3-pip locales && \
apt-get -y install python3.6-dev python3-venv python3-pip locales && \
Copy link
Member

Choose a reason for hiding this comment

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

No longer need python3-venv?

Copy link
Member Author

Choose a reason for hiding this comment

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

True, but on the other hand it couldn't hurt and makes this environment consistent with the other linux ones one travis

Copy link
Member

Choose a reason for hiding this comment

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

Come, come, you could install lots of things that don't hurt, but that is not a reason to do it.

Copy link
Member Author

Choose a reason for hiding this comment

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

ok, removing

@charris
Copy link
Member

charris commented Jan 27, 2019

LGTM, one possible nit.

@charris charris merged commit 5c370cb into numpy:master Jan 27, 2019
@charris
Copy link
Member

charris commented Jan 27, 2019

Thanks Matti.

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.

BUG: ndarrays pickled on 1.16.0 cannot be loaded in previous versions
2 participants