Example #1
0
def test_lri():
    cache_size = 10
    bc = LRI(cache_size, on_miss=lambda k: k.upper())
    for idx, char in enumerate(string.ascii_letters):
        x = bc[char]
        assert x == char.upper()
        least_recent_insert_index = idx - cache_size
        if least_recent_insert_index >= 0:
            # least recently inserted object evicted
            assert len(bc) == cache_size
            for char in string.ascii_letters[least_recent_insert_index+1:idx]:
                assert char in bc

    # test that reinserting an exising key changes eviction behavior
    bc[string.ascii_letters[-cache_size+1]] = "new value"
    least_recently_inserted_key = string.ascii_letters[-cache_size+2]
    bc["unreferenced_key"] = "value"
    keys_in_cache = [
        string.ascii_letters[i]
        for i in range(-cache_size + 1, 0)
        if string.ascii_letters[i] != least_recently_inserted_key
    ]
    keys_in_cache.append("unreferenced_key")
    assert len(bc) == cache_size
    for k in keys_in_cache:
        assert k in bc
Example #2
0
def test_lri_cache_eviction():
    """
    Regression test
    Original LRI implementation had a bug where the specified cache
    size only supported `max_size` number of inserts to the cache,
    rather than support `max_size` number of keys in the cache. This
    would result in some unintuitive behavior, where a key is evicted
    recently inserted value would be evicted from the cache if the key
    inserted was inserted `max_size` keys earlier.
    """
    test_cache = LRI(2)
    # dequeue: (key1); dict keys: (key1)
    test_cache["key1"] = "value1"
    # dequeue: (key1, key1); dict keys: (key1)
    test_cache["key1"] = "value1"
    # dequeue: (key1, key1, key2); dict keys: (key1, key2)
    test_cache["key2"] = "value2"
    # dequeue: (key1, key2, key3); dict keys: (key2, key3)
    test_cache["key3"] = "value3"
    # will error here since we evict key1 from the cache and it doesn't
    # exist in the dict anymore
    test_cache["key3"] = "value3"
def test_lri():
    bc = LRI(10, on_miss=lambda k: k.upper())
    for char in string.ascii_letters:
        x = bc[char]
        assert x == char.upper()
    assert len(bc) == 10
 def __init__(self, cache=None):
     self.h_cache = LRI() if cache is None else cache
     self.door_count = 0
     self.hook_count = 0
     self.hand_count = 0
Example #5
0
)

import inspect
from glob import glob
from importlib import import_module
from os import path

from boltons.cacheutils import LRI, cached

from .. import logger
from ..base import PackageManager


# Use a cache to keep re-computing the global pool of registered manager
# definitions.
@cached(LRI(max_size=1))
def pool():
    """ Search for package manager definitions locally and return a dict.

    Is considered valid package manager definitions classes which:
        1 - are sub-classes of PackageManager, and
        2 - is not PackageManager itself, and
        3 - are located in files at the same level or below this one, and
        4 - have a non null cli_name property.
    """
    register = {}

    here = path.dirname(path.abspath(__file__))

    for py_file in glob(path.join(here, '*.py')):
        module_name = path.splitext(path.basename(py_file))[0]
Example #6
0
    for straight_mapping in [
            RESERVED_COUNTRY_CODES,
            COUNTRY_ALIASES,
            SUBDIVISION_ALIASES,
            FOREIGN_TERRITORIES_MAPPING,
    ]:
        for alias_code, target_code in straight_mapping.items():
            mapping.setdefault(alias_code, set()).add(target_code)
    return mapping


REVERSE_MAPPING = generate_mapping()


@cached(LRI())
def supported_territory_codes() -> Set[str]:
    """Return a set of recognized territory codes."""
    return supported_country_codes().union(supported_subdivision_codes())


@cached(LRI())
def supported_country_codes() -> Set[str]:
    """Return a set of recognized country codes.

    Are supported:
        * ISO 3166-1 alpha-2 country codes and exceptional reservations
        * European Commision country code exceptions
    """
    return {country.alpha_2
            for country in countries} | set({
Example #7
0
File: classify.py Project: vunb/eva
from boltons.cacheutils import LRI

__all__ = [
    'get_intent'
]

cache = LRI(max_size=1)


def get_intent(*sents, **kwargs):
    if 'intent' not in cache:
        from eva.intents.train import IntentClassifier
        model_file = kwargs.pop(
            'model', 'intents.model'
        )
        classifier = IntentClassifier()
        cache['intent'] = classifier.load(model_file)
    return cache['intent'].predict(sents)
Example #8
0
class PluginConfig(BaseModel):
    """
    A helper base class for easier declarative plugin configuration.

    - can be used with [Configuration.parse_sub_config][releaseherald.configuration.Configuration.parse_sub_config]
      for easier parsing
    - Attributes can be Annotated types, which can contain
      [FromCommandline][releaseherald.plugins.plugin_config.FromCommandline] Annotation, that make the config setting
      overridable from commandline

    #Usage

    ```python
    class MyPluginConfig(PluginConfig):
        non_overridable_value: str = "some default

        # use type Annotation to connect the attribute wit the commandline Option
        overridable_value: Annotated[str, FromCommandline(
            "generate",
            click.Option(
                param_decls=["--override"],
                help="override the overrideable value",
            )
        )] = "default for overrideable value"

    class MyPlugin:
        @releaseherald.plugins.hookimpl
        def process_config(self, config: Configuration):
            # parse the config with the helper
            self.my_config = config.parse_sub_config("my_config", MyPluginConfig)

        @releaseherald.plugins.hookimpl
        def get_command_options(self, command: str) -> Optional[CommandOptions]:
            # just use the helper to return the right thing
            return self.my_config.get_command_options(command)

        @releaseherald.plugins.hookimpl
        def on_start_command(self, command: str, kwargs: Dict[str, Any]):
            # use the helper to reflect commandline overrides in the config
            self.my_config.update(command, kwargs)
    ```
    """

    @classmethod
    @cached(LRI())
    def _get_command_options_info(cls) -> Dict[str, CommandOptionsInfo]:

        command_options: DefaultDict[str, CommandOptionsInfo] = defaultdict(
            CommandOptionsInfo
        )

        for field in cls.__fields__.values():
            metadata = getattr(field.outer_type_, "__metadata__", None)
            if not metadata:
                continue

            for annotation in metadata:
                if not isinstance(annotation, FromCommandline):
                    continue
                command = command_options[annotation.command]

                command.options.append(annotation.option)
                command.update_mappings.append(
                    UpdateMapping(field.name, annotation.option.name)
                )

        return dict(command_options)

    def get_command_options(self, command: str) -> Optional[CommandOptions]:
        """
        Generate command options from Annotated fields which can be returned directly from
        [get_command_options hook][releaseherald.plugins.hookspecs.get_command_options]
        Args:
            command: the command these command options are registered with

        Returns:
            The command options that the [get_command_options hook][releaseherald.plugins.hookspecs.get_command_options]
             expects
        """
        command_options: CommandOptionsInfo = (
            self._get_command_options_info().get(command)
        )

        if command_options:

            def default_opts_callback(default_options: Dict[str, Any]):
                for update_mapping in command_options.update_mappings:
                    default_options[update_mapping.option_name] = getattr(
                        self, update_mapping.field_name
                    )

            return CommandOptions(
                command_options.options, default_opts_callback
            )

    def update(self, command: str, kwargs: Dict[str, Any]) -> None:
        """
        Update itself from commandline options, can be used in
        [on_start_command hook][releaseherald.plugins.hookspecs.on_start_command]
        Args:
            command: the command
            kwargs: the commandline args for the command
        """
        command_options: CommandOptionsInfo = (
            self._get_command_options_info().get(command)
        )

        if command_options:
            for update_mapping in command_options.update_mappings:
                setattr(
                    self,
                    update_mapping.field_name,
                    kwargs[update_mapping.option_name],
                )
Example #9
0
def search(ctx, extended, exact, query):
    """ Search packages from all managers. """
    active_managers = ctx.obj['active_managers']
    output_format = ctx.obj['output_format']
    sort_by = ctx.obj['sort_by']
    stats = ctx.obj['stats']

    # Build-up a global list of package matches per manager.
    matches = {}

    for manager in active_managers:
        matches[manager.id] = {
            'id': manager.id,
            'name': manager.name,
            'packages': list(manager.search(query, extended, exact).values())
        }

        # Serialize errors at the last minute to gather all we encountered.
        matches[manager.id]['errors'] = list(
            {expt.error
             for expt in manager.cli_errors})

    # Machine-friendly data rendering.
    if output_format == 'json':
        click.echo(json(matches))
        return

    # Prepare highlighting helpers.
    query_parts = {query}.union(map(str, TokenizedString(query)))

    @cached(LRI(max_size=1000))
    def highlight(string):
        # Ranges of character indices flagged for highlighting.
        ranges = set()

        # TODO: Fix upper-case matching, as tokenizer lower them down.

        for part in query_parts:
            # Search for occurrences of query parts in original string.
            if part in string:
                # Flag matching substrings for highlighting.
                occurrences = [
                    match.start() for match in re.finditer(part, string)
                ]

                for match_start in occurrences:
                    match_end = match_start + len(part) - 1
                    ranges.add('{}-{}'.format(match_start, match_end))

        # Reduce index ranges, compute complement ranges, transform them to
        # list of integers.
        ranges = ','.join(ranges)
        bold_ranges = int_ranges_from_int_list(ranges)
        normal_ranges = int_ranges_from_int_list(
            complement_int_list(ranges, range_end=len(string)))

        # Apply style to range of characters flagged as matching.
        styled_str = ''
        for i, j in sorted(bold_ranges + normal_ranges):
            segment = getitem(string, slice(i, j + 1))
            if (i, j) in bold_ranges:
                segment = click.style(segment, bold=True)
            styled_str += segment

        return styled_str

    # Human-friendly content rendering.
    table = []
    for manager_id, matching_pkg in matches.items():
        table += [[
            highlight(info['name']),
            highlight(info['id']), manager_id,
            info['latest_version'] if info['latest_version'] else '?'
        ] for info in matching_pkg['packages']]

    # Sort and print table.
    print_table([('Package name', 'package_name'), ('ID', 'package_id'),
                 ('Manager', 'manager_id'), ('Latest version', 'version')],
                table, sort_by)

    if stats:
        print_stats(matches)
Example #10
0
def search(ctx, extended, exact, query):
    """ Search packages from all managers. """
    active_managers = ctx.obj['active_managers']
    output_format = ctx.obj['output_format']
    sort_by = ctx.obj['sort_by']
    stats = ctx.obj['stats']

    # Build-up a global list of package matches per manager.
    matches = {}

    for manager in active_managers:
        matches[manager.id] = {
            'id': manager.id,
            'name': manager.name,
            'packages': list(manager.search(query, extended, exact).values())}

        # Serialize errors at the last minute to gather all we encountered.
        matches[manager.id]['errors'] = list({
            expt.error for expt in manager.cli_errors})

    # Machine-friendly data rendering.
    if output_format == 'json':
        click.echo(json(matches))
        return

    # Prepare highlighting helpers.
    query_parts = {query}.union(map(str, TokenizedString(query)))

    # XXX Local copy of boltons.strutils.complement_int_list().
    # TODO: Replace with the boltons util function above once 20.1.1.dev0 is
    # released.
    def complement_int_list(
            range_string, range_start=0, range_end=None,
            delim=',', range_delim='-'):
        """ Returns range string that is the complement of the one provided as
        *range_string* parameter.

        These range strings are of the kind produce by :func:`format_int_list`,
        and parseable by :func:`parse_int_list`.

        Args:
            range_string (str): String of comma separated positive integers or
               ranges (e.g. '1,2,4-6,8'). Typical of a custom page range string
               used in printer dialogs.
            range_start (int): A positive integer from which to start the
               resulting range. Value is inclusive. Defaults to ``0``.
            range_end (int): A positive integer from which the produced range
               is stopped. Value is exclusive. Defaults to the maximum value
               found in the provided ``range_string``.
            delim (char): Defaults to ','. Separates integers and contiguous
               ranges of integers.
            range_delim (char): Defaults to '-'. Indicates a contiguous range
               of integers.

        >>> complement_int_list('1,3,5-8,10-11,15')
        '0,2,4,9,12-14'

        >>> complement_int_list('1,3,5-8,10-11,15', range_start=0)
        '0,2,4,9,12-14'

        >>> complement_int_list('1,3,5-8,10-11,15', range_start=1)
        '2,4,9,12-14'

        >>> complement_int_list('1,3,5-8,10-11,15', range_start=2)
        '2,4,9,12-14'

        >>> complement_int_list('1,3,5-8,10-11,15', range_start=3)
        '4,9,12-14'

        >>> complement_int_list('1,3,5-8,10-11,15', range_end=15)
        '0,2,4,9,12-14'

        >>> complement_int_list('1,3,5-8,10-11,15', range_end=14)
        '0,2,4,9,12-13'

        >>> complement_int_list('1,3,5-8,10-11,15', range_end=13)
        '0,2,4,9,12'

        >>> complement_int_list('1,3,5-8,10-11,15', range_end=20)
        '0,2,4,9,12-14,16-19'

        >>> complement_int_list('1,3,5-8,10-11,15', range_end=0)
        ''

        >>> complement_int_list('1,3,5-8,10-11,15', range_start=-1)
        '0,2,4,9,12-14'

        >>> complement_int_list('1,3,5-8,10-11,15', range_end=-1)
        ''

        >>> complement_int_list('1,3,5-8', range_start=1, range_end=1)
        ''

        >>> complement_int_list('1,3,5-8', range_start=2, range_end=2)
        ''

        >>> complement_int_list('1,3,5-8', range_start=2, range_end=3)
        '2'

        >>> complement_int_list('1,3,5-8', range_start=-10, range_end=-5)
        ''

        >>> complement_int_list('1,3,5-8', range_start=20, range_end=10)
        ''

        >>> complement_int_list('')
        ''
        """
        int_list = set(parse_int_list(range_string, delim, range_delim))
        if range_end is None:
            if int_list:
                range_end = max(int_list) + 1
            else:
                range_end = range_start
        complement_values = set(
            range(range_end)) - int_list - set(range(range_start))
        return format_int_list(complement_values, delim, range_delim)

    # XXX Local copy of boltons.strutils.int_ranges_from_int_list().
    # TODO: Replace with the boltons util function above once 20.1.1.dev0 is
    # released.
    def int_ranges_from_int_list(range_string, delim=',', range_delim='-'):
        """ Transform a string of ranges (*range_string*) into a tuple of
        tuples.

        Args:
            range_string (str): String of comma separated positive integers or
               ranges (e.g. '1,2,4-6,8'). Typical of a custom page range string
               used in printer dialogs.
            delim (char): Defaults to ','. Separates integers and contiguous
               ranges of integers.
            range_delim (char): Defaults to '-'. Indicates a contiguous range
               of integers.

        >>> int_ranges_from_int_list('1,3,5-8,10-11,15')
        ((1, 1), (3, 3), (5, 8), (10, 11), (15, 15))

        >>> int_ranges_from_int_list('1')
        ((1, 1),)

        >>> int_ranges_from_int_list('')
        ()
        """
        int_tuples = []
        # Normalize the range string to our internal format for processing.
        range_string = format_int_list(
            parse_int_list(range_string, delim, range_delim))
        if range_string:
            for bounds in range_string.split(','):
                if '-' in bounds:
                    start, end = bounds.split('-')
                else:
                    start, end = bounds, bounds
                int_tuples.append((int(start), int(end)))
        return tuple(int_tuples)

    @cached(LRI(max_size=1000))
    def highlight(string):
        # Ranges of character indices flagged for highlighting.
        ranges = set()

        # TODO: Fix upper-case matching, as tokenizer lower them down.

        for part in query_parts:
            # Search for occurences of query parts in original string.
            if part in string:
                # Flag matching substrings for highlighting.
                occurences = [
                    match.start() for match in re.finditer(part, string)]

                for match_start in occurences:
                    match_end = match_start + len(part) - 1
                    ranges.add('{}-{}'.format(match_start, match_end))

        # Reduce index ranges, compute complement ranges, transform them to
        # list of integers.
        ranges = ','.join(ranges)
        bold_ranges = int_ranges_from_int_list(ranges)
        normal_ranges = int_ranges_from_int_list(
            complement_int_list(ranges, range_end=len(string)))

        # Apply style to range of characters flagged as matching.
        styled_str = ''
        for i, j in sorted(bold_ranges + normal_ranges):
            segment = getitem(string, slice(i, j + 1))
            if (i, j) in bold_ranges:
                segment = click.style(segment, bold=True)
            styled_str += segment

        return styled_str

    # Human-friendly content rendering.
    table = []
    for manager_id, matching_pkg in matches.items():
        table += [[
            highlight(info['name']),
            highlight(info['id']),
            manager_id,
            info['latest_version'] if info['latest_version'] else '?']
                  for info in matching_pkg['packages']]

    # Sort and print table.
    print_table([
        ('Package name', 'package_name'),
        ('ID', 'package_id'),
        ('Manager', 'manager_id'),
        ('Latest version', 'version')],
                table, sort_by)

    if stats:
        print_stats(matches)