1# emacs: -*- mode: python; py-indent-offset: 4; tab-width: 4; indent-tabs-mode: nil -*- 2# ex: set sts=4 ts=4 sw=4 noet: 3# ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## 4# 5# See COPYING file distributed along with the duecredit package for the 6# copyright and license terms. 7# 8# ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## 9"""Provides an adapter to switch between two (active, inactive) collectors 10""" 11 12import os 13import atexit 14 15from .log import lgr 16from .utils import never_fail 17 18def _get_duecredit_enable(): 19 env_enable = os.environ.get('DUECREDIT_ENABLE', 'no') 20 if not env_enable.lower() in ('0', '1', 'yes', 'no', 'true', 'false'): 21 lgr.warning("Misunderstood value %s for DUECREDIT_ENABLE. " 22 "Use 'yes' or 'no', or '0' or '1'") 23 return env_enable.lower() in ('1', 'yes', 'true') 24 25 26@never_fail 27def _get_inactive_due(): 28 from .stub import InactiveDueCreditCollector 29 return InactiveDueCreditCollector() 30 31 32@never_fail 33def _get_active_due(): 34 from .config import CACHE_DIR, DUECREDIT_FILE 35 from duecredit.collector import CollectorSummary, DueCreditCollector 36 from .io import load_due 37 38 # TODO: this needs to move to atexit handling, that we load previous 39 # one and them merge with new ones. Informative bits could be -- how 40 # many new citations we got 41 if os.path.exists(DUECREDIT_FILE): 42 try: 43 due_ = load_due(DUECREDIT_FILE) 44 except Exception as e: 45 lgr.warning("Failed to load previously collected %s. " 46 "DueCredit will not be active for this session." 47 % DUECREDIT_FILE) 48 return _get_inactive_due() 49 else: 50 due_ = DueCreditCollector() 51 52 return due_ 53 54 55class DueSwitch(object): 56 """Adapter between two types of collectors -- Inactive and Active 57 58 Once activated though, cannot be fully deactivated since it would inject 59 duecredit decorators and register an event atexit. 60 """ 61 def __init__(self, inactive, active, activate=False): 62 self.__active = None 63 self.__collectors = {False: inactive, True: active} 64 self.__activations_done = False 65 if not (inactive and active): 66 raise ValueError( 67 "Both inactive and active collectors should be provided. " 68 "Got active=%r, inactive=%r" % (active, inactive) 69 ) 70 self.activate(activate) 71 72 @property 73 def active(self): 74 return self.__active 75 76 @never_fail 77 def dump(self, **kwargs): 78 """Dumps summary of the citations 79 80 Parameters 81 ---------- 82 **kwargs: dict 83 Passed to `CollectorSummary` constructor. 84 """ 85 from duecredit.collector import CollectorSummary 86 due_summary = CollectorSummary(self.__collectors[True], **kwargs) 87 due_summary.dump() 88 89 def __prepare_exit_and_injections(self): 90 # Wrapper to create and dump summary... passing method doesn't work: 91 # probably removes instance too early 92 93 atexit.register(self.dump) 94 95 # Deal with injector 96 from .injections import DueCreditInjector 97 injector = DueCreditInjector(collector=self.__collectors[True]) 98 injector.activate() 99 100 @never_fail 101 def activate(self, activate=True): 102 # 1st step -- if activating/deactivating switch between the two collectors 103 if self.__active is not activate: 104 # we need to switch the state 105 # InactiveDueCollector also has those special methods defined 106 # in DueSwitch so that client code could query/call (for no effect). 107 # So we shouldn't delete or bind them either 108 is_public_or_special = \ 109 lambda x: not (x.startswith('_') 110 or x in ('activate', 'active', 'dump')) 111 # Clean up current bindings first 112 for k in filter(is_public_or_special, dir(self)): 113 delattr(self, k) 114 115 new_due = self.__collectors[activate] 116 for k in filter(is_public_or_special, dir(new_due)): 117 setattr(self, k, getattr(new_due, k)) 118 119 self.__active = activate 120 121 # 2nd -- if activating, we might still need to have activations done 122 if activate and not self.__activations_done: 123 try: 124 self.__prepare_exit_and_injections() 125 except Exception as e: 126 lgr.error("Failed to prepare injections etc: %s" % str(e)) 127 finally: 128 self.__activations_done = True 129 130 131due = DueSwitch(_get_inactive_due(), _get_active_due(), _get_duecredit_enable())