refactor: add pipe module

This commit is contained in:
2023-03-20 01:10:17 +03:00
parent 81bf685fec
commit 81c704576a
13 changed files with 219 additions and 100 deletions
+3
View File
@@ -0,0 +1,3 @@
from .core import PIPE_REGISTRY
PIPE_REGISTRY.register_module("project_manager.pipe.type")
+83
View File
@@ -0,0 +1,83 @@
import importlib
import re
from pathlib import Path
from typing import Any, Generic, List, Optional, Type, TypeVar
__all__ = ("Pipe", "PipeRegistry", "PipeParser", "PipeRunner", "PIPE_REGISTRY")
I = TypeVar("I")
O = TypeVar("O")
class Pipe(Generic[I, O]):
type: str
def apply(self, value: I, basepath: Path) -> Optional[O]:
raise NotImplementedError
class PipeRegistry:
def __init__(self):
self._pipes = {}
def register(self, pipe_type: Type[Pipe]):
self._pipes[pipe_type.type] = pipe_type
def register_module(self, module_name: str):
module = importlib.import_module(module_name)
modulepath = Path(module.__file__).parent
for item in modulepath.iterdir():
if item.suffix == ".py" and not item.name.startswith("__"):
sub_module = importlib.import_module(f"{module_name}.{item.stem}")
for v in sub_module.__dict__.values():
if isinstance(v, type):
for baseclass in v.__bases__:
if baseclass == Pipe:
self.register(v)
def get(self, pipe_type: str) -> Type[Pipe]:
return self._pipes[pipe_type]
class PipeParser:
_PIPE_PATTERN = re.compile("^(\\w+)\\((.*)\\)")
def parse(self, pipeline: str) -> List[Pipe]:
chain = [item.strip() for item in pipeline.split("|")]
result = []
for part in chain:
matched = self._PIPE_PATTERN.match(part)
if matched:
pipe_name = matched.group(1)
pipe_args = [item.strip() for item in matched.group(2).split(",")]
else:
pipe_name = part.strip()
pipe_args = []
pipe_type = PIPE_REGISTRY.get(pipe_name)
result.append(pipe_type(*pipe_args))
return result
class PipeRunner:
def __init__(self, basepath: Path, parser: Optional[PipeParser] = None):
self._basepath = basepath
self._parser = parser or PipeParser()
def run(self, pipes: List[Pipe]) -> Any:
result = None
for pipe in pipes:
result = pipe.apply(result, self._basepath)
return result
def resolve_value(self, pipeline: str) -> Any:
pipe = self._parser.parse(pipeline)
return self.run(pipe)
def check_conditions(self, conditions: List[str]) -> bool:
for condition in conditions:
if not self.resolve_value(condition):
return False
return True
PIPE_REGISTRY = PipeRegistry()
+16
View File
@@ -0,0 +1,16 @@
from pathlib import Path
from typing import Any
from ..core import Pipe
__all__ = ("EqualPipe",)
class EqualPipe(Pipe[Any, bool]):
type = "equal"
def __init__(self, value: Any):
self._value = value
def apply(self, value: Any, basepath: Path) -> bool:
return self._value == value
+37
View File
@@ -0,0 +1,37 @@
from pathlib import Path
from typing import Any, Optional
from ..core import Pipe
from ..util import reflect
__all__ = ("FilePipe",)
class FilePipe(Pipe[None, Any]):
type = "file"
_LOADERS = {
".toml": "tomli:load",
".json": "json:load",
}
def __init__(self, filename: Optional[str], loader: Optional[str] = None):
self._filename = filename
self._loader = loader
def _resolve_loader(self, filepath: Path):
loader = self._loader or self._LOADERS.get(filepath.suffix)
return reflect(loader) if loader else None
def apply(self, value: Optional[str], basepath: Path) -> Optional[Any]:
filepath = Path(value or self._filename) # TODO: raise none filepath exception
if not filepath.is_absolute():
filepath = basepath / filepath
if not filepath.exists():
return None
loader = self._resolve_loader(filepath)
with open(filepath, "rb") as fd:
if loader:
result = loader(fd)
else:
result = fd.read()
return result
+21
View File
@@ -0,0 +1,21 @@
from pathlib import Path
from typing import Any, Optional
from ..core import Pipe
__all__ = ("GetPipe",)
class GetPipe(Pipe[dict, Any]):
type = "get"
def __init__(self, keypath: str):
self._keypath = keypath
def apply(self, value: dict, basepath: Path) -> Optional[Any]:
path = self._keypath.split(".")
result = value.get(path[0])
for key in path[1:]:
if result:
result = result.get(key)
return result
+13
View File
@@ -0,0 +1,13 @@
from pathlib import Path
from typing import Any
from ..core import Pipe
__all__ = ("NotPipe",)
class NotPipe(Pipe[Any, bool]):
type = "not"
def apply(self, value: Any, basepath: Path) -> bool:
return not value
+20
View File
@@ -0,0 +1,20 @@
import re
from pathlib import Path
from typing import Optional
from ..core import Pipe
__all__ = ("SearchPipe",)
class SearchPipe(Pipe[str, str]):
type = "search"
def __init__(self, patter: str, position: str = "1"):
self._patter = patter
self._position = int(position)
def apply(self, value: str, basepath: Path) -> Optional[str]:
matched = re.compile(self._patter).search(value)
if matched:
return matched.group(self._position)
+13
View File
@@ -0,0 +1,13 @@
import importlib
from typing import Any
__all__ = ("reflect",)
def reflect(value: str) -> Any:
if ":" in value:
module_name, item_name = value.split(":")
else:
module_name, item_name = "builtins", value
module = importlib.import_module(module_name)
return getattr(module, item_name)