diff --git a/setup.py b/setup.py index 1eae1d1..26b8667 100644 --- a/setup.py +++ b/setup.py @@ -27,7 +27,7 @@ packages=['stencila.pyla'], install_requires=[ 'astor==0.8.0', - 'stencila-schema==0.29.0' + 'stencila-schema==0.37.2' ], extras_require={}, include_package_data=True, diff --git a/stencila/pyla/code_parsing.py b/stencila/pyla/code_parsing.py index 4fed740..6467f6b 100644 --- a/stencila/pyla/code_parsing.py +++ b/stencila/pyla/code_parsing.py @@ -6,8 +6,9 @@ import traceback import typing -from stencila.schema.types import SoftwareSourceCode, Function, Variable, CodeError, CodeChunk, SchemaTypes, \ - BooleanSchema, StringSchema, IntegerSchema, NumberSchema, ArraySchema, TupleSchema, CodeExpression, Parameter +from stencila.schema.types import SoftwareSourceCode, Function, Variable, CodeError, CodeChunk, ValidatorTypes, \ + BooleanValidator, StringValidator, IntegerValidator, NumberValidator, ArrayValidator, TupleValidator, \ + CodeExpression, Parameter ImportsType = typing.List[typing.Union[str, SoftwareSourceCode]] OptionalStringList = typing.Optional[typing.List[str]] @@ -158,18 +159,18 @@ class CodeChunkExecution(typing.NamedTuple): parse_result: CodeChunkParseResult -def annotation_name_to_schema(name: typing.Optional[str]) -> typing.Optional[SchemaTypes]: - """Parse a Python annotation string (basically a type name) and convert to a `Schema` type.""" +def annotation_name_to_validator(name: typing.Optional[str]) -> typing.Optional[ValidatorTypes]: + """Parse a Python annotation string (basically a type name) and convert to a `Validator` type.""" if name is None: return None return { - 'bool': BooleanSchema(), - 'str': StringSchema(), - 'int': IntegerSchema(), - 'float': NumberSchema(), - 'list': ArraySchema(), - 'tuple': TupleSchema() + 'bool': BooleanValidator(), + 'str': StringValidator(), + 'int': IntegerValidator(), + 'float': NumberValidator(), + 'list': ArrayValidator(), + 'tuple': TupleValidator() }.get(name) @@ -219,8 +220,11 @@ def parse_open_filename(open_call: ast.Call) -> typing.Optional[str]: def exception_to_code_error(exception: Exception) -> CodeError: """Convert an `Exception` to a `CodeError` entity.""" - return CodeError(type(exception).__name__, message=str(exception), trace=traceback.format_exc()) - + return CodeError( + errorType=type(exception).__name__, + errorMessage=str(exception), + stackTrace=traceback.format_exc() + ) def set_code_error(code: typing.Union[CodeChunk, CodeExpression], error: typing.Union[Exception, CodeError]) -> None: @@ -282,14 +286,14 @@ def add_variable(self, name: str, type_annotation: typing.Optional[str]) -> None """ Store a variable declaration. - Parses the `type_annotation` (if set) and transforms to a Schema subclass. + Parses the `type_annotation` (if set) and transforms to a Validator subclass. """ if name in self.seen_vars: return self.seen_vars.append(name) self.declares.append( - Variable(name, schema=annotation_name_to_schema(type_annotation)) + Variable(name, validator=annotation_name_to_validator(type_annotation)) ) def add_name(self, name: str, target: typing.List) -> None: @@ -567,13 +571,13 @@ def _parse_function_def(self, statement: ast.FunctionDef) -> None: return_ann = statement.returns.id if isinstance(statement.returns, ast.Name) else None - func = Function(statement.name, returns=annotation_name_to_schema(return_ann), parameters=[]) + func = Function(name=statement.name, returns=annotation_name_to_validator(return_ann), parameters=[]) for i, arg in enumerate(statement.args.args): param = Parameter(arg.arg) if arg.annotation and hasattr(arg.annotation, 'id'): - param.schema = annotation_name_to_schema(arg.annotation.id) # type: ignore + param.validator = annotation_name_to_validator(arg.annotation.id) # type: ignore default_index = len(statement.args.defaults) - len(statement.args.args) + i # Only the last len(statement.args.defaults) can have defaults (since they must come after non-default @@ -597,10 +601,10 @@ def _parse_function_def(self, statement: ast.FunctionDef) -> None: func.parameters.append(param) if statement.args.vararg: - func.parameters.append(Parameter(statement.args.vararg.arg, required=False, repeats=True)) + func.parameters.append(Parameter(name=statement.args.vararg.arg, required=False, repeats=True)) if statement.args.kwarg: - func.parameters.append(Parameter(statement.args.kwarg.arg, required=False, extends=True)) + func.parameters.append(Parameter(name=statement.args.kwarg.arg, required=False, extends=True)) self.seen_vars.append(func.name) self.declares.append(func) diff --git a/stencila/pyla/interpreter.py b/stencila/pyla/interpreter.py index b66e10e..a519f3a 100644 --- a/stencila/pyla/interpreter.py +++ b/stencila/pyla/interpreter.py @@ -23,8 +23,8 @@ import ast import astor from stencila.schema.types import Node, Parameter, CodeChunk, Article, Entity, CodeExpression, \ - ConstantSchema, EnumSchema, BooleanSchema, NumberSchema, IntegerSchema, StringSchema, \ - ArraySchema, TupleSchema, ImageObject, Datatable, DatatableColumn, Function + ConstantValidator, EnumValidator, BooleanValidator, NumberValidator, IntegerValidator, StringValidator, \ + ArrayValidator, TupleValidator, ImageObject, Datatable, DatatableColumn, Function from stencila.schema.util import from_json, to_json from .errors import CapabilityError @@ -461,21 +461,21 @@ def decode_dataframe(data_frame: DataFrame) -> Datatable: column = data_frame[column_name] values = column.tolist() if column.dtype in (numpy.bool_, numpy.bool8): - schema = BooleanSchema() + validator = BooleanValidator() values = [bool(row) for row in values] elif column.dtype in (numpy.int8, numpy.int16, numpy.int32, numpy.int64): - schema = IntegerSchema() + validator = IntegerValidator() values = [int(row) for row in values] elif column.dtype in (numpy.float16, numpy.float32, numpy.float64): - schema = NumberSchema() + validator = NumberValidator() values = [float(row) for row in values] elif column.dtype in (numpy.str_, numpy.unicode_,): - schema = StringSchema() + validator = StringValidator() else: - schema = None + validator = None columns.append( - DatatableColumn(column_name, values, schema=ArraySchema(items=schema)) + DatatableColumn(column_name, values, validator=ArrayValidator(items=validator)) ) return Datatable(columns) @@ -533,7 +533,7 @@ def parse_cli_args(self, cli_args: typing.List[str]) -> None: param_parser = argparse.ArgumentParser(description='Parse Parameters') for param in self.parameters.values(): - if not isinstance(param.schema, ConstantSchema): + if not isinstance(param.validator, ConstantValidator): param_parser.add_argument('--' + param.name, dest=param.name, required=param.required) args, _ = param_parser.parse_known_args(cli_args) @@ -550,25 +550,25 @@ def parse_cli_args(self, cli_args: typing.List[str]) -> None: @staticmethod def deserialize_parameter(parameter: Parameter, value: typing.Any) -> typing.Any: """Convert a value (usually a string) into the type specified by the parameter's schema.""" - if isinstance(parameter.schema, ConstantSchema): - # A ConstantSchema doesn't take a value it stores its own value. - return parameter.schema.value + if isinstance(parameter.validator, ConstantValidator): + # A ConstantValidator doesn't take a value it stores its own value. + return parameter.validator.value - if isinstance(parameter.schema, EnumSchema): - if value not in parameter.schema.values: + if isinstance(parameter.validator, EnumValidator): + if value not in parameter.validator.values: raise TypeError('{} not found in enum values for {}'.format(value, parameter.name)) return value - if isinstance(parameter.schema, BooleanSchema): + if isinstance(parameter.validator, BooleanValidator): return value.lower() in ('true', 'yes', '1', 't') conversion_function: typing.Optional[typing.Callable] = None - if isinstance(parameter.schema, IntegerSchema): + if isinstance(parameter.validator, IntegerValidator): conversion_function = int - elif isinstance(parameter.schema, NumberSchema): + elif isinstance(parameter.validator, NumberValidator): conversion_function = float # If there are problems with inaccuracy consider using Decimal instead - elif isinstance(parameter.schema, (ArraySchema, TupleSchema)): + elif isinstance(parameter.validator, (ArrayValidator, TupleValidator)): conversion_function = json.loads return conversion_function(value) if conversion_function else value diff --git a/tests/test_code_parsing.py b/tests/test_code_parsing.py index 7640c1f..58aaa5e 100644 --- a/tests/test_code_parsing.py +++ b/tests/test_code_parsing.py @@ -1,8 +1,8 @@ import typing -from stencila.schema.types import Variable, IntegerSchema, CodeChunk, Function, Parameter, SchemaTypes, StringSchema, \ - BooleanSchema, NumberSchema, ArraySchema, TupleSchema +from stencila.schema.types import Variable, IntegerValidator, CodeChunk, Function, Parameter, ValidatorTypes, StringValidator, \ + BooleanValidator, NumberValidator, ArrayValidator, TupleValidator -from stencila.pyla.code_parsing import CodeChunkParseResult, annotation_name_to_schema, CodeChunkParser +from stencila.pyla.code_parsing import CodeChunkParseResult, annotation_name_to_validator, CodeChunkParser ASSIGNMENT_CODE = """ # this code assigns variables @@ -41,10 +41,11 @@ def annotated_types(h: int, j: str = 'bar') -> bool: def named_constants(t = True, f = False, n = None): return False - + + def function_defaults(v = somefunc()): return 0 - + def basic(): # don't add it twice return 2 """ @@ -206,12 +207,12 @@ def check_result_fields_empty(result: CodeChunkParseResult, non_empty_fields: ty def check_parameter(p: Parameter, name: str, required: bool, default: typing.Any, - schema: typing.Optional[typing.Type[SchemaTypes]]) -> None: + validator: typing.Optional[typing.Type[ValidatorTypes]]) -> None: assert p.name == name assert p.required == required assert p.default == default - if schema is not None: - assert isinstance(p.schema, schema) + if validator is not None: + assert isinstance(p.validator, validator) def test_variable_parsing() -> None: @@ -229,7 +230,7 @@ def test_variable_parsing() -> None: assert len(parse_result.declares) == 2 assert type(parse_result.declares[0]) == Variable assert parse_result.declares[0].name == 'c' - assert type(parse_result.declares[0].schema) == IntegerSchema + assert type(parse_result.declares[0].validator) == IntegerValidator assert type(parse_result.declares[1]) == Function # The correctness of parsing the function is tested elsewhere @@ -278,9 +279,9 @@ def test_function_def_parsing(): assert annotated_types.name == 'annotated_types' assert len(annotated_types.parameters) == 2 - assert isinstance(annotated_types.returns, BooleanSchema) - check_parameter(annotated_types.parameters[0], 'h', True, None, IntegerSchema) - check_parameter(annotated_types.parameters[1], 'j', False, 'bar', StringSchema) + assert isinstance(annotated_types.returns, BooleanValidator) + check_parameter(annotated_types.parameters[0], 'h', True, None, IntegerValidator) + check_parameter(annotated_types.parameters[1], 'j', False, 'bar', StringValidator) assert named_constants.name == 'named_constants' assert len(named_constants.parameters) == 3 @@ -309,8 +310,8 @@ def test_uses_parsing(): def test_parsing_error(): parse_result = parse_code('this is invalid python++ code') - assert parse_result.error.kind == 'SyntaxError' - assert parse_result.error.message == 'invalid syntax (, line 1)' + assert parse_result.error.errorType == 'SyntaxError' + assert parse_result.error.errorMessage == 'invalid syntax (, line 1)' def test_reads_parsing(): @@ -393,13 +394,13 @@ def test_except_parsing(): def test_annotation_parsing(): - assert annotation_name_to_schema(None) is None - assert isinstance(annotation_name_to_schema('bool'), BooleanSchema) - assert isinstance(annotation_name_to_schema('str'), StringSchema) - assert isinstance(annotation_name_to_schema('int'), IntegerSchema) - assert isinstance(annotation_name_to_schema('float'), NumberSchema) - assert isinstance(annotation_name_to_schema('list'), ArraySchema) - assert isinstance(annotation_name_to_schema('tuple'), TupleSchema) + assert annotation_name_to_validator(None) is None + assert isinstance(annotation_name_to_validator('bool'), BooleanValidator) + assert isinstance(annotation_name_to_validator('str'), StringValidator) + assert isinstance(annotation_name_to_validator('int'), IntegerValidator) + assert isinstance(annotation_name_to_validator('float'), NumberValidator) + assert isinstance(annotation_name_to_validator('list'), ArrayValidator) + assert isinstance(annotation_name_to_validator('tuple'), TupleValidator) def test_lambda_parsing(): diff --git a/tests/test_document_compiler.py b/tests/test_document_compiler.py index 685c8e3..1964b09 100644 --- a/tests/test_document_compiler.py +++ b/tests/test_document_compiler.py @@ -22,8 +22,8 @@ def test_compile_article(): { 'type': 'Parameter', 'name': 'parameter_one', - 'schema': { - 'type': 'IntegerSchema' + 'validator': { + 'type': 'IntegerValidator' } }, { diff --git a/tests/test_interpreter.py b/tests/test_interpreter.py index 1e067ba..3980396 100644 --- a/tests/test_interpreter.py +++ b/tests/test_interpreter.py @@ -28,9 +28,9 @@ def test_catch_code_expression_error(): ce = CodeExpression('1 / 0') Interpreter().execute(ce) assert ce.output is None - assert ce.errors[0].kind == 'ZeroDivisionError' - assert ce.errors[0].message == 'division by zero' - assert ce.errors[0].trace is not None + assert ce.errors[0].errorType == 'ZeroDivisionError' + assert ce.errors[0].errorMessage == 'division by zero' + assert ce.errors[0].stackTrace is not None @unittest.mock.patch('stencila.pyla.interpreter.LOGGER') @@ -78,7 +78,7 @@ def test_code_chunk_exception_capture(): interpreter.execute(cc) assert cc1.outputs == [7, 'Goodbye world!\n'] - assert cc1.errors[0].kind == 'NameError' + assert cc1.errors[0].errorType == 'NameError' assert cc2.outputs == [4, 'CodeChunk2\n'] diff --git a/tests/test_parameter_parser.py b/tests/test_parameter_parser.py index 853bc8c..9d13898 100644 --- a/tests/test_parameter_parser.py +++ b/tests/test_parameter_parser.py @@ -2,8 +2,8 @@ import pytest import unittest.mock -from stencila.schema.types import Parameter, ConstantSchema, EnumSchema, BooleanSchema, IntegerSchema, NumberSchema, \ - StringSchema, ArraySchema, TupleSchema +from stencila.schema.types import Parameter, ConstantValidator, EnumValidator, BooleanValidator, IntegerValidator, NumberValidator, \ + StringValidator, ArrayValidator, TupleValidator from stencila.pyla.interpreter import ParameterParser @@ -11,32 +11,32 @@ def test_parameter_deserializer(): pp = ParameterParser([]) - # Constant schemas should always return the value from the schema - assert pp.deserialize_parameter(Parameter('p', schema=ConstantSchema(value='abc123')), 'some string') == 'abc123' - assert pp.deserialize_parameter(Parameter('p', schema=ConstantSchema(value='abc123')), None) == 'abc123' + # Constant validators should always return the value from the validator + assert pp.deserialize_parameter(Parameter('p', validator=ConstantValidator(value='abc123')), 'some string') == 'abc123' + assert pp.deserialize_parameter(Parameter('p', validator=ConstantValidator(value='abc123')), None) == 'abc123' - assert pp.deserialize_parameter(Parameter('p', schema=EnumSchema(values=['a', 'b'])), 'a') == 'a' + assert pp.deserialize_parameter(Parameter('p', validator=EnumValidator(values=['a', 'b'])), 'a') == 'a' with pytest.raises(TypeError): # value must be in the enum - pp.deserialize_parameter(Parameter('p', schema=EnumSchema(values=['a', 'b'])), 'c') + pp.deserialize_parameter(Parameter('p', validator=EnumValidator(values=['a', 'b'])), 'c') for t in ['TRUE', 'true', 't', 'T', '1', 'YES', 'yes']: - assert pp.deserialize_parameter(Parameter('p', schema=BooleanSchema()), t) is True + assert pp.deserialize_parameter(Parameter('p', validator=BooleanValidator()), t) is True for f in ['false', '0', 'no', '', 'anything else']: - assert pp.deserialize_parameter(Parameter('p', schema=BooleanSchema()), f) is False + assert pp.deserialize_parameter(Parameter('p', validator=BooleanValidator()), f) is False - assert pp.deserialize_parameter(Parameter('p', schema=IntegerSchema()), '1000') == 1000 + assert pp.deserialize_parameter(Parameter('p', validator=IntegerValidator()), '1000') == 1000 # Not pi. But close. - assert pp.deserialize_parameter(Parameter('p', schema=NumberSchema()), '3.1418') == 3.1418 + assert pp.deserialize_parameter(Parameter('p', validator=NumberValidator()), '3.1418') == 3.1418 - assert pp.deserialize_parameter(Parameter('p', schema=StringSchema()), '3.1418') == '3.1418' + assert pp.deserialize_parameter(Parameter('p', validator=StringValidator()), '3.1418') == '3.1418' - assert pp.deserialize_parameter(Parameter('p', schema=ArraySchema()), '[5, 6, 7]') == [5, 6, 7] + assert pp.deserialize_parameter(Parameter('p', validator=ArrayValidator()), '[5, 6, 7]') == [5, 6, 7] - assert pp.deserialize_parameter(Parameter('p', schema=TupleSchema()), '[1, true, "Up"]') == [1, True, 'Up'] + assert pp.deserialize_parameter(Parameter('p', validator=TupleValidator()), '[1, true, "Up"]') == [1, True, 'Up'] # default to String assert pp.deserialize_parameter(Parameter('p'), '321bca') == '321bca' @@ -72,7 +72,7 @@ def test_parameter_cli_parsing(): Parameter('bar', required=True), Parameter('baz', required=False), Parameter('rex', required=False, default='rex_default'), - Parameter('quz', required=False, schema=IntegerSchema()), + Parameter('quz', required=False, validator=IntegerValidator()), ]) pp.parse_cli_args(['--foo', 'fooval', '--bar', 'barval', '--quz', '1024', '--invalid', 'not a real param'])