복붙노트

[PYTHON] 파이썬 요청은 로컬 URL에서 파일을 가져옵니다.

PYTHON

파이썬 요청은 로컬 URL에서 파일을 가져옵니다.

내 응용 프로그램의 한 가지 방법으로 파이썬의 요청 라이브러리를 사용하고 있습니다. 메소드의 본문은 다음과 같습니다.

def handle_remote_file(url, **kwargs):
    response = requests.get(url, ...)
    buff = StringIO.StringIO()
    buff.write(response.content)
    ...
    return True

그 메서드에 대한 몇 가지 단위 테스트를 작성하고 싶습니다, 그러나 내가하고 싶은 일은 다음과 같은 가짜 로컬 URL을 전달하는 것입니다.

class RemoteTest(TestCase):
    def setUp(self):
        self.url = 'file:///tmp/dummy.txt'

    def test_handle_remote_file(self):
        self.assertTrue(handle_remote_file(self.url))

로컬 URL로 requests.get을 호출하면 아래에있는 KeyError 예외가 발생합니다.

requests.get('file:///tmp/dummy.txt')

/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/requests/packages/urllib3/poolmanager.pyc in connection_from_host(self, host, port, scheme)
76 
77         # Make a fresh ConnectionPool of the desired type
78         pool_cls = pool_classes_by_scheme[scheme]
79         pool = pool_cls(host, port, **self.connection_pool_kw)
80 

KeyError: 'file'

질문은 요청에 로컬 URL을 전달할 수있는 방법입니다.

추신 : 나는 위의 예를 구성했다. 많은 오류가있을 수 있습니다.

해결법

  1. ==============================

    1.@ WooParadog는 요청 라이브러리가 로컬 파일을 처리하는 방법을 알지 못한다고 설명했습니다. 현재 버전에서는 전송 어댑터를 정의 할 수 있습니다.

    @ WooParadog는 요청 라이브러리가 로컬 파일을 처리하는 방법을 알지 못한다고 설명했습니다. 현재 버전에서는 전송 어댑터를 정의 할 수 있습니다.

    따라서 로컬 파일을 처리 할 수있는 자체 어댑터를 정의 할 수 있습니다 (예 :

    from requests_testadapter import Resp
    
    class LocalFileAdapter(requests.adapters.HTTPAdapter):
        def build_response_from_file(self, request):
            file_path = request.url[7:]
            with open(file_path, 'rb') as file:
                buff = bytearray(os.path.getsize(file_path))
                file.readinto(buff)
                resp = Resp(buff)
                r = self.build_response(request, resp)
    
                return r
    
        def send(self, request, stream=False, timeout=None,
                 verify=True, cert=None, proxies=None):
    
            return self.build_response_from_file(request)
    
    requests_session = requests.session()
    requests_session.mount('file://', LocalFileAdapter())
    requests_session.get('file://<some_local_path>')
    

    위 예제에서 requests-testadapter 모듈을 사용하고 있습니다.

  2. ==============================

    2.다음은 b1r3k의 것보다 더 특징적인 전송 어댑터이며 Request 자체를 넘어서는 추가 종속성이 없습니다. 나는 그것을 철저히 시험하지는 않았지만, 내가 시도한 것은 버그가없는 것 같다.

    다음은 b1r3k의 것보다 더 특징적인 전송 어댑터이며 Request 자체를 넘어서는 추가 종속성이 없습니다. 나는 그것을 철저히 시험하지는 않았지만, 내가 시도한 것은 버그가없는 것 같다.

    import requests
    import os, sys
    
    if sys.version_info.major < 3:
        from urllib import url2pathname
    else:
        from urllib.request import url2pathname
    
    class LocalFileAdapter(requests.adapters.BaseAdapter):
        """Protocol Adapter to allow Requests to GET file:// URLs
    
        @todo: Properly handle non-empty hostname portions.
        """
    
        @staticmethod
        def _chkpath(method, path):
            """Return an HTTP status for the given filesystem path."""
            if method.lower() in ('put', 'delete'):
                return 501, "Not Implemented"  # TODO
            elif method.lower() not in ('get', 'head'):
                return 405, "Method Not Allowed"
            elif os.path.isdir(path):
                return 400, "Path Not A File"
            elif not os.path.isfile(path):
                return 404, "File Not Found"
            elif not os.access(path, os.R_OK):
                return 403, "Access Denied"
            else:
                return 200, "OK"
    
        def send(self, req, **kwargs):  # pylint: disable=unused-argument
            """Return the file specified by the given request
    
            @type req: C{PreparedRequest}
            @todo: Should I bother filling `response.headers` and processing
                   If-Modified-Since and friends using `os.stat`?
            """
            path = os.path.normcase(os.path.normpath(url2pathname(req.path_url)))
            response = requests.Response()
    
            response.status_code, response.reason = self._chkpath(req.method, path)
            if response.status_code == 200 and req.method.lower() != 'head':
                try:
                    response.raw = open(path, 'rb')
                except (OSError, IOError) as err:
                    response.status_code = 500
                    response.reason = str(err)
    
            if isinstance(req.url, bytes):
                response.url = req.url.decode('utf-8')
            else:
                response.url = req.url
    
            response.request = req
            response.connection = self
    
            return response
    
        def close(self):
            pass
    

    (이름에도 불구하고, Google을 확인하기 전에 완전히 작성되었으므로 b1r3k와는 아무런 관련이 없습니다.) 다른 답변과 마찬가지로 다음을 수행하십시오.

    requests_session = requests.session()
    requests_session.mount('file://', LocalFileAdapter())
    r = requests_session.get('file:///path/to/your/file')
    
  3. ==============================

    3.packages / urllib3 / poolmanager.py는 그것을 아주 잘 설명합니다. 요청은 로컬 URL을 지원하지 않습니다.

    packages / urllib3 / poolmanager.py는 그것을 아주 잘 설명합니다. 요청은 로컬 URL을 지원하지 않습니다.

    pool_classes_by_scheme = {                                                        
        'http': HTTPConnectionPool,                                                   
        'https': HTTPSConnectionPool,                                              
    }                                                                                 
    
  4. ==============================

    4.최근 프로젝트에서 같은 문제가 발생했습니다. 요청은 "파일"스키마를 지원하지 않으므로 코드를 패치하여 컨텐츠를 로컬로로드합니다. 먼저 requests.get을 대체 할 함수를 정의합니다.

    최근 프로젝트에서 같은 문제가 발생했습니다. 요청은 "파일"스키마를 지원하지 않으므로 코드를 패치하여 컨텐츠를 로컬로로드합니다. 먼저 requests.get을 대체 할 함수를 정의합니다.

    def local_get(self, url):
        "Fetch a stream from local files."
        p_url = six.moves.urllib.parse.urlparse(url)
        if p_url.scheme != 'file':
            raise ValueError("Expected file scheme")
    
        filename = six.moves.urllib.request.url2pathname(p_url.path)
        return open(filename, 'rb')
    

    그런 다음, 테스트 설정이나 테스트 함수 꾸미기에서 mock.patch를 사용하여 요청에서 get 함수를 패치합니다.

    @mock.patch('requests.get', local_get)
    def test_handle_remote_file(self):
        ...
    

    이 기법은 다소 취약합니다. 기본 코드가 requests.request를 호출하거나 Session을 생성하고 호출하면 도움이되지 않습니다. 파일 : URL을 지원하기 위해 낮은 수준의 요청을 패치하는 방법이있을 수 있지만, 초기 조사에서 명백한 후크 포인트가없는 것 같아서이 간단한 접근 방식을 사용했습니다.

  5. from https://stackoverflow.com/questions/10123929/python-requests-fetch-a-file-from-a-local-url by cc-by-sa and MIT license