98 lines
		
	
	
		
			3.1 KiB
		
	
	
	
		
			Python
		
	
	
	
			
		
		
	
	
			98 lines
		
	
	
		
			3.1 KiB
		
	
	
	
		
			Python
		
	
	
	
"""
 | 
						|
this is a place where we put datastructures used by legacy apis
 | 
						|
we hope ot remove
 | 
						|
"""
 | 
						|
import attr
 | 
						|
import keyword
 | 
						|
 | 
						|
from . import MarkInfo, MarkDecorator
 | 
						|
 | 
						|
from _pytest.config import UsageError
 | 
						|
 | 
						|
 | 
						|
@attr.s
 | 
						|
class MarkMapping(object):
 | 
						|
    """Provides a local mapping for markers where item access
 | 
						|
    resolves to True if the marker is present. """
 | 
						|
 | 
						|
    own_mark_names = attr.ib()
 | 
						|
 | 
						|
    @classmethod
 | 
						|
    def from_keywords(cls, keywords):
 | 
						|
        mark_names = set()
 | 
						|
        for key, value in keywords.items():
 | 
						|
            if isinstance(value, MarkInfo) or isinstance(value, MarkDecorator):
 | 
						|
                mark_names.add(key)
 | 
						|
        return cls(mark_names)
 | 
						|
 | 
						|
    def __getitem__(self, name):
 | 
						|
        return name in self.own_mark_names
 | 
						|
 | 
						|
 | 
						|
class KeywordMapping(object):
 | 
						|
    """Provides a local mapping for keywords.
 | 
						|
    Given a list of names, map any substring of one of these names to True.
 | 
						|
    """
 | 
						|
 | 
						|
    def __init__(self, names):
 | 
						|
        self._names = names
 | 
						|
 | 
						|
    @classmethod
 | 
						|
    def from_item(cls, item):
 | 
						|
        mapped_names = set()
 | 
						|
 | 
						|
        # Add the names of the current item and any parent items
 | 
						|
        import pytest
 | 
						|
        for item in item.listchain():
 | 
						|
            if not isinstance(item, pytest.Instance):
 | 
						|
                mapped_names.add(item.name)
 | 
						|
 | 
						|
        # Add the names added as extra keywords to current or parent items
 | 
						|
        for name in item.listextrakeywords():
 | 
						|
            mapped_names.add(name)
 | 
						|
 | 
						|
        # Add the names attached to the current function through direct assignment
 | 
						|
        if hasattr(item, 'function'):
 | 
						|
            for name in item.function.__dict__:
 | 
						|
                mapped_names.add(name)
 | 
						|
 | 
						|
        return cls(mapped_names)
 | 
						|
 | 
						|
    def __getitem__(self, subname):
 | 
						|
        for name in self._names:
 | 
						|
            if subname in name:
 | 
						|
                return True
 | 
						|
        return False
 | 
						|
 | 
						|
 | 
						|
python_keywords_allowed_list = ["or", "and", "not"]
 | 
						|
 | 
						|
 | 
						|
def matchmark(colitem, markexpr):
 | 
						|
    """Tries to match on any marker names, attached to the given colitem."""
 | 
						|
    return eval(markexpr, {}, MarkMapping.from_keywords(colitem.keywords))
 | 
						|
 | 
						|
 | 
						|
def matchkeyword(colitem, keywordexpr):
 | 
						|
    """Tries to match given keyword expression to given collector item.
 | 
						|
 | 
						|
    Will match on the name of colitem, including the names of its parents.
 | 
						|
    Only matches names of items which are either a :class:`Class` or a
 | 
						|
    :class:`Function`.
 | 
						|
    Additionally, matches on names in the 'extra_keyword_matches' set of
 | 
						|
    any item, as well as names directly assigned to test functions.
 | 
						|
    """
 | 
						|
    mapping = KeywordMapping.from_item(colitem)
 | 
						|
    if " " not in keywordexpr:
 | 
						|
        # special case to allow for simple "-k pass" and "-k 1.3"
 | 
						|
        return mapping[keywordexpr]
 | 
						|
    elif keywordexpr.startswith("not ") and " " not in keywordexpr[4:]:
 | 
						|
        return not mapping[keywordexpr[4:]]
 | 
						|
    for kwd in keywordexpr.split():
 | 
						|
        if keyword.iskeyword(kwd) and kwd not in python_keywords_allowed_list:
 | 
						|
            raise UsageError("Python keyword '{}' not accepted in expressions passed to '-k'".format(kwd))
 | 
						|
    try:
 | 
						|
        return eval(keywordexpr, {}, mapping)
 | 
						|
    except SyntaxError:
 | 
						|
        raise UsageError("Wrong expression passed to '-k': {}".format(keywordexpr))
 |