Currently job artifacts in CI/CD pipelines on LRZ GitLab never expire. Starting from Wed 26.1.2022 the default expiration time will be 30 days (GitLab default). Currently existing artifacts in already completed jobs will not be affected by the change. The latest artifacts for all jobs in the latest successful pipelines will be kept. More information: https://gitlab.lrz.de/help/user/admin_area/settings/continuous_integration.html#default-artifacts-expiration

Commit 175bc030 authored by eckhart's avatar eckhart
Browse files

- transform.py: more generalized content_from_child() and replace_by_child() functions

parent aa6c640c
...@@ -80,8 +80,7 @@ KeyFunc = Callable[[Node], str] ...@@ -80,8 +80,7 @@ KeyFunc = Callable[[Node], str]
CriteriaType = Union[int, str, Callable] CriteriaType = Union[int, str, Callable]
# TODO: Add more optional type dispatch paramters, e.g. t2=None, t3=None, t4=None def transformation_factory(t1=None, t2=None, t3=None, t4=None, t5=None):
def transformation_factory(t=None):
"""Creates factory functions from transformation-functions that """Creates factory functions from transformation-functions that
dispatch on the first parameter after the context parameter. dispatch on the first parameter after the context parameter.
...@@ -113,7 +112,7 @@ def transformation_factory(t=None): ...@@ -113,7 +112,7 @@ def transformation_factory(t=None):
trans_table = { 'expression': partial(remove_tokens, tokens={'+', '-'}) } trans_table = { 'expression': partial(remove_tokens, tokens={'+', '-'}) }
Parameters: Parameters:
t: type of the second argument of the transformation function, t1: type of the second argument of the transformation function,
only necessary if the transformation functions' parameter list only necessary if the transformation functions' parameter list
does not have type annotations. does not have type annotations.
""" """
...@@ -123,10 +122,10 @@ def transformation_factory(t=None): ...@@ -123,10 +122,10 @@ def transformation_factory(t=None):
params = list(sig.parameters.values())[1:] params = list(sig.parameters.values())[1:]
if len(params) == 0: if len(params) == 0:
return f # '@transformer' not needed w/o free parameters return f # '@transformer' not needed w/o free parameters
assert t or params[0].annotation != params[0].empty, \ assert t1 or params[0].annotation != params[0].empty, \
"No type information on second parameter found! Please, use type " \ "No type information on second parameter found! Please, use type " \
"annotation or provide the type information via transfomer-decorator." "annotation or provide the type information via transfomer-decorator."
p1type = t or params[0].annotation p1type = t1 or params[0].annotation
f = singledispatch(f) f = singledispatch(f)
try: try:
if len(params) == 1 and issubclass(p1type, Container) \ if len(params) == 1 and issubclass(p1type, Container) \
...@@ -146,15 +145,19 @@ def transformation_factory(t=None): ...@@ -146,15 +145,19 @@ def transformation_factory(t=None):
d.update(kwargs) d.update(kwargs)
return partial(f, **d) return partial(f, **d)
f.register(p1type, gen_partial) for t in (p1type, t2, t3, t4, t5):
if t:
f.register(t, gen_partial)
else:
break
return f return f
if isinstance(t, type(lambda: 1)): if isinstance(t1, type(lambda: 1)):
# Provide for the case that transformation_factory has been # Provide for the case that transformation_factory has been
# written as plain decorator and not as a function call that # written as plain decorator and not as a function call that
# returns the decorator proper. # returns the decorator proper.
func = t func = t1
t = None t1 = None
return decorator(func) return decorator(func)
else: else:
return decorator return decorator
...@@ -265,6 +268,20 @@ def traverse(root_node: Node, ...@@ -265,6 +268,20 @@ def traverse(root_node: Node,
# ------------------------------------------------ # ------------------------------------------------
def replace_by(node: Node, child: Node):
if not child.parser.name:
child.parser = MockParser(node.parser.name, child.parser.ptype)
# parser names must not be overwritten, else: child.parser.name = node.parser.name
node.parser = child.parser
node._errors.extend(child._errors)
node.result = child.result
def reduce_child(node: Node, child: Node):
node._errors.extend(child._errors)
node.result = child.result
def pick_child(context: List[Node], criteria: CriteriaType): def pick_child(context: List[Node], criteria: CriteriaType):
"""Returns the first child that meets the criteria.""" """Returns the first child that meets the criteria."""
if isinstance(criteria, int): if isinstance(criteria, int):
...@@ -287,74 +304,62 @@ def pick_child(context: List[Node], criteria: CriteriaType): ...@@ -287,74 +304,62 @@ def pick_child(context: List[Node], criteria: CriteriaType):
return None return None
def replace_by(node: Node, child: Node): def single_child(context: List[Node]) -> bool:
if not child.parser.name: return len(context[-2].children) == 1
child.parser = MockParser(node.parser.name, child.parser.ptype)
# parser names must not be overwritten, else: child.parser.name = node.parser.name
node.parser = child.parser
node._errors.extend(child._errors)
node.result = child.result
def reduce_child(node: Node, child: Node):
node._errors.extend(child._errors)
node.result = child.result
@transformation_factory(int, str, Callable)
# TODO: default value = lambda context: len(context[-1].children) == 1 def replace_by_child(context: List[Node], criteria: CriteriaType=single_child):
# @transformation_factory(int, str, Callable)
# def replace_by_child(context: List[Node], criteria: CriteriaType=0):
# """
# Replace a node by the first of its immediate descendants
# that meets the `criteria`. The criteria can either be the
# index of the child (counting from zero), or the tag name or
# a boolean-valued function on the context of the child.
# If no child matching the criteria is found, the node will
# not be replaced.
# """
# child = pick_child(context, criteria)
# if child:
# print(child)
# replace_by(context[-1], child)
# @transformation_factory(int, str, Callable)
# def content_from_child(context: List[None], criteria: CriteriaType=0):
# """
# Reduce a node, by transferring the result of the first of its
# immediate descendants that meets the `criteria` to this node,
# but keeping this node's parser entry. The criteria can either
# be the index of the child (counting from zero), or the tag
# name or a boolean-valued function on the context of the child.
# If no child matching the criteria is found, the node will
# not be replaced.
# """
# child = pick_child(context, criteria)
# if child:
# reduce_child(context[-1], child)
def replace_by_child(context: List[Node]):
""" """
Remove single branch node, replacing it by its immediate descendant Replace a node by the first of its immediate descendants
if and only if the condition on the descendant is true. that meets the `criteria`. The criteria can either be the
index of the child (counting from zero), or the tag name or
a boolean-valued function on the context of the child.
If no child matching the criteria is found, the node will
not be replaced.
""" """
node = context[-1] child = pick_child(context, criteria)
if len(node.children) == 1: if child:
replace_by(node, node.children[0]) replace_by(context[-1], child)
def content_from_child(context: List[Node]): @transformation_factory(int, str, Callable)
def content_from_child(context: List[None], criteria: CriteriaType=single_child):
""" """
Reduce a single branch node, by transferring the result of its Reduce a node, by transferring the result of the first of its
immediate descendant to this node, but keeping this node's parser entry. immediate descendants that meets the `criteria` to this node,
If the condition evaluates to false on the descendant, it will not but keeping this node's parser entry. The criteria can either
be reduced. be the index of the child (counting from zero), or the tag
name or a boolean-valued function on the context of the child.
If no child matching the criteria is found, the node will
not be replaced.
""" """
node = context[-1] child = pick_child(context, criteria)
if len(node.children) == 1: if child:
reduce_child(node, node.children[0]) reduce_child(context[-1], child)
# def replace_by_child(context: List[Node]):
# """
# Remove single branch node, replacing it by its immediate descendant
# if and only if the condition on the descendant is true.
# """
# node = context[-1]
# if len(node.children) == 1:
# replace_by(node, node.children[0])
#
#
# def content_from_child(context: List[Node]):
# """
# Reduce a single branch node, by transferring the result of its
# immediate descendant to this node, but keeping this node's parser entry.
# If the condition evaluates to false on the descendant, it will not
# be reduced.
# """
# node = context[-1]
# if len(node.children) == 1:
# reduce_child(node, node.children[0])
def is_named(context: List[Node]) -> bool: def is_named(context: List[Node]) -> bool:
......
...@@ -55,7 +55,7 @@ def tst_func(): ...@@ -55,7 +55,7 @@ def tst_func():
files = os.listdir('testdata') files = os.listdir('testdata')
files.sort() files.sort()
for file in files: for file in files:
if fnmatch.fnmatch(file, '*2.tex') and file.lower().find('error') < 0: if fnmatch.fnmatch(file, '*.tex') and file.lower().find('error') < 0:
with open(os.path.join('testdata', file), 'r', encoding='utf-8') as f: with open(os.path.join('testdata', file), 'r', encoding='utf-8') as f:
doc = f.read() doc = f.read()
print('\n\nParsing document: "%s"\n' % file) print('\n\nParsing document: "%s"\n' % file)
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment