mirror of
https://github.com/PaiGramTeam/PaiGram.git
synced 2024-11-26 02:11:03 +00:00
✨ 添加错误平台
Co-authored-by: SiHuan <sihuan@sakuya.love> Co-authored-by: 洛水居室 <luoshuijs@outlook.com>
This commit is contained in:
parent
8c12237e85
commit
1f17e56824
@ -58,3 +58,9 @@ LOGGER_LOCALS_MAX_STRING=80
|
|||||||
# WEB_URL=http://localhost:8080/
|
# WEB_URL=http://localhost:8080/
|
||||||
# WEB_HOST=localhost
|
# WEB_HOST=localhost
|
||||||
# WEB_PORT=8080
|
# WEB_PORT=8080
|
||||||
|
|
||||||
|
# error
|
||||||
|
# ERROR_PB_URL=https://fars.ee
|
||||||
|
# ERROR_PB_SUNSET=43200
|
||||||
|
# ERROR_PB_MAX_LINES=1000
|
||||||
|
# ERROR_SENTRY_DSN=
|
||||||
|
@ -64,6 +64,11 @@ class BotConfig(BaseSettings):
|
|||||||
web_host: str = "localhost"
|
web_host: str = "localhost"
|
||||||
web_port: int = 8080
|
web_port: int = 8080
|
||||||
|
|
||||||
|
error_pb_url: str = ""
|
||||||
|
error_pb_sunset: int = 43200
|
||||||
|
error_pb_max_lines: int = 1000
|
||||||
|
error_sentry_dsn: str = ""
|
||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
case_sensitive = False
|
case_sensitive = False
|
||||||
json_loads = json.loads
|
json_loads = json.loads
|
||||||
|
26
modules/error/pb.py
Normal file
26
modules/error/pb.py
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import httpx
|
||||||
|
|
||||||
|
from core.config import config
|
||||||
|
|
||||||
|
|
||||||
|
class PbClient:
|
||||||
|
def __init__(self):
|
||||||
|
self.client = httpx.AsyncClient()
|
||||||
|
self.PB_API = config.error_pb_url
|
||||||
|
self.sunset: int = config.error_pb_sunset # 自动销毁时间 秒
|
||||||
|
self.private: bool = True
|
||||||
|
self.max_lines: int = config.error_pb_max_lines
|
||||||
|
|
||||||
|
async def create_pb(self, content: str) -> str:
|
||||||
|
if not self.PB_API:
|
||||||
|
return ""
|
||||||
|
content = "\n".join(content.splitlines()[-self.max_lines :]) + "\n"
|
||||||
|
data = {
|
||||||
|
"c": content,
|
||||||
|
}
|
||||||
|
if self.private:
|
||||||
|
data["p"] = "1"
|
||||||
|
if self.sunset:
|
||||||
|
data["sunset"] = self.sunset
|
||||||
|
data = await self.client.post(self.PB_API, data=data) # 需要错误处理
|
||||||
|
return data.headers["location"]
|
42
modules/error/sentry.py
Normal file
42
modules/error/sentry.py
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
|
import sentry_sdk
|
||||||
|
from git.repo import Repo
|
||||||
|
from git.repo.fun import rev_parse
|
||||||
|
from sentry_sdk.integrations.excepthook import ExcepthookIntegration
|
||||||
|
from sentry_sdk.integrations.httpx import HttpxIntegration
|
||||||
|
from sentry_sdk.integrations.logging import LoggingIntegration
|
||||||
|
from sentry_sdk.integrations.sqlalchemy import SqlalchemyIntegration
|
||||||
|
from telegram import Update
|
||||||
|
|
||||||
|
from core.config import config
|
||||||
|
|
||||||
|
repo = Repo(os.getcwd())
|
||||||
|
sentry_sdk_git_hash = rev_parse(repo, "HEAD").hexsha
|
||||||
|
sentry_sdk.init(
|
||||||
|
config.error_sentry_dsn,
|
||||||
|
traces_sample_rate=1.0,
|
||||||
|
release=sentry_sdk_git_hash,
|
||||||
|
environment="production",
|
||||||
|
integrations=[
|
||||||
|
HttpxIntegration(),
|
||||||
|
ExcepthookIntegration(always_run=False),
|
||||||
|
LoggingIntegration(event_level=50),
|
||||||
|
SqlalchemyIntegration(),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Sentry:
|
||||||
|
@staticmethod
|
||||||
|
def report_error(update: Update, exc_info):
|
||||||
|
if not config.error_sentry_dsn:
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
sender_id = update.effective_user.id if update.effective_user else update.effective_chat.id
|
||||||
|
except AttributeError:
|
||||||
|
sender_id = 0
|
||||||
|
sentry_sdk.set_context(
|
||||||
|
"Target", {"ChatID": str(update.message.chat_id), "UserID": sender_id, "Msg": update.message.text or ""}
|
||||||
|
)
|
||||||
|
sentry_sdk.capture_exception(exc_info)
|
@ -11,6 +11,8 @@ from telegram.ext import CallbackContext
|
|||||||
|
|
||||||
from core.bot import bot
|
from core.bot import bot
|
||||||
from core.plugin import error_handler, Plugin
|
from core.plugin import error_handler, Plugin
|
||||||
|
from modules.error.pb import PbClient
|
||||||
|
from modules.error.sentry import Sentry
|
||||||
from utils.log import logger
|
from utils.log import logger
|
||||||
|
|
||||||
notice_chat_id = bot.config.error_notification_chat_id
|
notice_chat_id = bot.config.error_notification_chat_id
|
||||||
@ -21,6 +23,8 @@ if not os.path.exists(logs_dir):
|
|||||||
report_dir = os.path.join(current_dir, "report")
|
report_dir = os.path.join(current_dir, "report")
|
||||||
if not os.path.exists(report_dir):
|
if not os.path.exists(report_dir):
|
||||||
os.mkdir(report_dir)
|
os.mkdir(report_dir)
|
||||||
|
pb_client = PbClient()
|
||||||
|
sentry = Sentry()
|
||||||
|
|
||||||
|
|
||||||
class ErrorHandler(Plugin):
|
class ErrorHandler(Plugin):
|
||||||
@ -90,3 +94,19 @@ class ErrorHandler(Plugin):
|
|||||||
except (BadRequest, Forbidden) as exc:
|
except (BadRequest, Forbidden) as exc:
|
||||||
logger.error(f"发送 update_id[{update.update_id}] 错误信息失败 错误信息为")
|
logger.error(f"发送 update_id[{update.update_id}] 错误信息失败 错误信息为")
|
||||||
logger.exception(exc)
|
logger.exception(exc)
|
||||||
|
try:
|
||||||
|
pb_url = await pb_client.create_pb(error_text)
|
||||||
|
if pb_url:
|
||||||
|
await context.bot.send_message(
|
||||||
|
chat_id=notice_chat_id,
|
||||||
|
text=f"错误信息已上传至 <a href='{pb_url}'>fars</a> 请查看",
|
||||||
|
parse_mode=ParseMode.HTML,
|
||||||
|
)
|
||||||
|
except Exception as exc: # pylint: disable=W0703
|
||||||
|
logger.error("上传错误信息至 fars 失败")
|
||||||
|
logger.exception(exc)
|
||||||
|
try:
|
||||||
|
sentry.report_error(update, context.error)
|
||||||
|
except Exception as exc: # pylint: disable=W0703
|
||||||
|
logger.error("上传错误信息至 sentry 失败")
|
||||||
|
logger.exception(exc)
|
||||||
|
@ -5,6 +5,7 @@ from telegram.constants import ChatAction
|
|||||||
from telegram.ext import CommandHandler, CallbackContext
|
from telegram.ext import CommandHandler, CallbackContext
|
||||||
|
|
||||||
from core.plugin import Plugin, handler
|
from core.plugin import Plugin, handler
|
||||||
|
from modules.error.pb import PbClient
|
||||||
from utils.decorators.admins import bot_admins_rights_check
|
from utils.decorators.admins import bot_admins_rights_check
|
||||||
from utils.log import logger
|
from utils.log import logger
|
||||||
|
|
||||||
@ -14,6 +15,21 @@ debug_log = os.path.join(current_dir, "logs", "debug", "debug.log")
|
|||||||
|
|
||||||
|
|
||||||
class Log(Plugin):
|
class Log(Plugin):
|
||||||
|
def __init__(self):
|
||||||
|
self.pb_client = PbClient()
|
||||||
|
self.pb_client.sunset = 3600
|
||||||
|
self.pb_client.max_lines = 10000
|
||||||
|
|
||||||
|
async def send_to_pb(self, file_name: str):
|
||||||
|
pb_url = ""
|
||||||
|
try:
|
||||||
|
with open(file_name, "r", encoding="utf-8") as f:
|
||||||
|
pb_url = await self.pb_client.create_pb(f.read())
|
||||||
|
except Exception as exc: # pylint: disable=W0703
|
||||||
|
logger.error("上传错误信息至 fars 失败")
|
||||||
|
logger.exception(exc)
|
||||||
|
return pb_url
|
||||||
|
|
||||||
@handler(CommandHandler, command="send_log", block=False)
|
@handler(CommandHandler, command="send_log", block=False)
|
||||||
@bot_admins_rights_check
|
@bot_admins_rights_check
|
||||||
async def send_log(self, update: Update, _: CallbackContext):
|
async def send_log(self, update: Update, _: CallbackContext):
|
||||||
@ -21,12 +37,18 @@ class Log(Plugin):
|
|||||||
logger.info(f"用户 {user.full_name}[{user.id}] send_log 命令请求")
|
logger.info(f"用户 {user.full_name}[{user.id}] send_log 命令请求")
|
||||||
message = update.effective_message
|
message = update.effective_message
|
||||||
if os.path.exists(error_log) and os.path.getsize(error_log) > 0:
|
if os.path.exists(error_log) and os.path.getsize(error_log) > 0:
|
||||||
|
pb_url = await self.send_to_pb(error_log)
|
||||||
await message.reply_chat_action(ChatAction.UPLOAD_DOCUMENT)
|
await message.reply_chat_action(ChatAction.UPLOAD_DOCUMENT)
|
||||||
await message.reply_document(open(error_log, mode="rb+"), caption="Error Log")
|
await message.reply_document(
|
||||||
|
open(error_log, mode="rb+"), caption=f"Error Log\n{pb_url}/text" if pb_url else "Error Log"
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
await message.reply_text("错误日记未找到")
|
await message.reply_text("错误日记未找到")
|
||||||
if os.path.exists(debug_log) and os.path.getsize(debug_log) > 0:
|
if os.path.exists(debug_log) and os.path.getsize(debug_log) > 0:
|
||||||
|
pb_url = await self.send_to_pb(debug_log)
|
||||||
await message.reply_chat_action(ChatAction.UPLOAD_DOCUMENT)
|
await message.reply_chat_action(ChatAction.UPLOAD_DOCUMENT)
|
||||||
await message.reply_document(open(debug_log, mode="rb+"), caption="Debug Log")
|
await message.reply_document(
|
||||||
|
open(debug_log, mode="rb+"), caption=f"Debug Log\n{pb_url}/text" if pb_url else "Debug Log"
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
await message.reply_text("调试日记未找到")
|
await message.reply_text("调试日记未找到")
|
||||||
|
78
poetry.lock
generated
78
poetry.lock
generated
@ -388,6 +388,28 @@ url = "https://github.com/thesadru/genshin.py"
|
|||||||
reference = "HEAD"
|
reference = "HEAD"
|
||||||
resolved_reference = "7b3a4a71bfdf84d9f1bf984e91c0bcf73f9dfa7f"
|
resolved_reference = "7b3a4a71bfdf84d9f1bf984e91c0bcf73f9dfa7f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gitdb"
|
||||||
|
version = "4.0.9"
|
||||||
|
description = "Git Object Database"
|
||||||
|
category = "main"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.6"
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
smmap = ">=3.0.1,<6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "GitPython"
|
||||||
|
version = "3.1.29"
|
||||||
|
description = "GitPython is a python library used to interact with Git repositories"
|
||||||
|
category = "main"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
gitdb = ">=4.0.1,<5"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "greenlet"
|
name = "greenlet"
|
||||||
version = "1.1.3"
|
version = "1.1.3"
|
||||||
@ -899,6 +921,38 @@ typing-extensions = {version = ">=4.0.0,<5.0", markers = "python_version < \"3.9
|
|||||||
[package.extras]
|
[package.extras]
|
||||||
jupyter = ["ipywidgets (>=7.5.1,<8.0.0)"]
|
jupyter = ["ipywidgets (>=7.5.1,<8.0.0)"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sentry-sdk"
|
||||||
|
version = "1.9.10"
|
||||||
|
description = "Python client for Sentry (https://sentry.io)"
|
||||||
|
category = "main"
|
||||||
|
optional = false
|
||||||
|
python-versions = "*"
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
certifi = "*"
|
||||||
|
urllib3 = {version = ">=1.26.11", markers = "python_version >= \"3.6\""}
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
aiohttp = ["aiohttp (>=3.5)"]
|
||||||
|
beam = ["apache-beam (>=2.12)"]
|
||||||
|
bottle = ["bottle (>=0.12.13)"]
|
||||||
|
celery = ["celery (>=3)"]
|
||||||
|
chalice = ["chalice (>=1.16.0)"]
|
||||||
|
django = ["django (>=1.8)"]
|
||||||
|
falcon = ["falcon (>=1.4)"]
|
||||||
|
fastapi = ["fastapi (>=0.79.0)"]
|
||||||
|
flask = ["blinker (>=1.1)", "flask (>=0.11)"]
|
||||||
|
httpx = ["httpx (>=0.16.0)"]
|
||||||
|
pure_eval = ["asttokens", "executing", "pure-eval"]
|
||||||
|
pyspark = ["pyspark (>=2.4.4)"]
|
||||||
|
quart = ["blinker (>=1.1)", "quart (>=0.16.1)"]
|
||||||
|
rq = ["rq (>=0.6)"]
|
||||||
|
sanic = ["sanic (>=0.8)"]
|
||||||
|
sqlalchemy = ["sqlalchemy (>=1.2)"]
|
||||||
|
starlette = ["starlette (>=0.19.1)"]
|
||||||
|
tornado = ["tornado (>=5)"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "setuptools"
|
name = "setuptools"
|
||||||
version = "65.4.1"
|
version = "65.4.1"
|
||||||
@ -920,6 +974,14 @@ category = "main"
|
|||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
|
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "smmap"
|
||||||
|
version = "5.0.0"
|
||||||
|
description = "A pure Python implementation of a sliding window memory map manager"
|
||||||
|
category = "main"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sniffio"
|
name = "sniffio"
|
||||||
version = "1.3.0"
|
version = "1.3.0"
|
||||||
@ -1503,6 +1565,14 @@ frozenlist = [
|
|||||||
{file = "frozenlist-1.3.1.tar.gz", hash = "sha256:3a735e4211a04ccfa3f4833547acdf5d2f863bfeb01cfd3edaffbc251f15cec8"},
|
{file = "frozenlist-1.3.1.tar.gz", hash = "sha256:3a735e4211a04ccfa3f4833547acdf5d2f863bfeb01cfd3edaffbc251f15cec8"},
|
||||||
]
|
]
|
||||||
genshin = []
|
genshin = []
|
||||||
|
gitdb = [
|
||||||
|
{file = "gitdb-4.0.9-py3-none-any.whl", hash = "sha256:8033ad4e853066ba6ca92050b9df2f89301b8fc8bf7e9324d412a63f8bf1a8fd"},
|
||||||
|
{file = "gitdb-4.0.9.tar.gz", hash = "sha256:bac2fd45c0a1c9cf619e63a90d62bdc63892ef92387424b855792a6cabe789aa"},
|
||||||
|
]
|
||||||
|
GitPython = [
|
||||||
|
{file = "GitPython-3.1.29-py3-none-any.whl", hash = "sha256:41eea0deec2deea139b459ac03656f0dd28fc4a3387240ec1d3c259a2c47850f"},
|
||||||
|
{file = "GitPython-3.1.29.tar.gz", hash = "sha256:cc36bfc4a3f913e66805a28e84703e419d9c264c1077e537b54f0e1af85dbefd"},
|
||||||
|
]
|
||||||
greenlet = [
|
greenlet = [
|
||||||
{file = "greenlet-1.1.3-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:8c287ae7ac921dfde88b1c125bd9590b7ec3c900c2d3db5197f1286e144e712b"},
|
{file = "greenlet-1.1.3-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:8c287ae7ac921dfde88b1c125bd9590b7ec3c900c2d3db5197f1286e144e712b"},
|
||||||
{file = "greenlet-1.1.3-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:870a48007872d12e95a996fca3c03a64290d3ea2e61076aa35d3b253cf34cd32"},
|
{file = "greenlet-1.1.3-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:870a48007872d12e95a996fca3c03a64290d3ea2e61076aa35d3b253cf34cd32"},
|
||||||
@ -2084,6 +2154,10 @@ rich = [
|
|||||||
{file = "rich-12.6.0-py3-none-any.whl", hash = "sha256:a4eb26484f2c82589bd9a17c73d32a010b1e29d89f1604cd9bf3a2097b81bb5e"},
|
{file = "rich-12.6.0-py3-none-any.whl", hash = "sha256:a4eb26484f2c82589bd9a17c73d32a010b1e29d89f1604cd9bf3a2097b81bb5e"},
|
||||||
{file = "rich-12.6.0.tar.gz", hash = "sha256:ba3a3775974105c221d31141f2c116f4fd65c5ceb0698657a11e9f295ec93fd0"},
|
{file = "rich-12.6.0.tar.gz", hash = "sha256:ba3a3775974105c221d31141f2c116f4fd65c5ceb0698657a11e9f295ec93fd0"},
|
||||||
]
|
]
|
||||||
|
sentry-sdk = [
|
||||||
|
{file = "sentry-sdk-1.9.10.tar.gz", hash = "sha256:4fbace9a763285b608c06f01a807b51acb35f6059da6a01236654e08b0ee81ff"},
|
||||||
|
{file = "sentry_sdk-1.9.10-py2.py3-none-any.whl", hash = "sha256:2469240f6190aaebcb453033519eae69cfe8cc602065b4667e18ee14fc1e35dc"},
|
||||||
|
]
|
||||||
setuptools = [
|
setuptools = [
|
||||||
{file = "setuptools-65.4.1-py3-none-any.whl", hash = "sha256:1b6bdc6161661409c5f21508763dc63ab20a9ac2f8ba20029aaaa7fdb9118012"},
|
{file = "setuptools-65.4.1-py3-none-any.whl", hash = "sha256:1b6bdc6161661409c5f21508763dc63ab20a9ac2f8ba20029aaaa7fdb9118012"},
|
||||||
{file = "setuptools-65.4.1.tar.gz", hash = "sha256:3050e338e5871e70c72983072fe34f6032ae1cdeeeb67338199c2f74e083a80e"},
|
{file = "setuptools-65.4.1.tar.gz", hash = "sha256:3050e338e5871e70c72983072fe34f6032ae1cdeeeb67338199c2f74e083a80e"},
|
||||||
@ -2092,6 +2166,10 @@ six = [
|
|||||||
{file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
|
{file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
|
||||||
{file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
|
{file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
|
||||||
]
|
]
|
||||||
|
smmap = [
|
||||||
|
{file = "smmap-5.0.0-py3-none-any.whl", hash = "sha256:2aba19d6a040e78d8b09de5c57e96207b09ed71d8e55ce0959eeee6c8e190d94"},
|
||||||
|
{file = "smmap-5.0.0.tar.gz", hash = "sha256:c840e62059cd3be204b0c9c9f74be2c09d5648eddd4580d9314c3ecde0b30936"},
|
||||||
|
]
|
||||||
sniffio = [
|
sniffio = [
|
||||||
{file = "sniffio-1.3.0-py3-none-any.whl", hash = "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384"},
|
{file = "sniffio-1.3.0-py3-none-any.whl", hash = "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384"},
|
||||||
{file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"},
|
{file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"},
|
||||||
|
@ -37,6 +37,8 @@ lxml = "^4.9.1"
|
|||||||
arko-wrapper = "^0.2.3"
|
arko-wrapper = "^0.2.3"
|
||||||
fastapi = "^0.85.1"
|
fastapi = "^0.85.1"
|
||||||
uvicorn = {extras = ["standard"], version = "^0.18.3"}
|
uvicorn = {extras = ["standard"], version = "^0.18.3"}
|
||||||
|
sentry-sdk = "^1.9.10"
|
||||||
|
GitPython = "^3.1.29"
|
||||||
|
|
||||||
[tool.poetry.extras]
|
[tool.poetry.extras]
|
||||||
pyro = ["Pyrogram", "TgCrypto"]
|
pyro = ["Pyrogram", "TgCrypto"]
|
||||||
|
@ -254,17 +254,5 @@ class Handler(DefaultRichHandler):
|
|||||||
class FileHandler(Handler):
|
class FileHandler(Handler):
|
||||||
def __init__(self, *args, path: Path, **kwargs):
|
def __init__(self, *args, path: Path, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
while True:
|
path.parent.mkdir(exist_ok=True, parents=True)
|
||||||
try:
|
|
||||||
path.parent.mkdir(exist_ok=True)
|
|
||||||
break
|
|
||||||
except FileNotFoundError:
|
|
||||||
parent = path.parent
|
|
||||||
while True:
|
|
||||||
try:
|
|
||||||
parent.mkdir(exist_ok=True)
|
|
||||||
break
|
|
||||||
except FileNotFoundError:
|
|
||||||
parent = parent.parent
|
|
||||||
path.parent.mkdir(exist_ok=True)
|
|
||||||
self.console = Console(width=180, file=FileIO(path), theme=Theme(DEFAULT_STYLE))
|
self.console = Console(width=180, file=FileIO(path), theme=Theme(DEFAULT_STYLE))
|
||||||
|
Loading…
Reference in New Issue
Block a user