Skip to content

Commit

Permalink
init
Browse files Browse the repository at this point in the history
  • Loading branch information
elliotgao2 committed Mar 24, 2017
1 parent 01fe7ad commit c19e229
Show file tree
Hide file tree
Showing 4 changed files with 269 additions and 0 deletions.
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
## XData

A simple but useful library for validating data.

## Installation

## Usage

## License

MIT
24 changes: 24 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from setuptools import setup

import xdata

setup(
name=xdata.__name__,
version=xdata.__version__,
author="gaojiuli",
author_email="gaojiuli@gmail.com",
description="Simple data validation library",
license="MIT",
keywords="schema json validation",
url="https://github.com/gaojiuli/xdata",
py_modules=['xdata'],
platforms='any',
classifiers=[
"Development Status :: 2 - Pre-Alpha",
"Topic :: Utilities",
"Programming Language :: Python :: 3.5",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: Implementation :: PyPy",
"License :: OSI Approved :: MIT License",
],
)
7 changes: 7 additions & 0 deletions test_xdata.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from xdata import *


class UserSchema(Schema):
telephone = Str(max_length=12, min_length=16, requred=True)
password = Str(max_length=12, min_length=16, requred=True)
code = Str(length=4, requred=True)
227 changes: 227 additions & 0 deletions xdata.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
import re
from datetime import datetime

__name__ = 'xdata'
__version__ = '0.0.1'


class DataType:
def __init__(self, *args, **kwargs):
self.required = kwargs.get('required', False)
self.default = kwargs.get('default', None)
self.choices = kwargs.get('choices', [])
self.fn = kwargs.get('fn', None)
self.value = None
self.name = None

def check(self):
if self.default is not None and not self.required:
self.value = self.default

if self.required and self.value is None:
return '{} is required'.format(self.name)

if self.choices is not None and self.value not in self.choices:
return '{} should be in [{}]'.format(self.name, ','.join(self.choices))

if self.fn is not None and not self.fn(self.value):
return '{} should be satisfied function {}'.format(self.name, self.fn)


class Str(DataType):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.max_length = kwargs.get('max_length', None)
self.min_length = kwargs.get('max_length', None)
self.length = kwargs.get('length', None)
self.regex = kwargs.get('regex', None)

def check(self):
super().check()

if not isinstance(self.value, str):
return 'type of {} should string'.format(self.name)

if self.min_length is not None and len(self.value) < self.min_length:
return 'length of {} should be larger than {}'.format(self.name, self.min_length)

if self.max_length is not None and len(self.value) > self.max_length:
return 'length of {} should be less than {}'.format(self.name, self.max_length)

if self.length is not None and len(self.value) != self.length:
return 'length of {} should be equal to {}'.format(self.name, self.max_length)

if self.regex is not None and re.compile(self.regex).match(self.value):
return '{} should match with regex "{}"'.format(self.name, self.regex)


class Int(DataType):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.max = kwargs.get('max', None)
self.min = kwargs.get('min', None)

def check(self):
super().check()

if not isinstance(self.value, int):
return 'type of {} should be integer'.format(self.name)

if self.max is not None and self.value > self.max:
return '{} should be less than {}'.format(self.name, self.max)

if self.min is not None and self.value < self.min:
return '{} should be larger than {}'.format(self.name, self.min)


class Bool(DataType):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

def check(self):
super().check()
if not isinstance(self.value, bool):
return 'type of {} should be boolean'.format(self.name)


class Decimal(DataType):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.left = kwargs.get('left', 8)
self.right = kwargs.get('right', 2)

def check(self):
super().check()
if not isinstance(self.value, float):
return 'type of {} should be decimal'.format(self.name)

point_left, point_right = str(self.value).split('.')

if len(point_left) > self.left:
return 'length of the right of the decimal point should be less than {}'.format(self.left)
if len(point_right) == self.right:
return 'length of the left of the decimal point should be equal to {}'.format(self.right)


class DateTime(DataType):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.max_datetime = kwargs.get('max_datetime', None)
self.min_datetime = kwargs.get('min_datetime', None)

self.max_datetime = datetime.strptime(self.max_datetime, "%Y-%m-%d %H:%M:%S")
self.min_datetime = datetime.strptime(self.min_datetime, "%Y-%m-%d %H:%M:%S")

def check(self):
super().check()

try:
self.value = datetime.strptime(self.value, "%Y-%m-%d %H:%M:%S")
except ValueError:
return '{} should be right datetime'.format(self.name)

if self.value > self.max_datetime:
return '{} should be before {}'.format(self.name, self.max_datetime)
if self.value < self.min_datetime:
return '{} should be after {}'.format(self.name, self.min_datetime)


class Date(DataType):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.max_date = kwargs.get('max_date', None)
self.min_date = kwargs.get('min_date', None)

self.max_date = datetime.strptime(self.max_date, "%Y-%m-%d")
self.min_date = datetime.strptime(self.min_date, "%Y-%m-%d")

def check(self):
super().check()

try:
self.value = datetime.strptime(self.value, "%Y-%m-%d")
except ValueError:
return '{} should be right date'.format(self.name)

if self.value > self.max_date:
return '{} should be before {}'.format(self.name, self.max_date)
if self.value < self.min_date:
return '{} should be after {}'.format(self.name, self.min_date)


class Time(DataType):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.max_time = kwargs.get('max_time', None)
self.min_time = kwargs.get('min_time', None)

self.max_time = datetime.strptime(self.max_time, "%H:%M:%S")
self.min_time = datetime.strptime(self.min_time, "%H:%M:%S")

def check(self):
super().check()

try:
self.value = datetime.strptime(self.value, "%H:%M:%S")
except ValueError:
return '{} should be right time'.format(self.name)
if self.value > self.max_time:
return '{} should be before {}'.format(self.name, self.max_time)
if self.value < self.min_time:
return '{} should be after {}'.format(self.name, self.min_time)


class CheckException(Exception):
"""check exception"""


class SchemaMeta(type):
def __new__(mcs, name, bases, attrs):
checkers = {}
for k, v in attrs.items:
if isinstance(v, DataType):
checkers[k] = v

for k in checkers:
attrs.pop(k)

attrs['checkers'] = checkers
return super().__new__(mcs, name, bases, attrs)


class Schema(metaclass=SchemaMeta):
def __init__(self):
self._data = {}
self._validated_data = {}
self._errors = {}
self._checked = False

def validate(self, data):
self._data = data

for k, v in self._data.items():
if k in self.checkers:
setattr(self.checkers[k], 'value', v)

for k, checker in self.checkers:
result = checker.check()
if result is None:
setattr(self._validated_data, k, self.checkers[k].value)
else:
setattr(self._errors, k, result)

@property
def errors(self):
if not self._checked:
raise CheckException('data should be validate before visit errors')
return self._errors

@property
def validated_data(self):
if not self._checked:
raise CheckException('data should be validate before visit validated_data')
return self._validated_data

@property
def data(self):
return self._data

0 comments on commit c19e229

Please sign in to comment.