# somedecorators
Some very useful Python decorators, functions, classes, continuously updated.
一些非常实用的 Python 装饰器、函数、类,持续更新。
- 新增 robot_on_exception,报错通过企业微信机器人发送至企业微信群聊 : from somedecorators import robot_on_exception
- 新增 wechat_on_exception,报错发送企业微信 : from somedecorators import wechat_on_exception
- 新增 setup_logger,快速配置日志 : from somedecorators import setup_logger
- 新增 ConfigManager,快速搞定配置文件 : from somedecorators import ConfigManager
使用方法如下:
## 安装
```sh
pip install somedecorators
```
## 装饰器介绍:
#### 发送企业微信机器人
调用代码
mentioned_list 参数可以不填写。
```python
@robot_on_exception(webhook_url= "你的企业威胁你机器人 webhook",mentioned_list=["@谁就填写谁的企业微信账号","user2"], extra_msg="运行报错")
def myfunc(args):
if args == 1:
raise Exception1
elif args == 2:
raise Exception2
else:
raise Exception3
```
#### timeit
耗时统计装饰器,单位是秒,保留 4 位小数
使用方法:
```python
from somedecorators import timeit
@timeit()
def test_timeit():
time.sleep(1)
#test_timeit cost 1.0026 seconds
@timeit(logger = your_logger)
def test_timeit():
time.sleep(1)
```
#### timeout
超时装饰器,单位是秒,函数运行超过指定的时间会抛出 TimeOutError 异常。
使用方法:
```python
import time
from somedecorators import timeout
@timeout(2)
def test_timeit():
time.sleep(3)
#somedecorators.timeit.TimeoutError: Operation did not finish within 2 seconds
```
#### retry
重试装饰器
- 当被装饰的函数调用抛出指定的异常时,函数会被重新调用。
- 直到达到指定的最大调用次数才重新抛出指定的异常,可以指定时间间隔,默认 5 秒后重试。
- traced_exceptions 为监控的异常,可以为 None(默认)、异常类、或者一个异常类的列表或元组 tuple。
- traced_exceptions 如果为 None,则监控所有的异常;如果指定了异常类,则若函数调用抛出指定的异常时,重新调用函数,直至成功返回结果。
- 未出现监控的异常时,如果指定定了 reraised_exception 则抛出 reraised_exception,否则抛出原来的异常。
```python
from somedecorators import retry
@retry(
times=2,
wait_seconds=1,
traced_exceptions=myException,
reraised_exception=CustomException,
)
def test_retry():
# time.sleep(1)
raise myException
test_retry()
```
#### email_on_exception
报错发邮件装饰器。当被装饰的函数调用抛出指定的异常时,函数发送邮件给指定的人员,使用独立的 [djangomail](https://github.com/somenzz/djangomail) 发邮件模块,非常好用。
- recipient_list: 一个字符串列表,每项都是一个邮箱地址。recipient_list 中的每个成员都可以在邮件的 "收件人:" 中看到其他的收件人。
- traced_exceptions 为监控的异常,可以为 None(默认)、异常类、或者一个异常类的元组。
traced_exceptions 如果为 None,则监控所有的异常;如果指定了异常类,则若函数调用抛出指定的异常时,发送邮件。
**使用方法**:
首先在项目目录新建 settings.py,配置邮件服务器或企业微信,内容如下:
```python
EMAIL_USE_LOCALTIME = True
#for unitest
#EMAIL_BACKEND = 'djangomail.backends.console.EmailBackend'
#EMAIL_BACKEND = 'djangomail.backends.smtp.EmailBackend'
EMAIL_USE_SSL = True
EMAIL_HOST = 'smtp.163.com' #可以换其他邮箱
EMAIL_PORT = 465
EMAIL_HOST_USER = 'your-username'
EMAIL_HOST_PASSWORD = '********'
DEFAULT_FROM_EMAIL = EMAIL_HOST_USER
SERVER_EMAIL = EMAIL_HOST_USER
# 用于发送企业微信
CORPID="**********************" # 企业 ID
APPID="*******" # 企业应用 ID
CORPSECRET="************************" # 企业应用 Secret
```
如果你的文件名不是 settings.py,假如是 mysettings.py 则需要修改环境变量:
```python
os.environ.setdefault("SETTINGS_MODULE", "mysettings")
```
然后主程序中这样使用:
##### 监控所有的异常
```python
from somedecorators import email_on_exception
#import os
#os.environ.setdefault("SETTINGS_MODULE", "settings") #默认配置,可以不写此行代码
@email_on_exception(['somenzz@163.com'])
def myfunc(arg):
1/arg
myfunc(0)
```
你会收到如下的邮件信息,非常便于排查错误。
```sh
Subject: myfunc(arg=0) raise Exception
From: your-username
To: somenzz@163.com
Date: Fri, 11 Jun 2021 20:55:01 -0500
Message-ID:
<162346290112.13869.15957310483971819045@1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa>
myfunc(arg=0) raise Exception: division by zero
traceback:
Traceback (most recent call last):
File "/Users/aaron/github/somenzz/somedecorators/somedecorators/email.py", line 35, in wrapper
return func(*args, **kwargs)
File "/Users/aaron/github/somenzz/somedecorators/tests/tests.py", line 55, in myfunc
return 1/arg
ZeroDivisionError: division by zero
extra_msg = 严重错误
```
##### 监控指定的异常
```python
from somedecorators import email_on_exception
import os
os.environ.setdefault("SETTINGS_MODULE", "settings")
class Exception1(Exception):
pass
class Exception2(Exception):
pass
class Exception3(Exception):
pass
@email_on_exception(['somenzz@163.com'],traced_exceptions = Exception2)
def myfunc(args):
if args == 1:
raise Exception1
elif args == 2:
raise Exception2
else:
raise Exception3
myfunc(2)
```
上述代码只有在 raise Exception2 时才会发送邮件:
##### 不同的异常发给不同的人
```python
@email_on_exception(['somenzz@163.com'],traced_exceptions = Exception2)
@email_on_exception(['others@163.com'],traced_exceptions = (Exception1, Exception3))
def myfunc(args):
if args == 1:
raise Exception1
elif args == 2:
raise Exception2
else:
raise Exception3
```
是不是非常方便?
#### 发送企业微信
发送前需要在 settings.py 文件企业微信相关信息
settings.py 示例:
```python
CORPID="**********************" # 企业 ID
APPID="*******" # 企业应用 ID
CORPSECRET="************************" # 企业应用 Secret
```
调用代码
```python
@wechat_on_exception(['企业微信接收者ID'],traced_exceptions = Exception2)
def myfunc(args):
if args == 1:
raise Exception1
elif args == 2:
raise Exception2
else:
raise Exception3
```
#### 快速配置日志 setup_logger
一个简单的使用日志的方法
```python
from somedecorators import setup_logger
logger = setup_logger("myapp")
logger.info("hello this is myapp log")
logger2 = setup_logger("myapp2", log_path="./log/app.log")
logger3 = setup_logger("myapp3",handlers=['console'])
logger4 = setup_logger("myapp4",handlers=['console','file'])
```
#### 快速搞定配置文件
```python
from somedecorators import ConfigManager
config_manager = ConfigManager("config.yml")
config_data = config_manager.get_all()
print(config_data)
```
## 参与项目
欢迎分享你最常用的装饰器、类、函数,加入到这里。
## 联系我
微信:somenzz-enjoy
Raw data
{
"_id": null,
"home_page": "https://github.com/somenzz/somedecorators",
"name": "somedecorators",
"maintainer": null,
"docs_url": null,
"requires_python": null,
"maintainer_email": null,
"keywords": "awesome decorators",
"author": "somenzz",
"author_email": "somenzz@163.com",
"download_url": "https://files.pythonhosted.org/packages/a5/5c/5fc15a2d43f722cd639d5fffdb868818d79a3de001cddef80b10d72499be/somedecorators-1.2.5.tar.gz",
"platform": null,
"description": "# somedecorators\n\nSome very useful Python decorators, functions, classes, continuously updated.\n\n\n\u4e00\u4e9b\u975e\u5e38\u5b9e\u7528\u7684 Python \u88c5\u9970\u5668\u3001\u51fd\u6570\u3001\u7c7b\uff0c\u6301\u7eed\u66f4\u65b0\u3002\n\n- \u65b0\u589e robot_on_exception\uff0c\u62a5\u9519\u901a\u8fc7\u4f01\u4e1a\u5fae\u4fe1\u673a\u5668\u4eba\u53d1\u9001\u81f3\u4f01\u4e1a\u5fae\u4fe1\u7fa4\u804a : from somedecorators import robot_on_exception\n- \u65b0\u589e wechat_on_exception\uff0c\u62a5\u9519\u53d1\u9001\u4f01\u4e1a\u5fae\u4fe1 : from somedecorators import wechat_on_exception\n- \u65b0\u589e setup_logger\uff0c\u5feb\u901f\u914d\u7f6e\u65e5\u5fd7 : from somedecorators import setup_logger \n- \u65b0\u589e ConfigManager\uff0c\u5feb\u901f\u641e\u5b9a\u914d\u7f6e\u6587\u4ef6 : from somedecorators import ConfigManager \n\n\u4f7f\u7528\u65b9\u6cd5\u5982\u4e0b\uff1a\n\n## \u5b89\u88c5\n\n```sh\npip install somedecorators\n```\n\n## \u88c5\u9970\u5668\u4ecb\u7ecd\uff1a\n\n#### \u53d1\u9001\u4f01\u4e1a\u5fae\u4fe1\u673a\u5668\u4eba\n\n\u8c03\u7528\u4ee3\u7801 \nmentioned_list \u53c2\u6570\u53ef\u4ee5\u4e0d\u586b\u5199\u3002\n```python\n@robot_on_exception(webhook_url= \"\u4f60\u7684\u4f01\u4e1a\u5a01\u80c1\u4f60\u673a\u5668\u4eba webhook\",mentioned_list=[\"@\u8c01\u5c31\u586b\u5199\u8c01\u7684\u4f01\u4e1a\u5fae\u4fe1\u8d26\u53f7\",\"user2\"], extra_msg=\"\u8fd0\u884c\u62a5\u9519\")\ndef myfunc(args):\n if args == 1:\n raise Exception1\n elif args == 2:\n raise Exception2\n else:\n raise Exception3\n```\n\n\n\n#### timeit\n\n\u8017\u65f6\u7edf\u8ba1\u88c5\u9970\u5668\uff0c\u5355\u4f4d\u662f\u79d2\uff0c\u4fdd\u7559 4 \u4f4d\u5c0f\u6570\n\n\n\u4f7f\u7528\u65b9\u6cd5\uff1a\n\n```python\nfrom somedecorators import timeit\n@timeit()\ndef test_timeit():\n time.sleep(1)\n\n#test_timeit cost 1.0026 seconds\n\n@timeit(logger = your_logger)\ndef test_timeit():\n time.sleep(1)\n```\n\n\n#### timeout\n\n\u8d85\u65f6\u88c5\u9970\u5668\uff0c\u5355\u4f4d\u662f\u79d2\uff0c\u51fd\u6570\u8fd0\u884c\u8d85\u8fc7\u6307\u5b9a\u7684\u65f6\u95f4\u4f1a\u629b\u51fa TimeOutError \u5f02\u5e38\u3002\n\n\u4f7f\u7528\u65b9\u6cd5\uff1a\n\n```python\nimport time\nfrom somedecorators import timeout\n@timeout(2)\ndef test_timeit():\n time.sleep(3)\n\n#somedecorators.timeit.TimeoutError: Operation did not finish within 2 seconds\n```\n\n\n\n#### retry\n\n\u91cd\u8bd5\u88c5\u9970\u5668\n- \u5f53\u88ab\u88c5\u9970\u7684\u51fd\u6570\u8c03\u7528\u629b\u51fa\u6307\u5b9a\u7684\u5f02\u5e38\u65f6\uff0c\u51fd\u6570\u4f1a\u88ab\u91cd\u65b0\u8c03\u7528\u3002\n- \u76f4\u5230\u8fbe\u5230\u6307\u5b9a\u7684\u6700\u5927\u8c03\u7528\u6b21\u6570\u624d\u91cd\u65b0\u629b\u51fa\u6307\u5b9a\u7684\u5f02\u5e38\uff0c\u53ef\u4ee5\u6307\u5b9a\u65f6\u95f4\u95f4\u9694\uff0c\u9ed8\u8ba4 5 \u79d2\u540e\u91cd\u8bd5\u3002\n- traced_exceptions \u4e3a\u76d1\u63a7\u7684\u5f02\u5e38\uff0c\u53ef\u4ee5\u4e3a None\uff08\u9ed8\u8ba4\uff09\u3001\u5f02\u5e38\u7c7b\u3001\u6216\u8005\u4e00\u4e2a\u5f02\u5e38\u7c7b\u7684\u5217\u8868\u6216\u5143\u7ec4 tuple\u3002\n- traced_exceptions \u5982\u679c\u4e3a None\uff0c\u5219\u76d1\u63a7\u6240\u6709\u7684\u5f02\u5e38\uff1b\u5982\u679c\u6307\u5b9a\u4e86\u5f02\u5e38\u7c7b\uff0c\u5219\u82e5\u51fd\u6570\u8c03\u7528\u629b\u51fa\u6307\u5b9a\u7684\u5f02\u5e38\u65f6\uff0c\u91cd\u65b0\u8c03\u7528\u51fd\u6570\uff0c\u76f4\u81f3\u6210\u529f\u8fd4\u56de\u7ed3\u679c\u3002\n- \u672a\u51fa\u73b0\u76d1\u63a7\u7684\u5f02\u5e38\u65f6\uff0c\u5982\u679c\u6307\u5b9a\u5b9a\u4e86 reraised_exception \u5219\u629b\u51fa reraised_exception\uff0c\u5426\u5219\u629b\u51fa\u539f\u6765\u7684\u5f02\u5e38\u3002\n\n\n```python\nfrom somedecorators import retry \n\n@retry(\n times=2,\n wait_seconds=1,\n traced_exceptions=myException,\n reraised_exception=CustomException,\n)\ndef test_retry():\n # time.sleep(1)\n raise myException\n\n\ntest_retry()\n```\n\n\n\n#### email_on_exception\n\n\u62a5\u9519\u53d1\u90ae\u4ef6\u88c5\u9970\u5668\u3002\u5f53\u88ab\u88c5\u9970\u7684\u51fd\u6570\u8c03\u7528\u629b\u51fa\u6307\u5b9a\u7684\u5f02\u5e38\u65f6\uff0c\u51fd\u6570\u53d1\u9001\u90ae\u4ef6\u7ed9\u6307\u5b9a\u7684\u4eba\u5458\uff0c\u4f7f\u7528\u72ec\u7acb\u7684 [djangomail](https://github.com/somenzz/djangomail) \u53d1\u90ae\u4ef6\u6a21\u5757\uff0c\u975e\u5e38\u597d\u7528\u3002\n\n- recipient_list: \u4e00\u4e2a\u5b57\u7b26\u4e32\u5217\u8868\uff0c\u6bcf\u9879\u90fd\u662f\u4e00\u4e2a\u90ae\u7bb1\u5730\u5740\u3002recipient_list \u4e2d\u7684\u6bcf\u4e2a\u6210\u5458\u90fd\u53ef\u4ee5\u5728\u90ae\u4ef6\u7684 \"\u6536\u4ef6\u4eba:\" \u4e2d\u770b\u5230\u5176\u4ed6\u7684\u6536\u4ef6\u4eba\u3002\n- traced_exceptions \u4e3a\u76d1\u63a7\u7684\u5f02\u5e38\uff0c\u53ef\u4ee5\u4e3a None\uff08\u9ed8\u8ba4\uff09\u3001\u5f02\u5e38\u7c7b\u3001\u6216\u8005\u4e00\u4e2a\u5f02\u5e38\u7c7b\u7684\u5143\u7ec4\u3002\ntraced_exceptions \u5982\u679c\u4e3a None\uff0c\u5219\u76d1\u63a7\u6240\u6709\u7684\u5f02\u5e38\uff1b\u5982\u679c\u6307\u5b9a\u4e86\u5f02\u5e38\u7c7b\uff0c\u5219\u82e5\u51fd\u6570\u8c03\u7528\u629b\u51fa\u6307\u5b9a\u7684\u5f02\u5e38\u65f6\uff0c\u53d1\u9001\u90ae\u4ef6\u3002\n\n**\u4f7f\u7528\u65b9\u6cd5**\uff1a\n\n\u9996\u5148\u5728\u9879\u76ee\u76ee\u5f55\u65b0\u5efa settings.py\uff0c\u914d\u7f6e\u90ae\u4ef6\u670d\u52a1\u5668\u6216\u4f01\u4e1a\u5fae\u4fe1\uff0c\u5185\u5bb9\u5982\u4e0b\uff1a\n\n```python\nEMAIL_USE_LOCALTIME = True\n\n#for unitest\n#EMAIL_BACKEND = 'djangomail.backends.console.EmailBackend'\n#EMAIL_BACKEND = 'djangomail.backends.smtp.EmailBackend'\nEMAIL_USE_SSL = True\nEMAIL_HOST = 'smtp.163.com' #\u53ef\u4ee5\u6362\u5176\u4ed6\u90ae\u7bb1\nEMAIL_PORT = 465\nEMAIL_HOST_USER = 'your-username'\nEMAIL_HOST_PASSWORD = '********'\nDEFAULT_FROM_EMAIL = EMAIL_HOST_USER\nSERVER_EMAIL = EMAIL_HOST_USER\n\n\n# \u7528\u4e8e\u53d1\u9001\u4f01\u4e1a\u5fae\u4fe1\nCORPID=\"**********************\" # \u4f01\u4e1a ID\nAPPID=\"*******\" # \u4f01\u4e1a\u5e94\u7528 ID\nCORPSECRET=\"************************\" # \u4f01\u4e1a\u5e94\u7528 Secret\n\n```\n\n\u5982\u679c\u4f60\u7684\u6587\u4ef6\u540d\u4e0d\u662f settings.py\uff0c\u5047\u5982\u662f mysettings.py \u5219\u9700\u8981\u4fee\u6539\u73af\u5883\u53d8\u91cf:\n\n```python\nos.environ.setdefault(\"SETTINGS_MODULE\", \"mysettings\")\n```\n\u7136\u540e\u4e3b\u7a0b\u5e8f\u4e2d\u8fd9\u6837\u4f7f\u7528\uff1a\n\n##### \u76d1\u63a7\u6240\u6709\u7684\u5f02\u5e38\n\n```python\nfrom somedecorators import email_on_exception \n#import os\n#os.environ.setdefault(\"SETTINGS_MODULE\", \"settings\") #\u9ed8\u8ba4\u914d\u7f6e\uff0c\u53ef\u4ee5\u4e0d\u5199\u6b64\u884c\u4ee3\u7801\n\n@email_on_exception(['somenzz@163.com'])\ndef myfunc(arg):\n 1/arg\n\nmyfunc(0)\n```\n\n\u4f60\u4f1a\u6536\u5230\u5982\u4e0b\u7684\u90ae\u4ef6\u4fe1\u606f\uff0c\u975e\u5e38\u4fbf\u4e8e\u6392\u67e5\u9519\u8bef\u3002\n\n```sh\nSubject: myfunc(arg=0) raise Exception\nFrom: your-username\nTo: somenzz@163.com\nDate: Fri, 11 Jun 2021 20:55:01 -0500\nMessage-ID: \n <162346290112.13869.15957310483971819045@1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa>\n\nmyfunc(arg=0) raise Exception: division by zero \n traceback:\n Traceback (most recent call last):\n File \"/Users/aaron/github/somenzz/somedecorators/somedecorators/email.py\", line 35, in wrapper\n return func(*args, **kwargs)\n File \"/Users/aaron/github/somenzz/somedecorators/tests/tests.py\", line 55, in myfunc\n return 1/arg\nZeroDivisionError: division by zero\n\nextra_msg = \u4e25\u91cd\u9519\u8bef\n```\n\n##### \u76d1\u63a7\u6307\u5b9a\u7684\u5f02\u5e38\n\n```python\n\nfrom somedecorators import email_on_exception\nimport os\nos.environ.setdefault(\"SETTINGS_MODULE\", \"settings\")\n\nclass Exception1(Exception):\n pass\n\nclass Exception2(Exception):\n pass\n\nclass Exception3(Exception):\n pass\n\n@email_on_exception(['somenzz@163.com'],traced_exceptions = Exception2)\ndef myfunc(args):\n if args == 1:\n raise Exception1\n elif args == 2:\n raise Exception2\n else:\n raise Exception3\n\nmyfunc(2)\n\n```\n\u4e0a\u8ff0\u4ee3\u7801\u53ea\u6709\u5728 raise Exception2 \u65f6\u624d\u4f1a\u53d1\u9001\u90ae\u4ef6\uff1a\n\n##### \u4e0d\u540c\u7684\u5f02\u5e38\u53d1\u7ed9\u4e0d\u540c\u7684\u4eba\n\n```python\n@email_on_exception(['somenzz@163.com'],traced_exceptions = Exception2)\n@email_on_exception(['others@163.com'],traced_exceptions = (Exception1, Exception3))\ndef myfunc(args):\n if args == 1:\n raise Exception1\n elif args == 2:\n raise Exception2\n else:\n raise Exception3\n```\n\n\u662f\u4e0d\u662f\u975e\u5e38\u65b9\u4fbf\uff1f\n\n#### \u53d1\u9001\u4f01\u4e1a\u5fae\u4fe1\n\n\u53d1\u9001\u524d\u9700\u8981\u5728 settings.py \u6587\u4ef6\u4f01\u4e1a\u5fae\u4fe1\u76f8\u5173\u4fe1\u606f\n\nsettings.py \u793a\u4f8b\uff1a\n\n```python\n\nCORPID=\"**********************\" # \u4f01\u4e1a ID\nAPPID=\"*******\" # \u4f01\u4e1a\u5e94\u7528 ID\nCORPSECRET=\"************************\" # \u4f01\u4e1a\u5e94\u7528 Secret\n\n```\n\n\u8c03\u7528\u4ee3\u7801 \n\n```python\n@wechat_on_exception(['\u4f01\u4e1a\u5fae\u4fe1\u63a5\u6536\u8005ID'],traced_exceptions = Exception2)\ndef myfunc(args):\n if args == 1:\n raise Exception1\n elif args == 2:\n raise Exception2\n else:\n raise Exception3\n```\n\n\n#### \u5feb\u901f\u914d\u7f6e\u65e5\u5fd7 setup_logger\n\n\u4e00\u4e2a\u7b80\u5355\u7684\u4f7f\u7528\u65e5\u5fd7\u7684\u65b9\u6cd5\n\n```python\nfrom somedecorators import setup_logger\nlogger = setup_logger(\"myapp\")\nlogger.info(\"hello this is myapp log\")\nlogger2 = setup_logger(\"myapp2\", log_path=\"./log/app.log\")\nlogger3 = setup_logger(\"myapp3\",handlers=['console']) \nlogger4 = setup_logger(\"myapp4\",handlers=['console','file']) \n```\n\n#### \u5feb\u901f\u641e\u5b9a\u914d\u7f6e\u6587\u4ef6\n\n```python\nfrom somedecorators import ConfigManager\nconfig_manager = ConfigManager(\"config.yml\")\nconfig_data = config_manager.get_all()\nprint(config_data)\n```\n\n## \u53c2\u4e0e\u9879\u76ee\n\n\u6b22\u8fce\u5206\u4eab\u4f60\u6700\u5e38\u7528\u7684\u88c5\u9970\u5668\u3001\u7c7b\u3001\u51fd\u6570\uff0c\u52a0\u5165\u5230\u8fd9\u91cc\u3002\n\n## \u8054\u7cfb\u6211 \n\n\u5fae\u4fe1\uff1asomenzz-enjoy\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "Some useful decorators in Python.",
"version": "1.2.5",
"project_urls": {
"Homepage": "https://github.com/somenzz/somedecorators"
},
"split_keywords": [
"awesome",
"decorators"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "e6ce6efcc44362923de0e5efbb9eb857fb4f6529f0e11e4ba3c17c843517cf03",
"md5": "586964f66c8149354f6452196911a07b",
"sha256": "09beb6d83ca64ce54056d3466b528809cf34879d7800ea630d229836c2f12e6c"
},
"downloads": -1,
"filename": "somedecorators-1.2.5-py3-none-any.whl",
"has_sig": false,
"md5_digest": "586964f66c8149354f6452196911a07b",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": null,
"size": 23156,
"upload_time": "2024-08-28T01:43:34",
"upload_time_iso_8601": "2024-08-28T01:43:34.468624Z",
"url": "https://files.pythonhosted.org/packages/e6/ce/6efcc44362923de0e5efbb9eb857fb4f6529f0e11e4ba3c17c843517cf03/somedecorators-1.2.5-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "a55c5fc15a2d43f722cd639d5fffdb868818d79a3de001cddef80b10d72499be",
"md5": "6baa54715ad4fa337ae3e3e2fd44d945",
"sha256": "caa213815e97e3b266a4c58d9a12d0f0e27c7150e32318252fdbce245158b482"
},
"downloads": -1,
"filename": "somedecorators-1.2.5.tar.gz",
"has_sig": false,
"md5_digest": "6baa54715ad4fa337ae3e3e2fd44d945",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 23544,
"upload_time": "2024-08-28T01:43:36",
"upload_time_iso_8601": "2024-08-28T01:43:36.026958Z",
"url": "https://files.pythonhosted.org/packages/a5/5c/5fc15a2d43f722cd639d5fffdb868818d79a3de001cddef80b10d72499be/somedecorators-1.2.5.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-08-28 01:43:36",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "somenzz",
"github_project": "somedecorators",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"tox": true,
"lcname": "somedecorators"
}