<commit_before>#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time: 2017/5/19 上午8:55
# @Author: Matrix
# @Site: https://github.com/blackmatrix7/apizen
# @File: types.py
# @Software: PyCharm
import re
import json
import copy
from json import JSONDecodeError
from datetime import datetime, date
from .exceptions import ApiSysExceptions

__author__ = 'blackmatrix'

"""
继承自某个内建类型，是为了解决Pycharm关于type hints的警告。
比如在一个函数中，type hints 使用自定义的DateTime，然后在函数内部使用了obj.year的方法，
因为DateTime本身与内建的datetime类型没有继承关系，并且没有year属性，Pycharm就会提示DateTime类型没有year属性的警告。
而type hints在接口参数中大量使用，这样会导致过多的警告信息。
为了解决这个问题，只好在类继承中，继承自某个内建的类型，然后通过元类，在创建类时，忽略掉内建类型的继承关系。
什么时候Pycharm不显示这个弱智的警告，就可以把内建类型的继承关系给取消了。

实质上,元类和继承系统内建的类型都是不必要的。不介意警告信息的话可以去除,让代码更加容易阅读。
"""


class Typed:

    @staticmethod
    def convert(*, value):
        return value


class TypeMeta(type):

    def __init__(cls,  classname, supers, clsdict):
        type.__init__(cls,  classname, supers, clsdict)

    def __new__(mcs, classname, supers, clsdict):
        return type.__new__(mcs, classname, (Typed, object), clsdict)


class TypeBase(metaclass=TypeMeta):
    @staticmethod
    def convert(*, value):
        raise NotImplementedError


class _Integer(int, TypeBase):
    __type__ = 'Integer'

    @staticmethod
    def convert(*, value):
        _value = copy.copy(value)
        _value = int(_value) if isinstance(_value, str) else _value
        if isinstance(_value, int):
            return _value
        else:
            raise ValueError


class _String(str, TypeBase):
    __type__ = 'String'

    @staticmethod
    def convert(*, value):
        return str(value)


class _Float(float, TypeBase):
    __type__ = 'Float'

    @staticmethod
    def convert(*, value):
        _value = copy.copy(value)
        return float(_value)


class _Dict(dict, TypeBase):
    __type__ = 'Dict'

    @staticmethod
    def convert(*, value):
        _value = copy.copy(value)
        _value = json.loads(_value) if isinstance(_value, str) else _value
        if isinstance(_value, dict):
            return _value
        else:
            raise ValueError


class _List(list, TypeBase):
    __type__ = 'List'

    @staticmethod
    def convert(*, value):
        _value = copy.copy(value)
        _value = json.loads(_value) if isinstance(_value, str) else _value
        if isinstance(_value, list):
            return _value
        else:
            raise ValueError


class _Date(date, TypeBase):
    __type__ = 'DateTime'

    def convert(self, *, value=None):
        _value = copy.copy(value)
        _value = datetime.strptime(_value, self.format_) if isinstance(_value, str) else _value
        return _value

    def __init__(self, format_='%Y-%m-%d'):
        self.format_ = format_
        super().__init__()


class _DateTime(datetime, TypeBase):
    __type__ = 'DateTime'

    def convert(self, *, value=None):
        _value = copy.copy(value)
        _value = datetime.strptime(_value, self.format_) if isinstance(_value, str) else _value
        return _value

    def __init__(self, format_='%Y-%m-%d %H:%M:%S'):
        self.format_ = format_
        super().__init__()


class _Bool(bool, TypeBase):

    __type__ = 'Bool'

    @staticmethod
    def convert(*, value=None):
        _value = str(value).lower()
        if _value in ('true', 'yes', '是', '0'):
            _value = True
        elif _value in ('false', 'no', '否', '1'):
            _value = False
        else:
            _value = value
        if isinstance(_value, bool):
            return _value
        else:
            raise ValueError


class _Email(_String):
    __type__ = 'Email'

    @staticmethod
    def convert(*, value):
        if re.match('^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$', value, flags=0):
            return value
        else:
            raise ValueError


class _Model(TypeBase):

    def __init__(self, model):
        self.model = model

    def convert(self, *, value=None):
        data = json.loads(value) if isinstance(value, str) else value
        result = self.model(**{key: value for key, value in data.items() if key in self.model.__table__.columns})
        result.raw_data = data
        return result


class _Money(_Float):

    __type__ = 'Money'

    @staticmethod
    def convert(*, value):
        value = float(value)
        if value >= 0 and value == round(value, 2):
            return value
        else:
            raise ValueError


Integer = _Integer()
List = _List()
Money = _Money()
Bool = _Bool()
Float = _Float()
String = _String()
Dict = _Dict()
Date = _Date
Model = _Model
DateTime = _DateTime
Email = _Email


def dict2model(data, model):
    """
    实验性功能，将dict转换成sqlalchemy model，不能处理 relationship<commit_msg>修改ApiZen内置类型的名称<commit_after>#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time: 2017/5/19 上午8:55
# @Author: Matrix
# @Site: https://github.com/blackmatrix7/apizen
# @File: types.py
# @Software: PyCharm
import re
import json
import copy
from json import JSONDecodeError
from datetime import datetime, date
from .exceptions import ApiSysExceptions

__author__ = 'blackmatrix'

"""
继承自某个内建类型，是为了解决Pycharm关于type hints的警告。
比如在一个函数中，type hints 使用自定义的DateTime，然后在函数内部使用了obj.year的方法，
因为DateTime本身与内建的datetime类型没有继承关系，并且没有year属性，Pycharm就会提示DateTime类型没有year属性的警告。
而type hints在接口参数中大量使用，这样会导致过多的警告信息。
为了解决这个问题，只好在类继承中，继承自某个内建的类型，然后通过元类，在创建类时，忽略掉内建类型的继承关系。
什么时候Pycharm不显示这个弱智的警告，就可以把内建类型的继承关系给取消了。

实质上,元类和继承系统内建的类型都是不必要的。不介意警告信息的话可以去除,让代码更加容易阅读。
"""


class Typed:

    @staticmethod
    def convert(*, value):
        return value


class TypeMeta(type):

    def __init__(cls,  classname, supers, clsdict):
        type.__init__(cls,  classname, supers, clsdict)

    def __new__(mcs, classname, supers, clsdict):
        return type.__new__(mcs, classname, (Typed, object), clsdict)


class TypeBase(metaclass=TypeMeta):
    @staticmethod
    def convert(*, value):
        raise NotImplementedError


class TypeInteger(int, TypeBase):
    __type__ = 'Integer'

    @staticmethod
    def convert(*, value):
        _value = copy.copy(value)
        _value = int(_value) if isinstance(_value, str) else _value
        if isinstance(_value, int):
            return _value
        else:
            raise ValueError


class TypeString(str, TypeBase):
    __type__ = 'String'

    @staticmethod
    def convert(*, value):
        return str(value)


class TypeFloat(float, TypeBase):
    __type__ = 'Float'

    @staticmethod
    def convert(*, value):
        _value = copy.copy(value)
        return float(_value)


class TypeDict(dict, TypeBase):
    __type__ = 'Dict'

    @staticmethod
    def convert(*, value):
        _value = copy.copy(value)
        _value = json.loads(_value) if isinstance(_value, str) else _value
        if isinstance(_value, dict):
            return _value
        else:
            raise ValueError


class TypeList(list, TypeBase):
    __type__ = 'List'

    @staticmethod
    def convert(*, value):
        _value = copy.copy(value)
        _value = json.loads(_value) if isinstance(_value, str) else _value
        if isinstance(_value, list):
            return _value
        else:
            raise ValueError


class TypeDate(date, TypeBase):
    __type__ = 'DateTime'

    def convert(self, *, value=None):
        _value = copy.copy(value)
        _value = datetime.strptime(_value, self.format_) if isinstance(_value, str) else _value
        return _value

    def __init__(self, format_='%Y-%m-%d'):
        self.format_ = format_
        super().__init__()


class TypeDatetime(datetime, TypeBase):
    __type__ = 'DateTime'

    def convert(self, *, value=None):
        _value = copy.copy(value)
        _value = datetime.strptime(_value, self.format_) if isinstance(_value, str) else _value
        return _value

    def __init__(self, format_='%Y-%m-%d %H:%M:%S'):
        self.format_ = format_
        super().__init__()


class TypeBool(bool, TypeBase):

    __type__ = 'Bool'

    @staticmethod
    def convert(*, value=None):
        _value = str(value).lower()
        if _value in ('true', 'yes', '是', '0'):
            _value = True
        elif _value in ('false', 'no', '否', '1'):
            _value = False
        else:
            _value = value
        if isinstance(_value, bool):
            return _value
        else:
            raise ValueError


class TypeEmail(TypeString):
    __type__ = 'Email'

    @staticmethod
    def convert(*, value):
        if re.match('^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$', value, flags=0):
            return value
        else:
            raise ValueError


class TypeModel(TypeBase):

    def __init__(self, model):
        self.model = model

    def convert(self, *, value=None):
        data = json.loads(value) if isinstance(value, str) else value
        result = self.model(**{key: value for key, value in data.items() if key in self.model.__table__.columns})
        result.raw_data = data
        return result


class TypeMoney(TypeFloat):

    __type__ = 'Money'

    @staticmethod
    def convert(*, value):
        value = float(value)
        if value >= 0 and value == round(value, 2):
            return value
        else:
            raise ValueError


Integer = TypeInteger()
List = TypeList()
Money = TypeMoney()
Bool = TypeBool()
Float = TypeFloat()
String = TypeString()
Dict = TypeDict()
Date = TypeDate
Model = TypeModel
DateTime = TypeDatetime
Email = TypeEmail


def dict2model(data, model):
    """
    实验性功能，将dict转换成sqlalchemy model，不能处理 relationship
{'fraction_non_alphanumeric': 0.11506100126209508, 'fraction_numerical': 0.005048380311316786, 'mean_word_length': 2.77491067884081, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 5, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1416225', 'n_tokens_mistral': 3348, 'n_tokens_neox': 3375, 'n_words': 759}