Download Review File Attachments as tarball

Review Request #6402 — Created Oct. 4, 2014 and submitted

adrw.hong
Review Board
master
6432
2f19217...
reviewboard, students

Download File Attachments as tarball:
- only working with files stored on host filesystem and Amazon S3.
- now compresses into .tar.gz file instead.
- properly streams compressed file back to client.
- Download Attachments and Download Diff now under Downloads
menu dropdown in review request editor.

- URL changed from: r/<requestID>/file/zip/ -> r/<requestID>/file/tar/

Tested on:
- Linux Mint 17: Chromium 37.0.2062.120
- Linux Mint 17: Firefox 32.0.3
- Windows 8.1: Chrome 38.0.2125.101
- Windows 8.1: Firefox 32.0.3
- Windows 8.1: Internet Explorer 11
Tested also with curl to see if file is streaming as expected.

Review requests with small files, large files (2mb+), and mix of both, tested and working with host file system and Amazon S3 storage option.

Description From Last Updated

Col: 80 E501 line too long (100 > 79 characters)

reviewbotreviewbot

Col: 1 E302 expected 2 blank lines, found 1

reviewbotreviewbot

local variable 'zip_path' is assigned to but never used

reviewbotreviewbot

Col: 9 E265 block comment should start with '# '

reviewbotreviewbot

Col: 19 E702 multiple statements on one line (semicolon)

reviewbotreviewbot

Col: 36 E703 statement ends with a semicolon

reviewbotreviewbot

Col: 9 E265 block comment should start with '# '

reviewbotreviewbot

local variable 'zip_filename' is assigned to but never used

reviewbotreviewbot

Col: 80 E501 line too long (82 > 79 characters)

reviewbotreviewbot

list comprehension redefines 'file_attachment' from line 621

reviewbotreviewbot

Col: 49 W291 trailing whitespace

reviewbotreviewbot

Col: 27 W291 trailing whitespace

reviewbotreviewbot

Col: 38 W291 trailing whitespace

reviewbotreviewbot

Col: 9 E113 unexpected indentation

reviewbotreviewbot

Col: 80 E501 line too long (80 > 79 characters)

reviewbotreviewbot

list comprehension redefines 'file_attachment' from line 618

reviewbotreviewbot

There should be a single blank line before a block

brenniebrennie

list comprehension redefines 'file_attachment' from line 619

reviewbotreviewbot

This is in the diff in https://reviews.reviewboard.org/r/6432/ You should mark this RR as depends on 6432 and base your RR ...

brenniebrennie

Col: 5 E303 too many blank lines (2)

reviewbotreviewbot

Col: 6 E111 indentation is not a multiple of four

reviewbotreviewbot

Col: 6 E113 unexpected indentation

reviewbotreviewbot

Col: 6 E265 block comment should start with '# '

reviewbotreviewbot

Col: 6 E303 too many blank lines (2)

reviewbotreviewbot

Col: 9 E265 block comment should start with '# '

reviewbotreviewbot

list comprehension redefines 'file_attachment' from line 629

reviewbotreviewbot

Col: 80 E501 line too long (82 > 79 characters)

reviewbotreviewbot

Col: 21 E128 continuation line under-indented for visual indent

reviewbotreviewbot

Col: 21 E128 continuation line under-indented for visual indent

reviewbotreviewbot

Col: 5 E303 too many blank lines (2)

reviewbotreviewbot

Col: 80 E501 line too long (87 > 79 characters)

reviewbotreviewbot

list comprehension redefines 'file_attachment' from line 634

reviewbotreviewbot

Col: 40 W291 trailing whitespace

reviewbotreviewbot

Col: 9 E128 continuation line under-indented for visual indent

reviewbotreviewbot

Col: 56 E502 the backslash is redundant between brackets

reviewbotreviewbot

Col: 72 E502 the backslash is redundant between brackets

reviewbotreviewbot

Col: 21 E128 continuation line under-indented for visual indent

reviewbotreviewbot

Col: 21 E128 continuation line under-indented for visual indent

reviewbotreviewbot

Col: 13 E128 continuation line under-indented for visual indent

reviewbotreviewbot

list comprehension redefines 'file_attachment' from line 635

reviewbotreviewbot

Col: 41 E502 the backslash is redundant between brackets

reviewbotreviewbot

Col: 9 E128 continuation line under-indented for visual indent

reviewbotreviewbot

Col: 13 E128 continuation line under-indented for visual indent

reviewbotreviewbot

list comprehension redefines 'file_attachment' from line 636

reviewbotreviewbot

undefined name 'eview_request_id'

reviewbotreviewbot

list comprehension redefines 'file_attachment' from line 636

reviewbotreviewbot

As mentioned earlier, can you pull the latest master and rebase?

daviddavid

One too many blank lines here.

daviddavid

You should use the 'six' version of StringIO for python 3.x compat: from django.utils.six.moves import cStringIO as StringIO ... s ...

daviddavid

Revert this change.

daviddavid

Since you only iterate over file_paths once, how about combining these? Then your loop belowe would look something like: for ...

daviddavid

Instead of hard-coding './reviewboard/htdocs' (which won't work in a deployed server), it should use siteconfig.get('site_media_root')

daviddavid

You should use os.path.join to create file paths.

brenniebrennie

You're leaking the file here. This should be: with open(file_path, 'r') as f: file_contents = f.read()

daviddavid

It's very weird to mix formatting styles here, plus you're doing two format operations where you only need one. How ...

daviddavid

Suggestion: why not bool(file_attachments)? Empty lists will evaluate to false and so will None.

brenniebrennie

list comprehension redefines 'file_attachment' from line 636

reviewbotreviewbot

Can we make the URL /file/zip/ ? That way it fits in with the existing /file/N/ URLs.

daviddavid

Is this necessary? Anything on PyPI should automatically be included...

daviddavid

Why not wrap it in () so that you don't need the \\, i.e. response['Content-Disposition'] = ( 'attachment; filename=review-attachments-%s' % ...

brenniebrennie

Shouldn't this have a .zip extension?

brenniebrennie

Docstrings should always be in this format: """Single-line summary.""" or: """Single-line summary. Multi-line description. """ with no blank line after. ...

chipx86chipx86

I'd just put .all() at the end of this one. No point doing it below.

chipx86chipx86

Blank line between these.

chipx86chipx86

Blank line between these.

chipx86chipx86

The point of the storage backends is that you should never have to do logic like this. Plus, there is ...

chipx86chipx86

As above, we're not actually taking advantage of the streaming here. What you're doing is reading everything into memory and ...

chipx86chipx86

"review-attachments" is a misleading prefix. This is for attachments on the review request, not reviews. However, we don't want the ...

chipx86chipx86

We should probably have both of these within a "Download" menu, like we have with "Update."

chipx86chipx86

I know the other links are hard-coded, but we should be using {% url download-zip-attachments review_request.display_id %} here.

chipx86chipx86

Should have a comma at end of line. That way future dependancies will only be a 1-line change.

brenniebrennie

local variable 'total_size' is assigned to but never used

reviewbotreviewbot

Col: 80 E501 line too long (80 > 79 characters)

reviewbotreviewbot

If you're only compressing as tar.gz you won't need zipstream.

brenniebrennie

'os' imported but unused

reviewbotreviewbot

local variable 'siteconfig' is assigned to but never used

reviewbotreviewbot

Col: 80 E501 line too long (80 > 79 characters)

reviewbotreviewbot

This should go under review_request_urls.

chipx86chipx86

No spaces before/after the """. This should read more like a sentence. In fact, it should really be fleshed out, ...

chipx86chipx86

I know we have some old docstrings in here, but this should be of the following format: """One-line summary (< ...

chipx86chipx86

This and all the other functions should go in a utility object that wraps tarfile.TarFile. This view should be able ...

chipx86chipx86

Docstrings shouldn't have a space after the first triple quote, e.g. """Build TarInfo...""" Same below

brenniebrennie

We should just let a link be a link. I think, given other issues reported during today's meeting, that links ...

chipx86chipx86

These don't look like they were meant to be in this change?

chipx86chipx86

undefined name 'time'

reviewbotreviewbot

This must be added alphabetically to the list of imports (after reviewboard.attachments.models.)

chipx86chipx86

How about "Downloads all file attachments on a review request as a tarball." ?

chipx86chipx86

"... are downloaded as a tarball." Not sure we need to talk about what isn't supported.

chipx86chipx86

Can you put the arguments on the first line? If they need to wrap, do: response[...] = ('...' % out_filename)

chipx86chipx86

Not sure we need this. The template can just check the file attachments list. I think we just want to ...

chipx86chipx86

I wasn't clear before, but what I meant in the previous review is that the templates can just check file_attachments, ...

chipx86chipx86

No space after """.

chipx86chipx86

Too many backticks. Generally, though, we don't add this sort of doc to the docstrings. This should be more higher-level, ...

chipx86chipx86

No need to say all the things being outputted. Just say what the function does at a high level, and ...

chipx86chipx86

These should be class-level constants.

chipx86chipx86

Will these ever be changed between instances? I can understand that it might be beneficial to keep the BLOCK_SIZE as ...

ML mloyzer

This should be written more like how you'd say it to someone. "Build a TarInfo object representing one file in ...

chipx86chipx86

This should have a try/except for each thing, since a storage backend may implement one but not the other. It ...

chipx86chipx86

We should yield before we close.

chipx86chipx86

Same comment as above. This shouldn't know how it's going to be used (the StreamingHttpResponse part). Just go into the ...

chipx86chipx86

Blank line between these.

chipx86chipx86

yield isn't a function, so you shouldn't use parens.

chipx86chipx86

Col: 80 E501 line too long (80 > 79 characters)

reviewbotreviewbot
reviewbot
  1. Tool: Pyflakes
    Processed Files:
        reviewboard/reviews/views.py
        setup.py
        reviewboard/reviews/urls.py
    
    Ignored Files:
        reviewboard/templates/reviews/review_detail.html
    
    
    
    Tool: PEP8 Style Checker
    Processed Files:
        reviewboard/reviews/views.py
        setup.py
        reviewboard/reviews/urls.py
    
    Ignored Files:
        reviewboard/templates/reviews/review_detail.html
    
    
  2. reviewboard/reviews/urls.py (Diff revision 1)
     
     
    Col: 80
     E501 line too long (100 > 79 characters)
    
  3. reviewboard/reviews/views.py (Diff revision 1)
     
     
    Col: 1
     E302 expected 2 blank lines, found 1
    
  4. reviewboard/reviews/views.py (Diff revision 1)
     
     
     local variable 'zip_path' is assigned to but never used
    
  5. reviewboard/reviews/views.py (Diff revision 1)
     
     
    Col: 9
     E265 block comment should start with '# '
    
  6. reviewboard/reviews/views.py (Diff revision 1)
     
     
    Col: 19
     E702 multiple statements on one line (semicolon)
    
  7. reviewboard/reviews/views.py (Diff revision 1)
     
     
    Col: 36
     E703 statement ends with a semicolon
    
  8. reviewboard/reviews/views.py (Diff revision 1)
     
     
    Col: 9
     E265 block comment should start with '# '
    
  9. reviewboard/reviews/views.py (Diff revision 1)
     
     
     local variable 'zip_filename' is assigned to but never used
    
  10. reviewboard/reviews/views.py (Diff revision 1)
     
     
    Col: 80
     E501 line too long (82 > 79 characters)
    
  11. reviewboard/reviews/views.py (Diff revision 1)
     
     
     list comprehension redefines 'file_attachment' from line 621
    
  12. 
      
AD
reviewbot
  1. Tool: PEP8 Style Checker
    Processed Files:
        reviewboard/reviews/views.py
        setup.py
        reviewboard/reviews/urls.py
    
    Ignored Files:
        reviewboard/templates/reviews/review_detail.html
    
    
  2. reviewboard/reviews/urls.py (Diff revision 2)
     
     
    Col: 49
     W291 trailing whitespace
    
  3. reviewboard/reviews/urls.py (Diff revision 2)
     
     
    Col: 27
     W291 trailing whitespace
    
  4. reviewboard/reviews/views.py (Diff revision 2)
     
     
    Col: 38
     W291 trailing whitespace
    
  5. reviewboard/reviews/views.py (Diff revision 2)
     
     
    Col: 9
     E113 unexpected indentation
    
  6. 
      
AD
reviewbot
  1. Tool: Pyflakes
    Processed Files:
        reviewboard/reviews/views.py
        setup.py
        reviewboard/reviews/urls.py
    
    Ignored Files:
        reviewboard/templates/reviews/review_detail.html
    
    
    
    Tool: PEP8 Style Checker
    Processed Files:
        reviewboard/reviews/views.py
        setup.py
        reviewboard/reviews/urls.py
    
    Ignored Files:
        reviewboard/templates/reviews/review_detail.html
    
    
  2. reviewboard/reviews/views.py (Diff revision 3)
     
     
    Col: 80
     E501 line too long (80 > 79 characters)
    
  3. reviewboard/reviews/views.py (Diff revision 3)
     
     
     list comprehension redefines 'file_attachment' from line 618
    
  4. 
      
AD
reviewbot
  1. Tool: PEP8 Style Checker
    Processed Files:
        reviewboard/reviews/views.py
        setup.py
        reviewboard/reviews/urls.py
    
    Ignored Files:
        reviewboard/templates/reviews/review_detail.html
    
    
    
    Tool: Pyflakes
    Processed Files:
        reviewboard/reviews/views.py
        setup.py
        reviewboard/reviews/urls.py
    
    Ignored Files:
        reviewboard/templates/reviews/review_detail.html
    
    
  2. reviewboard/reviews/views.py (Diff revision 4)
     
     
     list comprehension redefines 'file_attachment' from line 619
    
  3. 
      
ML
  1. What happens when there is one file attachment (so the 'Download File Attachments as ZIP' button is there) then delete that file? Is the button removed/disabled? I'm trying to see if there is a way to execute the code in zip_attachments on a review request without any file attachments.

    1. When you delete and haven't published it, then there is a blank ZIP file download. I'm looking into preventing that from happening.
      The button is removed when that change is published or when there are no file attachments present though.

  2. 
      
brennie
  1. 
      
  2. reviewboard/reviews/views.py (Diff revision 4)
     
     
     

    There should be a single blank line before a block

  3. Other than that, it looks good!

AD
reviewbot
  1. Tool: Pyflakes
    Processed Files:
        reviewboard/reviews/views.py
        reviewboard/admin/forms.py
        setup.py
        reviewboard/reviews/urls.py
    
    Ignored Files:
        reviewboard/templates/reviews/review_detail.html
    
    
    
    Tool: PEP8 Style Checker
    Processed Files:
        reviewboard/reviews/views.py
        reviewboard/admin/forms.py
        setup.py
        reviewboard/reviews/urls.py
    
    Ignored Files:
        reviewboard/templates/reviews/review_detail.html
    
    
  2. reviewboard/reviews/views.py (Diff revision 5)
     
     
    Col: 5
     E303 too many blank lines (2)
    
  3. reviewboard/reviews/views.py (Diff revision 5)
     
     
    Col: 6
     E111 indentation is not a multiple of four
    
  4. reviewboard/reviews/views.py (Diff revision 5)
     
     
    Col: 6
     E113 unexpected indentation
    
  5. reviewboard/reviews/views.py (Diff revision 5)
     
     
    Col: 6
     E265 block comment should start with '# '
    
  6. reviewboard/reviews/views.py (Diff revision 5)
     
     
    Col: 6
     E303 too many blank lines (2)
    
  7. reviewboard/reviews/views.py (Diff revision 5)
     
     
    Col: 9
     E265 block comment should start with '# '
    
  8. reviewboard/reviews/views.py (Diff revision 5)
     
     
     list comprehension redefines 'file_attachment' from line 629
    
  9. 
      
mike_conley
  1. 
      
  2. reviewboard/reviews/views.py (Diff revisions 4 - 5)
     
     

    Out of curiosity, why the round-trip through the StringIO?

    1. I was playing around with different implementations to see if it makes any difference.
      There was a function, I forget which, that took a file-like object as a parameter instead of bytes.

  3. 
      
brennie
  1. 
      
  2. reviewboard/admin/forms.py (Diff revision 5)
     
     
     
     

    This is in the diff in https://reviews.reviewboard.org/r/6432/

    You should mark this RR as depends on 6432 and base your RR off of that diff via:

    rbt diff dev/fix-storage-saving..dev/zip-attachments

    but using the correct branch names.

    1. Would editting it in the review request page achieve the same thing?

    2. You should rebase your development branch on an updated master since 6432 has been submitted. Then you can re-run rbt post and the change should not appear. Alternatively, you can run rbt post -X reviewboard/admin/forms.py

  3. 
      
AD
reviewbot
  1. Tool: Pyflakes
    Processed Files:
        reviewboard/reviews/views.py
        reviewboard/admin/forms.py
        setup.py
        reviewboard/reviews/urls.py
    
    Ignored Files:
        reviewboard/templates/reviews/review_detail.html
    
    
    
    Tool: PEP8 Style Checker
    Processed Files:
        reviewboard/reviews/views.py
        reviewboard/admin/forms.py
        setup.py
        reviewboard/reviews/urls.py
    
    Ignored Files:
        reviewboard/templates/reviews/review_detail.html
    
    
  2. reviewboard/reviews/views.py (Diff revision 6)
     
     
    Col: 80
     E501 line too long (82 > 79 characters)
    
  3. reviewboard/reviews/views.py (Diff revision 6)
     
     
    Col: 21
     E128 continuation line under-indented for visual indent
    
  4. reviewboard/reviews/views.py (Diff revision 6)
     
     
    Col: 21
     E128 continuation line under-indented for visual indent
    
  5. reviewboard/reviews/views.py (Diff revision 6)
     
     
    Col: 5
     E303 too many blank lines (2)
    
  6. reviewboard/reviews/views.py (Diff revision 6)
     
     
    Col: 80
     E501 line too long (87 > 79 characters)
    
  7. reviewboard/reviews/views.py (Diff revision 6)
     
     
     list comprehension redefines 'file_attachment' from line 634
    
  8. 
      
AD
reviewbot
  1. Tool: Pyflakes
    Processed Files:
        reviewboard/reviews/views.py
        reviewboard/admin/forms.py
        setup.py
        reviewboard/reviews/urls.py
    
    Ignored Files:
        reviewboard/templates/reviews/review_detail.html
    
    
    
    Tool: PEP8 Style Checker
    Processed Files:
        reviewboard/reviews/views.py
        reviewboard/admin/forms.py
        setup.py
        reviewboard/reviews/urls.py
    
    Ignored Files:
        reviewboard/templates/reviews/review_detail.html
    
    
  2. reviewboard/reviews/views.py (Diff revision 7)
     
     
    Col: 40
     W291 trailing whitespace
    
  3. reviewboard/reviews/views.py (Diff revision 7)
     
     
    Col: 9
     E128 continuation line under-indented for visual indent
    
  4. reviewboard/reviews/views.py (Diff revision 7)
     
     
    Col: 56
     E502 the backslash is redundant between brackets
    
  5. reviewboard/reviews/views.py (Diff revision 7)
     
     
    Col: 72
     E502 the backslash is redundant between brackets
    
  6. reviewboard/reviews/views.py (Diff revision 7)
     
     
    Col: 21
     E128 continuation line under-indented for visual indent
    
  7. reviewboard/reviews/views.py (Diff revision 7)
     
     
    Col: 21
     E128 continuation line under-indented for visual indent
    
  8. reviewboard/reviews/views.py (Diff revision 7)
     
     
    Col: 13
     E128 continuation line under-indented for visual indent
    
  9. reviewboard/reviews/views.py (Diff revision 7)
     
     
     list comprehension redefines 'file_attachment' from line 635
    
  10. 
      
AD
reviewbot
  1. Tool: Pyflakes
    Processed Files:
        reviewboard/reviews/views.py
        reviewboard/admin/forms.py
        setup.py
        reviewboard/reviews/urls.py
    
    Ignored Files:
        reviewboard/templates/reviews/review_detail.html
    
    
    
    Tool: PEP8 Style Checker
    Processed Files:
        reviewboard/reviews/views.py
        reviewboard/admin/forms.py
        setup.py
        reviewboard/reviews/urls.py
    
    Ignored Files:
        reviewboard/templates/reviews/review_detail.html
    
    
  2. reviewboard/reviews/views.py (Diff revision 8)
     
     
    Col: 41
     E502 the backslash is redundant between brackets
    
  3. reviewboard/reviews/views.py (Diff revision 8)
     
     
    Col: 9
     E128 continuation line under-indented for visual indent
    
  4. reviewboard/reviews/views.py (Diff revision 8)
     
     
    Col: 13
     E128 continuation line under-indented for visual indent
    
  5. reviewboard/reviews/views.py (Diff revision 8)
     
     
     list comprehension redefines 'file_attachment' from line 636
    
  6. 
      
AD
reviewbot
  1. Tool: Pyflakes
    Processed Files:
        reviewboard/reviews/views.py
        reviewboard/admin/forms.py
        setup.py
        reviewboard/reviews/urls.py
    
    Ignored Files:
        reviewboard/templates/reviews/review_detail.html
    
    
    
    Tool: PEP8 Style Checker
    Processed Files:
        reviewboard/reviews/views.py
        reviewboard/admin/forms.py
        setup.py
        reviewboard/reviews/urls.py
    
    Ignored Files:
        reviewboard/templates/reviews/review_detail.html
    
    
  2. reviewboard/reviews/views.py (Diff revision 9)
     
     
     undefined name 'eview_request_id'
    
  3. reviewboard/reviews/views.py (Diff revision 9)
     
     
     list comprehension redefines 'file_attachment' from line 636
    
  4. 
      
AD
reviewbot
  1. Tool: Pyflakes
    Processed Files:
        reviewboard/reviews/views.py
        reviewboard/admin/forms.py
        setup.py
        reviewboard/reviews/urls.py
    
    Ignored Files:
        reviewboard/templates/reviews/review_detail.html
    
    
    
    Tool: PEP8 Style Checker
    Processed Files:
        reviewboard/reviews/views.py
        reviewboard/admin/forms.py
        setup.py
        reviewboard/reviews/urls.py
    
    Ignored Files:
        reviewboard/templates/reviews/review_detail.html
    
    
  2. reviewboard/reviews/views.py (Diff revision 10)
     
     
     list comprehension redefines 'file_attachment' from line 636
    
  3. 
      
AD
brennie
  1. 
      
  2. reviewboard/reviews/views.py (Diff revision 10)
     
     

    You should use os.path.join to create file paths.

  3. reviewboard/reviews/views.py (Diff revision 10)
     
     

    Suggestion: why not bool(file_attachments)?

    Empty lists will evaluate to false and so will None.

  4. 
      
david
  1. 
      
  2. reviewboard/admin/forms.py (Diff revision 10)
     
     
     

    As mentioned earlier, can you pull the latest master and rebase?

  3. reviewboard/reviews/views.py (Diff revision 10)
     
     

    One too many blank lines here.

  4. reviewboard/reviews/views.py (Diff revision 10)
     
     

    You should use the 'six' version of StringIO for python 3.x compat:

    from django.utils.six.moves import cStringIO as StringIO
    
    ...
    
    s = StringIO()
    
  5. reviewboard/reviews/views.py (Diff revision 10)
     
     

    Revert this change.

  6. reviewboard/reviews/views.py (Diff revision 10)
     
     

    Since you only iterate over file_paths once, how about combining these? Then your loop belowe would look something like:

    for fa in file_attachments.all():
        file_dir, file_name = os.path.split(fa.file.url)
        ...
    
  7. reviewboard/reviews/views.py (Diff revision 10)
     
     

    Instead of hard-coding './reviewboard/htdocs' (which won't work in a deployed server), it should use siteconfig.get('site_media_root')

  8. reviewboard/reviews/views.py (Diff revision 10)
     
     

    You're leaking the file here. This should be:

    with open(file_path, 'r') as f:
        file_contents = f.read()
    
  9. reviewboard/reviews/views.py (Diff revision 10)
     
     
     

    It's very weird to mix formatting styles here, plus you're doing two format operations where you only need one. How about just:

    response['Content-Disposition'] = (
        'attachment; filename=review-attachments-%s'
        % review_request_id)
    
  10. Can we make the URL /file/zip/ ? That way it fits in with the existing /file/N/ URLs.

  11. setup.py (Diff revision 10)
     
     

    Is this necessary? Anything on PyPI should automatically be included...

  12. 
      
AD
reviewbot
  1. Tool: Pyflakes
    Processed Files:
        reviewboard/reviews/views.py
        setup.py
        reviewboard/reviews/urls.py
    
    Ignored Files:
        reviewboard/templates/reviews/review_detail.html
    
    
    
    Tool: PEP8 Style Checker
    Processed Files:
        reviewboard/reviews/views.py
        setup.py
        reviewboard/reviews/urls.py
    
    Ignored Files:
        reviewboard/templates/reviews/review_detail.html
    
    
  2. 
      
brennie
  1. 
      
  2. reviewboard/reviews/views.py (Diff revision 11)
     
     
     

    Why not wrap it in () so that you don't need the \\, i.e.

    response['Content-Disposition'] = (
        'attachment; filename=review-attachments-%s' % review_request_id)
    
    1. EDIT: removed -- wrong comment.

  3. reviewboard/reviews/views.py (Diff revision 11)
     
     

    Shouldn't this have a .zip extension?

    1. Good catch! Thanks for pointing that out. The browser automatically knew it was a zip so I assumed it was all good there.

  4. 
      
AD
reviewbot
  1. Tool: Pyflakes
    Processed Files:
        reviewboard/reviews/views.py
        setup.py
        reviewboard/reviews/urls.py
    
    Ignored Files:
        reviewboard/templates/reviews/review_detail.html
    
    
    
    Tool: PEP8 Style Checker
    Processed Files:
        reviewboard/reviews/views.py
        setup.py
        reviewboard/reviews/urls.py
    
    Ignored Files:
        reviewboard/templates/reviews/review_detail.html
    
    
  2. 
      
brennie
  1. 
      
  2. setup.py (Diff revision 12)
     
     

    Should have a comma at end of line. That way future dependancies will only be a 1-line change.

  3. 
      
dkus
  1. 
      
  2. reviewboard/reviews/views.py (Diff revision 12)
     
     

    I am not too familiar with how python handles file IO, but shouldn't the file be read / written in chunks? What happens if the file is really big (many gbs), will this put all of it into memory?

  3. 
      
chipx86
  1. I have some comments about the design below. The summary is that this code doesn't actually stream zip files. Try attaching a large file (hundreds of megs or more), add some printing, open the browser's Network tab, open a process monitor to look at the dev server's memory usage, and then download. See what happens.

    I know we discussed using zipstream before, but it looks like we don't actually need to add yet another dependency. Check this out and see if this solution works (once all the notes are addressed and confirmed to work): http://stackoverflow.com/a/24173992

    1. Okay, I've looked into this further, and now I understand better. There's no way to chunk-write to a ZipFile instance without rewriting part of ZipFile itself.

      Given that, ZipStream does do what we want, and does it how we'd do it, so that's worth using.

  2. reviewboard/reviews/views.py (Diff revision 12)
     
     
     
     
     
     

    Docstrings should always be in this format:

    """Single-line summary."""
    

    or:

    """Single-line summary.
    
    Multi-line description.
    """
    

    with no blank line after.

    The total line length must not exceed 79 characters.

  3. reviewboard/reviews/views.py (Diff revision 12)
     
     

    I'd just put .all() at the end of this one. No point doing it below.

  4. reviewboard/reviews/views.py (Diff revision 12)
     
     
     

    Blank line between these.

  5. reviewboard/reviews/views.py (Diff revision 12)
     
     
     

    Blank line between these.

  6. reviewboard/reviews/views.py (Diff revision 12)
     
     
     
     
     
     
     
     
     

    The point of the storage backends is that you should never have to do logic like this. Plus, there is absolutely no guarantee that the path from the storage backend will be a URL that you can even directly read from. Even on S3, there may be an access token required to get the URL, breaking this urlopen.

    You should be able to do fa.file.open and read from that directly. The storage backend will handle the opening and reading.

    You also should not be reading the entire file in one go. The file could be 500MB in size, and you'll end up destroying the Apache process by reading it all in one go. Instead, you need to use the chunks() function, writing to the HTTP stream as you go.

    Look at the File documentation: https://docs.djangoproject.com/en/dev/ref/files/file/#django.core.files.File

  7. reviewboard/reviews/views.py (Diff revision 12)
     
     

    As above, we're not actually taking advantage of the streaming here. What you're doing is reading everything into memory and then writing it all at once, which will blow up Apache and time out the request.

    Read through the docs on StreamingHttpResponse: https://docs.djangoproject.com/en/1.6/ref/request-response/#streaminghttpresponse-objects

    Note the first bullet point. My understanding is that you just want to pass zf to StreamingHTTPResponse and then begin writing the chunks to it, instead of to a StringIO.

  8. reviewboard/reviews/views.py (Diff revision 12)
     
     

    "review-attachments" is a misleading prefix. This is for attachments on the review request, not reviews. However, we don't want the filename to be too long.

    How about just r%d-attachments.zip?

  9. reviewboard/templates/reviews/review_detail.html (Diff revision 12)
     
     
     
     
     
     
     

    We should probably have both of these within a "Download" menu, like we have with "Update."

  10. I know the other links are hard-coded, but we should be using {% url download-zip-attachments review_request.display_id %} here.

  11. 
      
AD
reviewbot
  1. Tool: PEP8 Style Checker
    Processed Files:
        reviewboard/reviews/views.py
        setup.py
        reviewboard/reviews/urls.py
    
    Ignored Files:
        reviewboard/templates/reviews/review_detail.html
        reviewboard/templates/reviews/review_request_actions_primary.html
        reviewboard/static/rb/js/views/reviewRequestEditorView.js
    
    
    
    Tool: Pyflakes
    Processed Files:
        reviewboard/reviews/views.py
        setup.py
        reviewboard/reviews/urls.py
    
    Ignored Files:
        reviewboard/templates/reviews/review_detail.html
        reviewboard/templates/reviews/review_request_actions_primary.html
        reviewboard/static/rb/js/views/reviewRequestEditorView.js
    
    
  2. reviewboard/reviews/views.py (Diff revision 13)
     
     
     local variable 'total_size' is assigned to but never used
    
  3. reviewboard/reviews/views.py (Diff revision 13)
     
     
    Col: 80
     E501 line too long (80 > 79 characters)
    
  4. 
      
AD
brennie
  1. 
      
  2. setup.py (Diff revision 13)
     
     

    If you're only compressing as tar.gz you won't need zipstream.

  3. 
      
AD
reviewbot
  1. Tool: Pyflakes
    Processed Files:
        reviewboard/reviews/views.py
        setup.py
        reviewboard/reviews/urls.py
    
    Ignored Files:
        reviewboard/templates/reviews/review_detail.html
        reviewboard/templates/reviews/review_request_actions_primary.html
        reviewboard/static/rb/js/views/reviewRequestEditorView.js
    
    
    
    Tool: PEP8 Style Checker
    Processed Files:
        reviewboard/reviews/views.py
        setup.py
        reviewboard/reviews/urls.py
    
    Ignored Files:
        reviewboard/templates/reviews/review_detail.html
        reviewboard/templates/reviews/review_request_actions_primary.html
        reviewboard/static/rb/js/views/reviewRequestEditorView.js
    
    
  2. reviewboard/reviews/views.py (Diff revision 14)
     
     
     'os' imported but unused
    
  3. reviewboard/reviews/views.py (Diff revision 14)
     
     
     local variable 'siteconfig' is assigned to but never used
    
  4. reviewboard/reviews/views.py (Diff revision 14)
     
     
    Col: 80
     E501 line too long (80 > 79 characters)
    
  5. 
      
AD
reviewbot
  1. Tool: Pyflakes
    Processed Files:
        reviewboard/reviews/views.py
        setup.py
        reviewboard/reviews/urls.py
    
    Ignored Files:
        reviewboard/templates/reviews/review_detail.html
        reviewboard/templates/reviews/review_request_actions_primary.html
        reviewboard/static/rb/js/views/reviewRequestEditorView.js
    
    
    
    Tool: PEP8 Style Checker
    Processed Files:
        reviewboard/reviews/views.py
        setup.py
        reviewboard/reviews/urls.py
    
    Ignored Files:
        reviewboard/templates/reviews/review_detail.html
        reviewboard/templates/reviews/review_request_actions_primary.html
        reviewboard/static/rb/js/views/reviewRequestEditorView.js
    
    
  2. 
      
chipx86
  1. 
      
  2. reviewboard/reviews/urls.py (Diff revision 15)
     
     
     
     

    This should go under review_request_urls.

  3. reviewboard/reviews/views.py (Diff revision 15)
     
     

    No spaces before/after the """.

    This should read more like a sentence. In fact, it should really be fleshed out, to full human-readable documentation, covering what this is, what it does, how, and why.

  4. reviewboard/reviews/views.py (Diff revision 15)
     
     
     
     
     

    I know we have some old docstrings in here, but this should be of the following format:

    """One-line summary (< 79 chars in line length).
    
    Multi-line description.
    """
    
  5. reviewboard/reviews/views.py (Diff revision 15)
     
     

    This and all the other functions should go in a utility object that wraps tarfile.TarFile. This view should be able to very simply instantiate a thing and return the result.

    I'd recommend that this go in reviewboard/attachments/streaming_tarfile.py.

  6. We should just let a link be a link.

    I think, given other issues reported during today's meeting, that links on the navbar are failing to just work, because we're blocking them. We should fix that instead of work around it here, since this only solves the one case.

  7. setup.py (Diff revision 15)
     
     
     
     

    These don't look like they were meant to be in this change?

  8. 
      
brennie
  1. 
      
  2. reviewboard/reviews/views.py (Diff revision 15)
     
     

    Docstrings shouldn't have a space after the first triple quote, e.g.

    """Build TarInfo..."""
    

    Same below

  3. 
      
AD
reviewbot
  1. Tool: Pyflakes
    Processed Files:
        reviewboard/reviews/views.py
        reviewboard/reviews/urls.py
    
    Ignored Files:
        reviewboard/templates/reviews/review_detail.html
        reviewboard/templates/reviews/review_request_actions_primary.html
        reviewboard/static/rb/js/views/reviewRequestEditorView.js
    
    
    
    Tool: PEP8 Style Checker
    Processed Files:
        reviewboard/reviews/views.py
        reviewboard/reviews/urls.py
    
    Ignored Files:
        reviewboard/templates/reviews/review_detail.html
        reviewboard/templates/reviews/review_request_actions_primary.html
        reviewboard/static/rb/js/views/reviewRequestEditorView.js
    
    
  2. reviewboard/reviews/views.py (Diff revision 16)
     
     
     undefined name 'time'
    
  3. 
      
AD
reviewbot
  1. Tool: PEP8 Style Checker
    Processed Files:
        reviewboard/reviews/views.py
        reviewboard/reviews/urls.py
    
    Ignored Files:
        reviewboard/templates/reviews/review_detail.html
        reviewboard/templates/reviews/review_request_actions_primary.html
        reviewboard/static/rb/js/views/reviewRequestEditorView.js
    
    
    
    Tool: Pyflakes
    Processed Files:
        reviewboard/reviews/views.py
        reviewboard/reviews/urls.py
    
    Ignored Files:
        reviewboard/templates/reviews/review_detail.html
        reviewboard/templates/reviews/review_request_actions_primary.html
        reviewboard/static/rb/js/views/reviewRequestEditorView.js
    
    
  2. 
      
chipx86
  1. 
      
  2. reviewboard/reviews/views.py (Diff revision 17)
     
     

    This must be added alphabetically to the list of imports (after reviewboard.attachments.models.)

  3. reviewboard/reviews/views.py (Diff revision 17)
     
     

    How about "Downloads all file attachments on a review request as a tarball." ?

  4. reviewboard/reviews/views.py (Diff revision 17)
     
     

    "... are downloaded as a tarball."

    Not sure we need to talk about what isn't supported.

  5. reviewboard/reviews/views.py (Diff revision 17)
     
     
     

    Can you put the arguments on the first line? If they need to wrap, do:

    response[...] = ('...'
                     % out_filename)
    
  6. reviewboard/reviews/views.py (Diff revision 17)
     
     

    Not sure we need this. The template can just check the file attachments list.

    I think we just want to check latest_file_attachments though, not file_attachments. The latter contains discarded ones as well.

  7. 
      
chipx86
  1. Looks like this is missing the implementation of the streaming tar class.

  2. 
      
AD
reviewbot
  1. Tool: Pyflakes
    Processed Files:
        reviewboard/reviews/views.py
        reviewboard/attachments/streaming_tarfile.py
        reviewboard/reviews/urls.py
    
    Ignored Files:
        reviewboard/templates/reviews/review_detail.html
        reviewboard/templates/reviews/review_request_actions_primary.html
        reviewboard/static/rb/js/views/reviewRequestEditorView.js
    
    
    
    Tool: PEP8 Style Checker
    Processed Files:
        reviewboard/reviews/views.py
        reviewboard/attachments/streaming_tarfile.py
        reviewboard/reviews/urls.py
    
    Ignored Files:
        reviewboard/templates/reviews/review_detail.html
        reviewboard/templates/reviews/review_request_actions_primary.html
        reviewboard/static/rb/js/views/reviewRequestEditorView.js
    
    
  2. reviewboard/reviews/views.py (Diff revision 18)
     
     
    Col: 80
     E501 line too long (80 > 79 characters)
    
  3. 
      
chipx86
  1. 
      
  2. reviewboard/reviews/views.py (Diff revisions 17 - 18)
     
     

    I wasn't clear before, but what I meant in the previous review is that the templates can just check file_attachments, since that will evaluate to a boolean. There's no need to have a separate boolean.

    We have this for has_diffs because it's a much more complex expression not easily represented in templates.

  3. No space after """.

  4. Too many backticks.

    Generally, though, we don't add this sort of doc to the docstrings. This should be more higher-level, as in what the class does, as if you were explaining to someone who hasn't seen the source code but just wants a conceptual overview.

    You don't need to document the arguments.

    Actually, given that this is for streaming, why do we even care about the out filename? Is that even used for anything at all? I thought we weren't writing to disk.

  5. No need to say all the things being outputted. Just say what the function does at a high level, and what the parameters are like. We don't need to go into the internal details.

  6. reviewboard/attachments/streaming_tarfile.py (Diff revision 18)
     
     
     

    These should be class-level constants.

  7. This should be written more like how you'd say it to someone. "Build a TarInfo object representing one file in a tarball."

  8. reviewboard/attachments/streaming_tarfile.py (Diff revision 18)
     
     
     
     
     
     

    This should have a try/except for each thing, since a storage backend may implement one but not the other.

    It should also explicitly check for a NotImplementedError. We want to know about other exception types.

    No blank line before the except.

  9. reviewboard/attachments/streaming_tarfile.py (Diff revision 18)
     
     
     

    We should yield before we close.

  10. Same comment as above.

    This shouldn't know how it's going to be used (the StreamingHttpResponse part). Just go into the higher-level bits, like:

    """Generates the tarball containing the file, in chunks.
    
    This will build the contents of the tarball, yielding chunks
    as they're generated.
    """
    
  11. reviewboard/attachments/streaming_tarfile.py (Diff revision 18)
     
     
     

    Blank line between these.

  12. yield isn't a function, so you shouldn't use parens.

  13. 
      
ML
  1. 
      
  2. reviewboard/attachments/streaming_tarfile.py (Diff revision 18)
     
     
     

    Will these ever be changed between instances? I can understand that it might be beneficial to keep the BLOCK_SIZE as an instance variable but would it make sense to put MODE as a class variable?

    I guess part of the reason is because they are assigned constants, can't be given inputs in the init function and there are no setters for them either.

    Secondly, can you maybe describe what MODE = 0644 means?

  3. 
      
AD
Review request changed

Status: Closed (submitted)

Change Summary:

Pushed to ucosp/adrw.hong/download-attachments (b133167)
Loading...