-
Notifications
You must be signed in to change notification settings - Fork 419
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 support for sending bytearrays. #622
Conversation
Codecov Report
@@ Coverage Diff @@
## master #622 +/- ##
=========================================
+ Coverage 96.78% 96.8% +0.02%
=========================================
Files 16 16
Lines 5628 5667 +39
Branches 392 391 -1
=========================================
+ Hits 5447 5486 +39
Misses 121 121
Partials 60 60
Continue to review full report at Codecov.
|
Hm. I would assume most people run the latest cffi anyways. Since we already have a conditional, how about we externalize it into a function N.B. I’m not telling, I’m asking. :) |
I'm certainly open to that approach. It'd be interesting for @alex or @reaperhulk to weigh in with some relative usage data of different cffi versions, or at least with gut feelings, to work out how valuable that code will be, especially as it will suck to test. 😉 |
I think we could make it suck a lot less with something like this: _is_acceptable_for_from_buffer = _make_buffer_checker(cffi.__version__) |
Yeah, that's true, that could absolutely work. |
We already have some code in cryptography dependent on cffi >= 1.7, but we don't require it so we do this: We get coverage on that branch by testing an old pypy with coverage enabled. https://github.com/pyca/cryptography/blob/master/.travis.yml#L27 The only real consequence to upping minimum cffi version is that you leave behind older PyPys (since they can't upgrade it). cryptography decided against doing that, but this project can make its own call. |
Here are the number of downloads by implementation for the last month for pyopenssl:
|
And here's the top 10 versions within that PyPy umbrella:
A (very) quick check shows that PyPy2 5.2 shipped with cffi 1.8 (5.1 shipped with 1.6). |
I would just go the hybrid way I’ve sketched out. It seems like something we can test easily and we’ve learned the hard way, that dependencies don’t get auto-updated. :| |
2d47b8b
to
c80ed17
Compare
src/OpenSSL/SSL.py
Outdated
""" | ||
cffi_version = tuple( | ||
int(component) for component in | ||
_CFFI_VERSION_RE.match(cffi_version_string).group(1, 2) |
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.
why not just cffi_version_string.split(".")
? A RE seems overkill here?
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.
Why not just from packaging.version import parse
? Having pyOpenSSL depend on packaging (which is a dependency of both setuptools and cryptography) doesn't seem unreasonable.
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.
@hynek The main reason is that you can conceivably have a version string like 1.8dev4
, which will not behave nicely under .split('.')
. It's possible I'm being too defensive here, but I was trying to avoid having everything explode in situations I didn't entirely forsee.
@reaperhulk That seems reasonable to me, if it's ok with @hynek I'll happily do that instead.
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.
Go ahead!
tests/test_ssl.py
Outdated
buffer_factory = _make_buffer_checker(cffi_version) | ||
cdata_buffer = buffer_factory(data) | ||
# We need an explicit length here because cffi on recent PyPy doesn't | ||
# 't correctly construct buffers when it knows the length of the cdata |
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.
't looks like an oops :)
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.
Resolved.
@njsmith would this make you happy? |
src/OpenSSL/SSL.py
Outdated
:param cffi_version_string: The version of CFFI in use, from | ||
ffi.__version__. | ||
""" | ||
cffi_version = parse(cffi_version_string) |
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.
A quick check shows that cffi has had a pre-parsed version tuple at cffi.__version_info__
for some time (I checked 1.0.0 and it had it). That might be nicer than pulling in a dependency on packaging
?
if isinstance(buf, _memoryview): | ||
buf = buf.tobytes() | ||
if isinstance(buf, _buffer): | ||
buf = str(buf) |
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.
- It may not matter much in practice, but I think on recent CFFI these lines are unnecessary and are inefficient in that they impose an extra copy?
- I might add a comment on the
str
line noting that this branch can only be taken on py2 (because py3 has no buffer objects)
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.
- They are, but the code refactor required to fix it and keep the tests in place was not something I wanted to undertake on this PR.
- I don't think it matters really?
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.
- Yeah, not really; I only mention it because I had a moment of "wtf you can't call
str
on abuffer
!" that took a few minutes to resolve :-)
src/OpenSSL/SSL.py
Outdated
ffi.__version__. | ||
""" | ||
cffi_version = parse(cffi_version_string) | ||
if cffi_version >= Version("1.7"): |
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.
The docs say "bytearray objects were supported in version 1.7 onwards ... and byte strings were supported in version 1.8 onwards." So the minimum version should probably be 1.8, not 1.7?
Some comments above to do with as you wish. In general I am not too picky though, because I'm just using PyOpenSSL in my test suite, so I don't care about speed or dependencies or any of that :-). I was just surprised when I had to add a call to |
Aside from the two points I addressed the rest are things I want to take action on, I'll do that sometime soon. |
oh looks like this only needs docs + changelog? completely lost track of it! |
I was about to suggest this same bugfix! I guess you don't like just letting cffi decide whether the buffer passed is acceptable or not? In It almost certainly calls from_buffer internally as part of any wrapped function that takes char*? |
Obsoleted by #852 |
This is fixed indeed, thanks @dholth! 🍰 |
Hooray! |
Resolves #621.
This patch adds support for sending bytearrays by using
ffi.from_buffer
for both bytearrays and byte strings.In general this patch is a good idea, however, it has some caveats. Most notable is that bytearray and byte string support for
from_buffer
only landed in cffi 1.8, but our lowest supported cryptography version only requires cffi 1.4. That may make this patch unacceptable. The minimum we could get away with would be cffi 1.7, and only then if we add separate code paths for byte strings and byte arrays.On the other hand, if we could go up to requiring cffi 1.10 then we could remove the conditionals for handling memoryview and buffer objects, as
ffi.from_buffer
also supports them directly.Anyway, here's a PoC, we can discuss whether it's acceptable or not.