mirror of
				https://github.com/python/cpython.git
				synced 2025-11-04 07:31:38 +00:00 
			
		
		
		
	
		
			
	
	
		
			139 lines
		
	
	
	
		
			4.4 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
		
		
			
		
	
	
			139 lines
		
	
	
	
		
			4.4 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| 
								 | 
							
								from collections import namedtuple
							 | 
						||
| 
								 | 
							
								import re
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								from .util import classonly, _NTBase
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								# XXX need tests:
							 | 
						||
| 
								 | 
							
								# * ID.match()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								UNKNOWN = '???'
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								NAME_RE = re.compile(r'^([a-zA-Z]|_\w*[a-zA-Z]\w*|[a-zA-Z]\w*)$')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class ID(_NTBase, namedtuple('ID', 'filename funcname name')):
							 | 
						||
| 
								 | 
							
								    """A unique ID for a single symbol or declaration."""
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    __slots__ = ()
							 | 
						||
| 
								 | 
							
								    # XXX Add optional conditions (tuple of strings) field.
							 | 
						||
| 
								 | 
							
								    #conditions = Slot()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @classonly
							 | 
						||
| 
								 | 
							
								    def from_raw(cls, raw):
							 | 
						||
| 
								 | 
							
								        if not raw:
							 | 
						||
| 
								 | 
							
								            return None
							 | 
						||
| 
								 | 
							
								        if isinstance(raw, str):
							 | 
						||
| 
								 | 
							
								            return cls(None, None, raw)
							 | 
						||
| 
								 | 
							
								        try:
							 | 
						||
| 
								 | 
							
								            name, = raw
							 | 
						||
| 
								 | 
							
								            filename = None
							 | 
						||
| 
								 | 
							
								        except ValueError:
							 | 
						||
| 
								 | 
							
								            try:
							 | 
						||
| 
								 | 
							
								                filename, name = raw
							 | 
						||
| 
								 | 
							
								            except ValueError:
							 | 
						||
| 
								 | 
							
								                return super().from_raw(raw)
							 | 
						||
| 
								 | 
							
								        return cls(filename, None, name)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def __new__(cls, filename, funcname, name):
							 | 
						||
| 
								 | 
							
								        self = super().__new__(
							 | 
						||
| 
								 | 
							
								                cls,
							 | 
						||
| 
								 | 
							
								                filename=str(filename) if filename else None,
							 | 
						||
| 
								 | 
							
								                funcname=str(funcname) if funcname else None,
							 | 
						||
| 
								 | 
							
								                name=str(name) if name else None,
							 | 
						||
| 
								 | 
							
								                )
							 | 
						||
| 
								 | 
							
								        #cls.conditions.set(self, tuple(str(s) if s else None
							 | 
						||
| 
								 | 
							
								        #                               for s in conditions or ()))
							 | 
						||
| 
								 | 
							
								        return self
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def validate(self):
							 | 
						||
| 
								 | 
							
								        """Fail if the object is invalid (i.e. init with bad data)."""
							 | 
						||
| 
								 | 
							
								        if not self.name:
							 | 
						||
| 
								 | 
							
								            raise TypeError('missing name')
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            if not NAME_RE.match(self.name):
							 | 
						||
| 
								 | 
							
								                raise ValueError(
							 | 
						||
| 
								 | 
							
								                        f'name must be an identifier, got {self.name!r}')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # Symbols from a binary might not have filename/funcname info.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if self.funcname:
							 | 
						||
| 
								 | 
							
								            if not self.filename:
							 | 
						||
| 
								 | 
							
								                raise TypeError('missing filename')
							 | 
						||
| 
								 | 
							
								            if not NAME_RE.match(self.funcname) and self.funcname != UNKNOWN:
							 | 
						||
| 
								 | 
							
								                raise ValueError(
							 | 
						||
| 
								 | 
							
								                        f'name must be an identifier, got {self.funcname!r}')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # XXX Require the filename (at least UNKONWN)?
							 | 
						||
| 
								 | 
							
								        # XXX Check the filename?
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @property
							 | 
						||
| 
								 | 
							
								    def islocal(self):
							 | 
						||
| 
								 | 
							
								        return self.funcname is not None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def match(self, other, *,
							 | 
						||
| 
								 | 
							
								              match_files=(lambda f1, f2: f1 == f2),
							 | 
						||
| 
								 | 
							
								              ):
							 | 
						||
| 
								 | 
							
								        """Return True if the two match.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        At least one of the two must be completely valid (no UNKNOWN
							 | 
						||
| 
								 | 
							
								        anywhere).  Otherwise False is returned.  The remaining one
							 | 
						||
| 
								 | 
							
								        *may* have UNKNOWN for both funcname and filename.  It must
							 | 
						||
| 
								 | 
							
								        have a valid name though.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        The caller is responsible for knowing which of the two is valid
							 | 
						||
| 
								 | 
							
								        (and which to use if both are valid).
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        # First check the name.
							 | 
						||
| 
								 | 
							
								        if self.name is None:
							 | 
						||
| 
								 | 
							
								            return False
							 | 
						||
| 
								 | 
							
								        if other.name != self.name:
							 | 
						||
| 
								 | 
							
								            return False
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # Then check the filename.
							 | 
						||
| 
								 | 
							
								        if self.filename is None:
							 | 
						||
| 
								 | 
							
								            return False
							 | 
						||
| 
								 | 
							
								        if other.filename is None:
							 | 
						||
| 
								 | 
							
								            return False
							 | 
						||
| 
								 | 
							
								        if self.filename == UNKNOWN:
							 | 
						||
| 
								 | 
							
								            # "other" must be the valid one.
							 | 
						||
| 
								 | 
							
								            if other.funcname == UNKNOWN:
							 | 
						||
| 
								 | 
							
								                return False
							 | 
						||
| 
								 | 
							
								            elif self.funcname != UNKNOWN:
							 | 
						||
| 
								 | 
							
								                # XXX Try matching funcname even though we don't
							 | 
						||
| 
								 | 
							
								                # know the filename?
							 | 
						||
| 
								 | 
							
								                raise NotImplementedError
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                return True
							 | 
						||
| 
								 | 
							
								        elif other.filename == UNKNOWN:
							 | 
						||
| 
								 | 
							
								            # "self" must be the valid one.
							 | 
						||
| 
								 | 
							
								            if self.funcname == UNKNOWN:
							 | 
						||
| 
								 | 
							
								                return False
							 | 
						||
| 
								 | 
							
								            elif other.funcname != UNKNOWN:
							 | 
						||
| 
								 | 
							
								                # XXX Try matching funcname even though we don't
							 | 
						||
| 
								 | 
							
								                # know the filename?
							 | 
						||
| 
								 | 
							
								                raise NotImplementedError
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                return True
							 | 
						||
| 
								 | 
							
								        elif not match_files(self.filename, other.filename):
							 | 
						||
| 
								 | 
							
								            return False
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # Finally, check the funcname.
							 | 
						||
| 
								 | 
							
								        if self.funcname == UNKNOWN:
							 | 
						||
| 
								 | 
							
								            # "other" must be the valid one.
							 | 
						||
| 
								 | 
							
								            if other.funcname == UNKNOWN:
							 | 
						||
| 
								 | 
							
								                return False
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                return other.funcname is not None
							 | 
						||
| 
								 | 
							
								        elif other.funcname == UNKNOWN:
							 | 
						||
| 
								 | 
							
								            # "self" must be the valid one.
							 | 
						||
| 
								 | 
							
								            if self.funcname == UNKNOWN:
							 | 
						||
| 
								 | 
							
								                return False
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                return self.funcname is not None
							 | 
						||
| 
								 | 
							
								        elif self.funcname == other.funcname:
							 | 
						||
| 
								 | 
							
								            # Both are valid.
							 | 
						||
| 
								 | 
							
								            return True
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return False
							 |