1# Copyright (C) 2020 Red Hat Inc. 2# 3# Authors: 4# Eduardo Habkost <ehabkost@redhat.com> 5# 6# This work is licensed under the terms of the GNU GPL, version 2. See 7# the COPYING file in the top-level directory. 8from typing import * 9 10import logging 11logger = logging.getLogger(__name__) 12DBG = logger.debug 13INFO = logger.info 14WARN = logger.warning 15 16T = TypeVar('T') 17def opt_compare(a: T, b: T) -> bool: 18 """Compare two values, ignoring mismatches if one of them is None""" 19 return (a is None) or (b is None) or (a == b) 20 21def merge(a: T, b: T) -> T: 22 """Merge two values if they matched using opt_compare()""" 23 assert opt_compare(a, b) 24 if a is None: 25 return b 26 else: 27 return a 28 29def test_comp_merge(): 30 assert opt_compare(None, 1) == True 31 assert opt_compare(2, None) == True 32 assert opt_compare(1, 1) == True 33 assert opt_compare(1, 2) == False 34 35 assert merge(None, None) is None 36 assert merge(None, 10) == 10 37 assert merge(10, None) == 10 38 assert merge(10, 10) == 10 39 40 41LineNumber = NewType('LineNumber', int) 42ColumnNumber = NewType('ColumnNumber', int) 43class LineAndColumn(NamedTuple): 44 line: int 45 col: int 46 47 def __str__(self): 48 return '%d:%d' % (self.line, self.col) 49 50def line_col(s, position: int) -> LineAndColumn: 51 """Return line and column for a char position in string 52 53 Character position starts in 0, but lines and columns start in 1. 54 """ 55 before = s[:position] 56 lines = before.split('\n') 57 line = len(lines) 58 col = len(lines[-1]) + 1 59 return LineAndColumn(line, col) 60 61def test_line_col(): 62 assert line_col('abc\ndefg\nhijkl', 0) == (1, 1) 63 assert line_col('abc\ndefg\nhijkl', 2) == (1, 3) 64 assert line_col('abc\ndefg\nhijkl', 3) == (1, 4) 65 assert line_col('abc\ndefg\nhijkl', 4) == (2, 1) 66 assert line_col('abc\ndefg\nhijkl', 10) == (3, 2) 67 68def not_optional(arg: Optional[T]) -> T: 69 assert arg is not None 70 return arg 71 72__all__ = ['not_optional', 'opt_compare', 'merge', 'line_col', 'LineAndColumn']