jit-utils-backend 0.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
jit_utils/__init__.py ADDED
@@ -0,0 +1,152 @@
1
+ # -*-coding:utf-8-*-
2
+ """
3
+ JIT Utils Backend - 极态后端工具包
4
+
5
+ A comprehensive utility package for backend development with JIT.
6
+
7
+ 主要功能:
8
+ - 时间处理工具
9
+ - 字符串处理工具
10
+ - 二维码和条形码生成
11
+ - 数据验证
12
+ - 网络工具
13
+ - 签名工具
14
+ 等等...
15
+ """
16
+
17
+ __version__ = '0.0.1'
18
+ __author__ = 'zangtao'
19
+
20
+ # 导入主要的工具类和函数
21
+ try:
22
+ from .decorator import forward
23
+ except ImportError:
24
+ pass
25
+
26
+ # 时间工具
27
+ try:
28
+ from . import time as time_utils
29
+ # 导出一些常用的时间函数
30
+ from .time import (
31
+ now, today, get, dayShift,
32
+ monday, weekShift, monthStart, monthShift,
33
+ quarterStart, quarterShift, yearStart, yearShift,
34
+ getTimestamp, timeStampToDateTime, strToTimestamp,
35
+ formatNow, datetime2string, string2datetime
36
+ )
37
+ except ImportError:
38
+ pass
39
+
40
+ # 字符串工具
41
+ try:
42
+ from . import string as string_utils
43
+ from .string import (
44
+ randomString, randomNum, getUuidStr,
45
+ md5Bytes, md5Str, getFileMd5,
46
+ renderTemplateString
47
+ )
48
+ except ImportError:
49
+ pass
50
+
51
+ # 二维码工具
52
+ try:
53
+ from .qrcode import Qrcode
54
+ except ImportError:
55
+ Qrcode = None
56
+
57
+ # 条形码工具
58
+ try:
59
+ from .barcode import Barcode
60
+ except ImportError:
61
+ Barcode = None
62
+
63
+ # 验证工具
64
+ try:
65
+ from .validator import ParamsValidator
66
+ except ImportError:
67
+ ParamsValidator = None
68
+
69
+ # 网络工具
70
+ try:
71
+ from . import network
72
+ except ImportError:
73
+ pass
74
+
75
+ # 签名工具
76
+ try:
77
+ from . import signature
78
+ except ImportError:
79
+ pass
80
+
81
+ # 匹配工具
82
+ try:
83
+ from . import matchTool
84
+ except ImportError:
85
+ pass
86
+
87
+ # 类工具
88
+ try:
89
+ from . import clsTool
90
+ except ImportError:
91
+ pass
92
+
93
+ # 转换工具
94
+ try:
95
+ from .convert import Converter, MemoryCompiler
96
+ except ImportError:
97
+ # 如果导入失败,创建占位符避免 __all__ 报错
98
+ Converter = None
99
+ MemoryCompiler = None
100
+
101
+ # 异常处理
102
+ try:
103
+ from . import exceptions
104
+ except ImportError:
105
+ pass
106
+
107
+ # 工作日常量
108
+ try:
109
+ from . import workday_constants
110
+ except ImportError:
111
+ pass
112
+
113
+ # 配置相关
114
+ try:
115
+ from . import config
116
+ except ImportError:
117
+ pass
118
+
119
+ # 定义 __all__ 列表,控制 from jit_utils import * 的行为
120
+ __all__ = [
121
+ # 版本信息
122
+ '__version__',
123
+ '__author__',
124
+
125
+ # 装饰器
126
+ 'forward',
127
+
128
+ # 时间工具
129
+ 'time_utils',
130
+ 'now', 'today', 'get', 'dayShift',
131
+ 'monday', 'weekShift', 'monthStart', 'monthShift',
132
+ 'quarterStart', 'quarterShift', 'yearStart', 'yearShift',
133
+ 'getTimestamp', 'timeStampToDateTime', 'strToTimestamp',
134
+ 'formatNow', 'datetime2string', 'string2datetime',
135
+
136
+ # 字符串工具
137
+ 'string_utils',
138
+ 'randomString', 'randomNum', 'getUuidStr',
139
+ 'md5Bytes', 'md5Str', 'getFileMd5',
140
+ 'renderTemplateString',
141
+
142
+ # 二维码和条形码
143
+ 'Qrcode', 'Barcode',
144
+
145
+ # 验证工具
146
+ 'ParamsValidator',
147
+
148
+ # 其他模块
149
+ 'network', 'signature', 'matchTool',
150
+ 'clsTool', 'exceptions', 'workday_constants',
151
+ 'config', 'Converter', 'MemoryCompiler'
152
+ ]
@@ -0,0 +1,73 @@
1
+ # -*-coding:utf-8-*-
2
+ """
3
+ Created on 2024/11/09
4
+
5
+ @author: 臧韬
6
+
7
+ @desc: 默认描述
8
+ """
9
+
10
+ import base64
11
+ import hashlib
12
+ import json
13
+ import time
14
+
15
+ from jit.commons.utils.logger import log
16
+
17
+
18
+ class Sign(object):
19
+ """
20
+ 这个签名算法用于认证服务和api授权元素
21
+ """
22
+
23
+ EXPIRE_TIME = 5 * 60 * 1000
24
+ # 签名返回的错误信息,如果是错误信息,返回空tuple,兼容原来通过bool方法校验签名是否通过的写法
25
+ TIME_OUT_ERROR = []
26
+ SIGN_CHECK_ERROR = []
27
+ SIGN_CHECK_SUCCESS = [0]
28
+
29
+ def __init__(self, secret):
30
+ self.secret = secret
31
+
32
+ def encode(self, args, timestamp, debug=False):
33
+ return self.getSign({**args, "timestamp": timestamp, "secret": self.secret}, debug=debug)
34
+
35
+ @staticmethod
36
+ def getSign(args, debug=False):
37
+ nArgs = {}
38
+ for k, v in args.items():
39
+ nArgs[k.lower()] = v
40
+ sortedKeys = sorted(nArgs.keys())
41
+ params = []
42
+ for key in sortedKeys:
43
+ value = nArgs[key]
44
+ if not isinstance(value, str):
45
+ value = json.dumps(value, sort_keys=True, separators=(",", ":"), ensure_ascii=False)
46
+ params.append(f"{key}={value}")
47
+ if debug:
48
+ # 隐藏secret
49
+ for idx, param in enumerate(params):
50
+ if param.split("=")[0] == "secret":
51
+ params[idx] = "secret=*********************"
52
+
53
+ paramStr = "&".join(params)
54
+
55
+ apiAuthDebug = {
56
+ "paramStr": paramStr,
57
+ }
58
+ log.debug(",".join("{k}:{v}".format(k=k, v=v) for k, v in apiAuthDebug.items()))
59
+ if hasattr(app.request, "respExtraData"):
60
+ app.request.respExtraData["apiAuthDebug"] = apiAuthDebug
61
+ return hashlib.sha1(base64.b64encode("&".join(params).encode("utf-8"))).hexdigest()
62
+
63
+ def verify(self, args):
64
+ args = args.copy()
65
+ timestamp = args.pop("timestamp")
66
+ if abs(int(time.time() * 1000) - int(timestamp)) > self.EXPIRE_TIME:
67
+ return self.TIME_OUT_ERROR
68
+ signature = args.pop("accessSign")
69
+ debug = app.request.headers.get("debug") == "1"
70
+ if signature == self.encode(args, timestamp, debug=debug):
71
+ return self.SIGN_CHECK_SUCCESS
72
+ else:
73
+ return self.SIGN_CHECK_ERROR
jit_utils/barcode.py ADDED
@@ -0,0 +1,50 @@
1
+ # -*-coding:utf-8-*-
2
+ """
3
+ Created on 2025/02/10
4
+
5
+ @author: 臧韬
6
+
7
+ @desc: 默认描述
8
+ """
9
+ import base64
10
+ import io
11
+
12
+ import barcode
13
+ from barcode.errors import BarcodeError
14
+ from barcode.writer import ImageWriter
15
+
16
+
17
+ class Barcode(object):
18
+ """
19
+ 二维码模块,暂用于文件渲染
20
+ """
21
+
22
+ def __init__(self, value: str, codeType="code128"):
23
+ self.value = value
24
+ self.codeType = codeType
25
+
26
+ def toByte(self):
27
+ file = self.toFile()
28
+ data = file.read()
29
+ return data
30
+
31
+ def toFile(self):
32
+ obj = barcode.get(self.codeType, self.value, writer=ImageWriter())
33
+ # 保存条形码为图像文件
34
+ imageBuffer = io.BytesIO()
35
+ obj.write(fp=imageBuffer)
36
+ imageBuffer.seek(0)
37
+ return imageBuffer
38
+
39
+ def toStr(self):
40
+ if not self.value:
41
+ return ""
42
+ try:
43
+ b64Code = base64.b64encode(self.toByte()).decode("utf-8")
44
+ except BarcodeError as e:
45
+ return "ERROR:{}".format(str(e))
46
+
47
+ return "<image:{}>".format(b64Code)
48
+
49
+ def __str__(self):
50
+ return self.toStr()
jit_utils/clsTool.py ADDED
@@ -0,0 +1,71 @@
1
+ # -*-coding:utf-8-*-
2
+ """
3
+ Created on 2024/09/29
4
+
5
+ @author: 臧韬
6
+
7
+ @desc: 因为框架打包不重启的时候会出现isinstance方法和issubclass函数出现问题,
8
+ 所以重新实现这两个方法,通过类名来确定是否为子类,而不是通过对象ID。
9
+ """
10
+
11
+
12
+ def issubclassByName(cls, class_or_tuple: type | tuple):
13
+ if isinstance(class_or_tuple, tuple):
14
+ for cls_name in class_or_tuple:
15
+ if issubclassByName(cls, cls_name):
16
+ return True
17
+ else:
18
+ return False
19
+
20
+ if not isinstance(cls, type):
21
+ raise TypeError("issubclassByName() arg 0 must be a class")
22
+
23
+ if not isinstance(class_or_tuple, type):
24
+ raise TypeError("isinstanceByName() arg 1 must be a class or tuple with class")
25
+
26
+ __className = class_or_tuple.__name__
27
+ if cls is type:
28
+ # 特殊情况
29
+ mroNames = [item.__name__ for item in type.mro(type)]
30
+ else:
31
+ mroNames = [item.__name__ for item in cls.mro()]
32
+ if __className in mroNames:
33
+ return True
34
+ else:
35
+ return False
36
+
37
+
38
+ def isinstanceByName(obj, class_or_tuple: type | tuple):
39
+ if isinstance(class_or_tuple, tuple):
40
+ for cls_name in class_or_tuple:
41
+ if isinstanceByName(obj, cls_name):
42
+ return True
43
+ else:
44
+ return False
45
+
46
+ if not isinstance(obj, object):
47
+ raise TypeError("isinstanceByName() arg 0 must be an object")
48
+
49
+ if not isinstance(class_or_tuple, type):
50
+ raise TypeError("isinstanceByName() arg 1 must be a class or tuple with class")
51
+
52
+ cls = type(obj)
53
+ if cls.__name__ == class_or_tuple.__name__:
54
+ return True
55
+ return issubclassByName(cls, class_or_tuple)
56
+
57
+
58
+ if __name__ == "__main__":
59
+ A = type("A", (object,), {})
60
+ B = type("B", (A,), {})
61
+
62
+ a = A()
63
+ b = B()
64
+
65
+ print("class B is subclass of A: ", issubclassByName(B, A))
66
+ print("object b is instance of A: ", isinstanceByName(b, A))
67
+ print("object b is instance of B: ", isinstanceByName(b, B))
68
+ print("object b is instance of B or A: ", isinstanceByName(b, (B, A)))
69
+ print("object a is instance of object: ", isinstanceByName(a, object))
70
+ print("class A is instance of type: ", isinstanceByName(A, type))
71
+ print("object a is not instance of type: ", not isinstanceByName(a, type))
@@ -0,0 +1,11 @@
1
+ # -*-coding:utf-8-*-
2
+ """
3
+ Created on 2023/10/23
4
+
5
+ @author: 臧韬
6
+
7
+ @desc: 默认描述
8
+ """
9
+
10
+ from commons.utils.config.config import TlConfig
11
+ from commons.utils.config.field import Field, IntField, StringField
@@ -0,0 +1,77 @@
1
+ # -*-coding:utf-8-*-
2
+ """
3
+ Created on 2023/10/23
4
+
5
+ @author: 臧韬
6
+
7
+ @desc: 默认描述
8
+ """
9
+ from commons.utils.config.config import TlConfig
10
+ from commons.utils.config.field import IntField, StringField
11
+
12
+
13
+ class TlConfigCase(object):
14
+ def normal(self):
15
+ """这是一个普通使用的例子"""
16
+
17
+ class IDCardConfig(TlConfig):
18
+ name = StringField(maxLen=8, minLen=2, required=True)
19
+ age = IntField(maxNum=200, minNum=0, default=18)
20
+
21
+ config = IDCardConfig(name="张三", age=18)
22
+ print("normal case: config.name -> {}".format(config.name))
23
+ print("normal case: config.age -> {}".format(config.age))
24
+ print("normal case: config.toDict -> {}".format(config.value))
25
+
26
+ # 如果没有传入会使用默认值
27
+ config2 = IDCardConfig(name="李四")
28
+ print("normal case: config2.age -> {}".format(config2.age))
29
+
30
+ try:
31
+ IDCardConfig(age=21)
32
+ except Exception as e:
33
+ print("normal case: error -> {}".format(e))
34
+
35
+ def transform(self):
36
+ class IDCardConfig(TlConfig):
37
+ name = StringField(maxLen=8, minLen=2)
38
+ age = IntField(maxNum=200, minNum=0)
39
+
40
+ config = IDCardConfig(name="张三", age="18")
41
+ print("transform case: config.age -> {}".format(config.age))
42
+ print("transform case: config.age type -> {}".format(type(config.age)))
43
+
44
+ def customTransform(self):
45
+ # 通过自定义的字段来自定义转换方式
46
+ class DoubleIntField(IntField):
47
+ def __init__(self, *args, **kwargs):
48
+ super(DoubleIntField, self).__init__(*args, **kwargs)
49
+
50
+ self.transformFunc = self.toDouble
51
+
52
+ @staticmethod
53
+ def toDouble(value):
54
+ return value * 2
55
+
56
+ class MyConfig(TlConfig):
57
+ # 使用自定义的转换方法
58
+ double = DoubleIntField(maxNum=200)
59
+
60
+ config = MyConfig(double=100)
61
+
62
+ # 输出的值变成了输入的两倍
63
+ print("transform case: config.double -> {}".format(config.double))
64
+
65
+ config.double = 20
66
+
67
+ # 如果是后面赋值,也会及时校验和转换
68
+ print("transform case: config.double -> {}".format(config.double))
69
+ # 检测会在转换后进行检测合法性,这里输入101,通过转换后变成202,超过了字段设置的最大值,所以会触发报错
70
+ try:
71
+ config.double = 101
72
+ except Exception as e:
73
+ print("transform case: errmsg -> {}".format(e))
74
+
75
+
76
+ if __name__ == "__main__":
77
+ TlConfigCase().normal()
@@ -0,0 +1,90 @@
1
+ # -*-coding:utf-8-*-
2
+ """
3
+ Created on 2023/10/23
4
+
5
+ @author: 臧韬
6
+
7
+ @desc: 默认描述
8
+ """
9
+ from commons.utils.config.field import Field, NoneType
10
+
11
+ __all__ = ["TlConfig"]
12
+
13
+
14
+ class TlConfigMeta(type):
15
+ def __new__(mcs, name, bases, attrs):
16
+ _fieldMap = {}
17
+ _newAttrs = {"_fieldMap": _fieldMap}
18
+
19
+ for key, value in attrs.items():
20
+ if isinstance(value, Field):
21
+ # 字段名写入到字段对象中
22
+ value.name = key
23
+ _fieldMap[key] = value
24
+ else:
25
+ _newAttrs[key] = value
26
+
27
+ _cls = super(TlConfigMeta, mcs).__new__(mcs, name, bases, _newAttrs)
28
+ return _cls
29
+
30
+
31
+ class TlConfig(metaclass=TlConfigMeta):
32
+ _autoCheck = True
33
+
34
+ def __init__(self, **kwargs):
35
+ self._originData = kwargs
36
+ self._transformData = {}
37
+ self._isTransform = False
38
+ for key, value in self._fieldMap.items():
39
+ super(TlConfig, self).__setattr__(key, NoneType)
40
+ if self._autoCheck:
41
+ self.check()
42
+
43
+ def check(self):
44
+ if not self._isTransform:
45
+ for fieldKey, field in self._fieldMap.items():
46
+ value = self._originData.get(fieldKey, NoneType)
47
+ setattr(self, fieldKey, value)
48
+ self._isTransform = True
49
+
50
+ def checkOne(self, field, value):
51
+ value = field.transform(value)
52
+ if value is NoneType and field.default is not NoneType:
53
+ value = field.default
54
+ field.check(value)
55
+ self._transformData[field.name] = value
56
+ super(TlConfig, self).__setattr__(field.name, value)
57
+
58
+ return value
59
+
60
+ def toDict(self):
61
+ if not self._isTransform:
62
+ print("警告,该配置没有经过校验和转换,请先调用 self.check()")
63
+ return self._transformData
64
+
65
+ def __setattr__(self, key, value):
66
+ if not self._autoCheck:
67
+ super(TlConfig, self).__setattr__("_isTransform", False)
68
+ if key in self._fieldMap:
69
+ self._originData[key] = value
70
+ if self._autoCheck:
71
+ field = self._fieldMap[key]
72
+ self.checkOne(field, value)
73
+ else:
74
+ super(TlConfig, self).__setattr__(key, value)
75
+
76
+ @classmethod
77
+ def getParamInfo(cls):
78
+ return list(cls._fieldMap.keys())
79
+
80
+ def __getattribute__(self, item):
81
+ """
82
+ DEBUG 代码排查模式,尽快删除
83
+ :param item:
84
+ :return:
85
+ """
86
+ value = super(TlConfig, self).__getattribute__(item)
87
+ if item.startswith("__"):
88
+ return value
89
+ # log.debug("DEBUG MODE: CLS:{}, KEY:{}, VALUE:{}".format(self.__class__.__name__, item, str(value)))
90
+ return value
@@ -0,0 +1,17 @@
1
+ # -*-coding:utf-8-*-
2
+ """
3
+ Created on 2023/10/23
4
+
5
+ @author: 臧韬
6
+
7
+ @desc: 默认描述
8
+ """
9
+
10
+ from jit.errcode import Code
11
+
12
+
13
+ class TlConfigException(Code):
14
+ DEFAULT_RESULT = "配置信息错误"
15
+
16
+ def __init__(self, reason=DEFAULT_RESULT, code=-2, msg=None, solution=None):
17
+ super(TlConfigException, self).__init__(code=code, reason=reason, msg=msg, solution=solution)