Skip to content

Commit 35fd658

Browse files
dstufftewdurbin
andauthored
Stream JSON responses (#11744)
* Stream responses * fix tests * lint Co-authored-by: Ee Durbin <[email protected]>
1 parent 3da3871 commit 35fd658

File tree

2 files changed

+195
-165
lines changed

2 files changed

+195
-165
lines changed

tests/unit/legacy/api/test_json.py

Lines changed: 180 additions & 162 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
# See the License for the specific language governing permissions and
1111
# limitations under the License.
1212

13+
import json as _json
14+
1315
import pretend
1416

1517
from pyramid.httpexceptions import HTTPMovedPermanently, HTTPNotFound
@@ -299,7 +301,7 @@ def test_detail_renders(self, pyramid_config, db_request, db_session):
299301

300302
db_request.route_url = pretend.call_recorder(lambda *args, **kw: url)
301303

302-
result = json.json_release(releases[3], db_request)
304+
json.json_release(releases[3], db_request)
303305

304306
assert set(db_request.route_url.calls) == {
305307
pretend.call("packaging.file", path=files[0].path),
@@ -315,86 +317,115 @@ def test_detail_renders(self, pyramid_config, db_request, db_session):
315317
_assert_has_cors_headers(db_request.response.headers)
316318
assert db_request.response.headers["X-PyPI-Last-Serial"] == str(je.id)
317319

318-
assert result == {
319-
"info": {
320-
"author": None,
321-
"author_email": None,
322-
"bugtrack_url": None,
323-
"classifiers": [],
324-
"description_content_type": description_content_type,
325-
"description": releases[-1].description.raw,
326-
"docs_url": "/the/fake/url/",
327-
"download_url": None,
328-
"downloads": {"last_day": -1, "last_week": -1, "last_month": -1},
329-
"home_page": None,
330-
"keywords": None,
331-
"license": None,
332-
"maintainer": None,
333-
"maintainer_email": None,
334-
"name": project.name,
335-
"package_url": "/the/fake/url/",
336-
"platform": None,
337-
"project_url": "/the/fake/url/",
338-
"project_urls": expected_urls,
339-
"release_url": "/the/fake/url/",
340-
"requires_dist": None,
341-
"requires_python": None,
342-
"summary": None,
343-
"yanked": False,
344-
"yanked_reason": None,
345-
"version": "3.0",
346-
},
347-
"releases": {
348-
"0.1": [],
349-
"1.0": [
350-
{
351-
"comment_text": None,
352-
"downloads": -1,
353-
"filename": files[0].filename,
354-
"has_sig": True,
355-
"md5_digest": files[0].md5_digest,
356-
"digests": {
357-
"md5": files[0].md5_digest,
358-
"sha256": files[0].sha256_digest,
359-
},
360-
"packagetype": None,
361-
"python_version": "source",
362-
"size": 200,
363-
"upload_time": files[0].upload_time.strftime(
364-
"%Y-%m-%dT%H:%M:%S"
365-
),
366-
"upload_time_iso_8601": files[0].upload_time.isoformat() + "Z",
367-
"url": "/the/fake/url/",
368-
"requires_python": None,
369-
"yanked": False,
370-
"yanked_reason": None,
371-
}
372-
],
373-
"2.0": [
374-
{
375-
"comment_text": None,
376-
"downloads": -1,
377-
"filename": files[1].filename,
378-
"has_sig": True,
379-
"md5_digest": files[1].md5_digest,
380-
"digests": {
381-
"md5": files[1].md5_digest,
382-
"sha256": files[1].sha256_digest,
383-
},
384-
"packagetype": None,
385-
"python_version": "source",
386-
"size": 200,
387-
"upload_time": files[1].upload_time.strftime(
388-
"%Y-%m-%dT%H:%M:%S"
389-
),
390-
"upload_time_iso_8601": files[1].upload_time.isoformat() + "Z",
391-
"url": "/the/fake/url/",
392-
"requires_python": None,
393-
"yanked": False,
394-
"yanked_reason": None,
395-
}
396-
],
397-
"3.0": [
320+
assert db_request.response.body == _json.dumps(
321+
{
322+
"info": {
323+
"author": None,
324+
"author_email": None,
325+
"bugtrack_url": None,
326+
"classifiers": [],
327+
"description_content_type": description_content_type,
328+
"description": releases[-1].description.raw,
329+
"docs_url": "/the/fake/url/",
330+
"download_url": None,
331+
"downloads": {"last_day": -1, "last_week": -1, "last_month": -1},
332+
"home_page": None,
333+
"keywords": None,
334+
"license": None,
335+
"maintainer": None,
336+
"maintainer_email": None,
337+
"name": project.name,
338+
"package_url": "/the/fake/url/",
339+
"platform": None,
340+
"project_url": "/the/fake/url/",
341+
"project_urls": expected_urls,
342+
"release_url": "/the/fake/url/",
343+
"requires_dist": None,
344+
"requires_python": None,
345+
"summary": None,
346+
"yanked": False,
347+
"yanked_reason": None,
348+
"version": "3.0",
349+
},
350+
"releases": {
351+
"0.1": [],
352+
"1.0": [
353+
{
354+
"comment_text": None,
355+
"downloads": -1,
356+
"filename": files[0].filename,
357+
"has_sig": True,
358+
"md5_digest": files[0].md5_digest,
359+
"digests": {
360+
"md5": files[0].md5_digest,
361+
"sha256": files[0].sha256_digest,
362+
},
363+
"packagetype": None,
364+
"python_version": "source",
365+
"size": 200,
366+
"upload_time": files[0].upload_time.strftime(
367+
"%Y-%m-%dT%H:%M:%S"
368+
),
369+
"upload_time_iso_8601": files[0].upload_time.isoformat()
370+
+ "Z",
371+
"url": "/the/fake/url/",
372+
"requires_python": None,
373+
"yanked": False,
374+
"yanked_reason": None,
375+
}
376+
],
377+
"2.0": [
378+
{
379+
"comment_text": None,
380+
"downloads": -1,
381+
"filename": files[1].filename,
382+
"has_sig": True,
383+
"md5_digest": files[1].md5_digest,
384+
"digests": {
385+
"md5": files[1].md5_digest,
386+
"sha256": files[1].sha256_digest,
387+
},
388+
"packagetype": None,
389+
"python_version": "source",
390+
"size": 200,
391+
"upload_time": files[1].upload_time.strftime(
392+
"%Y-%m-%dT%H:%M:%S"
393+
),
394+
"upload_time_iso_8601": files[1].upload_time.isoformat()
395+
+ "Z",
396+
"url": "/the/fake/url/",
397+
"requires_python": None,
398+
"yanked": False,
399+
"yanked_reason": None,
400+
}
401+
],
402+
"3.0": [
403+
{
404+
"comment_text": None,
405+
"downloads": -1,
406+
"filename": files[2].filename,
407+
"has_sig": True,
408+
"md5_digest": files[2].md5_digest,
409+
"digests": {
410+
"md5": files[2].md5_digest,
411+
"sha256": files[2].sha256_digest,
412+
},
413+
"packagetype": None,
414+
"python_version": "source",
415+
"size": 200,
416+
"upload_time": files[2].upload_time.strftime(
417+
"%Y-%m-%dT%H:%M:%S"
418+
),
419+
"upload_time_iso_8601": files[2].upload_time.isoformat()
420+
+ "Z",
421+
"url": "/the/fake/url/",
422+
"requires_python": None,
423+
"yanked": False,
424+
"yanked_reason": None,
425+
}
426+
],
427+
},
428+
"urls": [
398429
{
399430
"comment_text": None,
400431
"downloads": -1,
@@ -418,32 +449,11 @@ def test_detail_renders(self, pyramid_config, db_request, db_session):
418449
"yanked_reason": None,
419450
}
420451
],
452+
"last_serial": je.id,
453+
"vulnerabilities": [],
421454
},
422-
"urls": [
423-
{
424-
"comment_text": None,
425-
"downloads": -1,
426-
"filename": files[2].filename,
427-
"has_sig": True,
428-
"md5_digest": files[2].md5_digest,
429-
"digests": {
430-
"md5": files[2].md5_digest,
431-
"sha256": files[2].sha256_digest,
432-
},
433-
"packagetype": None,
434-
"python_version": "source",
435-
"size": 200,
436-
"upload_time": files[2].upload_time.strftime("%Y-%m-%dT%H:%M:%S"),
437-
"upload_time_iso_8601": files[2].upload_time.isoformat() + "Z",
438-
"url": "/the/fake/url/",
439-
"requires_python": None,
440-
"yanked": False,
441-
"yanked_reason": None,
442-
}
443-
],
444-
"last_serial": je.id,
445-
"vulnerabilities": [],
446-
}
455+
sort_keys=True,
456+
).encode("utf8")
447457

448458
def test_minimal_renders(self, pyramid_config, db_request):
449459
project = ProjectFactory.create(has_docs=False)
@@ -463,7 +473,7 @@ def test_minimal_renders(self, pyramid_config, db_request):
463473
url = "/the/fake/url/"
464474
db_request.route_url = pretend.call_recorder(lambda *args, **kw: url)
465475

466-
result = json.json_release(release, db_request)
476+
json.json_release(release, db_request)
467477

468478
assert set(db_request.route_url.calls) == {
469479
pretend.call("packaging.file", path=file.path),
@@ -476,37 +486,63 @@ def test_minimal_renders(self, pyramid_config, db_request):
476486
_assert_has_cors_headers(db_request.response.headers)
477487
assert db_request.response.headers["X-PyPI-Last-Serial"] == str(je.id)
478488

479-
assert result == {
480-
"info": {
481-
"author": None,
482-
"author_email": None,
483-
"bugtrack_url": None,
484-
"classifiers": [],
485-
"description_content_type": release.description.content_type,
486-
"description": release.description.raw,
487-
"docs_url": None,
488-
"download_url": None,
489-
"downloads": {"last_day": -1, "last_week": -1, "last_month": -1},
490-
"home_page": None,
491-
"keywords": None,
492-
"license": None,
493-
"maintainer": None,
494-
"maintainer_email": None,
495-
"name": project.name,
496-
"package_url": "/the/fake/url/",
497-
"platform": None,
498-
"project_url": "/the/fake/url/",
499-
"project_urls": None,
500-
"release_url": "/the/fake/url/",
501-
"requires_dist": None,
502-
"requires_python": None,
503-
"summary": None,
504-
"yanked": False,
505-
"yanked_reason": None,
506-
"version": "0.1",
507-
},
508-
"releases": {
509-
"0.1": [
489+
assert db_request.response.body == _json.dumps(
490+
{
491+
"info": {
492+
"author": None,
493+
"author_email": None,
494+
"bugtrack_url": None,
495+
"classifiers": [],
496+
"description_content_type": release.description.content_type,
497+
"description": release.description.raw,
498+
"docs_url": None,
499+
"download_url": None,
500+
"downloads": {"last_day": -1, "last_week": -1, "last_month": -1},
501+
"home_page": None,
502+
"keywords": None,
503+
"license": None,
504+
"maintainer": None,
505+
"maintainer_email": None,
506+
"name": project.name,
507+
"package_url": "/the/fake/url/",
508+
"platform": None,
509+
"project_url": "/the/fake/url/",
510+
"project_urls": None,
511+
"release_url": "/the/fake/url/",
512+
"requires_dist": None,
513+
"requires_python": None,
514+
"summary": None,
515+
"yanked": False,
516+
"yanked_reason": None,
517+
"version": "0.1",
518+
},
519+
"releases": {
520+
"0.1": [
521+
{
522+
"comment_text": None,
523+
"downloads": -1,
524+
"filename": file.filename,
525+
"has_sig": True,
526+
"md5_digest": file.md5_digest,
527+
"digests": {
528+
"md5": file.md5_digest,
529+
"sha256": file.sha256_digest,
530+
},
531+
"packagetype": None,
532+
"python_version": "source",
533+
"size": 200,
534+
"upload_time": file.upload_time.strftime(
535+
"%Y-%m-%dT%H:%M:%S"
536+
),
537+
"upload_time_iso_8601": file.upload_time.isoformat() + "Z",
538+
"url": "/the/fake/url/",
539+
"requires_python": None,
540+
"yanked": False,
541+
"yanked_reason": None,
542+
}
543+
]
544+
},
545+
"urls": [
510546
{
511547
"comment_text": None,
512548
"downloads": -1,
@@ -527,30 +563,12 @@ def test_minimal_renders(self, pyramid_config, db_request):
527563
"yanked": False,
528564
"yanked_reason": None,
529565
}
530-
]
566+
],
567+
"last_serial": je.id,
568+
"vulnerabilities": [],
531569
},
532-
"urls": [
533-
{
534-
"comment_text": None,
535-
"downloads": -1,
536-
"filename": file.filename,
537-
"has_sig": True,
538-
"md5_digest": file.md5_digest,
539-
"digests": {"md5": file.md5_digest, "sha256": file.sha256_digest},
540-
"packagetype": None,
541-
"python_version": "source",
542-
"size": 200,
543-
"upload_time": file.upload_time.strftime("%Y-%m-%dT%H:%M:%S"),
544-
"upload_time_iso_8601": file.upload_time.isoformat() + "Z",
545-
"url": "/the/fake/url/",
546-
"requires_python": None,
547-
"yanked": False,
548-
"yanked_reason": None,
549-
}
550-
],
551-
"last_serial": je.id,
552-
"vulnerabilities": [],
553-
}
570+
sort_keys=True,
571+
).encode("utf8")
554572

555573
def test_vulnerabilities_renders(self, pyramid_config, db_request):
556574
project = ProjectFactory.create(has_docs=False)
@@ -570,7 +588,7 @@ def test_vulnerabilities_renders(self, pyramid_config, db_request):
570588

571589
result = json.json_release(release, db_request)
572590

573-
assert result["vulnerabilities"] == [
591+
assert _json.loads(result.body)["vulnerabilities"] == [
574592
{
575593
"id": "PYSEC-001",
576594
"source": "the source",

0 commit comments

Comments
 (0)