كيفية إلغاء تمييز كائن مع pyyaml باستخدام safe_load؟
-
26-09-2019 - |
سؤال
وجود مقتطف مثل هذا:
import yaml
class User(object):
def __init__(self, name, surname):
self.name= name
self.surname= surname
user = User('spam', 'eggs')
serialized_user = yaml.dump(user)
#Network
deserialized_user = yaml.load(serialized_user)
print "name: %s, sname: %s" % (deserialized_user.name, deserialized_user.surname)
مستندات Yaml يقول أنه ليس من الآمن الاتصال yaml.load مع أي بيانات تم استلامها من مصدر غير موثوق ؛ لذا ، ما الذي يجب علي تعديله إلى الفصل الخاص بي للاستخدام Safe_Load طريقة؟
هل هو ممكن؟
المحلول
يبدو أن Safe_Load ، بحكم التعريف ، لا يتيح لك إلغاء فصولك الخاصة. إذا كنت تريد أن تكون آمنة ، فسأفعل شيئًا كهذا:
import yaml
class User(object):
def __init__(self, name, surname):
self.name= name
self.surname= surname
def yaml(self):
return yaml.dump(self.__dict__)
@staticmethod
def load(data):
values = yaml.safe_load(data)
return User(values["name"], values["surname"])
user = User('spam', 'eggs')
serialized_user = user.yaml()
print "serialized_user: %s" % serialized_user.strip()
#Network
deserialized_user = User.load(serialized_user)
print "name: %s, sname: %s" % (deserialized_user.name, deserialized_user.surname)
الميزة هنا هي أن لديك سيطرة مطلقة على كيفية تسلسل صفك (DE). هذا يعني أنك لن تحصل على رمز قابل للتنفيذ عشوائي عبر الشبكة وتشغيله. العيب هو أن لديك سيطرة مطلقة على كيفية تصنيف صفك (DE). هذا يعني أنه يجب عليك القيام بالكثير من العمل. ؛-)
نصائح أخرى
هناك طريقة أخرى. من مستندات Pyyaml:
يمكن تمييز كائن Python على أنه آمن وبالتالي يتم التعرف عليه بواسطة Yaml.Safe_Load. للقيام بذلك ، استمدها من yaml.yamlobject [...] وتعيين خاصية فئة yaml_loader بشكل صريح إلى yaml.safeloader.
يجب عليك أيضًا تعيين خاصية Yaml_Tag لجعلها تعمل.
يقوم Yamlobject ببعض السحر metaclass لجعل الكائن قابل للتحميل. لاحظ أنه إذا قمت بذلك ، فلن يتم تحميل الكائنات إلا عن طريق المحمل الآمن ، وليس مع yaml.load () العادية.
مثال العمل:
import yaml
class User(yaml.YAMLObject):
yaml_loader = yaml.SafeLoader
yaml_tag = u'!User'
def __init__(self, name, surname):
self.name= name
self.surname= surname
user = User('spam', 'eggs')
serialized_user = yaml.dump(user)
#Network
deserialized_user = yaml.safe_load(serialized_user)
print "name: %s, sname: %s" % (deserialized_user.name, deserialized_user.surname)
ميزة هذا هو أنه من السهل القيام به ؛ العيوب هي أنه يعمل فقط مع Safe_Load و Clutters صفك بسمات متعلقة بالتسلسل و metaclass.
إذا كان لديك العديد من العلامات ولا ترغب في إنشاء كائنات لها جميعًا ، أو في حالة عدم اهتمامك بالنوع الفعلي الذي تم إرجاعه ، فقط حول الوصول المنقط ، يمكنك التقاط جميع العلامات غير المحددة مع الكود التالي:
import yaml
class Blob(object):
def update(self, kw):
for k in kw:
setattr(self, k, kw[k])
from yaml.constructor import SafeConstructor
def my_construct_undefined(self, node):
data = Blob()
yield data
value = self.construct_mapping(node)
data.update(value)
SafeConstructor.add_constructor(None, my_construct_undefined)
class User(object):
def __init__(self, name, surname):
self.name= name
self.surname= surname
user = User('spam', 'eggs')
serialized_user = yaml.dump(user)
#Network
deserialized_user = yaml.safe_load(serialized_user)
print "name: %s, sname: %s" % (deserialized_user.name, deserialized_user.surname)
في حال كنت تتساءل لماذا my_construct_undefined
لديه yield
في الوسط: يسمح ذلك بتأسيس الكائن بشكل منفصل عن إنشاء أطفاله. بمجرد وجود الكائن ، يمكن الإشارة إليه في حالة وجود مرساة والأطفال (أو أطفالهم) مرجعًا. الآلية الفعلية لإنشاء الكائن أولاً تنشئه ، ثم تقوم next(x)
عليها لإنهاءها.