Skip to content

Commit

Permalink
add MemeArgsParser
Browse files Browse the repository at this point in the history
  • Loading branch information
MeetWq committed Mar 17, 2023
1 parent eb1e84c commit 62100a8
Show file tree
Hide file tree
Showing 16 changed files with 106 additions and 81 deletions.
6 changes: 3 additions & 3 deletions docs/develop.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ add_meme(
```python
@dataclass
class MemeArgsType:
parser: ArgumentParser # 参数解析器,将命令行形式的文本解析为字典形式,方便通过命令行使用
parser: MemeArgsParser # 参数解析器,将命令行形式的文本解析为字典形式,方便通过命令行使用
model: Type[MemeArgsModel] # 参数模型,用于验证字典形式的参数,并传入表情制作函数
instances: List[MemeArgsModel] = field(default_factory=list) # 可选,参数模型示例,推荐填写,方便生成不同参数下的预览图
```
Expand All @@ -102,9 +102,9 @@ class Model(MemeArgsModel):
同时定义如下的参数解析器:

```python
from argparse import ArgumentParser
from meme_generator import MemeArgsParser

parser = ArgumentParser(prefix_chars="-/")
parser = MemeArgsParser(prefix_chars="-/")
parser.add_argument("--circle", "/圆", action="store_true", help="是否将图片变为圆形")
```

Expand Down
1 change: 1 addition & 0 deletions meme_generator/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from meme_generator.manager import load_memes as load_memes
from meme_generator.meme import Meme as Meme
from meme_generator.meme import MemeArgsModel as MemeArgsModel
from meme_generator.meme import MemeArgsParser as MemeArgsParser
from meme_generator.meme import MemeArgsType as MemeArgsType
from meme_generator.meme import MemeParamsType as MemeParamsType
from meme_generator.version import __version__
Expand Down
15 changes: 2 additions & 13 deletions meme_generator/app.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import copy
from argparse import ArgumentParser
from typing import Any, Dict, List, Optional

import filetype
Expand All @@ -8,7 +6,7 @@
from pydantic import BaseModel, ValidationError

from meme_generator.config import meme_config
from meme_generator.exception import ArgParserExit, MemeGeneratorException, NoSuchMeme
from meme_generator.exception import MemeGeneratorException, NoSuchMeme
from meme_generator.manager import get_meme, get_meme_keys, get_memes
from meme_generator.meme import Meme, MemeArgsModel

Expand Down Expand Up @@ -141,16 +139,7 @@ async def _(key: str):
async def _(key: str, args: List[str] = []):
try:
meme = get_meme(key)
parser = (
copy.deepcopy(meme.params_type.args_type.parser)
if meme.params_type.args_type
else ArgumentParser()
)
parser.add_argument("texts", nargs="*", default=[])
try:
return vars(parser.parse_args(args))
except:
raise ArgParserExit(meme.key)
return meme.parse_args(args)
except MemeGeneratorException as e:
raise HTTPException(status_code=500, detail=str(e))

Expand Down
59 changes: 30 additions & 29 deletions meme_generator/exception.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,37 +2,44 @@


class MemeGeneratorException(Exception):
def __init__(self, message: str):
self.message = message

def __str__(self) -> str:
return self.__repr__()

def __repr__(self) -> str:
return f"Error in meme_generator: {self.message!r}"


class NoSuchMeme(MemeGeneratorException):
def __init__(self, meme_key: str):
self.meme_key = meme_key

def __repr__(self) -> str:
return f'No such meme with key="{self.meme_key}"'
message = f'No such meme with key="{self.meme_key}"'
super().__init__(message)


class TextOverLength(MemeGeneratorException):
def __init__(self, text: str):
self.text = text
message = f'Text "{self.text}" is too long!'
super().__init__(message)

def __repr__(self) -> str:
return f'Text "{self.text}" is too long!'

class OpenImageFailed(MemeGeneratorException):
def __init__(self, error_message: str):
self.error_message = error_message
message = f'Error opening images: "{self.error_message!r}"'
super().__init__(message)


class ParamsMismatch(MemeGeneratorException):
def __init__(self, meme_key: str, message: Optional[str] = None):
def __init__(self, meme_key: str, message: str):
self.meme_key = meme_key
self.message = message

def __repr__(self) -> str:
return (
f"ParamsMismatch(key={self.meme_key}"
+ (f", message={self.message!r}" if self.message else "")
+ ")"
)
return f'ParamsMismatch(key="{self.meme_key}", message="{self.message!r}")'


class ImageNumberMismatch(ParamsMismatch):
Expand All @@ -55,41 +62,35 @@ def __init__(self, meme_key: str, min_texts: int = 0, max_texts: int = 0):

class TextOrNameNotEnough(ParamsMismatch):
def __init__(self, meme_key: str, message: Optional[str] = None):
self.meme_key = meme_key
self.message = message or "The number of texts or user names is not enough"
message = message or "The number of texts or user names is not enough"
super().__init__(meme_key, message)


class ArgMismatch(ParamsMismatch):
pass


class ArgParserExit(ArgMismatch):
def __init__(
self, meme_key: str, status: int = 0, error_message: Optional[str] = None
):
class ParserExit(MemeGeneratorException):
def __init__(self, status: int = 0, error_message: Optional[str] = None):
self.status = status
self.error_message = error_message
self.error_message = error_message or ""
message = (
f"Argument parser failed to parse. (status={self.status}"
+ (f", message={self.error_message!r}" if self.error_message else "")
+ ")"
)
super().__init__(meme_key, message)
super().__init__(message)


class ArgModelMismatch(ArgMismatch):
def __init__(self, meme_key: str, error_message: Optional[str] = None):
class ArgParserExit(ArgMismatch):
def __init__(self, meme_key: str, error_message: str):
self.error_message = error_message
message = f"Argument model validation failed." + (
f" (message={self.error_message!r})" if self.error_message else ""
)
message = f"Argument parser failed to parse: {self.error_message!r}"
super().__init__(meme_key, message)


class OpenImageFailed(ParamsMismatch):
def __init__(self, meme_key: str, error_message: Optional[str] = None):
class ArgModelMismatch(ArgMismatch):
def __init__(self, meme_key: str, error_message: str):
self.error_message = error_message
message = f"Error opening images." + (
f" (message={self.error_message!r})" if self.error_message else ""
)
message = f"Argument model validation failed: {self.error_message!r}"
super().__init__(meme_key, message)
51 changes: 48 additions & 3 deletions meme_generator/meme.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
from argparse import ArgumentParser
import copy
from argparse import ArgumentError, ArgumentParser
from contextvars import ContextVar
from dataclasses import dataclass, field
from io import BytesIO
from pathlib import Path
from typing import (
IO,
Any,
Awaitable,
Callable,
Expand All @@ -21,8 +24,10 @@

from .exception import (
ArgModelMismatch,
ArgParserExit,
ImageNumberMismatch,
OpenImageFailed,
ParserExit,
TextNumberMismatch,
TextOrNameNotEnough,
)
Expand All @@ -46,9 +51,32 @@ class MemeArgsModel(BaseModel):
]


parser_message: ContextVar[str] = ContextVar("parser_message")


class MemeArgsParser(ArgumentParser):
"""`shell_like` 命令参数解析器,解析出错时不会退出程序。
用法:
用法与 `argparse.ArgumentParser` 相同,
参考文档: [argparse](https://docs.python.org/3/library/argparse.html)
"""

def _print_message(self, message: str, file: Optional[IO[str]] = None):
if (msg := parser_message.get(None)) is not None:
parser_message.set(msg + message)
else:
super()._print_message(message, file)

def exit(self, status: int = 0, message: Optional[str] = None):
if message:
self._print_message(message)
raise ParserExit(status=status, error_message=parser_message.get(None))


@dataclass
class MemeArgsType:
parser: ArgumentParser
parser: MemeArgsParser
model: Type[MemeArgsModel]
instances: List[MemeArgsModel] = field(default_factory=list)

Expand Down Expand Up @@ -107,7 +135,7 @@ async def __call__(
image = BytesIO(image)
imgs.append(BuildImage.open(image))
except Exception as e:
raise OpenImageFailed(self.key, str(e))
raise OpenImageFailed(str(e))

values = {"images": imgs, "texts": texts, "args": model}

Expand All @@ -118,6 +146,23 @@ async def __call__(
else:
return await run_sync(cast(Callable[..., BytesIO], self.function))(**values)

def parse_args(self, args: List[str] = []) -> Dict[str, Any]:
parser = (
copy.deepcopy(self.params_type.args_type.parser)
if self.params_type.args_type
else MemeArgsParser()
)
parser.add_argument("texts", nargs="*", default=[])
t = parser_message.set("")
try:
return vars(parser.parse_args(args))
except ArgumentError as e:
raise ArgParserExit(self.key, str(e))
except ParserExit as e:
raise ArgParserExit(self.key, e.error_message)
finally:
parser_message.reset(t)

async def generate_preview(self, *, args: Dict[str, Any] = {}) -> BytesIO:
default_images = [random_image() for _ in range(self.params_type.min_images)]
default_texts = (
Expand Down
5 changes: 2 additions & 3 deletions meme_generator/memes/always/__init__.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
from argparse import ArgumentParser
from typing import List, Literal

from pil_utils import BuildImage
from pydantic import Field

from meme_generator import MemeArgsModel, MemeArgsType, add_meme
from meme_generator import MemeArgsModel, MemeArgsParser, MemeArgsType, add_meme
from meme_generator.utils import (
FrameAlignPolicy,
Maker,
Expand All @@ -14,7 +13,7 @@

help = "生成模式"

parser = ArgumentParser(prefix_chars="-/")
parser = MemeArgsParser(prefix_chars="-/")
group = parser.add_mutually_exclusive_group()
group.add_argument(
"--mode",
Expand Down
5 changes: 2 additions & 3 deletions meme_generator/memes/bubble_tea/__init__.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
from argparse import ArgumentParser
from pathlib import Path
from typing import List, Literal

from PIL import Image
from pil_utils import BuildImage
from pydantic import Field

from meme_generator import MemeArgsModel, MemeArgsType, add_meme
from meme_generator import MemeArgsModel, MemeArgsParser, MemeArgsType, add_meme

img_dir = Path(__file__).parent / "images"


help = "奶茶的位置"

parser = ArgumentParser(prefix_chars="-/")
parser = MemeArgsParser(prefix_chars="-/")
group = parser.add_mutually_exclusive_group()
group.add_argument(
"-p",
Expand Down
5 changes: 2 additions & 3 deletions meme_generator/memes/crawl/__init__.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
import random
from argparse import ArgumentParser
from pathlib import Path
from typing import List

from pil_utils import BuildImage
from pydantic import Field

from meme_generator import MemeArgsModel, MemeArgsType, add_meme
from meme_generator import MemeArgsModel, MemeArgsParser, MemeArgsType, add_meme

img_dir = Path(__file__).parent / "images"


help = "图片编号,范围为 1~92"

parser = ArgumentParser()
parser = MemeArgsParser()
parser.add_argument("-n", "--number", type=int, default=0, help=help)


Expand Down
5 changes: 2 additions & 3 deletions meme_generator/memes/gun/__init__.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
from argparse import ArgumentParser
from pathlib import Path
from typing import List, Literal

from PIL import Image
from pil_utils import BuildImage
from pydantic import Field

from meme_generator import MemeArgsModel, MemeArgsType, add_meme
from meme_generator import MemeArgsModel, MemeArgsParser, MemeArgsType, add_meme

img_dir = Path(__file__).parent / "images"


help = "枪的位置"

parser = ArgumentParser(prefix_chars="-/")
parser = MemeArgsParser(prefix_chars="-/")
group = parser.add_mutually_exclusive_group()
group.add_argument(
"-p",
Expand Down
5 changes: 2 additions & 3 deletions meme_generator/memes/jiji_king/__init__.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
import math
from argparse import ArgumentParser
from pathlib import Path
from typing import List

from pil_utils import BuildImage
from pydantic import Field

from meme_generator import MemeArgsModel, MemeArgsType, add_meme
from meme_generator import MemeArgsModel, MemeArgsParser, MemeArgsType, add_meme
from meme_generator.exception import TextOverLength

img_dir = Path(__file__).parent / "images"

help = "是否将图片变为圆形"

parser = ArgumentParser(prefix_chars="-/")
parser = MemeArgsParser(prefix_chars="-/")
parser.add_argument("--circle", "/圆", action="store_true", help=help)


Expand Down
5 changes: 2 additions & 3 deletions meme_generator/memes/kaleidoscope/__init__.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
import math
from argparse import ArgumentParser
from typing import List

from pil_utils import BuildImage
from pydantic import Field

from meme_generator import MemeArgsModel, MemeArgsType, add_meme
from meme_generator import MemeArgsModel, MemeArgsParser, MemeArgsType, add_meme
from meme_generator.utils import make_jpg_or_gif

help = "是否将图片变为圆形"

parser = ArgumentParser(prefix_chars="-/")
parser = MemeArgsParser(prefix_chars="-/")
parser.add_argument("--circle", "/圆", action="store_true", help=help)


Expand Down
Loading

0 comments on commit 62100a8

Please sign in to comment.