зеркало из https://github.com/mozilla/gecko-dev.git
Backed out 3 changesets (bug 1536804) for /css/css-* failures CLOSED TREE
Backed out changeset e8758002d7d4 (bug 1536804) Backed out changeset 795287b1e059 (bug 1536804) Backed out changeset 9a680e886248 (bug 1536804)
This commit is contained in:
Родитель
c2cd4db477
Коммит
f81aa92481
|
@ -25,7 +25,6 @@ jobs-from:
|
|||
- node.yml
|
||||
- python.yml
|
||||
- webidl.yml
|
||||
- wpt-metadata.yml
|
||||
- wpt-manifest.yml
|
||||
|
||||
# This is used by run-task based tasks to lookup which build task it
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
job-defaults:
|
||||
platform: lint/opt
|
||||
treeherder:
|
||||
kind: test
|
||||
tier: 2
|
||||
worker-type: aws-provisioner-v1/gecko-t-linux-xlarge
|
||||
worker:
|
||||
docker-image: {in-tree: "lint"}
|
||||
max-run-time: 1800
|
||||
|
||||
summary:
|
||||
description: Summarize wpt metadata
|
||||
treeherder:
|
||||
symbol: wpt-meta
|
||||
index:
|
||||
product: source
|
||||
job-name: source-wpt-metadata-summary
|
||||
run:
|
||||
using: mach
|
||||
mach: wpt-metadata-summary --out-dir=/builds/worker/artifacts
|
||||
worker:
|
||||
artifacts:
|
||||
- type: directory
|
||||
path: /builds/worker/artifacts
|
||||
name: public
|
||||
max-run-time: 2700
|
||||
when:
|
||||
files-changed:
|
||||
- 'testing/web-platform/meta/**'
|
||||
- 'testing/web-platform/mozilla/meta/**'
|
||||
- 'testing/web-platform/metasummary.py'
|
|
@ -345,11 +345,6 @@ def create_parser_manifest_update():
|
|||
return manifestupdate.create_parser()
|
||||
|
||||
|
||||
def create_parser_metadata_summary():
|
||||
import metasummary
|
||||
return metasummary.create_parser()
|
||||
|
||||
|
||||
@CommandProvider
|
||||
class MachCommands(MachCommandBase):
|
||||
def setup(self):
|
||||
|
@ -437,12 +432,3 @@ class MachCommands(MachCommandBase):
|
|||
wpt_runner = WebPlatformTestsRunner(wpt_setup)
|
||||
logger = wpt_runner.setup_logging(**params)
|
||||
return 0 if wpt_runner.update_manifest(logger, **params) else 1
|
||||
|
||||
@Command("wpt-metadata-summary",
|
||||
category="testing",
|
||||
description="Create a json summary of the wpt metadata",
|
||||
parser=create_parser_metadata_summary)
|
||||
def wpt_summary(self, **params):
|
||||
import metasummary
|
||||
wpt_setup = self._spawn(WebPlatformTestsRunnerSetup)
|
||||
return metasummary.run(wpt_setup.topsrcdir, wpt_setup.topobjdir, **params)
|
||||
|
|
|
@ -1,299 +0,0 @@
|
|||
import argparse
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import urlparse
|
||||
from collections import defaultdict
|
||||
|
||||
import manifestupdate
|
||||
|
||||
from wptrunner import expected
|
||||
from wptrunner.wptmanifest.serializer import serialize
|
||||
from wptrunner.wptmanifest.backends import base
|
||||
|
||||
here = os.path.dirname(__file__)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Compiler(base.Compiler):
|
||||
def visit_KeyValueNode(self, node):
|
||||
key_name = node.data
|
||||
values = []
|
||||
for child in node.children:
|
||||
values.append(self.visit(child))
|
||||
|
||||
self.output_node.set(key_name, values)
|
||||
|
||||
def visit_ConditionalNode(self, node):
|
||||
assert len(node.children) == 2
|
||||
# For conditional nodes, just return the subtree
|
||||
return node.children[0], self.visit(node.children[1])
|
||||
|
||||
def visit_UnaryExpressionNode(self, node):
|
||||
raise NotImplementedError
|
||||
|
||||
def visit_BinaryExpressionNode(self, node):
|
||||
raise NotImplementedError
|
||||
|
||||
def visit_UnaryOperatorNode(self, node):
|
||||
raise NotImplementedError
|
||||
|
||||
def visit_BinaryOperatorNode(self, node):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class ExpectedManifest(base.ManifestItem):
|
||||
def __init__(self, node, test_path, url_base):
|
||||
"""Object representing all the tests in a particular manifest
|
||||
|
||||
:param name: Name of the AST Node associated with this object.
|
||||
Should always be None since this should always be associated with
|
||||
the root node of the AST.
|
||||
:param test_path: Path of the test file associated with this manifest.
|
||||
:param url_base: Base url for serving the tests in this manifest
|
||||
"""
|
||||
if test_path is None:
|
||||
raise ValueError("ExpectedManifest requires a test path")
|
||||
if url_base is None:
|
||||
raise ValueError("ExpectedManifest requires a base url")
|
||||
base.ManifestItem.__init__(self, node)
|
||||
self.child_map = {}
|
||||
self.test_path = test_path
|
||||
self.url_base = url_base
|
||||
|
||||
def append(self, child):
|
||||
"""Add a test to the manifest"""
|
||||
base.ManifestItem.append(self, child)
|
||||
self.child_map[child.id] = child
|
||||
|
||||
@property
|
||||
def url(self):
|
||||
return urlparse.urljoin(self.url_base,
|
||||
"/".join(self.test_path.split(os.path.sep)))
|
||||
|
||||
|
||||
class DirectoryManifest(base.ManifestItem):
|
||||
pass
|
||||
|
||||
|
||||
class TestManifestItem(base.ManifestItem):
|
||||
def __init__(self, node, **kwargs):
|
||||
"""Tree node associated with a particular test in a manifest
|
||||
|
||||
:param name: name of the test"""
|
||||
base.ManifestItem.__init__(self, node)
|
||||
self.subtests = {}
|
||||
|
||||
@property
|
||||
def id(self):
|
||||
return urlparse.urljoin(self.parent.url, self.name)
|
||||
|
||||
def append(self, node):
|
||||
"""Add a subtest to the current test
|
||||
|
||||
:param node: AST Node associated with the subtest"""
|
||||
child = base.ManifestItem.append(self, node)
|
||||
self.subtests[child.name] = child
|
||||
|
||||
def get_subtest(self, name):
|
||||
"""Get the SubtestNode corresponding to a particular subtest, by name
|
||||
|
||||
:param name: Name of the node to return"""
|
||||
if name in self.subtests:
|
||||
return self.subtests[name]
|
||||
return None
|
||||
|
||||
|
||||
class SubtestManifestItem(TestManifestItem):
|
||||
pass
|
||||
|
||||
|
||||
def data_cls_getter(output_node, visited_node):
|
||||
# visited_node is intentionally unused
|
||||
if output_node is None:
|
||||
return ExpectedManifest
|
||||
if isinstance(output_node, ExpectedManifest):
|
||||
return TestManifestItem
|
||||
if isinstance(output_node, TestManifestItem):
|
||||
return SubtestManifestItem
|
||||
raise ValueError
|
||||
|
||||
|
||||
def get_manifest(metadata_root, test_path, url_base):
|
||||
"""Get the ExpectedManifest for a particular test path, or None if there is no
|
||||
metadata stored for that test path.
|
||||
|
||||
:param metadata_root: Absolute path to the root of the metadata directory
|
||||
:param test_path: Path to the test(s) relative to the test root
|
||||
:param url_base: Base url for serving the tests in this manifest
|
||||
:param run_info: Dictionary of properties of the test run for which the expectation
|
||||
values should be computed.
|
||||
"""
|
||||
manifest_path = expected.expected_path(metadata_root, test_path)
|
||||
try:
|
||||
with open(manifest_path) as f:
|
||||
return compile(f,
|
||||
data_cls_getter=data_cls_getter,
|
||||
test_path=test_path,
|
||||
url_base=url_base)
|
||||
except IOError:
|
||||
return None
|
||||
|
||||
|
||||
def get_dir_manifest(path):
|
||||
"""Get the ExpectedManifest for a particular test path, or None if there is no
|
||||
metadata stored for that test path.
|
||||
|
||||
:param path: Full path to the ini file
|
||||
:param run_info: Dictionary of properties of the test run for which the expectation
|
||||
values should be computed.
|
||||
"""
|
||||
try:
|
||||
with open(path) as f:
|
||||
return compile(f, data_cls_getter=lambda x,y: DirectoryManifest)
|
||||
except IOError:
|
||||
return None
|
||||
|
||||
|
||||
def compile(stream, data_cls_getter=None, **kwargs):
|
||||
return base.compile(Compiler,
|
||||
stream,
|
||||
data_cls_getter=data_cls_getter,
|
||||
**kwargs)
|
||||
|
||||
|
||||
def create_parser():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--out-dir", help="Directory to store output files")
|
||||
return parser
|
||||
|
||||
|
||||
def run(src_root, obj_root, logger_=None, **kwargs):
|
||||
logger_obj = logger_ if logger_ is not None else logger
|
||||
|
||||
manifests = manifestupdate.run(src_root, obj_root, logger_obj, **kwargs)
|
||||
|
||||
rv = {}
|
||||
dirs_seen = set()
|
||||
|
||||
for meta_root, test_path, test_metadata in iter_tests(manifests):
|
||||
for dir_path in get_dir_paths(meta_root, test_path):
|
||||
if dir_path not in dirs_seen:
|
||||
dirs_seen.add(dir_path)
|
||||
dir_manifest = get_dir_manifest(dir_path)
|
||||
rel_path = os.path.relpath(dir_path, meta_root)
|
||||
if dir_manifest:
|
||||
add_manifest(rv, rel_path, dir_manifest)
|
||||
else:
|
||||
break
|
||||
add_manifest(rv, test_path, test_metadata)
|
||||
|
||||
if kwargs["out_dir"]:
|
||||
if not os.path.exists(kwargs["out_dir"]):
|
||||
os.makedirs(kwargs["out_dir"])
|
||||
out_path = os.path.join(kwargs["out_dir"], "summary.json")
|
||||
with open(out_path, "w") as f:
|
||||
json.dump(rv, f)
|
||||
else:
|
||||
print json.dumps(rv, indent=2)
|
||||
|
||||
|
||||
def get_dir_paths(test_root, test_path):
|
||||
if not os.path.isabs(test_path):
|
||||
test_path = os.path.join(test_root, test_path)
|
||||
dir_path = os.path.dirname(test_path)
|
||||
while dir_path != test_root:
|
||||
yield os.path.join(dir_path, "__dir__.ini")
|
||||
dir_path = os.path.dirname(dir_path)
|
||||
assert len(dir_path) >= len(test_root)
|
||||
|
||||
|
||||
def iter_tests(manifests):
|
||||
for manifest in manifests.iterkeys():
|
||||
for test_type, test_path, tests in manifest:
|
||||
url_base = manifests[manifest]["url_base"]
|
||||
metadata_base = manifests[manifest]["metadata_path"]
|
||||
expected_manifest = get_manifest(metadata_base, test_path, url_base)
|
||||
if expected_manifest:
|
||||
yield metadata_base, test_path, expected_manifest
|
||||
|
||||
|
||||
def add_manifest(target, path, metadata):
|
||||
dir_name = os.path.dirname(path)
|
||||
key = [dir_name]
|
||||
|
||||
add_metadata(target, key, metadata)
|
||||
|
||||
key.append("_tests")
|
||||
|
||||
for test_metadata in metadata.children:
|
||||
key.append(test_metadata.name)
|
||||
add_metadata(target, key, test_metadata)
|
||||
key.append("_subtests")
|
||||
for subtest_metadata in test_metadata.children:
|
||||
key.append(subtest_metadata.name)
|
||||
add_metadata(target,
|
||||
key,
|
||||
subtest_metadata)
|
||||
key.pop()
|
||||
key.pop()
|
||||
key.pop()
|
||||
|
||||
|
||||
simple_props = ["disabled", "min-asserts", "max-asserts", "lsan-allowed",
|
||||
"leak-allowed", "bug"]
|
||||
statuses = set(["CRASH"])
|
||||
|
||||
|
||||
def add_metadata(target, key, metadata):
|
||||
if not is_interesting(metadata):
|
||||
return
|
||||
|
||||
for part in key:
|
||||
if part not in target:
|
||||
target[part] = {}
|
||||
target = target[part]
|
||||
|
||||
for prop in simple_props:
|
||||
if metadata.has_key(prop):
|
||||
target[prop] = get_condition_value_list(metadata, prop)
|
||||
|
||||
if metadata.has_key("expected"):
|
||||
values = metadata.get("expected")
|
||||
by_status = defaultdict(list)
|
||||
for item in values:
|
||||
if isinstance(item, tuple):
|
||||
condition, status = item
|
||||
else:
|
||||
condition = None
|
||||
status = item
|
||||
by_status[status].append(condition)
|
||||
for status in statuses:
|
||||
if status in by_status:
|
||||
target["expected_%s" % status] = [serialize(item) if item else None
|
||||
for item in by_status[status]]
|
||||
|
||||
|
||||
def get_condition_value_list(metadata, key):
|
||||
conditions = []
|
||||
for item in metadata.get(key):
|
||||
if isinstance(item, tuple):
|
||||
assert len(item) == 2
|
||||
conditions.append((serialize(item[0]), item[1]))
|
||||
else:
|
||||
conditions.append((None, item))
|
||||
return conditions
|
||||
|
||||
|
||||
def is_interesting(metadata):
|
||||
if any(metadata.has_key(prop) for prop in simple_props):
|
||||
return True
|
||||
|
||||
if metadata.has_key("expected"):
|
||||
for item in metadata.get("expected"):
|
||||
if isinstance(item, tuple):
|
||||
if item[1] in statuses:
|
||||
return True
|
||||
elif item in statuses:
|
||||
return True
|
||||
return False
|
|
@ -2,7 +2,7 @@ import os
|
|||
import urlparse
|
||||
|
||||
from wptmanifest.backends import static
|
||||
from wptmanifest.backends.base import ManifestItem
|
||||
from wptmanifest.backends.static import ManifestItem
|
||||
|
||||
import expected
|
||||
|
||||
|
@ -98,7 +98,7 @@ def leak_threshold(node):
|
|||
|
||||
|
||||
class ExpectedManifest(ManifestItem):
|
||||
def __init__(self, node, test_path, url_base):
|
||||
def __init__(self, name, test_path, url_base):
|
||||
"""Object representing all the tests in a particular manifest
|
||||
|
||||
:param name: Name of the AST Node associated with this object.
|
||||
|
@ -107,14 +107,13 @@ class ExpectedManifest(ManifestItem):
|
|||
:param test_path: Path of the test file associated with this manifest.
|
||||
:param url_base: Base url for serving the tests in this manifest
|
||||
"""
|
||||
name = node.data
|
||||
if name is not None:
|
||||
raise ValueError("ExpectedManifest should represent the root node")
|
||||
if test_path is None:
|
||||
raise ValueError("ExpectedManifest requires a test path")
|
||||
if url_base is None:
|
||||
raise ValueError("ExpectedManifest requires a base url")
|
||||
ManifestItem.__init__(self, node)
|
||||
ManifestItem.__init__(self, name)
|
||||
self.child_map = {}
|
||||
self.test_path = test_path
|
||||
self.url_base = url_base
|
||||
|
@ -230,14 +229,13 @@ class DirectoryManifest(ManifestItem):
|
|||
def lsan_max_stack_depth(self):
|
||||
return int_prop("lsan-max-stack-depth", self)
|
||||
|
||||
|
||||
class TestNode(ManifestItem):
|
||||
def __init__(self, node, **kwargs):
|
||||
def __init__(self, name):
|
||||
"""Tree node associated with a particular test in a manifest
|
||||
|
||||
:param name: name of the test"""
|
||||
assert node.data is not None
|
||||
ManifestItem.__init__(self, node, **kwargs)
|
||||
assert name is not None
|
||||
ManifestItem.__init__(self, name)
|
||||
self.updated_expected = []
|
||||
self.new_expected = []
|
||||
self.subtests = {}
|
||||
|
@ -320,6 +318,12 @@ class TestNode(ManifestItem):
|
|||
|
||||
|
||||
class SubtestNode(TestNode):
|
||||
def __init__(self, name):
|
||||
"""Tree node associated with a particular subtest in a manifest
|
||||
|
||||
:param name: name of the subtest"""
|
||||
TestNode.__init__(self, name)
|
||||
|
||||
@property
|
||||
def is_empty(self):
|
||||
if self._data:
|
||||
|
|
|
@ -1,222 +0,0 @@
|
|||
import abc
|
||||
|
||||
from ..node import NodeVisitor
|
||||
from ..parser import parse
|
||||
|
||||
|
||||
class Compiler(NodeVisitor):
|
||||
__metaclass__ = abc.ABCMeta
|
||||
|
||||
def compile(self, tree, data_cls_getter=None, **kwargs):
|
||||
self._kwargs = kwargs
|
||||
return self._compile(tree, data_cls_getter, **kwargs)
|
||||
|
||||
def _compile(self, tree, data_cls_getter=None, **kwargs):
|
||||
"""Compile a raw AST into a form where conditional expressions
|
||||
are represented by ConditionalValue objects that can be evaluated
|
||||
at runtime.
|
||||
|
||||
tree - The root node of the wptmanifest AST to compile
|
||||
|
||||
data_cls_getter - A function taking two parameters; the previous
|
||||
output node and the current ast node and returning
|
||||
the class of the output node to use for the current
|
||||
ast node
|
||||
"""
|
||||
if data_cls_getter is None:
|
||||
self.data_cls_getter = lambda x, y: ManifestItem
|
||||
else:
|
||||
self.data_cls_getter = data_cls_getter
|
||||
|
||||
self.tree = tree
|
||||
self.output_node = self._initial_output_node(tree, **kwargs)
|
||||
self.visit(tree)
|
||||
if hasattr(self.output_node, "set_defaults"):
|
||||
self.output_node.set_defaults()
|
||||
assert self.output_node is not None
|
||||
return self.output_node
|
||||
|
||||
def _initial_output_node(self, node, **kwargs):
|
||||
return self.data_cls_getter(None, None)(node, **kwargs)
|
||||
|
||||
def visit_DataNode(self, node):
|
||||
if node != self.tree:
|
||||
output_parent = self.output_node
|
||||
self.output_node = self.data_cls_getter(self.output_node, node)(node, **self._kwargs)
|
||||
else:
|
||||
output_parent = None
|
||||
|
||||
assert self.output_node is not None
|
||||
|
||||
for child in node.children:
|
||||
self.visit(child)
|
||||
|
||||
if output_parent is not None:
|
||||
# Append to the parent *after* processing all the node data
|
||||
output_parent.append(self.output_node)
|
||||
self.output_node = self.output_node.parent
|
||||
|
||||
assert self.output_node is not None
|
||||
|
||||
@abc.abstractmethod
|
||||
def visit_KeyValueNode(self, node):
|
||||
pass
|
||||
|
||||
def visit_ListNode(self, node):
|
||||
return [self.visit(child) for child in node.children]
|
||||
|
||||
def visit_ValueNode(self, node):
|
||||
return node.data
|
||||
|
||||
def visit_AtomNode(self, node):
|
||||
return node.data
|
||||
|
||||
@abc.abstractmethod
|
||||
def visit_ConditionalNode(self, node):
|
||||
pass
|
||||
|
||||
def visit_StringNode(self, node):
|
||||
indexes = [self.visit(child) for child in node.children]
|
||||
|
||||
def value(x):
|
||||
rv = node.data
|
||||
for index in indexes:
|
||||
rv = rv[index(x)]
|
||||
return rv
|
||||
return value
|
||||
|
||||
def visit_NumberNode(self, node):
|
||||
if "." in node.data:
|
||||
return lambda x: float(node.data)
|
||||
else:
|
||||
return lambda x: int(node.data)
|
||||
|
||||
def visit_VariableNode(self, node):
|
||||
indexes = [self.visit(child) for child in node.children]
|
||||
|
||||
def value(x):
|
||||
data = x[node.data]
|
||||
for index in indexes:
|
||||
data = data[index(x)]
|
||||
return data
|
||||
return value
|
||||
|
||||
def visit_IndexNode(self, node):
|
||||
assert len(node.children) == 1
|
||||
return self.visit(node.children[0])
|
||||
|
||||
@abc.abstractmethod
|
||||
def visit_UnaryExpressionNode(self, node):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def visit_BinaryExpressionNode(self, node):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def visit_UnaryOperatorNode(self, node):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def visit_BinaryOperatorNode(self, node):
|
||||
pass
|
||||
|
||||
|
||||
class ManifestItem(object):
|
||||
def __init__(self, node, **kwargs):
|
||||
self.parent = None
|
||||
self.node = node
|
||||
self.children = []
|
||||
self._data = {}
|
||||
|
||||
def __repr__(self):
|
||||
return "<ManifestItem %s>" % (self.node.data)
|
||||
|
||||
def __str__(self):
|
||||
rv = [repr(self)]
|
||||
for item in self.children:
|
||||
rv.extend(" %s" % line for line in str(item).split("\n"))
|
||||
return "\n".join(rv)
|
||||
|
||||
def set_defaults(self):
|
||||
pass
|
||||
|
||||
@property
|
||||
def is_empty(self):
|
||||
if self._data:
|
||||
return False
|
||||
return all(child.is_empty for child in self.children)
|
||||
|
||||
@property
|
||||
def root(self):
|
||||
node = self
|
||||
while node.parent is not None:
|
||||
node = node.parent
|
||||
return node
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self.node.data
|
||||
|
||||
def get(self, key):
|
||||
for node in [self, self.root]:
|
||||
if key in node._data:
|
||||
return node._data[key]
|
||||
raise KeyError
|
||||
|
||||
def set(self, name, value):
|
||||
self._data[name] = value
|
||||
|
||||
def remove(self):
|
||||
if self.parent:
|
||||
self.parent.children.remove(child)
|
||||
self.parent = None
|
||||
|
||||
def iterchildren(self, name=None):
|
||||
for item in self.children:
|
||||
if item.name == name or name is None:
|
||||
yield item
|
||||
|
||||
def has_key(self, key):
|
||||
for node in [self, self.root]:
|
||||
if key in node._data:
|
||||
return True
|
||||
return False
|
||||
|
||||
def _flatten(self):
|
||||
rv = {}
|
||||
for node in [self, self.root]:
|
||||
for name, value in node._data.iteritems():
|
||||
if name not in rv:
|
||||
rv[name] = value
|
||||
return rv
|
||||
|
||||
def iteritems(self):
|
||||
for item in self._flatten().iteritems():
|
||||
yield item
|
||||
|
||||
def iterkeys(self):
|
||||
for item in self._flatten().iterkeys():
|
||||
yield item
|
||||
|
||||
def itervalues(self):
|
||||
for item in self._flatten().itervalues():
|
||||
yield item
|
||||
|
||||
def append(self, child):
|
||||
child.parent = self
|
||||
self.children.append(child)
|
||||
return child
|
||||
|
||||
|
||||
def compile_ast(compiler, ast, data_cls_getter=None, **kwargs):
|
||||
return compiler().compile(ast,
|
||||
data_cls_getter=data_cls_getter,
|
||||
**kwargs)
|
||||
|
||||
|
||||
def compile(compiler, stream, data_cls_getter=None, **kwargs):
|
||||
return compile_ast(compiler,
|
||||
parse(stream),
|
||||
data_cls_getter=data_cls_getter,
|
||||
**kwargs)
|
|
@ -1,10 +1,10 @@
|
|||
import operator
|
||||
|
||||
import base
|
||||
from ..node import NodeVisitor
|
||||
from ..parser import parse
|
||||
|
||||
|
||||
class Compiler(base.Compiler):
|
||||
class Compiler(NodeVisitor):
|
||||
"""Compiler backend that evaluates conditional expressions
|
||||
to give static output"""
|
||||
|
||||
|
@ -26,7 +26,29 @@ class Compiler(base.Compiler):
|
|||
self._kwargs = kwargs
|
||||
self.expr_data = expr_data
|
||||
|
||||
return self._compile(tree, data_cls_getter, **kwargs)
|
||||
if data_cls_getter is None:
|
||||
self.data_cls_getter = lambda x, y: ManifestItem
|
||||
else:
|
||||
self.data_cls_getter = data_cls_getter
|
||||
|
||||
self.output_node = None
|
||||
self.visit(tree)
|
||||
return self.output_node
|
||||
|
||||
def visit_DataNode(self, node):
|
||||
output_parent = self.output_node
|
||||
if self.output_node is None:
|
||||
assert node.parent is None
|
||||
self.output_node = self.data_cls_getter(None, None)(None, **self._kwargs)
|
||||
else:
|
||||
self.output_node = self.data_cls_getter(self.output_node, node)(node.data)
|
||||
|
||||
for child in node.children:
|
||||
self.visit(child)
|
||||
|
||||
if output_parent is not None:
|
||||
output_parent.append(self.output_node)
|
||||
self.output_node = self.output_node.parent
|
||||
|
||||
def visit_KeyValueNode(self, node):
|
||||
key_name = node.data
|
||||
|
@ -39,6 +61,15 @@ class Compiler(base.Compiler):
|
|||
if key_value is not None:
|
||||
self.output_node.set(key_name, key_value)
|
||||
|
||||
def visit_ValueNode(self, node):
|
||||
return node.data
|
||||
|
||||
def visit_AtomNode(self, node):
|
||||
return node.data
|
||||
|
||||
def visit_ListNode(self, node):
|
||||
return [self.visit(child) for child in node.children]
|
||||
|
||||
def visit_ConditionalNode(self, node):
|
||||
assert len(node.children) == 2
|
||||
if self.visit(node.children[0]):
|
||||
|
@ -50,12 +81,23 @@ class Compiler(base.Compiler):
|
|||
value = self.visit(child)(value)
|
||||
return value
|
||||
|
||||
def visit_NumberNode(self, node):
|
||||
if "." in node.data:
|
||||
return float(node.data)
|
||||
else:
|
||||
return int(node.data)
|
||||
|
||||
def visit_VariableNode(self, node):
|
||||
value = self.expr_data[node.data]
|
||||
for child in node.children:
|
||||
value = self.visit(child)(value)
|
||||
return value
|
||||
|
||||
def visit_IndexNode(self, node):
|
||||
assert len(node.children) == 1
|
||||
index = self.visit(node.children[0])
|
||||
return lambda x: x[index]
|
||||
|
||||
def visit_UnaryExpressionNode(self, node):
|
||||
assert len(node.children) == 2
|
||||
operator = self.visit(node.children[0])
|
||||
|
@ -81,6 +123,92 @@ class Compiler(base.Compiler):
|
|||
"!=": operator.ne}[node.data]
|
||||
|
||||
|
||||
class ManifestItem(object):
|
||||
def __init__(self, name, **kwargs):
|
||||
self.parent = None
|
||||
self.name = name
|
||||
self.children = []
|
||||
self._data = {}
|
||||
|
||||
def __repr__(self):
|
||||
return "<ManifestItem %s>" % (self.name)
|
||||
|
||||
def __str__(self):
|
||||
rv = [repr(self)]
|
||||
for item in self.children:
|
||||
rv.extend(" %s" % line for line in str(item).split("\n"))
|
||||
return "\n".join(rv)
|
||||
|
||||
def set_defaults(self):
|
||||
pass
|
||||
|
||||
@property
|
||||
def is_empty(self):
|
||||
if self._data:
|
||||
return False
|
||||
return all(child.is_empty for child in self.children)
|
||||
|
||||
@property
|
||||
def root(self):
|
||||
node = self
|
||||
while node.parent is not None:
|
||||
node = node.parent
|
||||
return node
|
||||
|
||||
def has_key(self, key):
|
||||
for node in [self, self.root]:
|
||||
if key in node._data:
|
||||
return True
|
||||
return False
|
||||
|
||||
def get(self, key):
|
||||
for node in [self, self.root]:
|
||||
if key in node._data:
|
||||
return node._data[key]
|
||||
raise KeyError
|
||||
|
||||
def set(self, name, value):
|
||||
self._data[name] = value
|
||||
|
||||
def remove(self):
|
||||
if self.parent:
|
||||
self.parent._remove_child(self)
|
||||
|
||||
def _remove_child(self, child):
|
||||
self.children.remove(child)
|
||||
child.parent = None
|
||||
|
||||
def iterchildren(self, name=None):
|
||||
for item in self.children:
|
||||
if item.name == name or name is None:
|
||||
yield item
|
||||
|
||||
def _flatten(self):
|
||||
rv = {}
|
||||
for node in [self, self.root]:
|
||||
for name, value in node._data.iteritems():
|
||||
if name not in rv:
|
||||
rv[name] = value
|
||||
return rv
|
||||
|
||||
def iteritems(self):
|
||||
for item in self._flatten().iteritems():
|
||||
yield item
|
||||
|
||||
def iterkeys(self):
|
||||
for item in self._flatten().iterkeys():
|
||||
yield item
|
||||
|
||||
def itervalues(self):
|
||||
for item in self._flatten().itervalues():
|
||||
yield item
|
||||
|
||||
def append(self, child):
|
||||
child.parent = self
|
||||
self.children.append(child)
|
||||
return child
|
||||
|
||||
|
||||
def compile_ast(ast, expr_data, data_cls_getter=None, **kwargs):
|
||||
return Compiler().compile(ast,
|
||||
expr_data,
|
||||
|
|
Загрузка…
Ссылка в новой задаче