-
-
Notifications
You must be signed in to change notification settings - Fork 2k
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
Content-Disposition fast access in ClientResponse #2455
Conversation
I dislike two properties. |
|
I like idea of |
PR number is ok for the case -- it doesn't cover the whole issue. |
Codecov Report
@@ Coverage Diff @@
## master #2455 +/- ##
==========================================
+ Coverage 97.14% 97.14% +<.01%
==========================================
Files 39 39
Lines 8125 8136 +11
Branches 1419 1420 +1
==========================================
+ Hits 7893 7904 +11
Misses 101 101
Partials 131 131
Continue to review full report at Codecov.
|
be43246
to
6afa66c
Compare
aiohttp/helpers.py
Outdated
@@ -737,3 +755,10 @@ def content_length(self, *, _CONTENT_LENGTH=hdrs.CONTENT_LENGTH): | |||
|
|||
if content_length: | |||
return int(content_length) | |||
|
|||
@property | |||
def content_disposition(self, *, header=hdrs.CONTENT_DISPOSITION): |
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 the property is in HeadersMixin
?
Should web.Request
has the property?
I thought it is only for ClientResponse
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.
Indeed it's response only header.
aiohttp/helpers.py
Outdated
else: | ||
value, params = multipart.parse_content_disposition(raw) | ||
filename = multipart.content_disposition_filename(params) | ||
self._content_disposition = ContentDisposition(value, params, |
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.
Convert params
into types.MappingProxyType
. The value should be immutable
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.
Will do in next patchset.
aiohttp/helpers.py
Outdated
@property | ||
def content_disposition(self, *, header=hdrs.CONTENT_DISPOSITION): | ||
"""The value of Content-Disposition HTTP header.""" | ||
if self._content_disposition == False: |
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.
is False
aiohttp/helpers.py
Outdated
@@ -703,6 +708,7 @@ class HeadersMixin: | |||
|
|||
_content_type = None | |||
_content_dict = None | |||
_content_disposition = False |
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.
Quite strange default. Why False
?
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.
False if header are not parsed/cached yet
None if there was an attempt to parse, but no header found
ContentDisposition if header was found and parsed
aiohttp/helpers.py
Outdated
@@ -714,6 +720,18 @@ def _parse_content_type(self, raw): | |||
else: | |||
self._content_type, self._content_dict = cgi.parse_header(raw) | |||
|
|||
def _parse_content_disposition(self, _CONTENT_DISPOSITION): | |||
from . import multipart |
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 hidden import?
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 was here due to circular import
6afa66c
to
41fd2c3
Compare
aiohttp/client_reqrep.py
Outdated
@@ -500,6 +505,7 @@ class ClientResponse(HeadersMixin): | |||
raw_headers = None # Response raw headers, a sequence of pairs | |||
|
|||
_connection = None # current connection | |||
_content_disposition = False # content disposition cache |
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.
I still think that's not good choice. None
means no value. We tried to parse it, nothing happened. Yes, this will trigger headers lookup for each property access - but that's not a problem.
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.
helpers.sentinel
is an option
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.
Good alternative, but None
is fine as well. However, even better would be to use reify here and avoid internal attributes. Headers are immutable in anyway.
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.
+1 for reify
41fd2c3
to
2ede86e
Compare
aiohttp/client_reqrep.py
Outdated
value, params = multipart.parse_content_disposition(raw) | ||
params = MappingProxyType(params) | ||
filename = multipart.content_disposition_filename(params) | ||
return ContentDisposition(value, params, filename) |
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.
Convert params into immutable structure
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.
What do you mean? params
is already converted to MappingProxyType line 565
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.
Sorry.
Nevermind
|
aiohttp/client_reqrep.py
Outdated
@@ -527,6 +532,7 @@ def __init__(self, method, url, *, | |||
self._request_info = request_info | |||
self._timer = timer if timer is not None else TimerNoop() | |||
self._auto_decompress = auto_decompress | |||
self._cache = {} |
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.
This looks like redundant.
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.
Looks like no. All classes with @reify methods have this and AttributeError is raising without this line.
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.
Implicit magic. Worth to add notice about.
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.
Yes, it's required.
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.
I mean the attr is required, not a notice :)
But the notice makes no harm
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.
Oh, ok. Last time I saw reify
in acttion, no special attrs were required.
tests/test_client_response.py
Outdated
|
||
assert 'attachment' == response.content_disposition.value | ||
assert 'bar' == response.content_disposition.parameters["foo"] | ||
assert 'archive.tar.gz' == response.content_disposition.filename |
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.
Worth to add two more cases:
content_disposition.parameters
is immutable- second access to
content_disposition
returns the same result ifClientResponse.headers
were modified in-between (I'm surprised to see it's not read-only property).
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.
Will do
docs/client_reference.rst
Outdated
|
||
.. attribute:: value | ||
|
||
Value of Content-Disposition header itself, e.g. ``attachment``. |
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.
"attachment"
- you want to notice that the value is a string typed.
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.
Will do
docs/client_reference.rst
Outdated
|
||
.. attribute:: parameters | ||
|
||
A :class:`dict` instance contains all parameters. |
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.
Actually, it's not a dict, but MapptingProxy
. Difference in mutability.
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.
We could just replace it with *mapping*
for sake of simplicity.
I pretty sure not many python developers know what MappingProxy
is :)
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.
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.
Good idea.
aiohttp/client_reqrep.py
Outdated
@@ -33,6 +34,10 @@ | |||
__all__ = ('ClientRequest', 'ClientResponse', 'RequestInfo') | |||
|
|||
|
|||
ContentDisposition = collections.namedtuple( | |||
'ContentDisposition', ('value', 'parameters', 'filename')) |
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.
I think value
is not the best choice. The RFC defines it as a type
which suites more to what the value it holds.
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.
Agree
aiohttp/client_reqrep.py
Outdated
raw = self._headers.get(hdrs.CONTENT_DISPOSITION) | ||
if raw is None: | ||
return None | ||
value, params = multipart.parse_content_disposition(raw) |
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 seems only two values here are valid for HTTP headers case: inline
and attachment
. May be worth to raise a warning about invalid type or turn the value
in Enum?
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.
According to https://tools.ietf.org/html/rfc6266#section-4.1 any token is valid
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.
I like Enum
but how the change affects multidict
and existing users.
I suspect parse_content_disposition
is internal API but not 100% sure.
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.
@redixin Yes, but RFC defines more general Content-Disposition definition. Mozilla explicitly splits it into HTTP headers and Multpart headers. The second one utilize token
rule completely, but here we are in HTTP response land.
ff12184
to
dec87c0
Compare
Add ContentDisposition class and content_disposition property Partially implements aio-libs#1670
dec87c0
to
9b6a528
Compare
Great work! |
This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a [new issue] for related bugs. |
Add content_disposition and content_disposition_dict properties
Partially implements #1670
What do these changes do?
Improves ClientResponse ergonomics
Are there changes in behavior for the user?
All changes are documented.
Related issue number
#1670
Checklist
CONTRIBUTORS.txt
CHANGES
folder<issue_id>.<type>
for example (588.bug)issue_id
change it to the pr id after creating the pr.feature
: Signifying a new feature..bugfix
: Signifying a bug fix..doc
: Signifying a documentation improvement..removal
: Signifying a deprecation or removal of public API..misc
: A ticket has been closed, but it is not of interest to users.