def test_validate_bad_field(self): badch = tuple(c for c in string.punctuation + string.digits) notnames = ( '1a', 'a.b', 'a-b', '&a', 'a++', ) + badch tests = [ ('filename', ()), # Any non-empty str is okay. ('funcname', notnames), ('name', notnames), ] seen = set() for field, invalid in tests: for value in invalid: seen.add(value) with self.subTest(f'{field}={value!r}'): id = ID(**self.VALID_KWARGS) id = id._replace(**{field: value}) with self.assertRaises(ValueError): id.validate() for field, invalid in tests: valid = seen - set(invalid) for value in valid: with self.subTest(f'{field}={value!r}'): id = ID(**self.VALID_KWARGS) id = id._replace(**{field: value}) id.validate() # This does not fail.
def test_typical(self): lines = textwrap.dedent(''' filename funcname name kind declaration file1.c - var1 variable static int file1.c func1 local1 variable static int file1.c - var2 variable int file1.c func2 local2 variable char * file2.c - var1 variable char * ''').strip().splitlines() lines = [re.sub(r'\s+', '\t', line, 4) for line in lines] self._return_read_tsv = [ tuple(v.strip() for v in line.split('\t')) for line in lines[1:] ] known = list(read_file('known.tsv', _read_tsv=self._read_tsv)) self.assertEqual(known, [ ('variable', ID('file1.c', '', 'var1'), 'static int'), ('variable', ID('file1.c', 'func1', 'local1'), 'static int'), ('variable', ID('file1.c', '', 'var2'), 'int'), ('variable', ID('file1.c', 'func2', 'local2'), 'char *'), ('variable', ID('file2.c', '', 'var1'), 'char *'), ]) self.assertEqual(self.calls, [ ('_read_tsv', ('known.tsv', 'filename\tfuncname\tname\tkind\tdeclaration')), ])
def test_typical(self): lines = textwrap.dedent(''' filename funcname name kind reason file1.c - var1 variable ... file1.c func1 local1 variable | file1.c - var2 variable ??? file1.c func2 local2 variable | file2.c - var1 variable reasons ''').strip().splitlines() lines = [ re.sub(r'\s{1,8}', '\t', line, 4).replace('|', '') for line in lines ] self._return_read_tsv = [ tuple(v.strip() for v in line.split('\t')) for line in lines[1:] ] ignored = ignored_from_file('spam.c', _read_tsv=self._read_tsv) self.assertEqual( ignored, { 'variables': { ID('file1.c', '', 'var1'): '...', ID('file1.c', 'func1', 'local1'): '', ID('file1.c', '', 'var2'): '???', ID('file1.c', 'func2', 'local2'): '', ID('file2.c', '', 'var1'): 'reasons', }, }) self.assertEqual(self.calls, [ ('_read_tsv', ('spam.c', 'filename\tfuncname\tname\tkind\treason')), ])
def test_validate_typical(self): id = ID( filename='x/y/z/spam.c', funcname='func', name='eggs', ) id.validate() # This does not fail.
def test_init_coercion(self): tests = [ ('str subclass', dict( id=PseudoStr('eggs'), kind=PseudoStr('variable'), external=0, ), ( ID(None, None, 'eggs'), Symbol.KIND.VARIABLE, False, )), ('with filename', dict( id=('x/y/z/spam.c', 'eggs'), kind=PseudoStr('variable'), external=0, ), ( ID('x/y/z/spam.c', None, 'eggs'), Symbol.KIND.VARIABLE, False, )), ('non-str 1', dict( id=('a', 'b', 'c'), kind=StrProxy('variable'), external=0, ), ( ID('a', 'b', 'c'), Symbol.KIND.VARIABLE, False, )), ('non-str 2', dict( id=('a', 'b', 'c'), kind=Object(), external=0, ), ( ID('a', 'b', 'c'), '<object>', False, )), ] for summary, kwargs, expected in tests: with self.subTest(summary): symbol = Symbol(**kwargs) for field in Symbol._fields: value = getattr(symbol, field) if field == 'external': self.assertIs(type(value), bool) elif field == 'id': self.assertIs(type(value), ID) else: self.assertIs(type(value), str) self.assertEqual(tuple(symbol), expected)
def test_validate_missing_field(self): for field in ID._fields: with self.subTest(field): id = ID(**self.VALID_KWARGS) id = id._replace(**{field: None}) if field == 'funcname': id.validate() # The field can be missing (not set). id = id._replace(filename=None) id.validate() # Both fields can be missing (not set). continue with self.assertRaises(TypeError): id.validate()
def test_init_all_coerced(self): tests = [ ('str subclass', dict( filename=PseudoStr('x/y/z/spam.c'), funcname=PseudoStr('func'), name=PseudoStr('eggs'), ), ('x/y/z/spam.c', 'func', 'eggs', )), ('non-str', dict( filename=StrProxy('x/y/z/spam.c'), funcname=Object(), name=('a', 'b', 'c'), ), ('x/y/z/spam.c', '<object>', "('a', 'b', 'c')", )), ] for summary, kwargs, expected in tests: with self.subTest(summary): id = ID(**kwargs) for field in ID._fields: value = getattr(id, field) self.assertIs(type(value), str) self.assertEqual(tuple(id), expected)
def test_iterable(self): id = ID(**self.VALID_KWARGS) filename, funcname, name = id values = (filename, funcname, name) for value, expected in zip(values, self.VALID_EXPECTED): self.assertEqual(value, expected)
def test_fields(self): id = ID('z', 'x', 'a') symbol = Symbol(id, 'b', False) self.assertEqual(symbol.id, id) self.assertEqual(symbol.kind, 'b') self.assertIs(symbol.external, False)
def __new__(cls, id, kind=KIND.VARIABLE, external=None): self = super().__new__( cls, id=ID.from_raw(id), kind=str(kind) if kind else None, external=bool(external) if external is not None else None, ) return self
def test_init_all_coerced(self): id = ID('x/y/z/spam.c', 'func', 'spam') tests = [ ('str subclass', dict( id=( PseudoStr('x/y/z/spam.c'), PseudoStr('func'), PseudoStr('spam'), ), storage=PseudoStr('static'), vartype=PseudoStr('int'), ), ( id, 'static', 'int', )), ('non-str 1', dict( id=id, storage=Object(), vartype=Object(), ), ( id, '<object>', '<object>', )), ('non-str 2', dict( id=id, storage=StrProxy('static'), vartype=StrProxy('variable'), ), ( id, 'static', 'variable', )), ('non-str', dict( id=id, storage=('a', 'b', 'c'), vartype=('x', 'y', 'z'), ), ( id, "('a', 'b', 'c')", "('x', 'y', 'z')", )), ] for summary, kwargs, expected in tests: with self.subTest(summary): static = Variable(**kwargs) for field in Variable._fields: value = getattr(static, field) if field == 'id': self.assertIs(type(value), ID) else: self.assertIs(type(value), str) self.assertEqual(tuple(static), expected)
def test_init_all_missing(self): id = ID(None, None, 'spam') symbol = Symbol(id) self.assertEqual(symbol, ( id, Symbol.KIND.VARIABLE, None, ))
def test_validate_typical(self): id = ID('z', 'x', 'a') symbol = Symbol( id=id, kind=Symbol.KIND.VARIABLE, external=False, ) symbol.validate() # This does not fail.
def test___getattr__(self): id = ID('z', 'x', 'a') symbol = Symbol(id, 'b', False) filename = symbol.filename funcname = symbol.funcname name = symbol.name self.assertEqual(filename, 'z') self.assertEqual(funcname, 'x') self.assertEqual(name, 'a')
def test_init_typical_global(self): id = ID( filename='x/y/z/spam.c', funcname=None, name='eggs', ) self.assertEqual(id, ( 'x/y/z/spam.c', None, 'eggs', ))
def test_init_typical_local(self): id = ID( filename='x/y/z/spam.c', funcname='func', name='eggs', ) self.assertEqual(id, ( 'x/y/z/spam.c', 'func', 'eggs', ))
def test_minimal(self): id = ID( filename=None, funcname=None, name='eggs', ) self.assertEqual(id, ( None, None, 'eggs', ))
def test_init_typical_binary_global(self): id = ID('Python/ceval.c', None, 'spam') symbol = Symbol( id=id, kind=Symbol.KIND.VARIABLE, external=False, ) self.assertEqual(symbol, ( id, Symbol.KIND.VARIABLE, False, ))
def test_init_all_missing(self): for value in ('', None): with self.subTest(repr(value)): id = ID( filename=value, funcname=value, name=value, ) self.assertEqual(id, ( None, None, None, ))
def test_from_raw(self): tests = [ ('', None), (None, None), ('spam', (None, None, 'spam')), (('spam',), (None, None, 'spam')), (('x/y/z/spam.c', 'spam'), ('x/y/z/spam.c', None, 'spam')), (self.VALID_ARGS, self.VALID_EXPECTED), (self.VALID_KWARGS, self.VALID_EXPECTED), ] for raw, expected in tests: with self.subTest(raw): id = ID.from_raw(raw) self.assertEqual(id, expected)
def test_validate_typical(self): validstorage = ('static', 'extern', 'implicit', 'local') self.assertEqual(set(validstorage), set(Variable.STORAGE)) for storage in validstorage: with self.subTest(storage): static = Variable( id=ID( filename='x/y/z/spam.c', funcname='func', name='eggs', ), storage=storage, vartype='int', ) static.validate() # This does not fail.
def test_init_typical_global(self): for storage in ('static', 'extern', 'implicit'): with self.subTest(storage): static = Variable( id=ID( filename='x/y/z/spam.c', funcname=None, name='eggs', ), storage=storage, vartype='int', ) self.assertEqual(static, ( ('x/y/z/spam.c', None, 'eggs'), storage, 'int', ))
def test_init_typical_local(self): for storage in ('static', 'local'): with self.subTest(storage): static = Variable( id=ID( filename='x/y/z/spam.c', funcname='func', name='eggs', ), storage=storage, vartype='int', ) self.assertEqual(static, ( ('x/y/z/spam.c', 'func', 'eggs'), storage, 'int', ))
def ignored_from_file( infile, *, _read_tsv=read_tsv, ): """Yield a Variable for each ignored var in the file.""" ignored = { 'variables': {}, #'types': {}, #'constants': {}, #'macros': {}, } for row in _read_tsv(infile, IGNORED_HEADER): filename, funcname, name, kind, reason = row if not funcname or funcname == '-': funcname = None id = ID(filename, funcname, name) if kind == 'variable': values = ignored['variables'] else: raise ValueError(f'unsupported kind in row {row}') values[id] = reason return ignored
def _known(symbol): if symbol.funcname: if symbol.funcname != UNKNOWN or symbol.filename != UNKNOWN: raise KeyError(symbol.name) filename, funcname, decl = LOCALS[symbol.name] varid = ID(filename, funcname, symbol.name) elif not symbol.filename or symbol.filename == UNKNOWN: raise KeyError(symbol.name) else: varid = symbol.id try: decl = GLOBALS[symbol.name] except KeyError: if symbol.name.endswith('_methods'): decl = 'static PyMethodDef ' elif symbol.filename == 'Objects/exceptions.c' and symbol.name.startswith( ('PyExc_', '_PyExc_')): decl = 'static PyTypeObject ' else: raise if symbol.name not in decl: decl = decl + symbol.name return Variable(varid, 'static', decl)
class SymbolTests(unittest.TestCase): VALID_ARGS = ( ID('x/y/z/spam.c', 'func', 'eggs'), Symbol.KIND.VARIABLE, False, ) VALID_KWARGS = dict(zip(Symbol._fields, VALID_ARGS)) VALID_EXPECTED = VALID_ARGS def test_init_typical_binary_local(self): id = ID(None, None, 'spam') symbol = Symbol( id=id, kind=Symbol.KIND.VARIABLE, external=False, ) self.assertEqual(symbol, ( id, Symbol.KIND.VARIABLE, False, )) def test_init_typical_binary_global(self): id = ID('Python/ceval.c', None, 'spam') symbol = Symbol( id=id, kind=Symbol.KIND.VARIABLE, external=False, ) self.assertEqual(symbol, ( id, Symbol.KIND.VARIABLE, False, )) def test_init_coercion(self): tests = [ ('str subclass', dict( id=PseudoStr('eggs'), kind=PseudoStr('variable'), external=0, ), ( ID(None, None, 'eggs'), Symbol.KIND.VARIABLE, False, )), ('with filename', dict( id=('x/y/z/spam.c', 'eggs'), kind=PseudoStr('variable'), external=0, ), ( ID('x/y/z/spam.c', None, 'eggs'), Symbol.KIND.VARIABLE, False, )), ('non-str 1', dict( id=('a', 'b', 'c'), kind=StrProxy('variable'), external=0, ), ( ID('a', 'b', 'c'), Symbol.KIND.VARIABLE, False, )), ('non-str 2', dict( id=('a', 'b', 'c'), kind=Object(), external=0, ), ( ID('a', 'b', 'c'), '<object>', False, )), ] for summary, kwargs, expected in tests: with self.subTest(summary): symbol = Symbol(**kwargs) for field in Symbol._fields: value = getattr(symbol, field) if field == 'external': self.assertIs(type(value), bool) elif field == 'id': self.assertIs(type(value), ID) else: self.assertIs(type(value), str) self.assertEqual(tuple(symbol), expected) def test_init_all_missing(self): id = ID(None, None, 'spam') symbol = Symbol(id) self.assertEqual(symbol, ( id, Symbol.KIND.VARIABLE, None, )) def test_fields(self): id = ID('z', 'x', 'a') symbol = Symbol(id, 'b', False) self.assertEqual(symbol.id, id) self.assertEqual(symbol.kind, 'b') self.assertIs(symbol.external, False) def test___getattr__(self): id = ID('z', 'x', 'a') symbol = Symbol(id, 'b', False) filename = symbol.filename funcname = symbol.funcname name = symbol.name self.assertEqual(filename, 'z') self.assertEqual(funcname, 'x') self.assertEqual(name, 'a') def test_validate_typical(self): id = ID('z', 'x', 'a') symbol = Symbol( id=id, kind=Symbol.KIND.VARIABLE, external=False, ) symbol.validate() # This does not fail. def test_validate_missing_field(self): for field in Symbol._fields: with self.subTest(field): symbol = Symbol(**self.VALID_KWARGS) symbol = symbol._replace(**{field: None}) with self.assertRaises(TypeError): symbol.validate() def test_validate_bad_field(self): badch = tuple(c for c in string.punctuation + string.digits) notnames = ( '1a', 'a.b', 'a-b', '&a', 'a++', ) + badch tests = [ ('id', notnames), ('kind', ('bogus', )), ] seen = set() for field, invalid in tests: for value in invalid: if field != 'kind': seen.add(value) with self.subTest(f'{field}={value!r}'): symbol = Symbol(**self.VALID_KWARGS) symbol = symbol._replace(**{field: value}) with self.assertRaises(ValueError): symbol.validate() for field, invalid in tests: if field == 'kind': continue valid = seen - set(invalid) for value in valid: with self.subTest(f'{field}={value!r}'): symbol = Symbol(**self.VALID_KWARGS) symbol = symbol._replace(**{field: value}) symbol.validate() # This does not fail.
def _handle_id(filename, funcname, name, *, _relpath=os.path.relpath, ): filename = _relpath(filename, REPO_ROOT) return ID(filename, funcname, name)
def from_name(cls, name, filename=None, kind=KIND.VARIABLE, external=None): """Return a new symbol based on the given name.""" id = ID(filename, None, name) return cls(id, kind, external)
def test_fields(self): id = ID('a', 'b', 'z') self.assertEqual(id.filename, 'a') self.assertEqual(id.funcname, 'b') self.assertEqual(id.name, 'z')