Skip to content
Open
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
89 changes: 89 additions & 0 deletions tests/test_lineno.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import pytest

from thriftpy2.parser import load


def test_struct_field_linenos():
"""Test that struct fields have correct lineno information."""
thrift = load('parser-cases/structs.thrift')

assert hasattr(thrift.Person, '_field_linenos')
assert thrift.Person._field_linenos['name'] == 2
assert thrift.Person._field_linenos['address'] == 3

assert hasattr(thrift.Email, '_field_linenos')
assert thrift.Email._field_linenos['subject'] == 11
assert thrift.Email._field_linenos['content'] == 12
assert thrift.Email._field_linenos['sender'] == 13
assert thrift.Email._field_linenos['recver'] == 14
assert thrift.Email._field_linenos['metadata'] == 15

assert hasattr(thrift.Dog, '_field_linenos')
assert thrift.Dog._field_linenos['name'] == 19
assert thrift.Dog._field_linenos['age'] == 20
assert thrift.Dog._field_linenos['nickname'] == 21


def test_enum_field_linenos():
"""Test that enum values have correct lineno information."""
thrift = load('parser-cases/enums.thrift')

assert hasattr(thrift.Lang, '_field_linenos')
assert thrift.Lang._field_linenos['C'] == 2
assert thrift.Lang._field_linenos['Go'] == 3
assert thrift.Lang._field_linenos['Java'] == 4
assert thrift.Lang._field_linenos['Javascript'] == 5
assert thrift.Lang._field_linenos['PHP'] == 6
assert thrift.Lang._field_linenos['Python'] == 7
assert thrift.Lang._field_linenos['Ruby'] == 8

assert hasattr(thrift.Country, '_field_linenos')
assert thrift.Country._field_linenos['US'] == 13
assert thrift.Country._field_linenos['UK'] == 14
assert thrift.Country._field_linenos['CN'] == 15

assert hasattr(thrift.OS, '_field_linenos')
assert thrift.OS._field_linenos['OSX'] == 20
assert thrift.OS._field_linenos['Win'] == 21
assert thrift.OS._field_linenos['Linux'] == 22


def test_service_function_linenos():
"""Test that service functions have correct lineno information."""
thrift = load('parser-cases/service.thrift')

assert hasattr(thrift.EmailService, '_field_linenos')
assert thrift.EmailService._field_linenos['ping'] == 17
assert thrift.EmailService._field_linenos['send'] == 19
assert thrift.EmailService._field_linenos['receive'] == 22
assert thrift.EmailService._field_linenos['empty'] == 23


def test_type_linenos():
"""Test that types have correct lineno information."""
thrift = load('parser-cases/service.thrift')

assert hasattr(thrift.User, '__thrift_lineno__')
assert thrift.User.__thrift_lineno__ == 1
assert hasattr(thrift.User, '__thrift_file__')

assert hasattr(thrift.NetworkError, '__thrift_lineno__')
assert thrift.NetworkError.__thrift_lineno__ == 11
assert hasattr(thrift.NetworkError, '__thrift_file__')

assert hasattr(thrift.EmailService, '__thrift_lineno__')
assert thrift.EmailService.__thrift_lineno__ == 16
assert hasattr(thrift.EmailService, '__thrift_file__')


def test_include_file_path():
"""Test that included types have correct file path information."""
thrift = load('parser-cases/include.thrift', include_dirs=[
'./parser-cases'], module_name='include_thrift')

assert hasattr(thrift, '__thrift_file__')
assert 'include.thrift' in thrift.__thrift_file__

included_thrift = getattr(thrift, 'included')
assert hasattr(included_thrift, '__thrift_file__')
assert 'included.thrift' in included_thrift.__thrift_file__
61 changes: 41 additions & 20 deletions thriftpy2/parser/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ def p_typedef(p):

def p_enum(p): # noqa
'''enum : ENUM IDENTIFIER '{' enum_seq '}' type_annotations'''
val = _make_enum(p[2], p[4])
val = _make_enum(p[2], p[4], lineno=p.lineno(2))
setattr(threadlocal.thrift_stack[-1], p[2], val)
_add_thrift_meta('enums', val)

Expand All @@ -230,9 +230,9 @@ def p_enum_item(p):
| IDENTIFIER type_annotations
|'''
if len(p) == 5:
p[0] = [p[1], p[3]]
p[0] = [p[1], p[3], p.lineno(1)]
elif len(p) == 3:
p[0] = [p[1], None]
p[0] = [p[1], None, p.lineno(1)]


def p_struct(p):
Expand All @@ -243,7 +243,7 @@ def p_struct(p):

def p_seen_struct(p):
'''seen_struct : STRUCT IDENTIFIER '''
val = _make_empty_struct(p[2])
val = _make_empty_struct(p[2], lineno=p.lineno(2))
setattr(threadlocal.thrift_stack[-1], p[2], val)
p[0] = val

Expand All @@ -256,14 +256,14 @@ def p_union(p):

def p_seen_union(p):
'''seen_union : UNION IDENTIFIER '''
val = _make_empty_struct(p[2])
val = _make_empty_struct(p[2], lineno=p.lineno(2))
setattr(threadlocal.thrift_stack[-1], p[2], val)
p[0] = val


def p_exception(p):
'''exception : EXCEPTION IDENTIFIER '{' field_seq '}' type_annotations '''
val = _make_struct(p[2], p[4], base_cls=TException)
val = _make_struct(p[2], p[4], base_cls=TException, lineno=p.lineno(2))
setattr(threadlocal.thrift_stack[-1], p[2], val)
_add_thrift_meta('exceptions', val)

Expand All @@ -290,7 +290,7 @@ def p_simple_service(p):
else:
extends = None

val = _make_service(p[2], p[len(p) - 2], extends)
val = _make_service(p[2], p[len(p) - 2], extends, lineno=p.lineno(2))
setattr(thrift, p[2], val)
_add_thrift_meta('services', val)

Expand Down Expand Up @@ -318,7 +318,7 @@ def p_simple_function(p):
else:
throws = p[len(p) - 1]

p[0] = [oneway, p[base + 1], p[base + 2], p[base + 4], throws]
p[0] = [oneway, p[base + 1], p[base + 2], p[base + 4], throws, p.lineno(base + 2)]


def p_function(p):
Expand Down Expand Up @@ -370,7 +370,7 @@ def p_simple_field(p):
else:
val = None

p[0] = [p[1], p[2], p[3], p[4], val]
p[0] = [p[1], p[2], p[3], p[4], val, p.lineno(4)]


def p_field(p):
Expand Down Expand Up @@ -860,16 +860,21 @@ def __cast_struct(v):
return __cast_struct


def _make_enum(name, kvs):
def _make_enum(name, kvs, lineno=None):
thrift = threadlocal.thrift_stack[-1]
attrs = {
'__module__': threadlocal.thrift_stack[-1].__name__,
'_ttype': TType.I32
'__module__': thrift.__name__,
'_ttype': TType.I32,
'__thrift_lineno__': lineno,
'__thrift_file__': getattr(thrift, '__thrift_file__', None)
}
cls = type(name, (object, ), attrs)

_values_to_names = {}
_names_to_values = {}

_field_linenos = {}

if kvs:
val = kvs[0][1]
if val is None:
Expand All @@ -878,19 +883,24 @@ def _make_enum(name, kvs):
if item[1] is None:
item[1] = val + 1
val = item[1]
for key, val in kvs:
for key, val, field_lineno in kvs:
setattr(cls, key, val)
_values_to_names[val] = key
_names_to_values[key] = val
_field_linenos[key] = field_lineno
setattr(cls, '_VALUES_TO_NAMES', _values_to_names)
setattr(cls, '_NAMES_TO_VALUES', _names_to_values)
setattr(cls, '_field_linenos', _field_linenos)
return cls


def _make_empty_struct(name, ttype=TType.STRUCT, base_cls=TPayload):
def _make_empty_struct(name, ttype=TType.STRUCT, base_cls=TPayload, lineno=None):
thrift = threadlocal.thrift_stack[-1]
attrs = {
'__module__': threadlocal.thrift_stack[-1].__name__,
'_ttype': ttype
'__module__': thrift.__name__,
'_ttype': ttype,
'__thrift_lineno__': lineno,
'__thrift_file__': getattr(thrift, '__thrift_file__', None)
}
return type(name, (base_cls, ), attrs)

Expand All @@ -899,6 +909,7 @@ def _fill_in_struct(cls, fields, _gen_init=True):
thrift_spec = {}
default_spec = []
_tspec = {}
_field_linenos = {}

for field in fields:
if field[0] in thrift_spec or field[3] in _tspec:
Expand All @@ -909,27 +920,35 @@ def _fill_in_struct(cls, fields, _gen_init=True):
thrift_spec[field[0]] = _ttype_spec(ttype, field[3], field[1])
default_spec.append((field[3], field[4]))
_tspec[field[3]] = field[1], ttype
_field_linenos[field[3]] = field[5] # lineno
setattr(cls, 'thrift_spec', thrift_spec)
setattr(cls, 'default_spec', default_spec)
setattr(cls, '_tspec', _tspec)
setattr(cls, '_field_linenos', _field_linenos)
if _gen_init:
gen_init(cls, thrift_spec, default_spec)
return cls


def _make_struct(name, fields, ttype=TType.STRUCT, base_cls=TPayload,
_gen_init=True):
cls = _make_empty_struct(name, ttype=ttype, base_cls=base_cls)
_gen_init=True, lineno=None):
cls = _make_empty_struct(name, ttype=ttype, base_cls=base_cls, lineno=lineno)
return _fill_in_struct(cls, fields, _gen_init=_gen_init)


def _make_service(name, funcs, extends):
def _make_service(name, funcs, extends, lineno=None):
if extends is None:
extends = object

attrs = {'__module__': threadlocal.thrift_stack[-1].__name__}
thrift = threadlocal.thrift_stack[-1]
attrs = {
'__module__': thrift.__name__,
'__thrift_lineno__': lineno,
'__thrift_file__': getattr(thrift, '__thrift_file__', None)
}
cls = type(name, (extends, ), attrs)
thrift_services = []
_field_linenos = {}

for func in funcs:
func_name = func[2]
Expand All @@ -956,9 +975,11 @@ def _make_service(name, funcs, extends):
gen_init(result_cls, result_cls.thrift_spec, result_cls.default_spec)
setattr(cls, result_name, result_cls)
thrift_services.append(func_name)
_field_linenos[func_name] = func[5] # function lineno
if extends is not None and hasattr(extends, 'thrift_services'):
thrift_services.extend(extends.thrift_services)
setattr(cls, 'thrift_services', thrift_services)
setattr(cls, '_field_linenos', _field_linenos)
return cls


Expand Down
Loading