2014-09-01 4 views
0

인스턴스 - resp에서 구성 파일 항목에 액세스하는 방법을 찾고있었습니다. 클래스 바인딩 된 변수. Configurable 클래스는 지금 여러 하위 클래스에 의해 상속인스턴스는 생성 후 다른 인스턴스에 속성을 전달합니다.

from ..lib.files import ConfigFile 
from abc import abstractmethod 

__all__ = ['ClassConfig', 
      'InstanceConfig', 
      'Configurable'] 

class ConfigEntry(): 
    """ 
    A Config entry 
    """ 
    __value = None 

    def __init__(self, value=None): 
     """ 
     Initializes the 
     """ 
     self.__value = value 

    def __set__(self, __, value): 
     self.__value = value 

    @property 
    def value(self): 
     """ 
     Returns the value 
     """ 
     return self.__value 


class ClassConfig(ConfigEntry): 
    """ 
    A class config entry 
    """ 
    def __get__(self, obj, cls): 
     """ 
     Returns its value, when called by a class, else itself 
     """ 
     if obj == None: 
      return self.value 
     else: 
      return self 


class InstanceConfig(ConfigEntry): 
    """ 
    An instance config entry 
    """ 
    def __get__(self, obj, cls): 
     """ 
     Returns its value, when called by an instance, else itself 
     """ 
     if obj != None: 
      return self.value 
     else: 
      return self 


class Configurable(): 
    """ 
    Configuration file binding 
    """ 
    __SUFFIX = '.conf' 
    __TYPES = {int: 'int', 
       float: 'float', 
       str: 'str', 
       bool: 'bool'} 
    __file_ = None 
    __lbi = '[' 
    __lei = ']' 
    __ls = ',' 
    __ts = '←' 
    __loaded = False 

    def __init__(self, path, suffix=None): 
     """ 
     Initialize the config file 
     """ 
     # Initializes instance methods 
     self.__setinstattr() 
     suffix = suffix if suffix != None else self.__SUFFIX 
     self.__file_ = ConfigFile(path + suffix) 
     self.load() 

    def __setinstattr(self): 
     """ 
     Set instance attributes 
     """ 
     self.__fields = self.__inst___fields 
     self._file = self.__inst____file 
     self._force_load = self.__inst__force_load 
     self.load = self.__inst_load 
     self.store = self.__inst_store 

    @staticmethod   
    def __filter(attrs): 
     return [a for a in attrs 
       if a == a.upper() 
       and not a.startswith('_')] 

    @staticmethod 
    def __encode(val): 
     """ 
     Encode a value 
     """ 
     t = type(val)   
     if t == list: 
      return Configurable.__lbi + \ 
       Configurable.__ls.join([Configurable.__encode(i) 
           for i in val]) \ 
        + Configurable.__lei 
     elif val == None: 
      return None 
     else: 
      return Configurable.__ts.join([str(val), 
              Configurable.__TYPES.get(t, '?')]) 

    @staticmethod 
    def __decode(val): 
     """ 
     Decode a value 
     """ 
     def det_type(token): 
      """ 
      Determine the type of a token 
      """ 
      t = token.strip().split(Configurable.__ts) 
      if len(t) == 2: 
       raw_val = t[0] 
       tpe = t[1] 
       if tpe == Configurable.__TYPES[str]: 
        return str(raw_val) 
       elif tpe == Configurable.__TYPES[int]: 
        return int(raw_val) 
       elif tpe == Configurable.__TYPES[float]: 
        return float(raw_val) 
       elif tpe == Configurable.__TYPES[bool]: 
        return True if raw_val.lower() in ['1', 
                 'true', 
                 't'] else False 
       else: 
        try: 
         return int(raw_val) 
        except: 
         try: 
          return float(raw_val) 
         except: 
          return raw_val 
      return token 

     def str2list(s): 
      """ 
      Try to parse a list from a string 
      """ 
      def getlist(val): 
       """ 
       Get a list from a reversed character list of a string 
       """ 
       result = [] 
       token = '' 
       while val: 
        c = val.pop() 
        if c == Configurable.__lei: 
         token = Configurable.__lei 
         result = [getlist(val)] + result 
        elif c == Configurable.__lbi: 
         if (not Configurable.__lbi in token) and (not Configurable.__lei in token): 
          result = [det_type(token)] + result 
         token = c 
         return result 
        elif c == Configurable.__ls: 
         if (not Configurable.__lbi in token) and (not Configurable.__lei in token): 
          result = [det_type(token)] + result 
         token = '' 
        else: 
         token = c + token 
       if token: 
        result = [det_type(token)] + result 
       return result 
      l = [] 
      for char in s: 
       l.append(char) 
      l = getlist(l) 
      if len(l) == 0: 
       return l 
      return l.pop() 

     return str2list(val) 

    @classmethod 
    def __fields(cls): 
     """ 
     Get fields for an instance 
     """ 
     result = {} 
     class Subclass(cls): 
      def __init__(self): 
       pass  
     instance = Subclass() 
     attrs = Configurable.__filter(dir(instance)) 
     for a in attrs: 
      aval = getattr(instance, a) 
      if isinstance(aval, ClassConfig): 
       value = getattr(cls, a) 
       result[a] = value 
     return result 

    def __inst___fields(self): 
     """ 
     Get fields of an instance 
     """ 
     result = {} 
     cls = self.__class__ 
     attrs = Configurable.__filter(dir(cls)) 
     for a in attrs: 
      val = getattr(cls, a) 
      if isinstance(val, InstanceConfig): 
       value = getattr(self, a) 
       result[a] = value 
     return result 

    @classmethod 
    @abstractmethod 
    def _file(cls): 
     """ 
     Returns the file 
     XXX: Implement by calling 
     super()._file(static_path) 
     """ 
     pass 

    @classmethod 
    def _file_path(cls, path, suffix=None): 
     """ 
     Returns the file relative to a path 
     """ 
     suffix = suffix if suffix != None else cls.__SUFFIX 
     f = ConfigFile(path + suffix) 
     f.create() 
     return f 

    def __inst____file(self): 
     """ 
     Returns the file 
     """ 
     return self.__file_ 

    @classmethod 
    def load(cls): 
     """ 
     Loads the config file content, if not yet done into the class 
     """ 
     if not cls.__loaded: 
      return cls._force_load() 
     return True 

    def __inst_load(self): 
     """ 
     Loads the config file content, if not yet done into the instance 
     """ 
     if not self.__loaded: 
      return self._force_load() 
     return True 

    @classmethod 
    def _force_load(cls): 
     """ 
     Loads the config file's content to the class 
     """ 
     if cls._file().exists: 
      data = cls._file().dict() 
      for field in Configurable.__filter(data): 
       setattr(cls, field, 
         Configurable.__decode(data[field])) 
      cls.__loaded = True 
      return True 
     return False 

    def __inst__force_load(self): 
     """ 
     Loads the config file's content to the instance 
     """ 
     if self._file().exists: 
      data = self._file().dict() 
      for field in Configurable.__filter(data): 
       setattr(self, field, 
         Configurable.__decode(data[field])) 
      self.__loaded = True 
      return True 
     return False 

    @classmethod 
    def store(cls): 
     """ 
     Writes class config to file 
     """ 
     result = True 
     content = cls.__fields() 
     if not cls._file().exists: 
      cls._file().create() 
     for new_field in content: 
      set_result = cls._file().set(new_field, 
         Configurable.__encode(content[new_field])) 
      result = False if not set_result else result 
     return result 

    def __inst_store(self): 
     """ 
     Writes instance config to file 
     """ 
     result = True 
     content = self.__fields() 
     if not self._file().exists: 
      self._file().create() 
     for new_field in content: 
      set_result = self._file().set(new_field, 
         Configurable.__encode(content[new_field])) 
      result = False if not set_result else result 
     return result 

, 글로벌 구성 (클래스 바인딩 재료) 및 사용자 dependen 구성 (인스턴스 바인딩 물건이있을 수 있습니다 : 그 때문에 나는 다음과 같은 모듈을 만들었습니다) 그와 같은

지금은 load() 순서에 많은 경우에서 수행 될 때마다 상기 InstanceConfigEntry가 previuos 인스턴스의 값을 복사 것이라는 문제를 직면
class Spam(Configurable): 
    EGGS = InstanceConfig('foo') 
    GLOBAL_EGGS = ClassConfig('bar') 

:

class RETARD(Daemon): 
    """ 
    Real Estate Translation, Archiving and Redirection Daemon 
    """ 
    __source = None # The source interface instance 
    __targets = [] # The target interface instances  
    __locked = False # System locked state flag 
    __start_time = None # Start time of loop 
    __sleeping = 0 # Remaining time to sleep 
    #=========================================================================== 
    # Default customer config 
    #=========================================================================== 
    SOURCE = InstanceConfig('')  # Name of the source interface 
    TARGETS = InstanceConfig([]) # Names of the target interfaces 
    INTERVAL = InstanceConfig(120.0) # Loop interval 
    DEBUG = InstanceConfig(False) # Print the import config? 

    def __init__(self, customer): 
     """ 
     Constructor 
     """ 
     print('SOURCE1: ' + str(self.SOURCE)) 
     super().__init__(customer) 
     print('SOURCE2: ' + str(self.SOURCE)) 
     self.__load() 
     print('SOURCE3: ' + str(self.SOURCE)) 
     # Disable logger on high level to prevent PyXB 
     # from printing messages to the terminal 
     logging.disable(9999) 

<SNIP> 
다음과 같이로드 할 때

은 (데몬 네 다른 인스턴스를 포함) : 난을 변경하지 않았기 때문에,

SOURCE1: 
SOURCE2: IS24 
SOURCE3: IS24 
SOURCE1: IS24 
SOURCE2: is24 
SOURCE3: is24 
SOURCE1: is24 
SOURCE2: infobase 
SOURCE3: infobase 
SOURCE1: infobase 
SOURCE2: infobase 
SOURCE3: infobase 

나는이 문제를 이해하지 :이 출력을 생성

daemons = [] 
for customer in customers: 
    daemons.append(RETARD(customer)) 

클래스 '속성을 인스턴스의 인스턴스가 아닌 다른 곳에 배치 할 수 있습니다. 인스턴스가 변경된 속성을 다음 인스턴스로 전달하지 않도록하려면 어떻게해야합니까?

답변

1

여기서 문제는 내가 깨닫지 못했을 때, 그 시간에 클래스에 바인드 된 InstanceConfigClassConfig 항목이 모듈로드라는 것입니다. 인스턴스 내에서 런타임 중에 각 속성에 다른 내용을 할당하면 물론 클래스 클래스 * Config 인스턴스의 내용이 변경되었습니다. 각 설정 파일에 포함되지 않은 값을 기본값으로 사용하여이 문제를 해결했습니다 :

from ..lib.files import ConfigFile 
from abc import abstractmethod 

__all__ = ['ClassConfig', 
      'InstanceConfig', 
      'Configurable'] 

class ConfigEntry(): 
    """ 
    A Config entry 
    """ 
    __value = None 
    __default = None 

    def __init__(self, default=None): 
     """ 
     Initializes the 
     """ 
     self.__default = default 
     self.__value = default 

    def __set__(self, __, value): 
     """ 
     Sets the value 
     """ 
     self.__value = value 

    @property 
    def value(self): 
     """ 
     Returns the value 
     """ 
     return self.__value 

    @property 
    def default(self): 
     """ 
     Access default value 
     """ 
     return self.__default 


class ClassConfig(ConfigEntry): 
    """ 
    A class config entry 
    """ 
    def __get__(self, obj, cls): 
     """ 
     Returns its value, when called by a class, else itself 
     """ 
     if obj == None: 
      return self.value 
     else: 
      return self 


class InstanceConfig(ConfigEntry): 
    """ 
    An instance config entry 
    """ 
    def __get__(self, obj, cls): 
     """ 
     Returns its value, when called by an instance, else itself 
     """ 
     if obj != None: 
      return self.value 
     else: 
      return self 


class Configurable(): 
    """ 
    Configuration file binding 
    """ 
    __SUFFIX = '.conf' 
    __TYPES = {int: 'int', 
       float: 'float', 
       str: 'str', 
       bool: 'bool'} 
    __file_ = None 
    __lbi = '[' # List begin identifier 
    __lei = ']' # List end identifier 
    __ls = ',' # List separator 
    __ts = '←' # Type separator 
    __loaded = False 

    def __init__(self, path, suffix=None): 
     """ 
     Initialize the config file 
     """ 
     # Initializes instance methods 
     self.__setinstattr() 
     suffix = suffix if suffix != None else self.__SUFFIX 
     self.__file_ = ConfigFile(path + suffix) 
     self.load() 

    def __setinstattr(self): 
     """ 
     Set instance attributes 
     """ 
     self.__fields = self.__inst___fields 
     self._file = self.__inst____file 
     self._force_load = self.__inst__force_load 
     self.load = self.__inst_load 
     self.store = self.__inst_store 

    @staticmethod   
    def __filter(attrs): 
     return [a for a in attrs 
       if a == a.upper() 
       and not a.startswith('_')] 

    @staticmethod 
    def __encode(val): 
     """ 
     Encode a value 
     """ 
     t = type(val)   
     if t == list: 
      return Configurable.__lbi + \ 
       Configurable.__ls.join([Configurable.__encode(i) 
           for i in val]) \ 
        + Configurable.__lei 
     elif val == None: 
      return None 
     else: 
      return Configurable.__ts.join([str(val), 
              Configurable.__TYPES.get(t, '?')]) 

    @staticmethod 
    def __decode(val): 
     """ 
     Decode a value 
     """ 
     def det_type(token): 
      """ 
      Determine the type of a token 
      """ 
      t = token.strip().split(Configurable.__ts) 
      if len(t) == 2: 
       raw_val = t[0] 
       tpe = t[1] 
       if tpe == Configurable.__TYPES[str]: 
        return str(raw_val) 
       elif tpe == Configurable.__TYPES[int]: 
        return int(raw_val) 
       elif tpe == Configurable.__TYPES[float]: 
        return float(raw_val) 
       elif tpe == Configurable.__TYPES[bool]: 
        return True if raw_val.lower() in ['1', 
                 'true', 
                 't'] else False 
       else: 
        try: 
         return int(raw_val) 
        except: 
         try: 
          return float(raw_val) 
         except: 
          return raw_val 
      return token 

     def str2list(s): 
      """ 
      Try to parse a list from a string 
      """ 
      def getlist(val): 
       """ 
       Get a list from a reversed character list of a string 
       """ 
       result = [] 
       token = '' 
       while val: 
        c = val.pop() 
        if c == Configurable.__lei: 
         token = Configurable.__lei 
         result = [getlist(val)] + result 
        elif c == Configurable.__lbi: 
         if (not Configurable.__lbi in token) and (not Configurable.__lei in token): 
          result = [det_type(token)] + result 
         token = c 
         return result 
        elif c == Configurable.__ls: 
         if (not Configurable.__lbi in token) and (not Configurable.__lei in token): 
          result = [det_type(token)] + result 
         token = '' 
        else: 
         token = c + token 
       if token: 
        result = [det_type(token)] + result 
       return result 
      l = [] 
      for char in s: 
       l.append(char) 
      l = getlist(l) 
      if len(l) == 0: 
       return l 
      return l.pop() 

     return str2list(val) 

    @classmethod 
    def __fields(cls): 
     """ 
     Get fields for an instance 
     """ 
     result = {} 
     class Subclass(cls): 
      def __init__(self): 
       pass  
     instance = Subclass() 
     attrs = Configurable.__filter(dir(instance)) 
     for a in attrs: 
      aval = getattr(instance, a) 
      if isinstance(aval, ClassConfig): 
       result[a] = aval 
     return result 

    def __inst___fields(self): 
     """ 
     Get fields of an instance 
     """ 
     result = {} 
     cls = self.__class__ 
     attrs = Configurable.__filter(dir(cls)) 
     for a in attrs: 
      val = getattr(cls, a) 
      if isinstance(val, InstanceConfig): 
       result[a] = val 
     return result 

    @classmethod 
    @abstractmethod 
    def _file(cls): 
     """ 
     Returns the file 
     XXX: Implement by calling 
     super()._file(static_path) 
     """ 
     pass 

    @classmethod 
    def _file_path(cls, path, suffix=None): 
     """ 
     Returns the file relative to a path 
     """ 
     suffix = suffix if suffix != None else cls.__SUFFIX 
     f = ConfigFile(path + suffix) 
     f.create() 
     return f 

    def __inst____file(self): 
     """ 
     Returns the file 
     """ 
     return self.__file_ 

    @classmethod 
    def load(cls): 
     """ 
     Loads the config file content, if not yet done into the class 
     """ 
     if not cls.__loaded: 
      return cls._force_load() 
     return True 

    def __inst_load(self): 
     """ 
     Loads the config file content, if not yet done into the instance 
     """ 
     if not self.__loaded: 
      return self._force_load() 
     return True 

    @classmethod 
    def _force_load(cls): 
     """ 
     Loads the config file's content to the class 
     """ 
     if cls._file().exists: 
      data = cls._file().dict() 
     else: 
      data = {} 
     fields = cls.__fields() 
     for field in fields: 
      val = data.get(field) 
      if val == None: 
       val = fields[field].default 
      else: 
       val = Configurable.__decode(val) 
      setattr(cls, field, val) 
     cls.__loaded = True 
     return True 

    def __inst__force_load(self): 
     """ 
     Loads the config file's content to the instance 
     """ 
     if self._file().exists: 
      data = self._file().dict() 
     else: 
      data = {} 
     fields = self.__fields() 
     for field in fields: 
      val = data.get(field) 
      if val == None: 
       val = fields[field].default 
      else: 
       val = Configurable.__decode(val) 
      setattr(self, field, val) 
     self.__loaded = True 
     return True 

    @classmethod 
    def store(cls): 
     """ 
     Writes class config to file 
     """ 
     result = True 
     fields = cls.__fields() 
     if not cls._file().exists: 
      cls._file().create() 
     for field in fields: 
      val = fields[field].value 
      set_result = cls._file().set(field, 
         Configurable.__encode(val)) 
      result = False if not set_result else result 
     return result 

    def __inst_store(self): 
     """ 
     Writes instance config to file 
     """ 
     result = True 
     fields = self.__fields() 
     if not self._file().exists: 
      self._file().create() 
     for field in fields: 
      val = fields[field].value 
      set_result = self._file().set(field, 
         Configurable.__encode(val)) 
      result = False if not set_result else result 
     return result