Update
This commit is contained in:
@@ -1,5 +1,8 @@
|
||||
*.pyc
|
||||
*.pyo
|
||||
__pycache__
|
||||
.pytest_cache
|
||||
*.egg-info
|
||||
|
||||
prototypes
|
||||
dist
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"app1": {
|
||||
"route": {
|
||||
"operator": "and",
|
||||
"rules": [
|
||||
{
|
||||
"key": "url.host",
|
||||
"operator": "regex",
|
||||
"value": ".*"
|
||||
},
|
||||
{
|
||||
"key": "url.path",
|
||||
"operator": "regex",
|
||||
"value": "\/"
|
||||
}
|
||||
]
|
||||
},
|
||||
"function": {
|
||||
"operator": "const",
|
||||
"value": "OK"
|
||||
}
|
||||
}
|
||||
}
|
||||
+4
-2
@@ -1,15 +1,17 @@
|
||||
[project]
|
||||
name = "router"
|
||||
version ="0.1.0"
|
||||
version ="0.2"
|
||||
authors = [
|
||||
{ name="RemiZOffAlex", email="remizoffalex@gmail.com" },
|
||||
]
|
||||
description = "Маршрутизатор"
|
||||
requires-python = ">=3.10"
|
||||
classifiers = [
|
||||
"Programming Language :: Python",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Programming Language :: Python :: 3.10",
|
||||
"Programming Language :: Python :: 3.11",
|
||||
"Programming Language :: Python :: 3.12"
|
||||
]
|
||||
keywords = ["documentation"]
|
||||
keywords = ["broker", "router"]
|
||||
dependencies = []
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
__author__ = 'RemiZOffAlex'
|
||||
__email__ = 'remizoffalex@mail.ru'
|
||||
|
||||
|
||||
import logging
|
||||
|
||||
from collections.abc import Callable
|
||||
|
||||
from .route import Route
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Broker:
|
||||
def __init__(self):
|
||||
self.routes = []
|
||||
|
||||
def __call__(self, query):
|
||||
"""Вызов метода
|
||||
"""
|
||||
for route in self.routes:
|
||||
log.debug(route)
|
||||
output = route.comparator(query)
|
||||
if output:
|
||||
return (route.endpoint, output)
|
||||
|
||||
def __getitem__(self, key):
|
||||
method = self.methods[key]
|
||||
return method.function
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self.methods)
|
||||
|
||||
def __len__(self):
|
||||
return len(self.methods)
|
||||
|
||||
def __setitem__(self, key, function, pre=None):
|
||||
method = Method(function=function, pre=pre)
|
||||
self.methods[key] = method
|
||||
|
||||
def __delitem__(self, key):
|
||||
del self.methods[key]
|
||||
|
||||
def __repr__(self):
|
||||
return repr(self.methods)
|
||||
|
||||
def route(self, comparator: Callable, *args):
|
||||
"""Декоратор
|
||||
"""
|
||||
def wrap(function):
|
||||
_route = Route(
|
||||
comparator=comparator,
|
||||
endpoint=function
|
||||
)
|
||||
self.routes.append(_route)
|
||||
return function
|
||||
return wrap
|
||||
@@ -1,37 +0,0 @@
|
||||
__author__ = 'RemiZOffAlex'
|
||||
__email__ = 'remizoffalex@mail.ru'
|
||||
|
||||
import logging
|
||||
|
||||
from .dsl.common import And, Equal, Or, Regex
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Compiler:
|
||||
def __call__(self, rule):
|
||||
if isinstance(rule, list):
|
||||
val = []
|
||||
for item in rule:
|
||||
val.append(self(item))
|
||||
return val
|
||||
elif isinstance(rule, dict):
|
||||
assert 'operator' in rule, \
|
||||
'Not operator in rule: {rule}'.format(rule=rule)
|
||||
if rule['operator'] == 'and':
|
||||
val = self(rule['rules'])
|
||||
result = And(val)
|
||||
return result
|
||||
elif rule['operator'] == 'or':
|
||||
val = self(rule['rules'])
|
||||
return Or(val)
|
||||
elif rule['operator'] == 'equal':
|
||||
return Equal(
|
||||
key=rule['key'],
|
||||
value=rule['value']
|
||||
)
|
||||
elif rule['operator'] == 'regex':
|
||||
return Regex(
|
||||
key=rule['key'],
|
||||
value=rule['value']
|
||||
)
|
||||
@@ -1,95 +0,0 @@
|
||||
__author__ = 'RemiZOffAlex'
|
||||
__email__ = 'remizoffalex@mail.ru'
|
||||
|
||||
import re
|
||||
|
||||
|
||||
_part_re = re.compile(
|
||||
r"""
|
||||
(?:
|
||||
(?P<slash>/) # a slash
|
||||
|
|
||||
(?P<static>[^</]+) # static rule data
|
||||
|
|
||||
(?:
|
||||
<
|
||||
(?:
|
||||
(?P<converter>[a-zA-Z_][a-zA-Z0-9_]*) # converter name
|
||||
(?:\((?P<arguments>.*?)\))? # converter arguments
|
||||
: # variable delimiter
|
||||
)?
|
||||
(?P<variable>[a-zA-Z_][a-zA-Z0-9_]*) # variable name
|
||||
>
|
||||
)
|
||||
)
|
||||
""",
|
||||
re.VERBOSE,
|
||||
)
|
||||
|
||||
|
||||
def rule_parse(rule: str) -> list:
|
||||
"""Парсим правило
|
||||
"""
|
||||
result = []
|
||||
pos = 0
|
||||
|
||||
while pos < len(rule):
|
||||
match = _part_re.match(rule, pos)
|
||||
print('match:', match)
|
||||
pos = match.end()
|
||||
data = match.groupdict()
|
||||
print('data:', data)
|
||||
result.append(data)
|
||||
return result
|
||||
|
||||
def rule_compile(rule: list):
|
||||
"""Компилируем регулярку из правила
|
||||
"""
|
||||
result = []
|
||||
params = {}
|
||||
for item in rule:
|
||||
if item['slash']:
|
||||
result.append(item['slash'])
|
||||
elif item['static']:
|
||||
result.append(item['static'])
|
||||
elif item['converter']:
|
||||
params[item['variable']] = None
|
||||
if item['converter']=='int':
|
||||
result.append('(?P<{}>\d+)'.format(item['variable']))
|
||||
print('item:', item)
|
||||
regexp = ''.join(result)
|
||||
print('regexp:', regexp)
|
||||
return (re.compile(regexp), params)
|
||||
|
||||
def rule_extract(regexp, request):
|
||||
pass
|
||||
|
||||
def rule_comparator(rule, request):
|
||||
try:
|
||||
rule_proto = rule_parse(rule)
|
||||
print('rule_proto:', rule_proto)
|
||||
rule_regex, params = rule_compile(rule_proto)
|
||||
print('rule:', rule_regex)
|
||||
result = rule_regex.match(url)
|
||||
for key in params:
|
||||
params[key] = result.group(key)
|
||||
print(key, ':', params[key])
|
||||
return params
|
||||
except Exception as error:
|
||||
return None
|
||||
|
||||
def main():
|
||||
print('_part_re.groups:', _part_re.groups)
|
||||
print('_part_re.groups:', _part_re.groups)
|
||||
rule = '/v<int:major>.<int:minor>'
|
||||
urls = [
|
||||
'/v3451.5432',
|
||||
'/v3sdfsdf451.5432'
|
||||
]
|
||||
|
||||
for url in urls:
|
||||
params = rule_comparator(rule, url)
|
||||
if params:
|
||||
print('route {} is valid:'.format(url), params)
|
||||
else:
|
||||
print('route {} invalid'.format(url))
|
||||
@@ -0,0 +1,67 @@
|
||||
__author__ = 'RemiZOffAlex'
|
||||
__email__ = 'remizoffalex@mail.ru'
|
||||
|
||||
|
||||
import logging
|
||||
|
||||
from collections.abc import Callable
|
||||
|
||||
from .route import Route
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Broker:
|
||||
def __init__(self, debug: bool = False):
|
||||
self.routes = {}
|
||||
self.debug = debug
|
||||
|
||||
def __call__(self, query):
|
||||
"""Вызов метода
|
||||
"""
|
||||
for key in self.routes:
|
||||
_route = self.routes[key]
|
||||
if self.debug:
|
||||
log.debug('{key}: {route}'.format(
|
||||
key=key,
|
||||
route=_route
|
||||
))
|
||||
print(key)
|
||||
print(_route)
|
||||
print(_route.comparator)
|
||||
print(query)
|
||||
output = _route.comparator(query)
|
||||
if output:
|
||||
return (_route.endpoint, output)
|
||||
|
||||
def __getitem__(self, key):
|
||||
method = self.routes[key]
|
||||
return method.function
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self.routes)
|
||||
|
||||
def __len__(self):
|
||||
return len(self.routes)
|
||||
|
||||
def __setitem__(self, key, function, pre=None):
|
||||
method = Method(function=function, pre=pre)
|
||||
self.routes[key] = method
|
||||
|
||||
def __delitem__(self, key):
|
||||
del self.routes[key]
|
||||
|
||||
def __repr__(self):
|
||||
return repr(self.routes)
|
||||
|
||||
def route(self, name: str, comparator: Callable, *args):
|
||||
"""Декоратор
|
||||
"""
|
||||
def wrap(function):
|
||||
_route = Route(
|
||||
comparator=comparator,
|
||||
endpoint=function
|
||||
)
|
||||
self.routes[name] = _route
|
||||
return function
|
||||
return wrap
|
||||
@@ -0,0 +1,68 @@
|
||||
__author__ = 'RemiZOffAlex'
|
||||
__email__ = 'remizoffalex@mail.ru'
|
||||
|
||||
import re
|
||||
import logging
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Node:
|
||||
__params__ = {}
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
log.debug('{}.__init__'.format(self.__class__.__name__))
|
||||
for item in self.__params__:
|
||||
setattr(self, item, kwargs[item])
|
||||
|
||||
def __call__(self, state):
|
||||
print(self.__class__.__name__)
|
||||
raise ValueError('Метод не реализован')
|
||||
|
||||
def __repr__(self):
|
||||
log.debug('{}.__repr__'.format(self.__class__.__name__))
|
||||
result = self.__class__.__name__ + '(' + ')'
|
||||
params = []
|
||||
for item in self.__params__:
|
||||
param = getattr(self, item)
|
||||
params.append('{} = {}'.format(item, getattr(self, item)))
|
||||
result = self.__class__.__name__ + '(' + ', '.join(params) + ')'
|
||||
|
||||
log.debug('__repr__')
|
||||
return result
|
||||
|
||||
|
||||
class Regex(Node):
|
||||
__params__ = {
|
||||
'key': '',
|
||||
'value': ''
|
||||
}
|
||||
|
||||
def __init__(self, key, value):
|
||||
self.key = key
|
||||
self.value = re.compile(value)
|
||||
|
||||
def __json__(self):
|
||||
return {
|
||||
'operator': 'regex',
|
||||
'key': self.key,
|
||||
'value': self.value.pattern
|
||||
}
|
||||
|
||||
def __call__(self, query):
|
||||
print('Regex.__call__')
|
||||
if isinstance(query, dict):
|
||||
result = self.value.match(query[self.key])
|
||||
else:
|
||||
if '.' in self.key:
|
||||
keys = self.key.split('.')
|
||||
value = query
|
||||
for key in keys:
|
||||
value = getattr(value, key)
|
||||
else:
|
||||
value = getattr(query, self.key)
|
||||
print(value)
|
||||
print(type(value).__name__)
|
||||
result = self.value.match(value)
|
||||
print(result)
|
||||
return result
|
||||
@@ -4,12 +4,15 @@ __email__ = 'remizoffalex@mail.ru'
|
||||
import re
|
||||
import logging
|
||||
|
||||
from .common import Node
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class And:
|
||||
def __init__(self, rules):
|
||||
self.rules = rules
|
||||
class And(Node):
|
||||
__params__ = {
|
||||
'rules': ''
|
||||
}
|
||||
|
||||
def __json__(self):
|
||||
result = []
|
||||
@@ -26,6 +29,10 @@ class And:
|
||||
|
||||
|
||||
class Equal:
|
||||
__params__ = {
|
||||
'key': '',
|
||||
'value': ''
|
||||
}
|
||||
def __init__(self, key, value):
|
||||
self.key = key
|
||||
self.value = value
|
||||
@@ -43,6 +50,9 @@ class Equal:
|
||||
|
||||
|
||||
class Or:
|
||||
__params__ = {
|
||||
'rules': ''
|
||||
}
|
||||
def __init__(self, rules):
|
||||
self.rules = rules
|
||||
|
||||
@@ -58,21 +68,3 @@ class Or:
|
||||
def __call__(self, query):
|
||||
print('Or.__call__')
|
||||
return any(map(lambda x: x(query), self.rules))
|
||||
|
||||
|
||||
class Regex:
|
||||
def __init__(self, key, value):
|
||||
self.key = key
|
||||
self.value = re.compile(value)
|
||||
|
||||
def __json__(self):
|
||||
return {
|
||||
'operator': 'regex',
|
||||
'key': self.key,
|
||||
'value': self.value.pattern
|
||||
}
|
||||
|
||||
def __call__(self, query):
|
||||
print('Regex.__call__')
|
||||
result = self.value.match(query[self.key])
|
||||
return result
|
||||
@@ -11,3 +11,9 @@ class Route:
|
||||
print('Route.__init__')
|
||||
self.comparator = comparator
|
||||
self.endpoint = endpoint
|
||||
|
||||
def __repr__(self):
|
||||
return 'Route(comparator={comparator}, endpoint={endpoint})'.format(
|
||||
comparator=self.comparator,
|
||||
endpoint=self.endpoint
|
||||
)
|
||||
Reference in New Issue
Block a user