Skip to content
This repository has been archived by the owner on Aug 31, 2021. It is now read-only.

Commit

Permalink
fix(Dependencies): Upgrade to stencila-schema 0.37.2
Browse files Browse the repository at this point in the history
  • Loading branch information
Nokome Bentley committed Feb 9, 2020
1 parent 9bc193b commit a3ef225
Show file tree
Hide file tree
Showing 7 changed files with 84 additions and 79 deletions.
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
40 changes: 22 additions & 18 deletions stencila/pyla/code_parsing.py
Original file line number Diff line number Diff line change
Expand Up @@ -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]]
Expand Down Expand Up @@ -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)


Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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
Expand All @@ -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)
Expand Down
36 changes: 18 additions & 18 deletions stencila/pyla/interpreter.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand All @@ -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
Expand Down
43 changes: 22 additions & 21 deletions tests/test_code_parsing.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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
"""
Expand Down Expand Up @@ -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:
Expand All @@ -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

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 (<unknown>, line 1)'
assert parse_result.error.errorType == 'SyntaxError'
assert parse_result.error.errorMessage == 'invalid syntax (<unknown>, line 1)'


def test_reads_parsing():
Expand Down Expand Up @@ -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():
Expand Down
4 changes: 2 additions & 2 deletions tests/test_document_compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ def test_compile_article():
{
'type': 'Parameter',
'name': 'parameter_one',
'schema': {
'type': 'IntegerSchema'
'validator': {
'type': 'IntegerValidator'
}
},
{
Expand Down
8 changes: 4 additions & 4 deletions tests/test_interpreter.py
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down Expand Up @@ -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']

Expand Down
30 changes: 15 additions & 15 deletions tests/test_parameter_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,41 +2,41 @@

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


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'
Expand Down Expand Up @@ -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'])
Expand Down

0 comments on commit a3ef225

Please sign in to comment.