1""" 2pgxnclient -- network interaction 3""" 4 5# Copyright (C) 2011-2021 Daniele Varrazzo 6 7# This file is part of the PGXN client 8 9import os 10from six.moves.urllib.request import build_opener 11from six.moves.urllib.error import HTTPError, URLError 12from six.moves.urllib.parse import urlsplit 13from itertools import count 14from contextlib import closing 15 16from pgxnclient import __version__ 17from pgxnclient.i18n import _ 18from pgxnclient.errors import ( 19 PgxnClientException, 20 NetworkError, 21 ResourceNotFound, 22 BadRequestError, 23) 24 25import logging 26 27logger = logging.getLogger('pgxnclient.network') 28 29 30def get_file(url): 31 opener = build_opener() 32 opener.addheaders = [('User-agent', 'pgxnclient/%s' % __version__)] 33 logger.debug('opening url: %s', url) 34 try: 35 return closing(opener.open(url)) 36 except HTTPError as e: 37 if e.code == 404: 38 raise ResourceNotFound(_("resource not found: '%s'") % e.url) 39 elif e.code == 400: 40 raise BadRequestError(_("bad request on '%s'") % e.url) 41 elif e.code == 500: 42 raise NetworkError(_("server error")) 43 elif e.code == 503: 44 raise NetworkError(_("service unavailable")) 45 else: 46 raise NetworkError( 47 _("unexpected response %d for '%s'") % (e.code, e.url) 48 ) 49 except URLError as e: 50 raise NetworkError(_("network error: %s") % e.reason) 51 52 53def get_local_file_name(target, url): 54 """Return a good name for a local file. 55 56 If *target* is a dir, make a name out of the url. Otherwise return target 57 itself. Always return an absolute path. 58 """ 59 if os.path.isdir(target): 60 basename = urlsplit(url)[2].rsplit('/', 1)[-1] 61 fn = os.path.join(target, basename) 62 else: 63 fn = target 64 65 return os.path.abspath(fn) 66 67 68def download(f, fn, rename=True): 69 """Download a file locally. 70 71 :param f: open file to read 72 :param fn: name of the file to write. If a dir, save into it. 73 :param rename: if true and a file *fn* exist, rename the downloaded file 74 adding a prefix ``-1``, ``-2``... before the extension. 75 76 Return the name of the file saved. 77 """ 78 if os.path.isdir(fn): 79 fn = get_local_file_name(fn, f.url) 80 81 if rename: 82 if os.path.exists(fn): 83 base, ext = os.path.splitext(fn) 84 for i in count(1): 85 logger.debug(_("file %s exists"), fn) 86 fn = "%s-%d%s" % (base, i, ext) 87 if not os.path.exists(fn): 88 break 89 90 logger.info(_("saving %s"), fn) 91 try: 92 fout = open(fn, "wb") 93 except Exception as e: 94 raise PgxnClientException( 95 _("cannot open target file: %s: %s") % (e.__class__.__name__, e) 96 ) 97 try: 98 while 1: 99 data = f.read(8192) 100 if not data: 101 break 102 fout.write(data) 103 finally: 104 fout.close() 105 106 return fn 107