Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prophyc Babel #20

Merged
merged 15 commits into from
Mar 13, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .bumpversion.cfg
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[bumpversion]
current_version = 1.1.2
current_version = 1.2.0

[bumpversion:file:setup.py]

Expand Down
9 changes: 6 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,11 @@ waf-*
tmp_*

# Test tools
.tox
.cache
.tox/
.cache/
.pytest_cache
.coverage*
htmlcov/
htmlcov/

# Documentation
dev_doc/
18 changes: 18 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,24 @@ forthcoming
-----------------------------------
- prophyc: Python codecs source code is pep8-compliant


1.2.0
-----------------------------------
- prophyc.generators.prophy: introduced prophy language generator
- prophyc.generators.python:
* generated source code is pep8-compliant (at least much more than it was before)
* generated imports do not use asterisk, implicitly lists imported names
* generated file contains annotation about fact of being generated by prophy
- prophyc.parsers.isar collects docstrings
- prophyc.model:
* refactor performed, shaped nodes' inheritance tree
* each node gets reproducible repr() (added renew package as dependency)
* each node gets str implementation that returns its prophy language representation
* each node may have a doc-string
* node name and docstring can be defined in unicode (special care in python2)
* Constant can be defined with numbers in hexadecimal literal format


1.1.2
-----------------------------------
- prophyc: python3 bug in C++ frontend fixed (libclang invocation contained unicode strings)
Expand Down
2 changes: 1 addition & 1 deletion MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ include *.py
include *.sh
include .coveragerc
include .bumpversion.cfg

include requirements.txt
include AUTHORS
include HOWTO
include CHANGELOG
Expand Down
4 changes: 2 additions & 2 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,9 @@
# built documents.
#
# The short X.Y version.
version = '1.1.2'
version = '1.2.0'
# The full version, including alpha/beta/rc tags.
release = '1.1.2'
release = '1.2.0'

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
Expand Down
2 changes: 1 addition & 1 deletion prophy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,4 @@
'with_metaclass',
]

__version__ = '1.1.2'
__version__ = '1.2.0'
8 changes: 4 additions & 4 deletions prophy/descriptor.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,11 @@ def evaluate_codecs(self):
codec_kind.BYTES: (encode_bytes, decode_bytes),
codec_kind.SCALAR: (encode_scalar, decode_scalar)
}
kind = codec_kind.classify(self.type)
assert kind in all_codecs
self.encode_fcn, self.decode_fcn = all_codecs.get(kind)
kind_ = codec_kind.classify(self.type)
assert kind_ in all_codecs
self.encode_fcn, self.decode_fcn = all_codecs.get(kind_)

if kind == codec_kind.OPTIONAL:
if kind_ == codec_kind.OPTIONAL:
base_kind = codec_kind.classify(self.type.__bases__[0])
opt_encode, opt_decode = all_codecs.get(base_kind)

Expand Down
25 changes: 18 additions & 7 deletions prophy/generators.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import collections

from .base_array import base_array
from .composite import codec_kind, distance_to_next_multiply, struct_packed
from .descriptor import DescriptorField
Expand Down Expand Up @@ -69,14 +71,16 @@ def _build_up_implementation(cls):
def validate(cls):
for name, number in cls._enumerators:
if not isinstance(name, str):
raise ProphyError("enum member's first argument has to be string")
msg = "enum ({}) member's first argument has to be string, got '{}'"
raise ProphyError(msg.format(cls.__name__, type(name).__name__))

if not isinstance(number, (int, long)):
raise ProphyError("enum member's second argument has to be an integer")
msg = "enum member's ({}.{}) second argument has to be an integer, got '{}'"
raise ProphyError(msg.format(cls.__name__, name, type(number).__name__))

names = set(name for name, _ in cls._enumerators)
if len(names) < len(cls._enumerators):
raise ProphyError("names overlap in '{}' enum".format(cls.__name__))
duplicates = ", ".join(_list_duplicates(name for name, _ in cls._enumerators))
if duplicates:
raise ProphyError("names overlap in '{}' enum, duplicates: {}".format(cls.__name__, duplicates))

def add_attributes(self):
def check(cls, value):
Expand Down Expand Up @@ -107,9 +111,11 @@ class struct_generator(_composite_generator_base):
def validate(cls):
for field in cls._descriptor:
if not isinstance(field.name, str):
raise ProphyError("member name must be a string type")
msg = "struct ({}) member's name must be a string type, got: '{}'"
raise ProphyError(msg.format(cls.__name__, type(field.name).__name__))
if not hasattr(field.type, "_is_prophy_object"):
raise ProphyError("member type must be a prophy object, is: {!r}".format(field.type))
msg = "struct member's ({}.{}) type must be a prophy object, is: {!r}"
raise ProphyError(msg.format(cls.__name__, field.name, field.type))

types = list(cls._types())
for type_ in types[:-1]:
Expand Down Expand Up @@ -397,3 +403,8 @@ def setter(self, new_value):
self._fields[field.name] = new_value

setattr(cls, field.name, property(getter, setter))


def _list_duplicates(iterable):
iterable = list(iterable)
return sorted((collections.Counter(iterable) - collections.Counter(set(iterable))).keys())
2 changes: 1 addition & 1 deletion prophy/scalar.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ def check(value):
if not isinstance(value, (int, long)):
raise ProphyError("not an int")
if not min_ <= value <= max_:
raise ProphyError("out of bounds")
raise ProphyError("value: {} out of {}B integer's bounds: [{}, {}]".format(value, size, min_, max_))
return value

cls._check = check
Expand Down
4 changes: 2 additions & 2 deletions prophy/tests/test_array_bound.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ def test_bound_scalar_array_assignment(BoundScalarArray):
x.value = 10
with pytest.raises(Exception, match="not an int"):
x.value[0] = "will fail type check"
with pytest.raises(Exception, match="out of bounds"):
with pytest.raises(Exception, match=r"value: -1 out of 4B integer's bounds: \[0, 4294967295\]"):
x.value[0] = -1
with pytest.raises(Exception, match="not an int"):
x.value[:] = [1, 2, "abc"]
Expand Down Expand Up @@ -109,7 +109,7 @@ class LengthFieldAfter(prophy.with_metaclass(prophy.struct_generator, prophy.str


def test_bound_scalar_array_bad_sizer_type():
msg = "member type must be a prophy object, is: 'not_an_int'"
msg = r"struct member's \(_.not_an_int\) type must be a prophy object, is: 'not_an_int'"
with pytest.raises(Exception, match=msg):
class _(prophy.with_metaclass(prophy.struct_generator, prophy.struct_packed)):
_descriptor = [("not_an_int", "not_an_int"),
Expand Down
2 changes: 1 addition & 1 deletion prophy/tests/test_array_fixed.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ def test_fixed_scalar_array_assignment(FixedScalarArray):
x.value[:] = (10,)
with pytest.raises(Exception, match="not an int"):
x.value[0] = "will fail type check"
with pytest.raises(Exception, match="out of bounds"):
with pytest.raises(Exception, match=r"value: -1 out of 4B integer's bounds: \[0, 4294967295\]"):
x.value[0] = -1

y = FixedScalarArray()
Expand Down
20 changes: 12 additions & 8 deletions prophy/tests/test_enum.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,31 +110,35 @@ class NoEnumerators(prophy.with_metaclass(prophy.enum_generator, prophy.enum)):


def test_enum_invalid_name():
msg = "enum member's first argument has to be string"
msg = r"enum \(BadNamed\) member's first argument has to be string, got 'float'"
with pytest.raises(prophy.ProphyError, match=msg):
class _(prophy.with_metaclass(prophy.enum_generator, prophy.enum)):
class BadNamed(prophy.with_metaclass(prophy.enum_generator, prophy.enum)):
_enumerators = [(3.14159, 1),
("correct_name", 2)]


def test_enum_invalid_value():
msg = "enum member's second argument has to be an integer"
msg = r"enum member's \(TheEnum.invalid_value\) second argument has to be an integer, got 'float'"
with pytest.raises(prophy.ProphyError, match=msg):
class _(prophy.with_metaclass(prophy.enum_generator, prophy.enum)):
class TheEnum(prophy.with_metaclass(prophy.enum_generator, prophy.enum)):
_enumerators = [("correct_value", 1),
("invalid_value", 3.14159)]


def test_enum_names_overlap():
msg = "names overlap in 'NamesOverlapping' enum"
msg = "names overlap in 'NamesOverlapping' enum, duplicates: OtherDuplicate, SameName"
with pytest.raises(prophy.ProphyError, match=msg):
class NamesOverlapping(prophy.with_metaclass(prophy.enum_generator, prophy.enum)):
_enumerators = [("NamesOverlapping_Overlap", 1),
("NamesOverlapping_Overlap", 2)]
_enumerators = [("SameName", 1),
("SameName", 2),
("SameName", 3),
("OtherDuplicate", 4),
("OtherDuplicate", 5),
("ValidName", 6)]


def test_enum_value_out_of_bounds():
msg = "out of bounds"
msg = r"value: 4294967296 out of 4B integer's bounds: \[0, 4294967295\]"
with pytest.raises(prophy.ProphyError, match=msg):
class _(prophy.with_metaclass(prophy.enum_generator, prophy.enum)):
_enumerators = [("OutOfBounds", 0xFFFFFFFF + 1)]
Expand Down
35 changes: 15 additions & 20 deletions prophy/tests/test_integers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,19 @@
import pytest


@pytest.mark.parametrize('IntType, min_, max_', [
(prophy.i8, -(0x80), 0x7F),
(prophy.i16, -(0x8000), 0x7FFF),
(prophy.i32, -(0x80000000), 0x7FFFFFFF),
(prophy.i64, -(0x8000000000000000), 0x7FFFFFFFFFFFFFFF),
@pytest.mark.parametrize('integer_type, min_, max_', [
(prophy.i8, -0x80, 0x7F),
(prophy.i16, -0x8000, 0x7FFF),
(prophy.i32, -0x80000000, 0x7FFFFFFF),
(prophy.i64, -0x8000000000000000, 0x7FFFFFFFFFFFFFFF),
(prophy.u8, 0, 0xFF),
(prophy.u16, 0, 0xFFFF),
(prophy.u32, 0, 0xFFFFFFFF),
(prophy.u64, 0, 0xFFFFFFFFFFFFFFFF)
])
def test_integer(IntType, min_, max_):
def test_integer(integer_type, min_, max_):
class X(prophy.with_metaclass(prophy.struct_generator, prophy.struct)):
_descriptor = [("value", IntType)]
_descriptor = [("value", integer_type)]

x = X()
assert x.value == 0
Expand All @@ -23,25 +23,22 @@ class X(prophy.with_metaclass(prophy.struct_generator, prophy.struct)):
x.value = min_
assert x.value == min_

with pytest.raises(prophy.ProphyError) as e:
with pytest.raises(prophy.ProphyError, match="not an int"):
x.value = "123"
assert "not an int" in str(e.value)

with pytest.raises(prophy.ProphyError) as e:
with pytest.raises(prophy.ProphyError, match=r"value: \d+ out of \dB integer's bounds: \[-?\d+, \d+\]"):
x.value = max_ + 1
assert "out of bounds" in str(e.value)

with pytest.raises(prophy.ProphyError) as e:
with pytest.raises(prophy.ProphyError, match=r"value: -?\d+ out of \dB integer's bounds: \[-?\d+, \d+\]"):
x.value = min_ - 1
assert "out of bounds" in str(e.value)

y = X()
y.value = 42
y.copy_from(x)
assert y.value == min_


@pytest.mark.parametrize('IntType, a, encoded_a, b, encoded_b, too_short, too_long', [
@pytest.mark.parametrize('integer_type, a, encoded_a, b, encoded_b, too_short, too_long', [
(prophy.i8,
1, b"\x01",
(-1), b"\xff",
Expand Down Expand Up @@ -83,9 +80,9 @@ class X(prophy.with_metaclass(prophy.struct_generator, prophy.struct)):
b"\xff\xff\xff\xff\xff\xff\xff",
b"\xff\xff\xff\xff\xff\xff\xff\xff\xff")
])
def test_integer_codec(IntType, a, encoded_a, b, encoded_b, too_short, too_long):
def test_integer_codec(integer_type, a, encoded_a, b, encoded_b, too_short, too_long):
class X(prophy.with_metaclass(prophy.struct_generator, prophy.struct)):
_descriptor = [("value", IntType)]
_descriptor = [("value", integer_type)]

x = X()
x.value = 8
Expand All @@ -101,10 +98,8 @@ class X(prophy.with_metaclass(prophy.struct_generator, prophy.struct)):
x.decode(encoded_b, ">")
assert x.value == b

with pytest.raises(prophy.ProphyError) as e:
with pytest.raises(prophy.ProphyError, match="X: too few bytes to decode integer"):
x.decode(too_short, ">")
assert "too few bytes to decode integer" in str(e.value)

with pytest.raises(prophy.ProphyError) as e:
with pytest.raises(prophy.ProphyError, match="not all bytes of X read"):
x.decode(too_long, ">")
assert "not all bytes of {} read".format(X.__name__) in str(e.value)
2 changes: 1 addition & 1 deletion prophy/tests/test_struct.py
Original file line number Diff line number Diff line change
Expand Up @@ -424,7 +424,7 @@ class X(prophy.with_metaclass(prophy.struct_generator, prophy.struct)):


def test_bad_struct_member_name():
with pytest.raises(prophy.ProphyError, match="member name must be a string type"):
with pytest.raises(prophy.ProphyError, match=r"struct \(X\) member's name must be a string type, got: 'float'"):
class X(prophy.with_metaclass(prophy.struct_generator, prophy.struct)):
_descriptor = [(3.14159, prophy.u32)]

Expand Down
2 changes: 1 addition & 1 deletion prophy/tests/test_version.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
def test_version_number():
import prophy
assert prophy.__version__ == '1.1.2'
assert prophy.__version__ == '1.2.0'
2 changes: 1 addition & 1 deletion prophy_cpp/wscript
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ from waflib.Task import Task
from waflib.Tools import waf_unit_test

APPNAME = 'prophy-cpp'
VERSION = '1.1.2'
VERSION = '1.2.0'

def options(ctx):
ctx.load('compiler_cxx python waf_unit_test')
Expand Down
Loading