2013-06-05 95 views
0

我希望能夠在yaml中定義模式,使用pyyaml讀取它,然後使用voluptuous(或其他模式驗證程序)進行驗證! 。然而,正如問題標題所述,我遇到需要將內建類str實例化爲性感而不是字符串表示。從字符串名稱中創建(實例化?)對內置類型的引用

from voluptuous import Schema 
import yaml 


y = ''' 
a: str 
b: int 
c: 
    d: float 
    e: str 
''' 

yaml_schema = yaml.load(y, 
         Loader=yaml.CLoader) 

schema1 = Schema(yaml_schema, required=True) 

然而,這種模式現在正在尋找字符串stra唯一可接受的價值。使用直接pyyaml(例如'a':!! python/int)失敗。相反,我想下面的模式:

schema2 = Schema({'a': str, 
       'b': int, 
       'c': {'d': float, 
         'e': str}}, 
       required=True) 

我清楚地知道,eval是不是生產的解決方案,但下面的功能evaler將轉換schema1schema2 ...

def evaler(d): 
    out = {} 
    for k, v in d.items(): 
     if isinstance(v, dict): 
      out[k] = evaler(v) 
     else: 
      out[k] = eval(v) 
    return out 

## Tests: 

## passing 
v.Schema(evaler(yaml_schema), 
     required=True)({'a': 'foo', 
         'b': 2, 
         'c': {'d': 2.0, 
           'e': 'bar'}}) 

## failling 
v.Schema(evaler(yaml_schema), 
     required=True)({'a': 3, 
         'b': 2, 
         'c': {'d': 2.0, 
           'e': 1}}) 

我也知道你可以實例化一個emp TY類:

class foo: pass 
globals()['foo'] 

但隨着建宏這是不可能的:

globals()['int'] 
# KeyError: 'int' 

我探討了newtype模塊,但沒有任何運氣...

回答

1

最安全的,最簡單,最清晰的解決方案是明確列出您關心的類型的映射:

TYPES = { 
    'str': str, 
    'int': int, 
    ... 
} 

您可以通過從類型列表創建這個字典消除重複(在一些靈活性爲代價):

TYPES = {cls.__name__: cls for cls in [str, int, ...]} 

然後你就可以遞歸走文件(如你在evaler做)和替換每個字符串與TYPES[s]。 如果您堅持要通過名稱支持所有內置類型,而不單獨列出它們,則可以使用builtins模塊(在Python 2中稱爲__builtin__)。 getattr是你的朋友在這裏。你應該檢查它是否是一種類型 - 有很多不是內置的名稱。

無論如何你需要走文件。從PyYAML的角度來看,用作映射值的字符串「str」與用作映射關鍵字的字符串「a」具有相同的標記,因此您無法通過爲該標記指定不同的類來做任何事情。雖然你也許可以深入其中,並引入一種對標量映射值進行不同處理的黑客攻擊,但這隻會是一個駭客。還有大量的額外工作可以啓動。不值得。

+0

謝謝,看到我對第二個答案的評論。有沒有一種方法可以避免文檔的完整漫遊,並通過'pyyaml'中的工具進行替換? – Justin

+0

@Justin我現在很害怕,看編輯。 – delnan

+0

完美,謝謝!爲了安全起見,我想我會去TYPES路線的'dict'。 – Justin

相關問題