Skip to content

Commit

Permalink
Merge 2402843 into 24d66df
Browse files Browse the repository at this point in the history
  • Loading branch information
obi1kenobi committed Jul 17, 2018
2 parents 24d66df + 2402843 commit 4f4856b
Show file tree
Hide file tree
Showing 14 changed files with 601 additions and 96 deletions.
12 changes: 6 additions & 6 deletions .pylintrc
Expand Up @@ -81,6 +81,7 @@ enable=abstract-class-instantiated,
bad-str-strip-call,
bad-super-call,
bare-except,
basestring-builtin,
binary-op-exception,
boolean-datetime
boolean-datetime,
Expand All @@ -99,6 +100,7 @@ enable=abstract-class-instantiated,
deprecated-lambda,
deprecated-method,
deprecated-module,
dict-iter-method,
dict-view-method,
duplicate-argument-name,
duplicate-bases,
Expand All @@ -110,7 +112,6 @@ enable=abstract-class-instantiated,
expression-not-assigned,
file-builtin,
filter-builtin-not-iterating,
fixme,
format-combined-specification,
format-needs-mapping,
function-redefined,
Expand Down Expand Up @@ -149,6 +150,7 @@ enable=abstract-class-instantiated,
lost-exception,
lowercase-l-suffix,
map-builtin-not-iterating,
metaclass-assignment,
method-hidden,
misplaced-bare-raise,
misplaced-future,
Expand Down Expand Up @@ -229,6 +231,7 @@ enable=abstract-class-instantiated,
unexpected-keyword-arg,
unexpected-special-method-signature,
unichr-builtin,
unicode-builtin,
unnecessary-lambda,
unnecessary-pass,
unnecessary-semicolon,
Expand All @@ -250,6 +253,7 @@ enable=abstract-class-instantiated,
using-cmp-argument,
using-constant-test,
wildcard-import,
xrange-builtin,
yield-inside-async-function,
yield-outside-function,
zip-builtin-not-iterating,
Expand All @@ -259,15 +263,11 @@ enable=abstract-class-instantiated,
docstyle,
# Disabled for now, maybe enable in the future:
# abstract-method, # needs some cleanup first
# basestring-builtin, # will come with Python 3 support
# dict-iter-method, # will come with Python 3 support
# metaclass-assignment, # will come with Python 3 support
# no-absolute-import, # maybe with Python 3 support
# parameter_documentation, # needs some cleanup and maybe configuration
# unicode-builtin, # will come with Python 3 support
# unused-argument, # needs some cleanup and per-line suppression,
# buggy / unclear how to suppress only a single function
# xrange-builtin, # will come with Python 3 support
# fixme, # sometimes there is a legitimate need for a TODO


# Consider the following sample rc files for errors to enable/disable:
Expand Down
29 changes: 14 additions & 15 deletions graphql_compiler/compiler/emit_match.py
Expand Up @@ -6,7 +6,7 @@

from .blocks import Filter, QueryRoot, Recurse, Traverse
from .expressions import TrueLiteral
from .helpers import validate_safe_string
from .helpers import get_only_element_from_collection, validate_safe_string


def _get_vertex_location_name(location):
Expand All @@ -20,31 +20,30 @@ def _get_vertex_location_name(location):

def _first_step_to_match(match_step):
"""Transform the very first MATCH step into a MATCH query string."""
if not isinstance(match_step.root_block, QueryRoot):
raise AssertionError(u'Expected QueryRoot root block, received: '
u'{} {}'.format(match_step.root_block, match_step))
parts = []

match_step.root_block.validate()
if match_step.root_block is not None:
if not isinstance(match_step.root_block, QueryRoot):
raise AssertionError(u'Expected None or QueryRoot root block, received: '
u'{} {}'.format(match_step.root_block, match_step))

match_step.root_block.validate()

start_class_set = match_step.root_block.start_class
if len(start_class_set) != 1:
raise AssertionError(u'Attempted to emit MATCH but did not have exactly one start class: '
u'{} {}'.format(start_class_set, match_step))
start_class = list(start_class_set)[0]
start_class = get_only_element_from_collection(match_step.root_block.start_class)
parts.append(u'class: %s' % (start_class,))

# MATCH steps with a QueryRoot root block shouldn't have a 'coerce_type_block'.
if match_step.coerce_type_block is not None:
raise AssertionError(u'Invalid MATCH step: {}'.format(match_step))

parts = [
u'class: %s' % (start_class,),
]

if match_step.where_block:
match_step.where_block.validate()
parts.append(u'where: (%s)' % (match_step.where_block.predicate.to_match(),))

if match_step.as_block:
if match_step.as_block is None:
raise AssertionError(u'Found a MATCH step without a corresponding Location. '
u'This should never happen: {}'.format(match_step))
else:
match_step.as_block.validate()
parts.append(u'as: %s' % (_get_vertex_location_name(match_step.as_block.location),))

Expand Down
9 changes: 9 additions & 0 deletions graphql_compiler/compiler/helpers.py
Expand Up @@ -3,6 +3,7 @@
from collections import namedtuple
import string

import funcy
from graphql import GraphQLList, GraphQLNonNull, GraphQLString, is_type
from graphql.language.ast import InlineFragment
from graphql.type.definition import GraphQLInterfaceType, GraphQLObjectType, GraphQLUnionType
Expand All @@ -23,6 +24,14 @@
('directive', 'field_ast', 'field_name', 'field_type'))


def get_only_element_from_collection(one_element_collection):
"""Assert that the collection has exactly one element, then return that element."""
if len(one_element_collection) != 1:
raise AssertionError(u'Expected a collection with exactly one element, but got: {}'
.format(one_element_collection))
return funcy.first(one_element_collection)


def get_ast_field_name(ast):
"""Return the normalized field name for the given AST node."""
replacements = {
Expand Down
10 changes: 3 additions & 7 deletions graphql_compiler/compiler/ir_lowering_gremlin/ir_lowering.py
Expand Up @@ -19,7 +19,8 @@
from ..expressions import (BinaryComposition, FoldedOutputContextField, Literal, LocalField,
NullLiteral)
from ..helpers import (STANDARD_DATE_FORMAT, STANDARD_DATETIME_FORMAT, FoldScopeLocation,
strip_non_null_from_type, validate_safe_string)
get_only_element_from_collection, strip_non_null_from_type,
validate_safe_string)
from ..ir_lowering_common import extract_folds_from_ir_blocks


Expand Down Expand Up @@ -55,12 +56,7 @@ def lower_coerce_type_block_type_data(ir_blocks, type_equivalence_hints):
for block in ir_blocks:
new_block = block
if isinstance(block, CoerceType):
if len(block.target_class) != 1:
raise AssertionError(u'Expected only a single target class for the type coercion, '
u'but received {}'.format(block.target_class))

# Sets are not indexable, so we have to grab the first element of its iterator.
target_class = next(x for x in block.target_class)
target_class = get_only_element_from_collection(block.target_class)
if target_class in equivalent_type_names:
new_block = CoerceType(equivalent_type_names[target_class])

Expand Down
5 changes: 4 additions & 1 deletion graphql_compiler/compiler/ir_lowering_match/__init__.py
Expand Up @@ -19,7 +19,8 @@
convert_optional_traversals_to_compound_match_query,
lower_context_field_expressions, prune_non_existent_outputs)
from ..match_query import convert_to_match_query
from ..workarounds import orientdb_class_with_while, orientdb_eval_scheduling
from ..workarounds import (orientdb_class_with_while, orientdb_eval_scheduling,
orientdb_query_execution)
from .utils import construct_where_filter_predicate

##############
Expand Down Expand Up @@ -106,5 +107,7 @@ def lower_ir(ir_blocks, location_types, type_equivalence_hints=None):

compound_match_query = truncate_repeated_single_step_traversals_in_sub_queries(
compound_match_query)
compound_match_query = orientdb_query_execution.expose_ideal_query_execution_start_points(
compound_match_query, location_types)

return compound_match_query
27 changes: 7 additions & 20 deletions graphql_compiler/compiler/ir_lowering_match/ir_lowering.py
Expand Up @@ -8,15 +8,13 @@
us to convert this Expression into other Expressions, using data already present in the IR,
to simplify the final code generation step.
"""
import funcy.py2 as funcy
import six

from ..blocks import Backtrack, CoerceType, Filter, MarkLocation, QueryRoot
from ..blocks import Backtrack, CoerceType, MarkLocation, QueryRoot
from ..expressions import (BinaryComposition, ContextField, ContextFieldExistence, FalseLiteral,
FoldedOutputContextField, Literal, LocalField, TernaryConditional,
TrueLiteral)
FoldedOutputContextField, Literal, TernaryConditional, TrueLiteral)
from ..helpers import FoldScopeLocation
from .utils import CompoundMatchQuery
from .utils import convert_coerce_type_to_instanceof_filter


##################################
Expand Down Expand Up @@ -324,21 +322,10 @@ def lower_folded_coerce_types_into_filter_blocks(folded_ir_blocks):
"""Lower CoerceType blocks into "INSTANCEOF" Filter blocks. Indended for folded IR blocks."""
new_folded_ir_blocks = []
for block in folded_ir_blocks:
new_block = block

if isinstance(block, CoerceType):
coerce_type_target = block.target_class
if len(coerce_type_target) != 1:
raise AssertionError(u'Unexpected "coerce_type_target" for MATCH query: '
u'{}'.format(coerce_type_target))
coerce_type_target = funcy.first(coerce_type_target)

# INSTANCEOF requires the target class to be passed in as a string,
# so we make the target class a string literal.
new_predicate = BinaryComposition(
u'INSTANCEOF', LocalField('@this'), Literal(coerce_type_target))

new_block = Filter(new_predicate)
new_block = convert_coerce_type_to_instanceof_filter(block)
else:
new_block = block

new_folded_ir_blocks.append(new_block)

Expand All @@ -361,4 +348,4 @@ def truncate_repeated_single_step_traversals_in_sub_queries(compound_match_query
new_match_query = truncate_repeated_single_step_traversals(match_query)
lowered_match_queries.append(new_match_query)

return CompoundMatchQuery(match_queries=lowered_match_queries)
return compound_match_query._replace(match_queries=lowered_match_queries)
28 changes: 26 additions & 2 deletions graphql_compiler/compiler/ir_lowering_match/utils.py
Expand Up @@ -3,9 +3,33 @@

import six

from ..expressions import (BinaryComposition, Expression, LocalField, NullLiteral,
from ..blocks import Filter
from ..expressions import (BinaryComposition, Expression, Literal, LocalField, NullLiteral,
SelectEdgeContextField, TrueLiteral, UnaryTransformation, ZeroLiteral)
from ..helpers import Location, is_vertex_field_name
from ..helpers import Location, get_only_element_from_collection, is_vertex_field_name


def convert_coerce_type_to_instanceof_filter(coerce_type_block):
"""Create an "INSTANCEOF" Filter block from a CoerceType block."""
coerce_type_target = get_only_element_from_collection(coerce_type_block.target_class)

# INSTANCEOF requires the target class to be passed in as a string,
# so we make the target class a string literal.
new_predicate = BinaryComposition(
u'INSTANCEOF', LocalField('@this'), Literal(coerce_type_target))

return Filter(new_predicate)


def convert_coerce_type_and_add_to_where_block(coerce_type_block, where_block):
"""Create an "INSTANCEOF" Filter from a CoerceType, adding to an existing Filter if any."""
instanceof_filter = convert_coerce_type_to_instanceof_filter(coerce_type_block)

if where_block:
# There was already a Filter block -- we'll merge the two predicates together.
return Filter(BinaryComposition(u'&&', instanceof_filter.predicate, where_block.predicate))
else:
return instanceof_filter


def expression_list_to_conjunction(expression_list):
Expand Down
28 changes: 3 additions & 25 deletions graphql_compiler/compiler/workarounds/orientdb_class_with_while.py
Expand Up @@ -4,30 +4,8 @@
For details, see:
https://github.com/orientechnologies/orientdb/issues/8129
"""
import funcy

from ..blocks import Filter, Recurse
from ..expressions import BinaryComposition, Literal, LocalField


def _coerce_block_to_filter_block(coerce_type_block, where_block):
"""Create an "INSTANCEOF" Filter block from a CoerceType block."""
coerce_type_target = coerce_type_block.target_class
if len(coerce_type_target) != 1:
raise AssertionError(u'Unexpected "coerce_type_target" for MATCH query: '
u'{}'.format(coerce_type_target))
coerce_type_target = funcy.first(coerce_type_target)

# INSTANCEOF requires the target class to be passed in as a string,
# so we make the target class a string literal.
new_predicate = BinaryComposition(
u'INSTANCEOF', LocalField('@this'), Literal(coerce_type_target))

if where_block:
# There was already a Filter block -- we'll merge the two predicates together.
new_predicate = BinaryComposition(u'&&', new_predicate, where_block.predicate)

return Filter(new_predicate)
from ..blocks import Recurse
from ..ir_lowering_match.utils import convert_coerce_type_and_add_to_where_block


def workaround_type_coercions_in_recursions(match_query):
Expand All @@ -52,7 +30,7 @@ def workaround_type_coercions_in_recursions(match_query):
has_recurse_root = isinstance(match_step.root_block, Recurse)

if has_coerce_type and has_recurse_root:
new_where_block = _coerce_block_to_filter_block(
new_where_block = convert_coerce_type_and_add_to_where_block(
match_step.coerce_type_block, match_step.where_block)
new_match_step = match_step._replace(coerce_type_block=None,
where_block=new_where_block)
Expand Down

0 comments on commit 4f4856b

Please sign in to comment.