Skip to content

Commit

Permalink
✨ add special pat: hex
Browse files Browse the repository at this point in the history
  • Loading branch information
RF-Tar-Railt committed Sep 22, 2023
1 parent 3ad1e0d commit 691c025
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 50 deletions.
95 changes: 45 additions & 50 deletions nepattern/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -424,7 +424,7 @@ def __init__(self, origin: type[TOrigin], func: Callable[[BasePattern, TInput],
"""匹配任意内容并转为字符串的表达式"""

def _string(_, x: str) -> str:
if not isinstance(x, str):
if not isinstance(x, str): # pragma: no cover
raise MatchFailed(lang.require("nepattern", "type_error").format(type=x.__class__, target=x, expected="str"))
return x

Expand All @@ -439,12 +439,10 @@ def match(self, input_: Union[str, int]) -> int:
return input_
if not isinstance(input_, str):
raise MatchFailed(lang.require("nepattern", "type_error").format(type=input_.__class__, target=input_, expected="int | str"))
if input_[0] == "-":
if input_[1:].isdigit():
return -int(input_[1:])
elif input_.isdigit():
try:
return int(input_)
raise MatchFailed(lang.require("nepattern", "content_error").format(target=input_, expected="int"))
except ValueError as e:
raise MatchFailed(lang.require("nepattern", "content_error").format(target=input_, expected="int")) from e

def prefixed(self):
return BasePattern(r"(\-?\d+)", MatchMode.REGEX_CONVERT, int, lambda _, x: int(x[1]), "int").prefixed()
Expand All @@ -460,56 +458,41 @@ class FloatPattern(BasePattern[float, Union[str, int, float]]):
def __init__(self):
super().__init__(mode=MatchMode.TYPE_CONVERT, origin=float, alias="float")

def match(self, input_: Union[str, float]) -> float:
def match(self, input_: Union[str, float, int]) -> float:
if isinstance(input_, float):
return input_
if isinstance(input_, int):
return float(input_)
if not isinstance(input_, str):
if not isinstance(input_, (str, int)):
raise MatchFailed(lang.require("nepattern", "type_error").format(type=input_.__class__, target=input_, expected="str | int | float"))
sig = 1
if input_[0] == "-":
sig = -1
input_ = input_[1:]
dot = input_.find(".")
if dot == -1:
if input_.isdigit():
return float(input_) * sig
elif input_[:dot].isdigit() and input_[dot + 1:].isdigit():
return float(input_) * sig
raise MatchFailed(lang.require("nepattern", "content_error").format(target=input_, expected="float"))
try:
return float(input_)
except ValueError as e:
raise MatchFailed(lang.require("nepattern", "content_error").format(target=input_, expected="float")) from e

def prefixed(self):
def prefixed(self): # pragma: no cover
return BasePattern(r"(\-?\d+\.?\d*)", MatchMode.REGEX_CONVERT, float, lambda _, x: float(x[1]), "float").prefixed()

def suffixed(self):
def suffixed(self): # pragma: no cover
return BasePattern(r"(\-?\d+\.?\d*)", MatchMode.REGEX_CONVERT, float, lambda _, x: float(x[1]), "float").suffixed()

FLOAT = FloatPattern()
"""浮点数表达式"""

class NumberPattern(BasePattern[Union[int, float], Union[str, int, float]]):
def __init__(self):
super().__init__(mode=MatchMode.TYPE_CONVERT, origin=Union[int, float], alias="float")
super().__init__(mode=MatchMode.TYPE_CONVERT, origin=Union[int, float], alias="number")

def match(self, input_: Union[str, float]) -> float:
if isinstance(input_, (float, int)):
return input_
if not isinstance(input_, str):
raise MatchFailed(lang.require("nepattern", "type_error").format(type=input_.__class__, target=input_, expected="str | int | float"))
sig = 1
if input_[0] == "-":
sig = -1
input_ = input_[1:]
dot = input_.find(".")
if dot == -1:
if input_.isdigit():
return int(input_) * sig
elif input_[:dot].isdigit() and input_[dot + 1:].isdigit():
return float(input_) * sig
raise MatchFailed(lang.require("nepattern", "content_error").format(target=input_, expected="int | float"))
try:
res = float(input_)
return int(res) if res.is_integer() else res
except ValueError as e:
raise MatchFailed(lang.require("nepattern", "content_error").format(target=input_, expected="int | float")) from e

def prefixed(self):
def prefixed(self): # pragma: no cover

return BasePattern(
r"(\-?\d+(?P<float>\.\d*)?)",
Expand All @@ -519,7 +502,7 @@ def prefixed(self):
"number",
).prefixed()

def suffixed(self):
def suffixed(self): # pragma: no cover
return BasePattern(
r"(\-?\d+(?P<float>\.\d*)?)",
MatchMode.REGEX_CONVERT,
Expand All @@ -536,21 +519,21 @@ class BoolPattern(BasePattern[bool, Union[str, bool]]):
def __init__(self):
super().__init__(mode=MatchMode.TYPE_CONVERT, origin=bool, alias="bool")

_BOOL = {"true": True, "false": False, "True": True, "False": False}

def match(self, input_: Union[str, bool]) -> bool:
if isinstance(input_, bool):
return input_
if not isinstance(input_, str):
raise MatchFailed(lang.require("nepattern", "type_error").format(type=input_.__class__, target=input_, expected="str | bool"))
if input_.lower() == "true":
return True
if input_.lower() == "false":
return False
if input_ in self._BOOL:
return self._BOOL[input_]
raise MatchFailed(lang.require("nepattern", "content_error").format(target=input_, expected="bool"))

def prefixed(self):
def prefixed(self): # pragma: no cover
return BasePattern(r"(?i:True|False)", MatchMode.REGEX_CONVERT, bool, lambda _, x: x[0].lower() == "true", "bool").prefixed()

def suffixed(self):
def suffixed(self): # pragma: no cover
return BasePattern(r"(?i:True|False)", MatchMode.REGEX_CONVERT, bool, lambda _, x: x[0].lower() == "true", "bool").suffixed()

BOOLEAN = BoolPattern()
Expand Down Expand Up @@ -578,13 +561,25 @@ def suffixed(self):
)
"""匹配网页链接的表达式"""

HEX = BasePattern(
r"((?:0x)?[0-9a-fA-F]+)",
MatchMode.REGEX_CONVERT,
int,
lambda _, x: int(x[1], 16),
"hex",
)
class HexPattern(BasePattern[int, str]):
def __init__(self):
super().__init__(mode=MatchMode.TYPE_CONVERT, origin=int, alias="hex", accepts=str)

def match(self, input_: str) -> int:
if not isinstance(input_, str):
raise MatchFailed(lang.require("nepattern", "type_error").format(type=input_.__class__, target=input_, expected="str"))
try:
return int(input_, 16)
except ValueError as e:
raise MatchFailed(lang.require("nepattern", "content_error").format(target=input_, expected="hex")) from e

def prefixed(self): # pragma: no cover
return BasePattern(r"((?:0x)?[0-9a-fA-F]+)", MatchMode.REGEX_CONVERT, int, lambda _, x: int(x[1], 16), "hex").prefixed()

def suffixed(self): # pragma: no cover
return BasePattern(r"((?:0x)?[0-9a-fA-F]+)", MatchMode.REGEX_CONVERT, int, lambda _, x: int(x[1], 16), "hex").suffixed()

HEX = HexPattern()
"""匹配16进制数的表达式"""

HEX_COLOR = BasePattern(r"(#[0-9a-fA-F]{6})", MatchMode.REGEX_CONVERT, str, lambda _, x: x[1][1:], "color")
Expand Down
40 changes: 40 additions & 0 deletions test.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,45 @@ def test_type():

assert isinstance(re.compile(""), TPattern) # type: ignore

def test_basic():
from datetime import datetime

assert INTEGER.validate(123).success
assert INTEGER.validate("123").value() == 123
assert INTEGER.validate(123.456).failed
assert INTEGER.validate("123.456").failed
assert INTEGER.validate("-123").success

assert FLOAT.validate(123).value() == 123.0
assert FLOAT.validate("123").value() == 123.0
assert FLOAT.validate(123.456).value() == 123.456
assert FLOAT.validate("123.456").value() == 123.456
assert FLOAT.validate("1e10").value() == 1e10
assert FLOAT.validate("-123").value() == -123.0
assert FLOAT.validate("-123.456").value() == -123.456
assert FLOAT.validate("-123.456e-2").value() == -1.23456
assert FLOAT.validate("aaa").failed
assert FLOAT.validate([]).failed

assert BOOLEAN.validate(True).value() == True
assert BOOLEAN.validate(False).value() == False
assert BOOLEAN.validate("True").value() == True
assert BOOLEAN.validate("False").value() == False
assert BOOLEAN.validate("true").value() == True
assert BOOLEAN.validate("false").value() == False
assert BOOLEAN.validate("1").failed
assert BOOLEAN.validate([]).failed

assert HEX.validate(123).failed
assert HEX.validate("0x123").value() == 0x123
assert HEX.validate("0o123").failed

assert DATETIME.validate("2020-01-01").value() == datetime(2020, 1, 1)
assert DATETIME.validate("2020-01-01-12:00:00").value() == datetime(2020, 1, 1, 12, 0, 0)
assert DATETIME.validate("2020-01-01-12:00:00.123").value() == datetime(2020, 1, 1, 12, 0, 0, 123000)
assert DATETIME.validate(1639411200).value() == datetime(2021, 12, 14, 0, 0, 0)
assert DATETIME.validate([]).failed


def test_result():
res = NUMBER.validate(123)
Expand All @@ -17,6 +56,7 @@ def test_result():
assert NUMBER.validate("123").value() == 123
assert NUMBER.validate(123.456).value() == 123.456
assert NUMBER.validate("123.456").value() == 123.456
assert NUMBER.validate("aaa").failed
res1 = NUMBER.validate([], -1)
assert res1.or_default
assert not res1.failed
Expand Down

0 comments on commit 691c025

Please sign in to comment.