Advertisement
Guest User

Untitled

a guest
Mar 9th, 2018
156
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 13.36 KB | None | 0 0
  1. # var s='выравнять разделитель экрана по этой стрелке -->';prompt('Ctrl+C:',`#${' '.repeat(79-s.length)+s}\n`)
  2. #                               выравнять разделитель экрана по этой стрелке -->
  3. class ValidationError(Exception):
  4.   pass
  5.  
  6.  
  7. import re
  8. from abc import ABC, abstractmethod
  9.  
  10.  
  11. def not_none(f):
  12.   def decorator(*args, **kw):
  13.     if args[2] is not None:
  14.       f(*args, **kw)
  15.   return decorator
  16.  
  17.  
  18. class BaseValidator(ABC):
  19.   @abstractmethod
  20.   def validate(self, field, value):
  21.     pass
  22.  
  23.  
  24. class RequiredValidator(BaseValidator):
  25.   def validate(self, field, value):
  26.     if value is None and field.required and not field.nullable:
  27.       raise ValidationError('required')
  28.  
  29.  
  30. class RangeValidator(BaseValidator):
  31.   @not_none
  32.   def validate(self, field, value):
  33.     if field.min is not None and value < field.min:
  34.       raise ValidationError(
  35.         'must be greater than or equal to {}'.format(field.min)
  36.       )
  37.     if field.max is not None and value > field.max:
  38.       raise ValidationError(
  39.         'must be less than or equal to {}'.format(field.max)
  40.       )
  41.  
  42.  
  43. class LengthValidator(BaseValidator):
  44.   @not_none
  45.   def validate(self, field, value):
  46.     length = len(value)
  47.     if field.minlength is not None and length < field.minlength:
  48.       raise ValidationError(
  49.         'must have at least {} character(s)'.format(field.minlength)
  50.       )
  51.     if field.maxlength is not None and length > field.maxlength:
  52.       raise ValidationError(
  53.         'must have no more than {} character(s)'.format(field.maxlength)
  54.       )
  55.  
  56.  
  57. class RegexValidator(BaseValidator):
  58.   @not_none
  59.   def validate(self, field, value):
  60.     regex = field.regex
  61.     if regex is not None:
  62.       if not isinstance(regex, re._pattern_type):
  63.         regex = re.compile(regex)
  64.       if not regex.match(value):
  65.         raise ValidationError(
  66.           'does not match pattern {!r}'.format(regex.pattern)
  67.         )
  68.  
  69.  
  70. class EnumValidator(BaseValidator):
  71.   @not_none
  72.   def validate(self, field, value):
  73.     if value not in field.enum:
  74.       raise ValidationError('must be one of {}'.format(field.enum))
  75.  
  76.  
  77. class IterableValidator(BaseValidator):
  78.   @not_none
  79.   def validate(self, field, value):
  80.     for item in value:
  81.       field.base_field.validate(item)
  82.  
  83.  
  84. class MetaField(type):
  85.   # Наследуем валидаторы
  86.   def __new__(mcls, name, bases, attrs):
  87.     cls = super().__new__(mcls, name, bases, attrs)
  88.     parents = [b for b in bases if isinstance(b, mcls)]
  89.     if not parents:
  90.       return cls
  91.     validators = []
  92.     for p in reversed(parents):
  93.       validators += getattr(p, 'validators', [])
  94.     cls.validators = validators + cls.validators
  95.     return cls
  96.  
  97.  
  98. import inspect
  99.  
  100.  
  101. # TODO: добавить отслеживание изменений
  102. class BaseField(metaclass=MetaField):
  103.   validators = [RequiredValidator]
  104.  
  105.   def __init__(
  106.     self,
  107.     name=None,
  108.     default=None,
  109.     required=False,
  110.     # https://docs.mongodb.com/manual/indexes/#index-types
  111.     index_type=None,
  112.     sparse=False,
  113.     unique=False,
  114.     # list field names
  115.     unique_with=None,
  116.     nullable=False
  117.   ):
  118.     self.name = name
  119.     self.default = default
  120.     self.required = required
  121.     self.index_type = index_type
  122.     self.sparse = sparse
  123.     self.unique = unique
  124.     self.unique_with = unique_with
  125.     self.nullable = nullable
  126.  
  127.   @property
  128.   def name(self):
  129.     return self._name
  130.  
  131.   @name.setter
  132.   def name(self, value):
  133.     if value is not None:
  134.       if not isinstance(value, str):
  135.         raise TypeError('must be a string')
  136.       if re.search(r'[$.\x00]', value):
  137.         raise TypeError('name contains illegal characters')
  138.     self._name = value
  139.  
  140.   # __set, __get__, __delete__ срабатывают только, если объявлены как свойства
  141.   # класса
  142.   def __set__(self, instance, value):
  143.     # В MySQL если мы попытаемся при update установить значение NULL, то будет
  144.     # использовано дефолтное значение. Тут такая же логика.
  145.     # Дефолтная функция так же может None возвратить.
  146.     value = self.get_default() if value is None else value
  147.     if value is None:
  148.       if not self.nullable:
  149.         # Удаляем поле, если ему присваивается None.
  150.         instance.delete_data(self.name)
  151.         return
  152.     else:
  153.       value = self.prepare_value(value)
  154.     instance.set_data(self.name, value)
  155.  
  156.   def __get__(self, instance, owner):
  157.     if instance is None:
  158.       return self
  159.     return instance.get_data(self.name)
  160.  
  161.   def __delete__(self, instance):
  162.     raise TypeError('cannot delete dependency')
  163.  
  164.   def prepare_value(self, value):
  165.     return value
  166.  
  167.   def has_default(self):
  168.     return self.default is not None
  169.  
  170.   def get_default(self):
  171.     return self.default() if callable(self.default) else self.default
  172.  
  173.   def validate(self, value):
  174.     for validator in self.validators:
  175.       if inspect.isclass(validator):
  176.         validator = validator()
  177.       if not isinstance(validator, BaseValidator):
  178.         raise ValueError()
  179.       validator.validate(self, value)
  180.  
  181.   def to_mongo(self, value):
  182.     """ Используется перед вставкой/обновлением. """
  183.     return value
  184.  
  185.   def to_json(self, value):
  186.     """ В нашем API данные выводятся в JSON. """
  187.     return value
  188.  
  189.  
  190. import bson
  191. import dateutil
  192. import datetime
  193. import uuid
  194.  
  195.  
  196. class ObjectIdField(BaseField):
  197.   def prepare_value(self, value):
  198.     return value if isinstance(value, bson.ObjectId) else bson.ObjectId(value)
  199.  
  200.   def to_json(self, value):
  201.     return str(value)
  202.  
  203.  
  204. class UUIDField(BaseField):
  205.   def prepare_value(self, value):
  206.     return value if isinstance(value, uuid.UUID) else uuid.UUID(value)
  207.  
  208.   def to_json(self, value):
  209.     return str(value)
  210.  
  211.  
  212. class DateTimeField(BaseField):
  213.   def prepare_value(self, value):
  214.     if isinstance(value, datetime.datetime):
  215.       return value
  216.     if isinstance(value, datetime.date):
  217.       return datetime.datetime(value.year, value.month, value.day)
  218.     if isinstance(value, (float, int)):
  219.       return datetime.datetime.utcfromtimestamp(value)
  220.     if isinstance(value, str):
  221.       # https://stackoverflow.com/questions/127803/how-to-parse-an-iso-8601-formatted-date
  222.       return dateutil.parser.parse(value)
  223.     raise ValueError(value)
  224.  
  225.   def to_json(self, value):
  226.     return value.isoformat()
  227.  
  228.  
  229. class BooleanField(BaseField):
  230.   def prepare_value(self, value):
  231.     return bool(value)
  232.  
  233.  
  234. class FloatField(BaseField):
  235.   validators = [RangeValidator]
  236.   def __init__(
  237.     self,
  238.     name=None,
  239.     default=None,
  240.     required=False,
  241.     min=None,
  242.     max=None,
  243.     index_type=None,
  244.     sparse=False,
  245.     unique=False,
  246.     unique_with=None,
  247.     nullable=False
  248.   ):
  249.     super().__init__(
  250.       name,
  251.       default,
  252.       required,
  253.       index_type,
  254.       sparse,
  255.       unique,
  256.       unique_with,
  257.       nullable
  258.     )
  259.     self.min = min
  260.     self.max = max
  261.  
  262.   def prepare_value(self, value):
  263.     return float(value)
  264.  
  265.  
  266. class IntField(FloatField):
  267.   def prepare_value(self, value):
  268.     return int(value)
  269.  
  270.  
  271. class StringField(BaseField):
  272.   validators = [LengthValidator, RegexValidator]
  273.  
  274.   def __init__(
  275.     self,
  276.     name=None,
  277.     default=None,
  278.     required=False,
  279.     minlength=None,
  280.     maxlength=None,
  281.     regex=None,
  282.     index_type=None,
  283.     sparse=False,
  284.     unique=False,
  285.     unique_with=None,
  286.     nullable=False
  287.   ):
  288.     super().__init__(
  289.       name,
  290.       default,
  291.       required,
  292.       index_type,
  293.       sparse,
  294.       unique,
  295.       unique_with,
  296.       nullable
  297.     )
  298.     self.minlength = minlength
  299.     self.maxlength = maxlength
  300.     self.regex = regex
  301.  
  302.   def prepare_value(self, value):
  303.     return str(value)
  304.  
  305.  
  306. class BytesField(StringField):
  307.   def prepare_value(self, value):
  308.     return bytes(value)
  309.  
  310.  
  311. class EnumField(BaseField):
  312.   validators = [EnumValidator]
  313.  
  314.   def __init__(
  315.     self,
  316.     enum,
  317.     name=None,
  318.     default=None,
  319.     required=False,
  320.     index_type=None,
  321.     sparse=False,
  322.     unique=False,
  323.     unique_with=None,
  324.     nullable=False
  325.   ):
  326.     super().__init__(
  327.       name,
  328.       default,
  329.       required,
  330.       index_type,
  331.       sparse,
  332.       unique,
  333.       unique_with,
  334.       nullable
  335.     )
  336.     self.enum = enum
  337.  
  338.  
  339. class ListField(BaseField):
  340.   validators = [LengthValidator, IterableValidator]
  341.  
  342.   def __init__(
  343.     self,
  344.     base_field,
  345.     name=None,
  346.     default=None,
  347.     required=False,
  348.     minlength=None,
  349.     maxlength=None,
  350.     index_type=None,
  351.     sparse=False,
  352.     unique=False,
  353.     unique_with=None,
  354.     nullable=False
  355.   ):
  356.     super().__init__(
  357.       name,
  358.       default,
  359.       required,
  360.       index_type,
  361.       sparse,
  362.       unique,
  363.       unique_with,
  364.       nullable
  365.     )
  366.     self.base_field = base_field
  367.     self.minlength = minlength
  368.     self.maxlength = maxlength
  369.  
  370.   def prepare_value(self, value):
  371.     return List(self.base_field, value)
  372.  
  373.  
  374. # https://docs.python.org/3/tutorial/datastructures.html#more-on-lists
  375. class List(list):
  376.   def __init__(self, base_field, *args, **kw):
  377.     self.base_field = base_field
  378.     L = list(*args, **kw)
  379.     self.extend(L)
  380.  
  381.   def prepare_value(self, v):
  382.     return self.base_field.prepare_value(v)
  383.  
  384.   def append(self, v):
  385.     v = self.prepare_value(v)
  386.     super().append(v)
  387.     # mark as changed
  388.  
  389.   def insert(self, i, v):
  390.     v = self.prepare_value(v)
  391.     super().insert(i, v)
  392.     # mark as changed
  393.  
  394.   def extend(self, L):
  395.     if L:
  396.       super().extend([self.prepare_value(v) for v in L])
  397.       # mark as changed
  398.  
  399.   def pop(self, *args, **kw):
  400.     super().pop(*args, **kw)
  401.     # mark as changed
  402.  
  403.   def sort(self, *args, **kw):
  404.     super().sort(*args, **kw)
  405.     # mark as changed
  406.  
  407.   def __setitem__(self, i, v):
  408.     v = self.prepare_value(v)
  409.     super().__setitem__(i, v)
  410.     # mark as changed
  411.  
  412.   def __delitem__(self, i):
  413.     super().__delitem__(i)
  414.     # mark as changed
  415.  
  416.  
  417. import re
  418.  
  419.  
  420. def snake_case(s):
  421.   s = re.sub(r'([A-Z]+)([A-Z])', r'\1_\2', s)
  422.   s = re.sub(r'([a-z])([A-Z])', r'\1_\2', s)
  423.   return s.lower()
  424.  
  425.  
  426.  
  427. class Meta:
  428.   indexes = []
  429.   collection = None
  430.   # Что еще нужно?
  431.  
  432.  
  433.  
  434. class MetaDocument(type):
  435.   def __new__(mcls, name, bases, attrs):
  436.     cls = super().__new__(mcls, name, bases, attrs)
  437.     fields = {}
  438.     for k, v in attrs.items():
  439.       if isinstance(v, BaseField):
  440.         if v.name is None:
  441.           v.name = k
  442.         fields[v.name] = v
  443.     cls._fields = fields
  444.     if cls.is_toplevel:
  445.       # Наследуем индексы
  446.       if hasattr(cls, 'Meta'):
  447.         class _Meta(cls.Meta, Meta):
  448.           pass
  449.         meta = _Meta()
  450.       else:
  451.         meta = Meta()
  452.       for base in reversed(bases):
  453.         if isinstance(base, mcls) and base.is_toplevel:
  454.           meta.indexes = base._meta.indexes + meta.indexes
  455.       if meta.collection is None:
  456.         meta.collection = snake_case(cls.__name__)
  457.       cls._meta = meta
  458.     return cls
  459.  
  460.  
  461. # TODO: добавить поддержку вложенных документов
  462. class EmbeddedDocument(metaclass=MetaDocument):
  463.   _toplevel = False
  464.  
  465.   def __init__(self, *args, **kw):
  466.     data = dict(*args, **kw)
  467.     self._data = {}
  468.     for key in self._fields.keys():
  469.       setattr(self, key, data.get(key))
  470.  
  471.   @property
  472.   def is_toplevel(self):
  473.     return self._toplevel
  474.  
  475.   def get_field(self, name):
  476.     return self._fields.get(name)
  477.  
  478.   def get_data(self, name):
  479.     return self._data.get(name)
  480.  
  481.   def set_data(self, name, value):
  482.     self._data[name] = value
  483.  
  484.   def delete_data(self, name):
  485.     self._data.pop(name, 0)
  486.  
  487.   def clean(self):
  488.     pass
  489.  
  490.   def validate(self):
  491.     self.clean()
  492.     errors = []
  493.     for name, field in self._fields.items():
  494.       try:
  495.         field.validate(self.get_data(name))
  496.       except ValidationError as e:
  497.         errors.append('{}: {}'.format(name, str(e)))
  498.     if errors:
  499.       raise ValidationError(
  500.         '{} validation failed: {}'.format(
  501.           self.__class__.__qualname__, '; '.join(errors)
  502.         )
  503.       )
  504.  
  505.   def to_mongo(self):
  506.     ret = {}
  507.     for name, value in self._data.items():
  508.       field = self.get_field(name)
  509.       ret[name] = field.to_mongo(value)
  510.     return ret
  511.  
  512.   def to_json(self):
  513.     ret = {}
  514.     for name, value in self._data.items():
  515.       field = self.get_field(name)
  516.       ret[name] = field.to_json(value)
  517.     return rett
  518.  
  519.  
  520. class Document(EmbeddedDocument):
  521.   _toplevel = True
  522.  
  523.   _id = ObjectIdField()
  524.  
  525.   # Тут методы для работы с базой
  526.  
  527.  
  528. class Foo(Document):
  529.   class Meta:
  530.     indexes = ['foo']
  531.  
  532.  
  533. class Bar(Foo):
  534.   class Meta:
  535.     indexes = ['bar']
  536.  
  537.  
  538. class Baz(Bar):
  539.   class Meta:
  540.     indexes = ['baz']
  541.  
  542.  
  543. print(Baz._meta.indexes)
  544. assert(Baz._meta.indexes == ['foo', 'bar', 'baz'])
  545.  
  546.  
  547. class User(Document):
  548.   username = StringField(regex=r'\w{3,10}$', required=True)
  549.   password = StringField(minlength=6, maxlength=255, required=False)
  550.   registration = DateTimeField(default=datetime.datetime.utcnow, required=True)
  551.   skills = ListField(StringField(regex=r'\w{1,31}$'), minlength=3, maxlength=10, required=True)
  552.  
  553.  
  554. user = User(username='tester', skills=['foo', 'bar', 'baz'])
  555. user.validate()
  556. print(user.to_json())
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement