diff --git a/.travis.yml b/.travis.yml index 970456cb9..d43b22319 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,10 +3,9 @@ language: python cache: pip python: - "2.7" - # - "3.6" # enable when compliant + - "3.6" install: - pip install -r dev-requirements.txt - - pip install -r requirements.txt - pip install -e . script: - flake8 graphql_compiler/ diff --git a/graphql_compiler/__init__.py b/graphql_compiler/__init__.py index b01fd2b7f..a64636c8c 100644 --- a/graphql_compiler/__init__.py +++ b/graphql_compiler/__init__.py @@ -18,13 +18,13 @@ def graphql_to_match(schema, graphql_query, parameters): Args: schema: GraphQL schema object describing the schema of the graph to be queried - graphql_string: the GraphQL query to compile to MATCH, as a basestring + graphql_string: the GraphQL query to compile to MATCH, as a string parameters: dict, mapping argument name to its value, for every parameter the query expects. Returns: a CompilationResult object, containing: - - query: basestring, the resulting compiled and parameterized query string - - language: basestring, specifying the language to which the query was compiled + - query: string, the resulting compiled and parameterized query string + - language: string, specifying the language to which the query was compiled - output_metadata: dict, output name -> OutputMetadata namedtuple object - input_metadata: dict, name of input variables -> inferred GraphQL type, based on use """ @@ -38,7 +38,7 @@ def graphql_to_gremlin(schema, graphql_query, parameters, type_equivalence_hints Args: schema: GraphQL schema object describing the schema of the graph to be queried - graphql_string: the GraphQL query to compile to Gremlin, as a basestring + graphql_string: the GraphQL query to compile to Gremlin, as a string parameters: dict, mapping argument name to its value, for every parameter the query expects. type_equivalence_hints: optional dict of GraphQL interface or type -> GraphQL union. Used as a workaround for Gremlin's lack of inheritance-awareness. @@ -57,8 +57,8 @@ def graphql_to_gremlin(schema, graphql_query, parameters, type_equivalence_hints Returns: a CompilationResult object, containing: - - query: basestring, the resulting compiled and parameterized query string - - language: basestring, specifying the language to which the query was compiled + - query: string, the resulting compiled and parameterized query string + - language: string, specifying the language to which the query was compiled - output_metadata: dict, output name -> OutputMetadata namedtuple object - input_metadata: dict, name of input variables -> inferred GraphQL type, based on use """ diff --git a/graphql_compiler/compiler/blocks.py b/graphql_compiler/compiler/blocks.py index 6abdd08ea..10d5b1726 100644 --- a/graphql_compiler/compiler/blocks.py +++ b/graphql_compiler/compiler/blocks.py @@ -1,4 +1,6 @@ # Copyright 2017 Kensho Technologies, Inc. +import six + from .expressions import Expression from .helpers import (CompilerEntity, ensure_unicode_string, safe_quoted_string, validate_marked_location, validate_safe_string) @@ -37,7 +39,7 @@ def __init__(self, start_class): """Construct a QueryRoot object that starts querying at the specified class name. Args: - start_class: set of basestring, class names from which to start the query. + start_class: set of string, class names from which to start the query. This will generally be a set of length 1, except when using Gremlin with a non-final class, where we have to include all subclasses of the start class. This is done using a Gremlin-only IR lowering step. @@ -52,8 +54,8 @@ def __init__(self, start_class): def validate(self): """Ensure that the QueryRoot block is valid.""" if not (isinstance(self.start_class, set) and - all(isinstance(x, basestring) for x in self.start_class)): - raise TypeError(u'Expected set of basestring start_class, got: {} {}'.format( + all(isinstance(x, six.string_types) for x in self.start_class)): + raise TypeError(u'Expected set of string start_class, got: {} {}'.format( type(self.start_class).__name__, self.start_class)) for cls in self.start_class: @@ -80,7 +82,7 @@ def __init__(self, target_class): """Construct a CoerceType object that filters out any data that is not of the given types. Args: - target_class: set of basestring, class names from which to start the query. + target_class: set of string, class names from which to start the query. This will generally be a set of length 1, except when using Gremlin with a non-final class, where we have to include all subclasses of the target class. This is done using a Gremlin-only IR lowering step. @@ -95,8 +97,8 @@ def __init__(self, target_class): def validate(self): """Ensure that the CoerceType block is valid.""" if not (isinstance(self.target_class, set) and - all(isinstance(x, basestring) for x in self.target_class)): - raise TypeError(u'Expected set of basestring target_class, got: {} {}'.format( + all(isinstance(x, six.string_types) for x in self.target_class)): + raise TypeError(u'Expected set of string target_class, got: {} {}'.format( type(self.target_class).__name__, self.target_class)) for cls in self.target_class: @@ -115,7 +117,7 @@ def __init__(self, fields): """Construct a ConstructResult object that maps the given field names to their expressions. Args: - fields: dict, variable name basestring -> Expression + fields: dict, variable name string -> Expression see rules for variable names in validate_safe_string(). Returns: @@ -123,7 +125,7 @@ def __init__(self, fields): """ self.fields = { ensure_unicode_string(key): value - for key, value in fields.iteritems() + for key, value in six.iteritems(fields) } # All key values are normalized to unicode before being passed to the parent constructor, @@ -137,7 +139,7 @@ def validate(self): raise TypeError(u'Expected dict fields, got: {} {}'.format( type(self.fields).__name__, self.fields)) - for key, value in self.fields.iteritems(): + for key, value in six.iteritems(self.fields): validate_safe_string(key) if not isinstance(value, Expression): raise TypeError( @@ -148,7 +150,7 @@ def visit_and_update_expressions(self, visitor_fn): """Create an updated version (if needed) of the ConstructResult via the visitor pattern.""" new_fields = {} - for key, value in self.fields.iteritems(): + for key, value in six.iteritems(self.fields): new_value = value.visit_and_update(visitor_fn) if new_value is not value: new_fields[key] = new_value @@ -237,8 +239,8 @@ def __init__(self, direction, edge_name, optional=False): """Create a new Traverse block in the given direction and across the given edge. Args: - direction: basestring, 'in' or 'out' - edge_name: basestring obeying variable name rules (see validate_safe_string). + direction: string, 'in' or 'out' + edge_name: string obeying variable name rules (see validate_safe_string). optional: optional bool, specifying whether the traversal to the given location is optional (i.e. non-filtering) or mandatory (filtering). @@ -253,8 +255,8 @@ def __init__(self, direction, edge_name, optional=False): def validate(self): """Ensure that the Traverse block is valid.""" - if not isinstance(self.direction, basestring): - raise TypeError(u'Expected basestring direction, got: {} {}'.format( + if not isinstance(self.direction, six.string_types): + raise TypeError(u'Expected string direction, got: {} {}'.format( type(self.direction).__name__, self.direction)) if self.direction not in {u'in', u'out'}: @@ -302,8 +304,8 @@ def __init__(self, direction, edge_name, depth): """Create a new Recurse block which traverses the given edge up to "depth" times. Args: - direction: basestring, 'in' or 'out'. - edge_name: basestring obeying variable name rules (see validate_safe_string). + direction: string, 'in' or 'out'. + edge_name: string obeying variable name rules (see validate_safe_string). depth: int, always greater than or equal to 1. Returns: @@ -317,8 +319,8 @@ def __init__(self, direction, edge_name, depth): def validate(self): """Ensure that the Traverse block is valid.""" - if not isinstance(self.direction, basestring): - raise TypeError(u'Expected basestring direction, got: {} {}'.format( + if not isinstance(self.direction, six.string_types): + raise TypeError(u'Expected string direction, got: {} {}'.format( type(self.direction).__name__, self.direction)) if self.direction not in {u'in', u'out'}: @@ -344,7 +346,7 @@ def to_gremlin(self): recurse_steps = [ recurse_base + (recurse_traversal * i) - for i in xrange(self.depth + 1) + for i in six.moves.xrange(self.depth + 1) ] return template.format(recurse=','.join(recurse_steps)) diff --git a/graphql_compiler/compiler/common.py b/graphql_compiler/compiler/common.py index 0aa01fb7e..807fd2c06 100644 --- a/graphql_compiler/compiler/common.py +++ b/graphql_compiler/compiler/common.py @@ -6,8 +6,8 @@ # The CompilationResult will have the following types for its members: -# - query: basestring, the resulting compiled query string, with placeholders for parameters -# - language: basestring, specifying the language to which the query was compiled +# - query: string, the resulting compiled query string, with placeholders for parameters +# - language: string, specifying the language to which the query was compiled # - output_metadata: dict, output name -> OutputMetadata namedtuple object # - input_metadata: dict, name of input variables -> inferred GraphQL type, based on use CompilationResult = namedtuple('CompilationResult', @@ -22,7 +22,7 @@ def compile_graphql_to_match(schema, graphql_string): Args: schema: GraphQL schema object describing the schema of the graph to be queried - graphql_string: the GraphQL query to compile to MATCH, as a basestring + graphql_string: the GraphQL query to compile to MATCH, as a string Returns: a CompilationResult object @@ -46,7 +46,7 @@ def compile_graphql_to_gremlin(schema, graphql_string, type_equivalence_hints=No Args: schema: GraphQL schema object describing the schema of the graph to be queried - graphql_string: the GraphQL query to compile to Gremlin, as a basestring + graphql_string: the GraphQL query to compile to Gremlin, as a string type_equivalence_hints: optional dict of GraphQL interface or type -> GraphQL union. Used as a workaround for Gremlin's lack of inheritance-awareness. When this parameter is not specified or is empty, type coercion diff --git a/graphql_compiler/compiler/compiler_frontend.py b/graphql_compiler/compiler/compiler_frontend.py index 5460c4466..0dd1dd52f 100644 --- a/graphql_compiler/compiler/compiler_frontend.py +++ b/graphql_compiler/compiler/compiler_frontend.py @@ -60,6 +60,7 @@ from graphql.type.definition import (GraphQLInterfaceType, GraphQLList, GraphQLObjectType, GraphQLUnionType) from graphql.validation import validate +import six from . import blocks, expressions from ..exceptions import GraphQLCompilationError, GraphQLParsingError, GraphQLValidationError @@ -95,7 +96,7 @@ def _get_directives(ast): ast: GraphQL AST node, obtained from the graphql library Returns: - dict of basestring to directive object, mapping directive names to their data + dict of string to directive object, mapping directive names to their data """ try: return get_uniquely_named_objects_by_name(ast.directives) @@ -225,7 +226,7 @@ def _process_output_source_directive(schema, current_schema_type, ast, def _validate_property_directives(directives): """Validate the directives that appear at a property field.""" - for directive_name in directives.iterkeys(): + for directive_name in six.iterkeys(directives): if directive_name in vertex_only_directives: raise GraphQLCompilationError( u'Found vertex-only directive {} set on property.'.format(directive_name)) @@ -233,7 +234,7 @@ def _validate_property_directives(directives): def _validate_vertex_directives(directives): """Validate the directives that appear at a vertex field.""" - for directive_name in directives.iterkeys(): + for directive_name in six.iterkeys(directives): if directive_name in property_only_directives: raise GraphQLCompilationError( u'Found property-only directive {} set on vertex.'.format(directive_name)) @@ -529,7 +530,7 @@ def _compile_vertex_ast(schema, current_schema_type, ast, def _validate_fold_has_outputs(fold_data, outputs): # At least one output in the outputs list must point to the fold_data, # or the scope corresponding to fold_data had no @outputs and is illegal. - for output in outputs.values(): + for output in six.itervalues(outputs): if output['fold'] is fold_data: return True @@ -651,8 +652,8 @@ def _compile_root_ast_to_ir(schema, ast): Returns: tuple of: - a list of IR basic block objects - - a dict of output name (basestring) -> OutputMetadata object - - a dict of expected input parameters (basestring) -> inferred GraphQL type, based on use + - a dict of output name (string) -> OutputMetadata object + - a dict of expected input parameters (string) -> inferred GraphQL type, based on use - a dict of location objects -> GraphQL type objects at that location """ if len(ast.selection_set.selections) != 1: @@ -694,7 +695,7 @@ def _compile_root_ast_to_ir(schema, ast): # Ensure the GraphQL query root doesn't have any vertex directives # that are disallowed on the root node. - directives_present_at_root = set(_get_directives(base_ast).iterkeys()) + directives_present_at_root = set(six.iterkeys(_get_directives(base_ast))) disallowed_directives = directives_present_at_root & vertex_directives_prohibited_on_root if disallowed_directives: raise GraphQLCompilationError(u'Found prohibited directives on root vertex: ' @@ -710,7 +711,7 @@ def _compile_root_ast_to_ir(schema, ast): basic_blocks.append(_compile_output_step(outputs_context)) output_metadata = { name: OutputMetadata(type=value['type'], optional=value['optional']) - for name, value in outputs_context.iteritems() + for name, value in six.iteritems(outputs_context) } return basic_blocks, output_metadata, context['inputs'], context['location_types'] @@ -720,7 +721,7 @@ def _compile_output_step(outputs): """Construct the final ConstructResult basic block that defines the output format of the query. Args: - outputs: dict, output name (basestring) -> output data dict, specifying the location + outputs: dict, output name (string) -> output data dict, specifying the location from where to get the data, and whether the data is optional (and therefore may be missing); missing optional data is replaced with 'null' @@ -732,7 +733,7 @@ def _compile_output_step(outputs): u'one field with the @output directive.') output_fields = {} - for output_name, output_context in outputs.iteritems(): + for output_name, output_context in six.iteritems(outputs): location = output_context['location'] optional = output_context['optional'] graphql_type = output_context['type'] @@ -776,13 +777,13 @@ def graphql_to_ir(schema, graphql_string): Args: schema: GraphQL schema object, created using the GraphQL library - graphql_string: basestring containing the GraphQL to compile to compiler IR + graphql_string: string containing the GraphQL to compile to compiler IR Returns: tuple of: - a list of IR basic block objects - - a dict of output name (basestring) -> OutputMetadata object - - a dict of expected input parameters (basestring) -> inferred GraphQL type, based on use + - a dict of output name (string) -> OutputMetadata object + - a dict of expected input parameters (string) -> inferred GraphQL type, based on use - a dict of location objects -> GraphQL type objects at that location Raises flavors of GraphQLError in the following cases: diff --git a/graphql_compiler/compiler/expressions.py b/graphql_compiler/compiler/expressions.py index f5dab0194..8a7867117 100644 --- a/graphql_compiler/compiler/expressions.py +++ b/graphql_compiler/compiler/expressions.py @@ -1,5 +1,6 @@ # Copyright 2017 Kensho Technologies, Inc. from graphql import GraphQLList, GraphQLNonNull +import six from ..exceptions import GraphQLCompilationError from ..schema import GraphQLDate, GraphQLDateTime @@ -75,7 +76,7 @@ def validate(self): return # Literal safe strings are correctly representable and supported. - if isinstance(self.value, basestring): + if isinstance(self.value, six.string_types): validate_safe_string(self.value) return @@ -99,12 +100,12 @@ def _to_output_code(self): return u'true' elif self.value is False: return u'false' - elif isinstance(self.value, basestring): + elif isinstance(self.value, six.string_types): return safe_quoted_string(self.value) elif isinstance(self.value, list): if len(self.value) == 0: return '[]' - elif all(isinstance(x, basestring) for x in self.value): + elif all(isinstance(x, six.string_types) for x in self.value): list_contents = ', '.join(safe_quoted_string(x) for x in sorted(self.value)) return '[' + list_contents + ']' else: @@ -128,7 +129,7 @@ def __init__(self, variable_name, inferred_type): """Construct a new Variable object for the given variable name. Args: - variable_name: basestring, should start with '$' and then obey variable naming rules + variable_name: string, should start with '$' and then obey variable naming rules (see validate_safe_string()) inferred_type: GraphQL type object, specifying the inferred type of the variable @@ -179,7 +180,7 @@ def to_match(self): # We don't want the dollar sign as part of the variable name. variable_with_no_dollar_sign = self.variable_name[1:] - match_variable_name = '{%s}' % (unicode(variable_with_no_dollar_sign),) + match_variable_name = '{%s}' % (six.text_type(variable_with_no_dollar_sign),) # We can't directly pass a Date or DateTime object, so we have to pass it as a string # and then parse it inline. For date format parameter meanings, see: @@ -203,7 +204,7 @@ def to_gremlin(self): elif GraphQLDateTime.is_same_type(self.inferred_type): return u'Date.parse("{}", {})'.format(STANDARD_DATETIME_FORMAT, self.variable_name) else: - return unicode(self.variable_name) + return six.text_type(self.variable_name) def __eq__(self, other): """Return True if the given object is equal to this one, and False otherwise.""" @@ -234,7 +235,7 @@ def validate(self): def to_match(self): """Return a unicode object with the MATCH representation of this LocalField.""" self.validate() - return unicode(self.field_name) + return six.text_type(self.field_name) def to_gremlin(self): """Return a unicode object with the Gremlin representation of this expression.""" @@ -415,7 +416,7 @@ def __init__(self, root_location, relative_position, field_name, field_type): root_location: Location, specifying where the @fold was applied. relative_position: tuple of (edge_direction, edge_name) specifying the field's enclosing vertex field, relative to the root_location. - field_name: basestring, the name of the field being output. + field_name: string, the name of the field being output. field_type: GraphQL type object, specifying the type of the field being output. Since the field is folded, this must be a GraphQLList of some kind. @@ -616,7 +617,7 @@ def __init__(self, operator, left, right): def validate(self): """Validate that the BinaryComposition is correctly representable.""" - if not isinstance(self.operator, unicode): + if not isinstance(self.operator, six.text_type): raise TypeError(u'Expected unicode operator, got: {} {}'.format( type(self.operator).__name__, self.operator)) diff --git a/graphql_compiler/compiler/filters.py b/graphql_compiler/compiler/filters.py index 6ba954dbc..a9321ce2a 100644 --- a/graphql_compiler/compiler/filters.py +++ b/graphql_compiler/compiler/filters.py @@ -64,7 +64,7 @@ def _represent_argument(schema, ast, context, argument, inferred_type): uniformity at the moment -- it is currently not used. context: dict, various per-compilation data (e.g. declared tags, whether the current block is optional, etc.). May be mutated in-place in this function! - argument: basestring, the name of the argument to the directive + argument: string, the name of the argument to the directive inferred_type: GraphQL type object specifying the inferred type of the argument Returns: diff --git a/graphql_compiler/compiler/helpers.py b/graphql_compiler/compiler/helpers.py index d45c9b56f..e5d210317 100644 --- a/graphql_compiler/compiler/helpers.py +++ b/graphql_compiler/compiler/helpers.py @@ -3,11 +3,12 @@ import string from graphql import GraphQLEnumType, GraphQLNonNull, GraphQLScalarType, GraphQLString, is_type +import six from ..exceptions import GraphQLCompilationError -VARIABLE_ALLOWED_CHARS = frozenset(unicode(string.ascii_letters + string.digits + '_')) +VARIABLE_ALLOWED_CHARS = frozenset(six.text_type(string.ascii_letters + string.digits + '_')) def get_ast_field_name(ast): @@ -56,10 +57,10 @@ def is_graphql_type(graphql_type): def ensure_unicode_string(value): - """Ensure the value is a basestring, and return it as unicode.""" - if not isinstance(value, basestring): - raise TypeError(u'Expected basestring value, got: {}'.format(value)) - return unicode(value) + """Ensure the value is a string, and return it as unicode.""" + if not isinstance(value, six.string_types): + raise TypeError(u'Expected string value, got: {}'.format(value)) + return six.text_type(value) def get_uniquely_named_objects_by_name(object_list): @@ -97,8 +98,8 @@ def validate_safe_string(value): # The following strings are explicitly allowed, despite having otherwise-illegal chars. legal_strings_with_special_chars = frozenset({'@rid', '@class', '@this', '%'}) - if not isinstance(value, basestring): - raise TypeError(u'Expected basestring value, got: {} {}'.format( + if not isinstance(value, six.string_types): + raise TypeError(u'Expected string value, got: {} {}'.format( type(value).__name__, value)) if not value: @@ -122,6 +123,7 @@ def validate_marked_location(location): raise GraphQLCompilationError(u'Cannot mark location at a field: {}'.format(location)) +@six.python_2_unicode_compatible class Location(object): def __init__(self, query_path, field=None, visit_counter=1): """Create a new Location object. @@ -143,9 +145,9 @@ def __init__(self, query_path, field=None, visit_counter=1): should have different 'visit_counter' values. Args: - query_path: tuple of basestrings, in-order, one for each vertex in the + query_path: tuple of strings, in-order, one for each vertex in the current nested position in the graph - field: basestring if at a field in a vertex, or None if at a vertex + field: string if at a field in a vertex, or None if at a vertex visit_counter: int, number that allows semantic disambiguation of otherwise equivalent Location objects -- see the explanation above. @@ -155,8 +157,8 @@ def __init__(self, query_path, field=None, visit_counter=1): if not isinstance(query_path, tuple): raise TypeError(u'Expected query_path to be a tuple, was: ' u'{} {}'.format(type(query_path).__name__, query_path)) - if field and not isinstance(field, basestring): - raise TypeError(u'Expected field to be None or basestring, was: ' + if field and not isinstance(field, six.string_types): + raise TypeError(u'Expected field to be None or string, was: ' u'{} {}'.format(type(field).__name__, field)) self.query_path = query_path @@ -182,8 +184,8 @@ def at_vertex(self): def navigate_to_subpath(self, child): """Return a new Location object at a child vertex of the current Location's vertex.""" - if not isinstance(child, basestring): - raise TypeError(u'Expected child to be a basestring, was: {}'.format(child)) + if not isinstance(child, six.string_types): + raise TypeError(u'Expected child to be a string, was: {}'.format(child)) if self.field: raise AssertionError(u'Currently at a field, cannot go to child: {}'.format(self)) return Location(self.query_path + (child,)) @@ -196,16 +198,12 @@ def revisit(self): def get_location_name(self): """Return a tuple of a unique name of the Location, and the current field name (or None).""" - mark_name = u'__'.join(self.query_path) + u'___' + unicode(self.visit_counter) + mark_name = u'__'.join(self.query_path) + u'___' + six.text_type(self.visit_counter) return (mark_name, self.field) - def __unicode__(self): - """Return a human-readable unicode representation of the Location object.""" - return u'Location({}, {}, {})'.format(self.query_path, self.field, self.visit_counter) - def __str__(self): """Return a human-readable str representation of the Location object.""" - return self.__unicode__().encode('utf-8') + return u'Location({}, {}, {})'.format(self.query_path, self.field, self.visit_counter) def __repr__(self): """Return a human-readable str representation of the Location object.""" @@ -226,11 +224,11 @@ def __hash__(self): return hash(self.query_path) ^ hash(self.field) ^ hash(self.visit_counter) +@six.python_2_unicode_compatible +@six.add_metaclass(ABCMeta) class CompilerEntity(object): """An abstract compiler entity. Can represent things like basic blocks and expressions.""" - __metaclass__ = ABCMeta - def __init__(self, *args, **kwargs): """Construct a new CompilerEntity.""" self._print_args = args @@ -240,7 +238,7 @@ def validate(self): """Ensure that the CompilerEntity is valid.""" pass - def __unicode__(self): + def __str__(self): """Return a human-readable unicode representation of this CompilerEntity.""" printed_args = [] if self._print_args: @@ -253,10 +251,6 @@ def __unicode__(self): args=self._print_args, kwargs=self._print_kwargs) - def __str__(self): - """Return a human-readable str representation of this CompilerEntity.""" - return self.__unicode__().encode('utf-8') - def __repr__(self): """Return a human-readable str representation of the CompilerEntity object.""" return self.__str__() diff --git a/graphql_compiler/compiler/ir_lowering_common.py b/graphql_compiler/compiler/ir_lowering_common.py index bc25bf0ae..77f23c200 100644 --- a/graphql_compiler/compiler/ir_lowering_common.py +++ b/graphql_compiler/compiler/ir_lowering_common.py @@ -1,7 +1,7 @@ # Copyright 2017 Kensho Technologies, Inc. """Language-independent IR lowering and optimization functions.""" -from funcy import pairwise +from funcy.py2 import pairwise from .blocks import (Backtrack, CoerceType, ConstructResult, Filter, MarkLocation, OutputSource, QueryRoot, Recurse, Traverse) diff --git a/graphql_compiler/compiler/ir_lowering_gremlin.py b/graphql_compiler/compiler/ir_lowering_gremlin.py index 90820c650..ae2961317 100644 --- a/graphql_compiler/compiler/ir_lowering_gremlin.py +++ b/graphql_compiler/compiler/ir_lowering_gremlin.py @@ -9,6 +9,7 @@ to simplify the final code generation step. """ from graphql.type import GraphQLInterfaceType, GraphQLObjectType, GraphQLUnionType +import six from ..exceptions import GraphQLCompilationError from .blocks import Backtrack, CoerceType, Filter, Traverse @@ -28,7 +29,7 @@ def lower_coerce_type_block_type_data(ir_blocks, type_equivalence_hints): allowed_value_type_spec = GraphQLUnionType # Validate that the type_equivalence_hints parameter has correct types. - for key, value in type_equivalence_hints.iteritems(): + for key, value in six.iteritems(type_equivalence_hints): if (not isinstance(key, allowed_key_type_spec) or not isinstance(value, allowed_value_type_spec)): msg = (u'Invalid type equivalence hints received! Hint {} ({}) -> {} ({}) ' @@ -43,7 +44,7 @@ def lower_coerce_type_block_type_data(ir_blocks, type_equivalence_hints): # a dict of type name -> set of names of equivalent types, which can be used more readily. equivalent_type_names = { key.name: {x.name for x in value.types} - for key, value in type_equivalence_hints.iteritems() + for key, value in six.iteritems(type_equivalence_hints) } new_ir_blocks = [] diff --git a/graphql_compiler/compiler/ir_lowering_match.py b/graphql_compiler/compiler/ir_lowering_match.py index 0f34e4e8e..f8d23a382 100644 --- a/graphql_compiler/compiler/ir_lowering_match.py +++ b/graphql_compiler/compiler/ir_lowering_match.py @@ -8,6 +8,8 @@ us to convert this Expression into other Expressions, using data already present in the IR, to simplify the final code generation step. """ +import six + from .blocks import Backtrack, MarkLocation, QueryRoot, Traverse from .expressions import (BinaryComposition, ContextField, ContextFieldExistence, FalseLiteral, Literal, TernaryConditional, TrueLiteral) @@ -261,7 +263,7 @@ def _flatten_location_translations(location_translations): location_translations: dict of Location -> Location, where the key translates to the value. Mutated in place for efficiency and simplicity of implementation. """ - sources_to_process = set(location_translations.iterkeys()) + sources_to_process = set(six.iterkeys(location_translations)) def _update_translation(source): """Return the proper (fully-flattened) translation for the given location.""" diff --git a/graphql_compiler/compiler/workarounds/orientdb_class_vs_instanceof.py b/graphql_compiler/compiler/workarounds/orientdb_class_vs_instanceof.py index 7f51285bf..a31582171 100644 --- a/graphql_compiler/compiler/workarounds/orientdb_class_vs_instanceof.py +++ b/graphql_compiler/compiler/workarounds/orientdb_class_vs_instanceof.py @@ -4,7 +4,7 @@ For details, see: https://github.com/orientechnologies/orientdb/issues/7225 """ -import funcy +import funcy.py2 as funcy from ..blocks import Filter, Recurse, Traverse from ..expressions import BinaryComposition, Literal, LocalField diff --git a/graphql_compiler/debugging_utils.py b/graphql_compiler/debugging_utils.py index 1bba0d9cc..b15cfd5bf 100644 --- a/graphql_compiler/debugging_utils.py +++ b/graphql_compiler/debugging_utils.py @@ -1,6 +1,8 @@ # Copyright 2017 Kensho Technologies, Inc. import re +import six + def remove_custom_formatting(query): """Prepare the query string for pretty-printing by removing all unusual formatting.""" @@ -16,12 +18,12 @@ def pretty_print_gremlin(gremlin): # Put the ) and } back on. parts = [ too_many_parts[i] + too_many_parts[i + 1] - for i in xrange(0, len(too_many_parts) - 1, 2) + for i in six.moves.xrange(0, len(too_many_parts) - 1, 2) ] parts.append(too_many_parts[-1]) # Put the . back on. - for i in xrange(1, len(parts)): + for i in six.moves.xrange(1, len(parts)): parts[i] = '.' + parts[i] indentation = 0 @@ -84,7 +86,7 @@ def pretty_print_match(match, parameterized=True): # For every subsequent item, the keyword and value are separated; join them # back together, outputting the comma, newline and indentation before them. output.append(indent + separate_keywords[0].lstrip()) - for i in xrange(1, len(separate_keywords) - 1, 2): + for i in six.moves.xrange(1, len(separate_keywords) - 1, 2): output.append(',\n{indent}{keyword} {value}'.format( keyword=separate_keywords[i].strip(), value=separate_keywords[i + 1].strip(), diff --git a/graphql_compiler/query_formatting/common.py b/graphql_compiler/query_formatting/common.py index 71357428d..8588080c0 100644 --- a/graphql_compiler/query_formatting/common.py +++ b/graphql_compiler/query_formatting/common.py @@ -1,5 +1,7 @@ # Copyright 2017 Kensho Technologies, Inc. """Safely insert runtime arguments into compiled GraphQL queries.""" +import six + from ..compiler import GREMLIN_LANGUAGE, MATCH_LANGUAGE from ..exceptions import GraphQLInvalidArgumentError from .gremlin_formatting import insert_arguments_into_gremlin_query @@ -10,8 +12,8 @@ def _ensure_arguments_are_provided(expected_types, arguments): """Ensure that all arguments expected by the query were actually provided.""" # This function only checks that the arguments were specified, # and does not check types. Type checking is done as part of the actual formatting step. - expected_arg_names = set(expected_types.iterkeys()) - provided_arg_names = set(arguments.iterkeys()) + expected_arg_names = set(six.iterkeys(expected_types)) + provided_arg_names = set(six.iterkeys(arguments)) if expected_arg_names != provided_arg_names: missing_args = expected_arg_names - provided_arg_names @@ -33,7 +35,7 @@ def insert_arguments_into_query(compilation_result, arguments): arguments: dict, mapping argument name to its value, for every parameter the query expects. Returns: - basestring, a query in the appropriate output language, with inserted argument data + string, a query in the appropriate output language, with inserted argument data """ _ensure_arguments_are_provided(compilation_result.input_metadata, arguments) diff --git a/graphql_compiler/query_formatting/graphql_formatting.py b/graphql_compiler/query_formatting/graphql_formatting.py index d7aa794d3..91982159d 100644 --- a/graphql_compiler/query_formatting/graphql_formatting.py +++ b/graphql_compiler/query_formatting/graphql_formatting.py @@ -2,6 +2,7 @@ from graphql import parse from graphql.language.printer import PrintingVisitor, join, wrap from graphql.language.visitor import visit +import six from ..schema import DIRECTIVES @@ -44,7 +45,7 @@ def leave_Directive(self, node, *args): encountered_argument_names = set() # Iterate through all defined arguments in the directive schema. - for defined_arg_name in directive.args.keys(): + for defined_arg_name in six.iterkeys(directive.args): if defined_arg_name in name_to_arg_value: # The argument was present in the query, print it in the correct order. encountered_argument_names.add(defined_arg_name) @@ -54,7 +55,7 @@ def leave_Directive(self, node, *args): # They will be printed after all the arguments that were in the schema. unsorted_args = [ value - for name, value in name_to_arg_value.items() + for name, value in six.iteritems(name_to_arg_value) if name not in encountered_argument_names ] diff --git a/graphql_compiler/query_formatting/gremlin_formatting.py b/graphql_compiler/query_formatting/gremlin_formatting.py index b7161cdbb..a28f2a9f3 100644 --- a/graphql_compiler/query_formatting/gremlin_formatting.py +++ b/graphql_compiler/query_formatting/gremlin_formatting.py @@ -6,6 +6,7 @@ import arrow from graphql import GraphQLBoolean, GraphQLFloat, GraphQLID, GraphQLInt, GraphQLList, GraphQLString +import six from ..compiler import GREMLIN_LANGUAGE from ..compiler.helpers import strip_non_null_from_type @@ -16,9 +17,12 @@ def _safe_gremlin_string(value): """Sanitize and represent a string argument in Gremlin.""" - if not isinstance(value, basestring): - raise GraphQLInvalidArgumentError(u'Attempting to convert a non-string into a string: ' - u'{}'.format(value)) + if not isinstance(value, six.string_types): + if isinstance(value, bytes): # should only happen in py3 + value = value.decode('utf-8') + else: + raise GraphQLInvalidArgumentError(u'Attempting to convert a non-string into a string: ' + u'{}'.format(value)) # Using JSON encoding means that all unicode literals and special chars # (e.g. newlines and backslashes) are replaced by appropriate escape sequences. @@ -86,8 +90,11 @@ def _safe_gremlin_argument(expected_type, argument_value): elif GraphQLID.is_same_type(expected_type): # IDs can be strings or numbers, but the GraphQL library coerces them to strings. # We will follow suit and treat them as strings. - if not isinstance(argument_value, basestring): - argument_value = unicode(argument_value) + if not isinstance(argument_value, six.string_types): + if isinstance(argument_value, bytes): # should only happen in py3 + argument_value = argument_value.decode('utf-8') + else: + argument_value = six.text_type(argument_value) return _safe_gremlin_string(argument_value) elif GraphQLFloat.is_same_type(expected_type): return represent_float_as_str(argument_value) @@ -132,7 +139,7 @@ def insert_arguments_into_gremlin_query(compilation_result, arguments): arguments: dict, mapping argument name to its value, for every parameter the query expects. Returns: - basestring, a Gremlin query with inserted argument data + string, a Gremlin query with inserted argument data """ if compilation_result.language != GREMLIN_LANGUAGE: raise AssertionError(u'Unexpected query output language: {}'.format(compilation_result)) @@ -143,7 +150,7 @@ def insert_arguments_into_gremlin_query(compilation_result, arguments): # The arguments are assumed to have already been validated against the query. sanitized_arguments = { key: _safe_gremlin_argument(argument_types[key], value) - for key, value in arguments.iteritems() + for key, value in six.iteritems(arguments) } return Template(base_query).substitute(sanitized_arguments) diff --git a/graphql_compiler/query_formatting/match_formatting.py b/graphql_compiler/query_formatting/match_formatting.py index a9efac52b..e397eefc1 100644 --- a/graphql_compiler/query_formatting/match_formatting.py +++ b/graphql_compiler/query_formatting/match_formatting.py @@ -5,6 +5,7 @@ import arrow from graphql import GraphQLBoolean, GraphQLFloat, GraphQLID, GraphQLInt, GraphQLList, GraphQLString +import six from ..compiler import MATCH_LANGUAGE from ..compiler.helpers import strip_non_null_from_type @@ -15,9 +16,12 @@ def _safe_match_string(value): """Sanitize and represent a string argument in MATCH.""" - if not isinstance(value, basestring): - raise GraphQLInvalidArgumentError(u'Attempting to convert a non-string into a string: ' - u'{}'.format(value)) + if not isinstance(value, six.string_types): + if isinstance(value, bytes): # should only happen in py3 + value = value.decode('utf-8') + else: + raise GraphQLInvalidArgumentError(u'Attempting to convert a non-string into a string: ' + u'{}'.format(value)) # Using JSON encoding means that all unicode literals and special chars # (e.g. newlines and backslashes) are replaced by appropriate escape sequences. @@ -72,8 +76,11 @@ def _safe_match_argument(expected_type, argument_value): elif GraphQLID.is_same_type(expected_type): # IDs can be strings or numbers, but the GraphQL library coerces them to strings. # We will follow suit and treat them as strings. - if not isinstance(argument_value, basestring): - argument_value = unicode(argument_value) + if not isinstance(argument_value, six.string_types): + if isinstance(argument_value, bytes): # should only happen in py3 + argument_value = argument_value.decode('utf-8') + else: + argument_value = six.text_type(argument_value) return _safe_match_string(argument_value) elif GraphQLFloat.is_same_type(expected_type): return represent_float_as_str(argument_value) @@ -110,7 +117,7 @@ def insert_arguments_into_match_query(compilation_result, arguments): arguments: dict, mapping argument name to its value, for every parameter the query expects. Returns: - basestring, a MATCH query with inserted argument data + string, a MATCH query with inserted argument data """ if compilation_result.language != MATCH_LANGUAGE: raise AssertionError(u'Unexpected query output language: {}'.format(compilation_result)) @@ -121,7 +128,7 @@ def insert_arguments_into_match_query(compilation_result, arguments): # The arguments are assumed to have already been validated against the query. sanitized_arguments = { key: _safe_match_argument(argument_types[key], value) - for key, value in arguments.iteritems() + for key, value in six.iteritems(arguments) } return base_query.format(**sanitized_arguments) diff --git a/graphql_compiler/tests/test_compiler.py b/graphql_compiler/tests/test_compiler.py index a6cbf0e1d..d48ad8d8d 100644 --- a/graphql_compiler/tests/test_compiler.py +++ b/graphql_compiler/tests/test_compiler.py @@ -3,6 +3,7 @@ import unittest from graphql import GraphQLID, GraphQLList, GraphQLString, GraphQLUnionType +import six from ..compiler import OutputMetadata, compile_graphql_to_gremlin, compile_graphql_to_match from ..exceptions import GraphQLCompilationError @@ -15,7 +16,7 @@ def check_test_data(test_case, graphql_input, expected_match, expected_gremlin, """Assert that the GraphQL input generates all expected MATCH and Gremlin data.""" result = compile_graphql_to_match(test_case.schema, graphql_input) compare_match(test_case, expected_match, result.query) - test_case.assertEquals(expected_output_metadata, result.output_metadata) + test_case.assertEqual(expected_output_metadata, result.output_metadata) compare_input_metadata(test_case, expected_input_metadata, result.input_metadata) if type_equivalence_hints: @@ -25,7 +26,7 @@ def check_test_data(test_case, graphql_input, expected_match, expected_gremlin, schema_based_type_equivalence_hints = {} name_format = 'temp_union_{}' name_counter = 0 - for key, value in type_equivalence_hints.iteritems(): + for key, value in six.iteritems(type_equivalence_hints): new_key = test_case.schema.get_type(key) new_value_name = name_format.format(name_counter) @@ -46,7 +47,7 @@ def check_test_data(test_case, graphql_input, expected_match, expected_gremlin, result = compile_graphql_to_gremlin(test_case.schema, graphql_input, type_equivalence_hints=schema_based_type_equivalence_hints) compare_gremlin(test_case, expected_gremlin, result.query) - test_case.assertEquals(expected_output_metadata, result.output_metadata) + test_case.assertEqual(expected_output_metadata, result.output_metadata) compare_input_metadata(test_case, expected_input_metadata, result.input_metadata) diff --git a/graphql_compiler/tests/test_graphql_pretty_print.py b/graphql_compiler/tests/test_graphql_pretty_print.py index ada34d041..5a1dcfc1d 100644 --- a/graphql_compiler/tests/test_graphql_pretty_print.py +++ b/graphql_compiler/tests/test_graphql_pretty_print.py @@ -28,9 +28,9 @@ def test_graphql_pretty_print_indentation(self): } } ''') - self.assertEquals(four_space_output, pretty_print_graphql(bad_query)) - self.assertEquals(two_space_output, - pretty_print_graphql(bad_query, use_four_spaces=False)) + self.assertEqual(four_space_output, pretty_print_graphql(bad_query)) + self.assertEqual(two_space_output, + pretty_print_graphql(bad_query, use_four_spaces=False)) def test_filter_directive_order(self): bad_query = '''{ @@ -58,7 +58,7 @@ def test_filter_directive_order(self): } ''') - self.assertEquals(expected_output, pretty_print_graphql(bad_query)) + self.assertEqual(expected_output, pretty_print_graphql(bad_query)) def test_args_not_in_schema(self): bad_query = '''{ @@ -86,7 +86,7 @@ def test_args_not_in_schema(self): } ''') - self.assertEquals(expected_output, pretty_print_graphql(bad_query)) + self.assertEqual(expected_output, pretty_print_graphql(bad_query)) def test_missing_args(self): bad_query = '''{ @@ -114,7 +114,7 @@ def test_missing_args(self): } ''') - self.assertEquals(expected_output, pretty_print_graphql(bad_query)) + self.assertEqual(expected_output, pretty_print_graphql(bad_query)) def test_other_directive(self): bad_query = '''{ @@ -142,4 +142,4 @@ def test_other_directive(self): } ''') - self.assertEquals(expected_output, pretty_print_graphql(bad_query)) + self.assertEqual(expected_output, pretty_print_graphql(bad_query)) diff --git a/graphql_compiler/tests/test_helpers.py b/graphql_compiler/tests/test_helpers.py index 4ee5bdab3..99368c357 100644 --- a/graphql_compiler/tests/test_helpers.py +++ b/graphql_compiler/tests/test_helpers.py @@ -5,6 +5,7 @@ from graphql import parse from graphql.utils.build_ast_schema import build_ast_schema +import six from ..debugging_utils import pretty_print_gremlin, pretty_print_match @@ -34,12 +35,12 @@ def compare_ir_blocks(test_case, expected_blocks, received_blocks): test_case.fail(u'Not the same number of blocks:\n\n' u'{}'.format(mismatch_message)) - for i in xrange(len(expected_blocks)): + for i in six.moves.xrange(len(expected_blocks)): expected = expected_blocks[i] received = received_blocks[i] - test_case.assertEquals(expected, received, - msg=u'Blocks at position {} were different: {} vs {}\n\n' - u'{}'.format(i, expected, received, mismatch_message)) + test_case.assertEqual(expected, received, + msg=u'Blocks at position {} were different: {} vs {}\n\n' + u'{}'.format(i, expected, received, mismatch_message)) def compare_match(test_case, expected, received, parameterized=True): @@ -61,10 +62,10 @@ def compare_gremlin(test_case, expected, received): def compare_input_metadata(test_case, expected, received): """Compare two dicts of input metadata, using proper GraphQL type comparison operators.""" # First, assert that the sets of keys in both dicts are equal. - test_case.assertEquals(set(expected.iterkeys()), set(received.iterkeys())) + test_case.assertEqual(set(six.iterkeys(expected)), set(six.iterkeys(received))) # Then, compare the values for each key in both dicts. - for key in expected.iterkeys(): + for key in six.iterkeys(expected): expected_value = expected[key] received_value = received[key] @@ -74,7 +75,7 @@ def compare_input_metadata(test_case, expected, received): def compare_ignoring_whitespace(test_case, expected, received, msg): """Compare expected and received code, ignoring whitespace, with the given failure message.""" - test_case.assertEquals(transform(expected), transform(received), msg=msg) + test_case.assertEqual(transform(expected), transform(received), msg=msg) def get_schema(): @@ -196,5 +197,5 @@ def construct_location_types(location_types_as_strings): return { location: schema.get_type(type_name) - for location, type_name in location_types_as_strings.iteritems() + for location, type_name in six.iteritems(location_types_as_strings) } diff --git a/graphql_compiler/tests/test_ir_generation.py b/graphql_compiler/tests/test_ir_generation.py index 401dc7bf2..ba8ddf0de 100644 --- a/graphql_compiler/tests/test_ir_generation.py +++ b/graphql_compiler/tests/test_ir_generation.py @@ -3,6 +3,7 @@ from graphql import GraphQLID, GraphQLList, GraphQLString import pytest +import six from ..compiler import blocks, expressions, helpers from ..compiler.compiler_frontend import OutputMetadata, graphql_to_ir @@ -17,16 +18,16 @@ def check_test_data(test_case, graphql_input, expected_blocks, graphql_to_ir(test_case.schema, graphql_input) compare_ir_blocks(test_case, expected_blocks, received_blocks) - test_case.assertEquals(expected_output_metadata, output_metadata) + test_case.assertEqual(expected_output_metadata, output_metadata) compare_input_metadata(test_case, expected_input_metadata, input_metadata) - test_case.assertEquals(expected_location_types, comparable_location_types(location_types)) + test_case.assertEqual(expected_location_types, comparable_location_types(location_types)) def comparable_location_types(location_types): - """Convert the dict of Location -> GraphQL object type into a dict of Location -> basestring.""" + """Convert the dict of Location -> GraphQL object type into a dict of Location -> string.""" return { location: graphql_type.name - for location, graphql_type in location_types.iteritems() + for location, graphql_type in six.iteritems(location_types) } diff --git a/graphql_compiler/tests/test_ir_generation_errors.py b/graphql_compiler/tests/test_ir_generation_errors.py index 7ece0d43d..8052b60dc 100644 --- a/graphql_compiler/tests/test_ir_generation_errors.py +++ b/graphql_compiler/tests/test_ir_generation_errors.py @@ -2,6 +2,8 @@ import string import unittest +import six + from ..compiler.compiler_frontend import graphql_to_ir from ..exceptions import GraphQLCompilationError, GraphQLParsingError, GraphQLValidationError from .test_helpers import get_schema @@ -462,7 +464,7 @@ def generate_args_string(num_args): if num_args >= len(variable_names): raise AssertionError('Invalid test data, too many variables to represent.') - args = (variable_names[i] for i in xrange(num_args)) + args = (variable_names[i] for i in six.moves.xrange(num_args)) array_contents = u','.join(u'"${}"'.format(x) for x in args) return u'[{}]'.format(array_contents) diff --git a/graphql_compiler/tests/test_ir_lowering.py b/graphql_compiler/tests/test_ir_lowering.py index 9f75e2009..2f4adb008 100644 --- a/graphql_compiler/tests/test_ir_lowering.py +++ b/graphql_compiler/tests/test_ir_lowering.py @@ -26,9 +26,9 @@ def check_test_data(test_case, expected_object, received_object): received_object)) if isinstance(expected_object, MatchQuery): - test_case.assertEquals(expected_object, received_object, - msg=u'\n{}\n\n!=\n\n{}'.format(pformat(expected_object), - pformat(received_object))) + test_case.assertEqual(expected_object, received_object, + msg=u'\n{}\n\n!=\n\n{}'.format(pformat(expected_object), + pformat(received_object))) else: compare_ir_blocks(test_case, expected_object, received_object) @@ -723,7 +723,7 @@ def test_optional_traversal_edge_case(self): final_query = ir_lowering_match.lower_ir(ir_blocks, location_types) - self.assertEquals( + self.assertEqual( expected_match_query, final_query, msg=u'\n{}\n\n!=\n\n{}'.format(pformat(expected_match_query), pformat(final_query))) diff --git a/graphql_compiler/tests/test_location.py b/graphql_compiler/tests/test_location.py index 9050fdd6d..4d1d2342d 100644 --- a/graphql_compiler/tests/test_location.py +++ b/graphql_compiler/tests/test_location.py @@ -7,23 +7,23 @@ class LocationTests(unittest.TestCase): def test_location_name(self): base_location = Location(('Animal',)) - self.assertEquals((u'Animal___1', None), base_location.get_location_name()) + self.assertEqual((u'Animal___1', None), base_location.get_location_name()) base_at_field = base_location.navigate_to_field(u'name') - self.assertEquals((u'Animal___1', u'name'), base_at_field.get_location_name()) + self.assertEqual((u'Animal___1', u'name'), base_at_field.get_location_name()) revisited_location = base_location.revisit() - self.assertEquals((u'Animal___2', None), revisited_location.get_location_name()) + self.assertEqual((u'Animal___2', None), revisited_location.get_location_name()) revisited_at_field = revisited_location.navigate_to_field(u'name') - self.assertEquals((u'Animal___2', u'name'), revisited_at_field.get_location_name()) + self.assertEqual((u'Animal___2', u'name'), revisited_at_field.get_location_name()) child_location = base_location.navigate_to_subpath(u'out_Animal_ParentOf') - self.assertEquals( + self.assertEqual( (u'Animal__out_Animal_ParentOf___1', None), child_location.get_location_name()) child_at_field = child_location.navigate_to_field(u'name') - self.assertEquals( + self.assertEqual( (u'Animal__out_Animal_ParentOf___1', u'name'), child_at_field.get_location_name()) diff --git a/graphql_compiler/tests/test_safe_match_and_gremlin.py b/graphql_compiler/tests/test_safe_match_and_gremlin.py index 46a100116..929dded69 100644 --- a/graphql_compiler/tests/test_safe_match_and_gremlin.py +++ b/graphql_compiler/tests/test_safe_match_and_gremlin.py @@ -5,6 +5,7 @@ import arrow from graphql import GraphQLBoolean, GraphQLFloat, GraphQLID, GraphQLInt, GraphQLList, GraphQLString import pytz +import six from ..exceptions import GraphQLInvalidArgumentError from ..query_formatting.gremlin_formatting import _safe_gremlin_argument @@ -59,21 +60,21 @@ def test_safe_match_argument_for_strings(self): u'injection: ${ -> (2 + 2 == 4)}': u'"injection: ${ -> (2 + 2 == 4)}"', } - for input_data, expected_value in test_data.iteritems(): + for input_data, expected_value in six.iteritems(test_data): unicode_string = input_data bytes_string = input_data.encode('utf-8') # String type - self.assertEquals(expected_value, _safe_match_argument(GraphQLString, unicode_string)) - self.assertEquals(expected_value, _safe_match_argument(GraphQLString, bytes_string)) + self.assertEqual(expected_value, _safe_match_argument(GraphQLString, unicode_string)) + self.assertEqual(expected_value, _safe_match_argument(GraphQLString, bytes_string)) # ID type -- IDs can be strings - self.assertEquals(expected_value, _safe_match_argument(GraphQLID, unicode_string)) - self.assertEquals(expected_value, _safe_match_argument(GraphQLID, bytes_string)) + self.assertEqual(expected_value, _safe_match_argument(GraphQLID, unicode_string)) + self.assertEqual(expected_value, _safe_match_argument(GraphQLID, bytes_string)) def test_incorrect_graphql_type_causes_errors(self): - for correct_graphql_type, value in REPRESENTATIVE_DATA_FOR_EACH_TYPE.iteritems(): - for other_graphql_type in REPRESENTATIVE_DATA_FOR_EACH_TYPE.iterkeys(): + for correct_graphql_type, value in six.iteritems(REPRESENTATIVE_DATA_FOR_EACH_TYPE): + for other_graphql_type in six.iterkeys(REPRESENTATIVE_DATA_FOR_EACH_TYPE): if correct_graphql_type.is_same_type(other_graphql_type): # No error -- GraphQL type is correct. _safe_match_argument(correct_graphql_type, value) @@ -115,21 +116,21 @@ def test_safe_gremlin_argument_for_strings(self): u'injection: ${ -> (2 + 2 == 4)}': u"'injection: ${ -> (2 + 2 == 4)}'", # noqa } - for input_data, expected_value in test_data.iteritems(): + for input_data, expected_value in six.iteritems(test_data): unicode_string = input_data bytes_string = input_data.encode('utf-8') # String type - self.assertEquals(expected_value, _safe_gremlin_argument(GraphQLString, unicode_string)) - self.assertEquals(expected_value, _safe_gremlin_argument(GraphQLString, bytes_string)) + self.assertEqual(expected_value, _safe_gremlin_argument(GraphQLString, unicode_string)) + self.assertEqual(expected_value, _safe_gremlin_argument(GraphQLString, bytes_string)) # ID type -- IDs can be strings - self.assertEquals(expected_value, _safe_gremlin_argument(GraphQLID, unicode_string)) - self.assertEquals(expected_value, _safe_gremlin_argument(GraphQLID, bytes_string)) + self.assertEqual(expected_value, _safe_gremlin_argument(GraphQLID, unicode_string)) + self.assertEqual(expected_value, _safe_gremlin_argument(GraphQLID, bytes_string)) def test_incorrect_graphql_type_causes_errors(self): - for correct_graphql_type, value in REPRESENTATIVE_DATA_FOR_EACH_TYPE.iteritems(): - for other_graphql_type in REPRESENTATIVE_DATA_FOR_EACH_TYPE.iterkeys(): + for correct_graphql_type, value in six.iteritems(REPRESENTATIVE_DATA_FOR_EACH_TYPE): + for other_graphql_type in six.iterkeys(REPRESENTATIVE_DATA_FOR_EACH_TYPE): if correct_graphql_type.is_same_type(other_graphql_type): # No error -- GraphQL type is correct. _safe_gremlin_argument(correct_graphql_type, value) @@ -143,4 +144,4 @@ def test_nested_lists_are_serialized_correctly(self): graphql_type = GraphQLList(GraphQLList(GraphQLInt)) expected_output = u'[[1,2,3],[4,5,6]]' - self.assertEquals(expected_output, _safe_gremlin_argument(graphql_type, value)) + self.assertEqual(expected_output, _safe_gremlin_argument(graphql_type, value)) diff --git a/graphql_compiler/tests/test_schema.py b/graphql_compiler/tests/test_schema.py index f560135fd..8164f53d6 100644 --- a/graphql_compiler/tests/test_schema.py +++ b/graphql_compiler/tests/test_schema.py @@ -6,6 +6,7 @@ from graphql.type import GraphQLField, GraphQLObjectType, GraphQLSchema, GraphQLString from graphql.utils.schema_printer import print_schema import pytz +import six from .. import schema from .test_helpers import get_schema @@ -35,7 +36,7 @@ def _get_directives_in_string_form(directives): test_directives = _get_directives_in_string_form(get_schema().get_directives()) actual_directives = _get_directives_in_string_form(schema.DIRECTIVES) - self.assertEquals(test_directives, actual_directives) + self.assertEqual(test_directives, actual_directives) def test_date_serialization_and_parsing(self): test_data = { @@ -44,9 +45,9 @@ def test_date_serialization_and_parsing(self): '1991-12-31': date(1991, 12, 31), } - for iso_date, date_obj in test_data.iteritems(): - self.assertEquals(iso_date, schema.GraphQLDate.serialize(date_obj)) - self.assertEquals(date_obj, schema.GraphQLDate.parse_value(iso_date)) + for iso_date, date_obj in six.iteritems(test_data): + self.assertEqual(iso_date, schema.GraphQLDate.serialize(date_obj)) + self.assertEqual(date_obj, schema.GraphQLDate.parse_value(iso_date)) def test_datetime_serialization_and_parsing(self): eastern_us_tz = pytz.timezone('US/Eastern') @@ -68,9 +69,9 @@ def test_datetime_serialization_and_parsing(self): } # Special case: a "Z" suffix == "00:00" timezone - self.assertEquals(datetime(2017, 1, 1, 0, 0, 0, tzinfo=pytz.utc), - schema.GraphQLDateTime.parse_value('2017-01-01T00:00:00Z')) + self.assertEqual(datetime(2017, 1, 1, 0, 0, 0, tzinfo=pytz.utc), + schema.GraphQLDateTime.parse_value('2017-01-01T00:00:00Z')) - for iso_datetime, datetime_obj in test_data.iteritems(): - self.assertEquals(iso_datetime, schema.GraphQLDateTime.serialize(datetime_obj)) - self.assertEquals(datetime_obj, schema.GraphQLDateTime.parse_value(iso_datetime)) + for iso_datetime, datetime_obj in six.iteritems(test_data): + self.assertEqual(iso_datetime, schema.GraphQLDateTime.serialize(datetime_obj)) + self.assertEqual(datetime_obj, schema.GraphQLDateTime.parse_value(iso_datetime)) diff --git a/requirements.txt b/requirements.txt index 95b714cbc..fb43d1f52 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,3 +2,4 @@ arrow==0.10.0 funcy==1.7.3 graphql-core==1.1 pytz==2017.2 +six==1.10.0 diff --git a/setup.py b/setup.py index 5ab17899d..f4278946f 100644 --- a/setup.py +++ b/setup.py @@ -1,7 +1,7 @@ from setuptools import find_packages, setup package_name = 'graphql-compiler' -version = '1.0.3' +version = '1.1.0' setup(name=package_name, version=version, @@ -15,7 +15,8 @@ 'arrow>=0.7.0', 'funcy>=1.6', 'graphql-core==1.1', - 'pytz>=2016.10' + 'pytz>=2016.10', + 'six>=1.10.0', ], classifiers=[ 'Development Status :: 5 - Production/Stable', @@ -25,7 +26,10 @@ 'License :: OSI Approved :: Apache Software License', 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', ], keywords='graphql database compiler orientdb', - python_requires='>=2.7, <3', + python_requires='>=2.7', )