{"action":"created","comment":{"url":"https://api.github.com/repos/python/cpython/pulls/comments/194913677","pull_request_review_id":128178580,"id":194913677,"node_id":"MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDE5NDkxMzY3Nw==","diff_hunk":"@@ -72,14 +83,124 @@ class RegistryError(Exception):\n     \"\"\"Raised when a registry operation with the archiving\n     and unpacking registries fails\"\"\"\n \n+class _GiveupOnFastCopy(Exception):\n+    \"\"\"Raised as a signal to fallback on using raw read()/write()\n+    file copy when fast-copy functions fail to do so.\n+    \"\"\"\n+\n+def _fastcopy_osx(fsrc, fdst, flags):\n+    \"\"\"Copy a regular file content or metadata by using high-performance\n+    fcopyfile(3) syscall (OSX).\n+    \"\"\"\n+    try:\n+        infd = fsrc.fileno()\n+        outfd = fdst.fileno()\n+    except Exception as err:\n+        raise _GiveupOnFastCopy(err)  # not a regular file\n+\n+    try:\n+        posix._fcopyfile(infd, outfd, flags)\n+    except OSError as err:\n+        err.filename = fsrc.name\n+        err.filename2 = fdst.name\n+        if err.errno in {errno.EINVAL, errno.ENOTSUP}:\n+            raise _GiveupOnFastCopy(err)\n+        else:\n+            raise err from None\n+\n+def _fastcopy_sendfile(fsrc, fdst):\n+    \"\"\"Copy data from one regular mmap-like fd to another by using\n+    high-performance sendfile(2) syscall.\n+    This should work on Linux >= 2.6.33 and Solaris only.\n+    \"\"\"\n+    # Note: copyfileobj() is left alone in order to not introduce any\n+    # unexpected breakage. Possible risks by using zero-copy calls\n+    # in copyfileobj() are:\n+    # - fdst cannot be open in \"a\"(ppend) mode\n+    # - fsrc and fdst may be open in \"t\"(ext) mode\n+    # - fsrc may be a BufferedReader (which hides unread data in a buffer),\n+    #   GzipFile (which decompresses data), HTTPResponse (which decodes\n+    #   chunks).\n+    # - possibly others (e.g. encrypted fs/partition?)\n+    global _HAS_SENDFILE\n+    try:\n+        infd = fsrc.fileno()\n+        outfd = fdst.fileno()\n+    except Exception as err:\n+        raise _GiveupOnFastCopy(err)  # not a regular file\n+\n+    # Hopefully the whole file will be copied in a single call.\n+    # sendfile() is called in a loop 'till EOF is reached (0 return)\n+    # so a bufsize smaller or bigger than the actual file size\n+    # should not make any difference, also in case the file content\n+    # changes while being copied.\n+    try:\n+        blocksize = max(os.fstat(infd).st_size, 2 ** 23)  # min 8MB\n+    except Exception:\n+        blocksize = 2 ** 27  # 128MB\n+\n+    offset = 0\n+    while True:\n+        try:\n+            sent = os.sendfile(outfd, infd, offset, blocksize)\n+        except OSError as err:\n+            # ...in oder to have a more informative exception.\n+            err.filename = fsrc.name\n+            err.filename2 = fdst.name\n+\n+            if err.errno == errno.ENOTSOCK:\n+                # sendfile() on this platform (probably Linux < 2.6.33)\n+                # does not support copies between regular files (only\n+                # sockets).\n+                _HAS_SENDFILE = False\n+                raise _GiveupOnFastCopy(err)\n+\n+            if err.errno == errno.ENOSPC:  # filesystem is full\n+                raise err from None\n+\n+            # Give up on first call and if no data was copied.\n+            if offset == 0 and os.lseek(outfd, 0, os.SEEK_CUR) == 0:\n+                raise _GiveupOnFastCopy(err)\n+\n+            raise err\n+        else:\n+            if sent == 0:\n+                break  # EOF\n+            offset += sent\n+\n+def _copybinfileobj(fsrc, fdst, length=COPY_BUFSIZE):\n+    \"\"\"Copy 2 regular file objects open in binary mode.\"\"\"\n+    # Localize variable access to minimize overhead.\n+    fsrc_readinto = fsrc.readinto\n+    fdst_write = fdst.write\n+    with memoryview(bytearray(length)) as mv:\n+        while True:\n+            n = fsrc_readinto(mv)\n+            if not n:\n+                break\n+            elif n < length:\n+                fdst_write(mv[:n])\n+            else:\n+                fdst_write(mv)\n+\n+def _is_binary_files_pair(fsrc, fdst):\n+    return hasattr(fsrc, 'readinto') and \\\n+        isinstance(fsrc, io.BytesIO) or 'b' in getattr(fsrc, 'mode', '') and \\","path":"Lib/shutil.py","position":131,"original_position":131,"commit_id":"07bcef5df6766454980f83c4668d0f2bbfece891","original_commit_id":"07bcef5df6766454980f83c4668d0f2bbfece891","user":{"login":"giampaolo","id":681260,"node_id":"MDQ6VXNlcjY4MTI2MA==","avatar_url":"https://avatars2.githubusercontent.com/u/681260?v=4","gravatar_id":"","url":"https://api.github.com/users/giampaolo","html_url":"https://github.com/giampaolo","followers_url":"https://api.github.com/users/giampaolo/followers","following_url":"https://api.github.com/users/giampaolo/following{/other_user}","gists_url":"https://api.github.com/users/giampaolo/gists{/gist_id}","starred_url":"https://api.github.com/users/giampaolo/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/giampaolo/subscriptions","organizations_url":"https://api.github.com/users/giampaolo/orgs","repos_url":"https://api.github.com/users/giampaolo/repos","events_url":"https://api.github.com/users/giampaolo/events{/privacy}","received_events_url":"https://api.github.com/users/giampaolo/received_events","type":"User","site_admin":false},"body":"I think catching `TypeError` on `fdst.write()` is too risky as we can deal with any kind of custom file-like object being passed here. It must be noted that the extra cost of this function is payed by users of `copyfileobj()` only (e.g. `tarfile` and `zipfile` modules). `copyfile()` (and others) will skip this check and call `_copybinfileobj()` directly.","created_at":"2018-06-12T22:59:45Z","updated_at":"2018-06-12T22:59:45Z","html_url":"https://github.com/python/cpython/pull/7160#discussion_r194913677","pull_request_url":"https://api.github.com/repos/python/cpython/pulls/7160","author_association":"CONTRIBUTOR","_links":{"self":{"href":"https://api.github.com/repos/python/cpython/pulls/comments/194913677"},"html":{"href":"https://github.com/python/cpython/pull/7160#discussion_r194913677"},"pull_request":{"href":"https://api.github.com/repos/python/cpython/pulls/7160"}},"in_reply_to_id":194910073},"pull_request":{"url":"https://api.github.com/repos/python/cpython/pulls/7160","id":190965206,"node_id":"MDExOlB1bGxSZXF1ZXN0MTkwOTY1MjA2","html_url":"https://github.com/python/cpython/pull/7160","diff_url":"https://github.com/python/cpython/pull/7160.diff","patch_url":"https://github.com/python/cpython/pull/7160.patch","issue_url":"https://api.github.com/repos/python/cpython/issues/7160","number":7160,"state":"closed","locked":false,"title":"bpo-33671: efficient zero-copy for shutil.copy* functions (Linux, OSX and Win)","user":{"login":"giampaolo","id":681260,"node_id":"MDQ6VXNlcjY4MTI2MA==","avatar_url":"https://avatars2.githubusercontent.com/u/681260?v=4","gravatar_id":"","url":"https://api.github.com/users/giampaolo","html_url":"https://github.com/giampaolo","followers_url":"https://api.github.com/users/giampaolo/followers","following_url":"https://api.github.com/users/giampaolo/following{/other_user}","gists_url":"https://api.github.com/users/giampaolo/gists{/gist_id}","starred_url":"https://api.github.com/users/giampaolo/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/giampaolo/subscriptions","organizations_url":"https://api.github.com/users/giampaolo/orgs","repos_url":"https://api.github.com/users/giampaolo/repos","events_url":"https://api.github.com/users/giampaolo/events{/privacy}","received_events_url":"https://api.github.com/users/giampaolo/received_events","type":"User","site_admin":false},"body":"<!-- issue-number: bpo-33671 -->\r\nhttps://bugs.python.org/issue33671\r\n<!-- /issue-number -->\r\n","created_at":"2018-05-28T16:21:01Z","updated_at":"2018-06-12T22:59:45Z","closed_at":"2018-06-12T21:04:51Z","merged_at":"2018-06-12T21:04:51Z","merge_commit_sha":"4a172ccc739065bb658c75e8929774a8e94af9e9","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[{"id":536945472,"node_id":"MDU6TGFiZWw1MzY5NDU0NzI=","url":"https://api.github.com/repos/python/cpython/labels/CLA%20signed","name":"CLA signed","color":"0e8a16","default":false}],"milestone":null,"commits_url":"https://api.github.com/repos/python/cpython/pulls/7160/commits","review_comments_url":"https://api.github.com/repos/python/cpython/pulls/7160/comments","review_comment_url":"https://api.github.com/repos/python/cpython/pulls/comments{/number}","comments_url":"https://api.github.com/repos/python/cpython/issues/7160/comments","statuses_url":"https://api.github.com/repos/python/cpython/statuses/07bcef5df6766454980f83c4668d0f2bbfece891","head":{"label":"giampaolo:shutil-zero-copy","ref":"shutil-zero-copy","sha":"07bcef5df6766454980f83c4668d0f2bbfece891","user":{"login":"giampaolo","id":681260,"node_id":"MDQ6VXNlcjY4MTI2MA==","avatar_url":"https://avatars2.githubusercontent.com/u/681260?v=4","gravatar_id":"","url":"https://api.github.com/users/giampaolo","html_url":"https://github.com/giampaolo","followers_url":"https://api.github.com/users/giampaolo/followers","following_url":"https://api.github.com/users/giampaolo/following{/other_user}","gists_url":"https://api.github.com/users/giampaolo/gists{/gist_id}","starred_url":"https://api.github.com/users/giampaolo/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/giampaolo/subscriptions","organizations_url":"https://api.github.com/users/giampaolo/orgs","repos_url":"https://api.github.com/users/giampaolo/repos","events_url":"https://api.github.com/users/giampaolo/events{/privacy}","received_events_url":"https://api.github.com/users/giampaolo/received_events","type":"User","site_admin":false},"repo":{"id":134839694,"node_id":"MDEwOlJlcG9zaXRvcnkxMzQ4Mzk2OTQ=","name":"cpython","full_name":"giampaolo/cpython","owner":{"login":"giampaolo","id":681260,"node_id":"MDQ6VXNlcjY4MTI2MA==","avatar_url":"https://avatars2.githubusercontent.com/u/681260?v=4","gravatar_id":"","url":"https://api.github.com/users/giampaolo","html_url":"https://github.com/giampaolo","followers_url":"https://api.github.com/users/giampaolo/followers","following_url":"https://api.github.com/users/giampaolo/following{/other_user}","gists_url":"https://api.github.com/users/giampaolo/gists{/gist_id}","starred_url":"https://api.github.com/users/giampaolo/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/giampaolo/subscriptions","organizations_url":"https://api.github.com/users/giampaolo/orgs","repos_url":"https://api.github.com/users/giampaolo/repos","events_url":"https://api.github.com/users/giampaolo/events{/privacy}","received_events_url":"https://api.github.com/users/giampaolo/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/giampaolo/cpython","description":"The Python programming language","fork":true,"url":"https://api.github.com/repos/giampaolo/cpython","forks_url":"https://api.github.com/repos/giampaolo/cpython/forks","keys_url":"https://api.github.com/repos/giampaolo/cpython/keys{/key_id}","collaborators_url":"https://api.github.com/repos/giampaolo/cpython/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/giampaolo/cpython/teams","hooks_url":"https://api.github.com/repos/giampaolo/cpython/hooks","issue_events_url":"https://api.github.com/repos/giampaolo/cpython/issues/events{/number}","events_url":"https://api.github.com/repos/giampaolo/cpython/events","assignees_url":"https://api.github.com/repos/giampaolo/cpython/assignees{/user}","branches_url":"https://api.github.com/repos/giampaolo/cpython/branches{/branch}","tags_url":"https://api.github.com/repos/giampaolo/cpython/tags","blobs_url":"https://api.github.com/repos/giampaolo/cpython/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/giampaolo/cpython/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/giampaolo/cpython/git/refs{/sha}","trees_url":"https://api.github.com/repos/giampaolo/cpython/git/trees{/sha}","statuses_url":"https://api.github.com/repos/giampaolo/cpython/statuses/{sha}","languages_url":"https://api.github.com/repos/giampaolo/cpython/languages","stargazers_url":"https://api.github.com/repos/giampaolo/cpython/stargazers","contributors_url":"https://api.github.com/repos/giampaolo/cpython/contributors","subscribers_url":"https://api.github.com/repos/giampaolo/cpython/subscribers","subscription_url":"https://api.github.com/repos/giampaolo/cpython/subscription","commits_url":"https://api.github.com/repos/giampaolo/cpython/commits{/sha}","git_commits_url":"https://api.github.com/repos/giampaolo/cpython/git/commits{/sha}","comments_url":"https://api.github.com/repos/giampaolo/cpython/comments{/number}","issue_comment_url":"https://api.github.com/repos/giampaolo/cpython/issues/comments{/number}","contents_url":"https://api.github.com/repos/giampaolo/cpython/contents/{+path}","compare_url":"https://api.github.com/repos/giampaolo/cpython/compare/{base}...{head}","merges_url":"https://api.github.com/repos/giampaolo/cpython/merges","archive_url":"https://api.github.com/repos/giampaolo/cpython/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/giampaolo/cpython/downloads","issues_url":"https://api.github.com/repos/giampaolo/cpython/issues{/number}","pulls_url":"https://api.github.com/repos/giampaolo/cpython/pulls{/number}","milestones_url":"https://api.github.com/repos/giampaolo/cpython/milestones{/number}","notifications_url":"https://api.github.com/repos/giampaolo/cpython/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/giampaolo/cpython/labels{/name}","releases_url":"https://api.github.com/repos/giampaolo/cpython/releases{/id}","deployments_url":"https://api.github.com/repos/giampaolo/cpython/deployments","created_at":"2018-05-25T10:09:56Z","updated_at":"2018-06-09T09:28:42Z","pushed_at":"2018-06-12T22:30:35Z","git_url":"git://github.com/giampaolo/cpython.git","ssh_url":"git@github.com:giampaolo/cpython.git","clone_url":"https://github.com/giampaolo/cpython.git","svn_url":"https://github.com/giampaolo/cpython","homepage":"https://www.python.org/","size":238904,"stargazers_count":0,"watchers_count":0,"language":"Python","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"open_issues_count":0,"license":{"key":"other","name":"Other","spdx_id":null,"url":null,"node_id":"MDc6TGljZW5zZTA="},"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"python:master","ref":"master","sha":"e55de2d77f10d524be0b426e587fbc820f76de71","user":{"login":"python","id":1525981,"node_id":"MDEyOk9yZ2FuaXphdGlvbjE1MjU5ODE=","avatar_url":"https://avatars3.githubusercontent.com/u/1525981?v=4","gravatar_id":"","url":"https://api.github.com/users/python","html_url":"https://github.com/python","followers_url":"https://api.github.com/users/python/followers","following_url":"https://api.github.com/users/python/following{/other_user}","gists_url":"https://api.github.com/users/python/gists{/gist_id}","starred_url":"https://api.github.com/users/python/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/python/subscriptions","organizations_url":"https://api.github.com/users/python/orgs","repos_url":"https://api.github.com/users/python/repos","events_url":"https://api.github.com/users/python/events{/privacy}","received_events_url":"https://api.github.com/users/python/received_events","type":"Organization","site_admin":false},"repo":{"id":81598961,"node_id":"MDEwOlJlcG9zaXRvcnk4MTU5ODk2MQ==","name":"cpython","full_name":"python/cpython","owner":{"login":"python","id":1525981,"node_id":"MDEyOk9yZ2FuaXphdGlvbjE1MjU5ODE=","avatar_url":"https://avatars3.githubusercontent.com/u/1525981?v=4","gravatar_id":"","url":"https://api.github.com/users/python","html_url":"https://github.com/python","followers_url":"https://api.github.com/users/python/followers","following_url":"https://api.github.com/users/python/following{/other_user}","gists_url":"https://api.github.com/users/python/gists{/gist_id}","starred_url":"https://api.github.com/users/python/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/python/subscriptions","organizations_url":"https://api.github.com/users/python/orgs","repos_url":"https://api.github.com/users/python/repos","events_url":"https://api.github.com/users/python/events{/privacy}","received_events_url":"https://api.github.com/users/python/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/python/cpython","description":"The Python programming language","fork":false,"url":"https://api.github.com/repos/python/cpython","forks_url":"https://api.github.com/repos/python/cpython/forks","keys_url":"https://api.github.com/repos/python/cpython/keys{/key_id}","collaborators_url":"https://api.github.com/repos/python/cpython/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/python/cpython/teams","hooks_url":"https://api.github.com/repos/python/cpython/hooks","issue_events_url":"https://api.github.com/repos/python/cpython/issues/events{/number}","events_url":"https://api.github.com/repos/python/cpython/events","assignees_url":"https://api.github.com/repos/python/cpython/assignees{/user}","branches_url":"https://api.github.com/repos/python/cpython/branches{/branch}","tags_url":"https://api.github.com/repos/python/cpython/tags","blobs_url":"https://api.github.com/repos/python/cpython/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/python/cpython/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/python/cpython/git/refs{/sha}","trees_url":"https://api.github.com/repos/python/cpython/git/trees{/sha}","statuses_url":"https://api.github.com/repos/python/cpython/statuses/{sha}","languages_url":"https://api.github.com/repos/python/cpython/languages","stargazers_url":"https://api.github.com/repos/python/cpython/stargazers","contributors_url":"https://api.github.com/repos/python/cpython/contributors","subscribers_url":"https://api.github.com/repos/python/cpython/subscribers","subscription_url":"https://api.github.com/repos/python/cpython/subscription","commits_url":"https://api.github.com/repos/python/cpython/commits{/sha}","git_commits_url":"https://api.github.com/repos/python/cpython/git/commits{/sha}","comments_url":"https://api.github.com/repos/python/cpython/comments{/number}","issue_comment_url":"https://api.github.com/repos/python/cpython/issues/comments{/number}","contents_url":"https://api.github.com/repos/python/cpython/contents/{+path}","compare_url":"https://api.github.com/repos/python/cpython/compare/{base}...{head}","merges_url":"https://api.github.com/repos/python/cpython/merges","archive_url":"https://api.github.com/repos/python/cpython/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/python/cpython/downloads","issues_url":"https://api.github.com/repos/python/cpython/issues{/number}","pulls_url":"https://api.github.com/repos/python/cpython/pulls{/number}","milestones_url":"https://api.github.com/repos/python/cpython/milestones{/number}","notifications_url":"https://api.github.com/repos/python/cpython/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/python/cpython/labels{/name}","releases_url":"https://api.github.com/repos/python/cpython/releases{/id}","deployments_url":"https://api.github.com/repos/python/cpython/deployments","created_at":"2017-02-10T19:23:51Z","updated_at":"2018-06-12T21:37:38Z","pushed_at":"2018-06-12T21:35:12Z","git_url":"git://github.com/python/cpython.git","ssh_url":"git@github.com:python/cpython.git","clone_url":"https://github.com/python/cpython.git","svn_url":"https://github.com/python/cpython","homepage":"https://www.python.org/","size":240356,"stargazers_count":18113,"watchers_count":18113,"language":"Python","has_issues":false,"has_projects":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":5652,"mirror_url":null,"archived":false,"open_issues_count":723,"license":{"key":"other","name":"Other","spdx_id":null,"url":null,"node_id":"MDc6TGljZW5zZTA="},"forks":5652,"open_issues":723,"watchers":18113,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/python/cpython/pulls/7160"},"html":{"href":"https://github.com/python/cpython/pull/7160"},"issue":{"href":"https://api.github.com/repos/python/cpython/issues/7160"},"comments":{"href":"https://api.github.com/repos/python/cpython/issues/7160/comments"},"review_comments":{"href":"https://api.github.com/repos/python/cpython/pulls/7160/comments"},"review_comment":{"href":"https://api.github.com/repos/python/cpython/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/python/cpython/pulls/7160/commits"},"statuses":{"href":"https://api.github.com/repos/python/cpython/statuses/07bcef5df6766454980f83c4668d0f2bbfece891"}},"author_association":"CONTRIBUTOR"}}
{"actor":{"display_login":"giampaolo"}}