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

Port to Python 3 #2

Merged
merged 11 commits into from May 15, 2017
Merged

Port to Python 3 #2

merged 11 commits into from May 15, 2017

Conversation

vincentfretin
Copy link
Member

The code now runs on both Python 2.7 and Python 3.4-3.6, the tests pass on both.
I replaced the paster server by gunicorn.
I dropped Python < 2.7 support (I removed the try except hashlib / md5)

Copy link
Member

@mgedmin mgedmin left a comment

Choose a reason for hiding this comment

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

LGTM. My comments are mostly stylistic suggestions.

I would prefer if you dropped the ZODB._compat dependency before merging this, but I won't care that much if you don't.

rm sample.key sample.cert

openssl req -new -x509 -nodes -sha1 -days 3650 -key sample.key > sample.crt
cat sample.crt sample.key > sample.pem
Copy link
Member

Choose a reason for hiding this comment

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

Maybe it's time to replace -sha1 with -sha256?

Copy link
Member

Choose a reason for hiding this comment

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

BTW is the .pem file still needed? I see you're configuring it to use .key and .crt separately.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, sha256 definitely.
You're right, the .pem can be removed if we use "--ca-certificate sample.crt" instead of "--ca-certificate sample.pem" with wget. I can change the commands in the README.

UuBcNoW6zXoEYU6oV1Oi6hjhW1eu6PuAv4jPY754XoiNEZdZqYQqo8BFkWtDW1/C
GXrtQRbMDPzD40UYB2UCQQCmJpJp+u2lHj7zuZikHIHQBNyXyoGnzgNs6XUj1Bs6
Y4vjue8w6RkRLZ1YGP+xqsngVqb9IRygyLDpEgwEnOT4
-----END RSA PRIVATE KEY-----
Copy link
Member

Choose a reason for hiding this comment

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

While this is convenient, if we don't ship a sample private key, people won't accidentally use 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.

The private key was already in the repo, the sample.pem contains both the public and private keys.
The sample certificate was committed I guess so you can test the app without having the openssl command. There is bin/testclient if you don't have wget too.

>>> data="encryptioniscool"*24*1024
>>> tmp_file.write(data)
>>> tmp_file.seek(0)
>>> data=b"encryptioniscool"*24*1024
Copy link
Member

Choose a reason for hiding this comment

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

As long as you're changing this, can add the missing spaces around =?

Copy link
Member Author

Choose a reason for hiding this comment

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

sure

>>> encrypted_file = tempfile.TemporaryFile()
>>> keys.encrypt_file(key, tmp_file, encrypted_file)
>>> tmp_file.close()

And decrypt the file

>>> decrypted_file = tempfile.TemporaryFile()
>>> encrypted_file.seek(0)
>>> pos =encrypted_file.seek(0)
Copy link
Member

Choose a reason for hiding this comment

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

A space after the = would be nice.

Copy link
Member Author

Choose a reason for hiding this comment

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

oups, my bad.

iv = ''.join(chr(random.randint(0, 0xFF)) for i in range(16))
# bytes(bytearray(generator)) is needed for Python 2,
# with Python 3 bytes(generator) works
iv = bytes(bytearray((random.randint(0, 0xFF)) for i in range(16)))
Copy link
Member

Choose a reason for hiding this comment

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

I'm not a cryptographer, but I think it's a good idea to use a strong RNG for IVs --> use os.urandom(16)? That would also make the code simpler.

Copy link
Member Author

Choose a reason for hiding this comment

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

Ah os.urandom(16) is really great! Much simpler indeed.

Copy link
Member Author

Choose a reason for hiding this comment

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

The random here is not the random package from Python, but "from Crypto.Random import random". I'm not sure we want to change that.

Copy link
Member

Choose a reason for hiding this comment

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

Oh dear, that's terribly misleading!

(I've lost the will to care.)

Copy link
Member

Choose a reason for hiding this comment

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

(If this was written today, I'd suggest using cryptography instead of M2Crypto.)

@@ -14,8 +14,7 @@
"""Encrypted persistent objects
"""
from __future__ import absolute_import
import cPickle
import cStringIO
from ZODB._compat import BytesIO, Pickler, Unpickler
Copy link
Member

Choose a reason for hiding this comment

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

Please do not import from private modules of other packages (here, ZODB). Create a keas.kmi._compat module and copy the relevant bits into it -- or just use six.

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, I'll do that.



def new_key(kmf):
sys.stdout.write(kmf.generate())
os.write(sys.stdout.fileno(), kmf.generate())
Copy link
Member

Choose a reason for hiding this comment

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

An interesting approach! I'd've gone with getattr(sys.stdout, 'buffer', sys.stdout).write(kmf.generate()).

I don't have any arguments in favour of one or the other approach. sys.stdout.buffer is easier to stub in unit tests, but os.write(...fileno(), ...) is shorter. It's fine.

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 did try sys.stdout.buffer first, but when I saw sys.stdout.buffer didn't work on Python 2, I searched for another solution and found this.

@@ -1,3 +1,4 @@
from __future__ import print_function
Copy link
Member

Choose a reason for hiding this comment

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

This should go after the block comment, right above the __docformat__ assignment (which can probably be thrown away).

except IOError, e:
print >> sys.stderr, "Could not read %s" % filename
print >> sys.stderr, e
return open(filename, 'rb').read()
Copy link
Member

Choose a reason for hiding this comment

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

A nice opportunity to use with open(...) as fp: return fp.read() -- it'll avoid ResourceWarnings on Python 3.

Copy link
Member Author

Choose a reason for hiding this comment

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

good point

.travis.yml Outdated
@@ -1,6 +1,8 @@
language: python
python:
- 2.7
- 3.4
- 3.6
Copy link
Member

Choose a reason for hiding this comment

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

Why no 3.5?

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 can add it.
I didn't put it because I didn't have a python 3.5 environment on my machine, so I didn't test it myself. But travis is here for that!

@mgedmin
Copy link
Member

mgedmin commented May 8, 2017

The Python 3.6 failure is about a different exception message. We usually deal with those by using a doctest renormalizer (see, e.g. zope.sqlalchemy for an example).

While I believe doctest allows you to ignore the exception message if you use ValueError: ..., that would make the doctest less readable, so I'd go with a RENormalizer.

@vincentfretin
Copy link
Member Author

Ah right, I did actually test only with the Python 3.4 on my machine. I'll look into fixing the error on Python 3.6 with a normalizer.
Thanks for the review @mgedmin !
I'll do the changes probably this weekend.

@vincentfretin
Copy link
Member Author

@mgedmin I think we can merge it now. FYI, I signed the zope contributor agreement.

Copy link
Member

@mgedmin mgedmin left a comment

Choose a reason for hiding this comment

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

LGTM. Feel free to merge.

Tell me your PyPI username if you wish to be able to make a release.


class Unpickler(zodbpickle.pickle.Unpickler):
def __init__(self, f):
super(Unpickler, self).__init__(f)
Copy link
Member

Choose a reason for hiding this comment

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

I don't understand why these __init__ methods are necessary. They don't seem to be doing anything, except calling the base version?


try:
# XXX: why not just import BytesIO from io?
from cStringIO import StringIO as BytesIO
Copy link
Member

Choose a reason for hiding this comment

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

I think io.BytesIO is noticeably slower on 2.7. I haven't personally benchmarked, and I don't remember why I think so.

It's probably mostly because old code used cStringIO and if we keep using the same thing, we're sure nothing unexpected will break.

Copy link
Member

Choose a reason for hiding this comment

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

I know the io package was slower on 2.6, because it was written in Python. But in 2.7 it was moved to C, so maybe that's not an issue anymore.

Updated module: The io library has been upgraded to the version shipped with Python 3.1. For 3.1, the I/O library was entirely rewritten in C and is 2 to 20 times faster depending on the task being performed.

(Using cStringIO on Python 2 but BytesIO on Python 3 is confusing and could be a source of bugs, IMO, because they accept different things. cStringIO will happily allow you to mix unicode and bytes, but BytesIO won't.)

iv = ''.join(chr(random.randint(0, 0xFF)) for i in range(16))
# bytes(bytearray(generator)) is needed for Python 2,
# with Python 3 bytes(generator) works
iv = bytes(bytearray((random.randint(0, 0xFF)) for i in range(16)))
Copy link
Member

Choose a reason for hiding this comment

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

Oh dear, that's terribly misleading!

(I've lost the will to care.)

@vincentfretin vincentfretin merged commit 85c14a0 into zopefoundation:master May 15, 2017
@vincentfretin
Copy link
Member Author

My username is vincentfretin on pypi.

@mgedmin
Copy link
Member

mgedmin commented May 15, 2017

You have PyPI rights now.

I would expect zest.releaser to work fine on this package. I would also install check-manifest for its zest.release hook to be doubly sure.

@vincentfretin
Copy link
Member Author

Changes released as 3.2.0

kraeks added a commit that referenced this pull request Mar 27, 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.

None yet

3 participants