Source

gosvideo / utils.py

Full commit
# -*- coding: utf-8 -*-

# This file is part of Gosvideo. 
# Gosvideo is a tool for ordering and downloading video files from http://www.gosuslugi.ru/.

# Copyright (C) 2012  Evstifeev Roman <someuniquename@gmail.com>

# Gosvideo is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.

# Gosvideo is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with Gosvideo.  If not, see <http://www.gnu.org/licenses/>.
# See file LICENSE.txt in the source code tree

import os, sys, re, json, threading, shutil, logging
from textwrap import dedent
import traceback
from collections import defaultdict

import errors


def cookies_simple(cookies_raw):
    res = {}
    for k,v in cookies_raw.items():
        try:
            res[k] = re.search('%s=([^;]*);?' % k, v).group(1)
        except Exception as e:
            import pdb4qt; pdb4qt.set_trace()
            continue
    return res 

    
def log_error_text(text):
    logging.error(u'*' * 20)
    logging.error(dedent(text).strip())
    logging.error(u'*' * 20)
    
    
def safe_load(filepath, default):
    if not os.path.exists(filepath):
        return default()
    
    try:
        with open(filepath, 'r') as f:
            content = f.read()
    except EnvironmentError:
        log_error_text(u'*** Невозможно открыть файл %s !' % filepath)
        raise

    try:
        return default(json.loads(content))
    except ValueError:
        base, ext = os.path.splitext(filepath)
        for n in xrange(1000):
            new_path = '%s_%d' % (base, n) + ext
            if not os.path.exists(new_path):
                break
        os.rename(filepath, new_path)
        
        text = u'''
            *** Файл %s был поврежден и создан заново! 
            *** Старый файл переименован в %s ''' % (filepath, new_path)
        log_error_text(text)
        return default()
        

class StoppableThread(threading.Thread):
    """Thread class with a stop() method. The thread itself has to check
    regularly for the stopped() condition."""

    def __init__(self, target = None, *args, **kwargs):
        target = log_trace(target)
        super(StoppableThread, self).__init__(target = target, *args, **kwargs)
        self._stop = threading.Event()
        self.setDaemon(True)

    def stop(self, wait = True):
        self._stop.set()
        if wait:
            self.join()

    def stopped(self):
        return self._stop.isSet()



CHUNK = 1024*1024
def download_file(src, dst_path, on_stop = None):
    dst = open(dst_path, 'wb+')
    try:
        while True:
            if threading.current_thread().stopped():
                logging.info(u'dowload thread stopped')
                dst.close()
                safe_rm(dst_path)
                if on_stop:
                    on_stop()
                sys.exit()
                
            data = src.read(CHUNK)
            #logging.info(u'got %.2f MB' % (len(data)/float(CHUNK)))
            if data:
                dst.write(data)
            else:
                break
    except Exception:
        safe_rm(dst_path)
        raise
    finally:
        dst.close()
        
        
def log_exception(e, tb_msg):
    if isinstance(e, errors.Uncritical):
        logging.error(u'*** %s' % str(e).decode('utf-8', 'replace'))
        return
        
    logging.error(u'*** Произошла ошибка!') 
    logging.error(u'*** Чтобы разработчики смогли ее исправить, cообщите пожалуйста им след. информацию:\n')
    logging.error(u'*' * 20)
    if sys.platform == 'win32' and isinstance(e, EnvironmentError):
        logging.error(str(e).decode('cp1251', 'replace'))
        logging.error(tb_msg.decode('cp1251', 'replace'))
    else:
        logging.error(str(e).decode('utf-8', 'replace'))
        logging.error(tb_msg.decode('utf-8', 'replace'))
    logging.error(u'*' * 20 + u'\n') 
           
           
def excepthook(type, value, tb):
    ''' if you set sys.excepthook to this function - it will log all exceptions
        to log only some functions you may use log_trace decorator instead
    '''
    tb_msg = "".join(traceback.format_exception(type, value, tb))
    log_exception(value, tb_msg)


def log_trace(f):
    def wrapped(*args, **kwargs):
        try:
            return f(*args, **kwargs)
        except Exception as e:
            log_exception(e, traceback.format_exc())
            raise
    return wrapped


def safe_path(path):
    if not isinstance(path, unicode):
        path = unicode(path, sys.getfilesystemencoding())
        
    if sys.platform == "win32":
        return os.path.normpath(path)
    
    return path

def safe_rm(path):
    if os.path.exists(path):
        os.remove(path)

class objdict(defaultdict):
    def __getattr__(self, key):
        try:
            return self.__dict__[key]
        except KeyError:
            return self.__getitem__(key)
           
    __setattr__ = lambda self, k, v: self.__setitem__(k,v)