<commit_before># Copyright (c) 2019-2021, Manfred Moitzi
# License: MIT License
from typing import (
    Optional,
    Iterable,
    Tuple,
    TYPE_CHECKING,
    Dict,
    Set,
    List,
    Iterator,
)
from contextlib import contextmanager
from ezdxf.tools.handle import HandleGenerator
from ezdxf.lldxf.types import is_valid_handle
from ezdxf.entities.dxfentity import DXFEntity
from ezdxf.audit import AuditError, Auditor
from ezdxf.lldxf.const import DXFInternalEzdxfError
from ezdxf.entities import factory

if TYPE_CHECKING:
    from ezdxf.eztypes import TagWriter

DATABASE_EXCLUDE = {
    "SECTION",
    "ENDSEC",
    "EOF",
    "TABLE",
    "ENDTAB",
    "CLASS",
    "ACDSRECORD",
    "ACDSSCHEMA",
}

class EntityDB:
    """A simple key/entity database.

    Every entity/object, except tables and sections, are represented as
    DXFEntity or inherited types, this entities are stored in the
    DXF document database, database-key is the `handle` as string.

    """

    class Trashcan:
        """Store handles to entities which should be deleted later."""

        def __init__(self, db: "EntityDB"):
            self._database = db._database
            self._handles: Set[str] = set()

        def add(self, handle: str):
            """Put handle into trashcan to delete the entity later, this is
            required for deleting entities while iterating the database.
            """
            self._handles.add(handle)

        def clear(self):
            """Remove handles in trashcan from database and destroy entities if
            still alive.
            """
            db = self._database
            for handle in self._handles:
                entity = db.get(handle)
                if entity and entity.is_alive:
                    entity.destroy()

                if handle in db:
                    del db[handle]

            self._handles.clear()

    def __init__(self):
        self._database: Dict[str, DXFEntity] = {}
        # DXF handles of entities to delete later:
        self.handles = HandleGenerator()
        self.locked: bool = False  # used only for debugging

    def __getitem__(self, handle: str) -> DXFEntity:
        """Get entity by `handle`, does not filter destroyed entities nor
        entities in the trashcan.
        """
        return self._database[handle]

    def __setitem__(self, handle: str, entity: DXFEntity) -> None:
        """Set `entity` for `handle`."""
        assert isinstance(handle, str), type(handle)
        assert isinstance(entity, DXFEntity), type(entity)
        assert entity.is_alive, "Can not store destroyed entity."
        if self.locked:
            raise DXFInternalEzdxfError("Locked entity database.")

        if handle == "0" or not is_valid_handle(handle):
            raise ValueError(f"Invalid handle {handle}.")
        self._database[handle] = entity

    def __delitem__(self, handle: str) -> None:
        """Delete entity by `handle`. Removes entity only from database, does
        not destroy the entity.
        """
        if self.locked:
            raise DXFInternalEzdxfError("Locked entity database.")
        del self._database[handle]

    def __contains__(self, handle: str) -> bool:
        """``True`` if database contains `handle`."""
        if handle is None:
            return False
        assert isinstance(handle, str), type(handle)
        return handle in self._database

    def __len__(self) -> int:
        """Count of database items."""
        return len(self._database)

    def __iter__(self) -> Iterator[str]:
        """Iterable of all handles, does filter destroyed entities but not
        entities in the trashcan.
        """
        return self.keys()  # type: ignore

    def get(self, handle: str) -> Optional[DXFEntity]:
        """Returns entity for `handle` or ``None`` if no entry exist, does
        not filter destroyed entities.
        """
        return self._database.get(handle)

    def next_handle(self) -> str:
        """Returns next unique handle."""
        while True:
            handle = self.handles.next()
            if handle not in self._database:
                return handle

    def keys(self) -> Iterable[str]:
        """Iterable of all handles, does filter destroyed entities."""
        return (handle for handle, entity in self.items())

    def values(self) -> Iterable[DXFEntity]:
        """Iterable of all entities, does filter destroyed entities."""
        return (entity for handle, entity in self.items())

    def items(self) -> Iterable[Tuple[str, DXFEntity]]:
        """Iterable of all (handle, entities) pairs, does filter destroyed
        entities.
        """
        return (
            (handle, entity)
            for handle, entity in self._database.items()
            if entity.is_alive
        )

    def add(self, entity: DXFEntity) -> None:
        """Add `entity` to database, assigns a new handle to the `entity`
        if :attr:`entity.dxf.handle` is ``None``. Adding the same entity
        multiple times is possible and creates only a single database entry.

        """
        if entity.dxftype() in DATABASE_EXCLUDE:
            if entity.dxf.handle is not None:
                # Mark existing entity handle as used to avoid
                # reassigning the same handle again.
                self[entity.dxf.handle] = entity
            return
        handle: str = entity.dxf.handle
        if handle is None:
            handle = self.next_handle()
            entity.update_handle(handle)
        self[handle] = entity

        # Add sub entities ATTRIB, VERTEX and SEQEND to database:
        # Add linked MTEXT columns to database:
        if hasattr(entity, "add_sub_entities_to_entitydb"):
            entity.add_sub_entities_to_entitydb(self)

    def delete_entity(self, entity: DXFEntity) -> None:
        """Remove `entity` from database and destroy the `entity`."""
        if entity.is_alive:
            del self[entity.dxf.handle]
            entity.destroy()

    def discard(self, entity: DXFEntity) -> None:
        """Discard `entity` from database without destroying the `entity`."""
        if entity.is_alive:
            if hasattr(entity, "process_sub_entities"):
                entity.process_sub_entities(lambda e: self.discard(e))

            handle = entity.dxf.handle
            try:
                del self._database[handle]
                entity.dxf.handle = None
            except KeyError:
                pass

    def duplicate_entity(self, entity: DXFEntity) -> DXFEntity:
        """Duplicates `entity` and its sub entities (VERTEX, ATTRIB, SEQEND)
        and store them with new handles in the entity database.
        Graphical entities have to be added to a layout by
        :meth:`~ezdxf.layouts.BaseLayout.add_entity`.

        To import DXF entities from another drawing use the
        :class:`~ezdxf.addons.importer.Importer` add-on.

        A new owner handle will be set by adding the duplicated entity to a
        layout.

        """
        new_entity: DXFEntity = entity.copy()
        new_entity.dxf.handle = self.next_handle()
        factory.bind(new_entity, entity.doc)
        return new_entity

    def audit(self, auditor: "Auditor"):
        """Restore database integrity:

        - restore database entries with modified handles (key != entity.dxf.handle)
        - remove entities with invalid handles
        - empty trashcan - destroy all entities in the trashcan
        - removes destroyed database entries (purge)

        """
        assert self.locked is False, "Database is locked!"
        add_entities = []

        with self.trashcan() as trash:
            for handle, entity in self.items():
                # Destroyed entities are already filtered!
                if not is_valid_handle(handle):
                    auditor.fixed_error(
                        code=AuditError.INVALID_ENTITY_HANDLE,
                        message=f"Removed entity {entity.dxftype()} with invalid "
                        f'handle "{handle}" from entity database.',
                    )
                    trash.add(handle)
                if handle != entity.dxf.get("handle"):
                    # database handle != stored entity handle
                    # prevent entity from being destroyed:
                    self._database[handle] = None
                    trash.add(handle)
                    add_entities.append(entity)

        # Remove all destroyed entities from database:
        self.purge()

        for entity in add_entities:
            handle = entity.dxf.get("handle")
            if handle is None:
                auditor.fixed_error(
                    code=AuditError.INVALID_ENTITY_HANDLE,
                    message=f"Removed entity {entity.dxftype()} without handle "
                    f"from entity database.",
                )
                continue
            if not is_valid_handle(handle) or handle == "0":
                auditor.fixed_error(
                    code=AuditError.INVALID_ENTITY_HANDLE,
                    message=f"Removed entity {entity.dxftype()} with invalid "
                    f'handle "{handle}" from entity database.',
                )
                continue
            self[handle] = entity

    def new_trashcan(self) -> "EntityDB.Trashcan":
        """Returns a new trashcan, empty trashcan manually by: :
        func:`Trashcan.clear()`.
        """
        return EntityDB.Trashcan(self)

    @contextmanager
    def trashcan(self) -> "EntityDB.Trashcan":
        """Returns a new trashcan in context manager mode, trashcan will be
        emptied when leaving context.
        """
        trashcan_ = self.new_trashcan()
        yield trashcan_
        # try ... finally is not required, in case of an exception the database
        # is maybe already in an unreliable state.
        trashcan_.clear()

    def purge(self) -> None:
        """Remove all destroyed entities from database, but does not empty the
        trashcan.
        """
        # Important: operate on underlying data structure:
        db = self._database
        dead_handles = [
            handle for handle, entity in db.items() if not entity.is_alive
        ]
        for handle in dead_handles:
            del db[handle]

    def dxf_types_in_use(self) -> Set[str]:
        return set(entity.dxftype() for entity in self.values())

class EntitySpace:
    """
    An :class:`EntitySpace` is a collection of :class:`~ezdxf.entities.DXFEntity`
    objects, that stores only  references to :class:`DXFEntity` objects.

    The :class:`~ezdxf.layouts.Modelspace`, any :class:`~ezdxf.layouts.Paperspace`
    layout and :class:`~ezdxf.layouts.BlockLayout` objects have an
    :class:`EntitySpace` container to store their entities.

    """

    def __init__(self, entities: Iterable[DXFEntity] = None):
        self.entities: List[DXFEntity] = (
            list(e for e in entities if e.is_alive) if entities else []
        )

    def __iter__(self) -> Iterator[DXFEntity]:
        """Iterable of all entities, filters destroyed entities."""
        return (e for e in self.entities if e.is_alive)

    def __getitem__(self, index) -> DXFEntity:
        """Get entity at index `item`

        :class:`EntitySpace` has a standard Python list like interface,
        therefore `index` can be any valid list indexing or slicing term, like
        a single index ``layout[-1]`` to get the last entity, or an index slice
        ``layout[:10]`` to get the first 10 or less entities as
        ``List[DXFEntity]``. Does not filter destroyed entities.

        """
        return self.entities[index]

    def __len__(self) -> int:
        """Count of entities including destroyed entities."""
        return len(self.entities)

    def has_handle(self, handle: str) -> bool:
        """``True`` if `handle` is present, does filter destroyed entities."""
        assert isinstance(handle, str), type(handle)
        return any(e.dxf.handle == handle for e in self)

    def purge(self):
        """Remove all destroyed entities from entity space."""
        self.entities = list(self)

    def add(self, entity: DXFEntity) -> None:
        """Add `entity`."""
        assert isinstance(entity, DXFEntity), type(entity)
        assert entity.is_alive, "Can not store destroyed entities"
        self.entities.append(entity)

    def extend(self, entities: Iterable[DXFEntity]) -> None:
        """Add multiple `entities`."""
        for entity in entities:
            self.add(entity)

    def export_dxf(self, tagwriter: "TagWriter") -> None:
        """Export all entities into DXF file by `tagwriter`.

        (internal API)
        """
        for entity in iter(self):
            entity.export_dxf(tagwriter)

    def remove(self, entity: DXFEntity) -> None:
        """Remove `entity`."""
        self.entities.remove(entity)

    def clear(self) -> None:
        """Remove all entities."""
        # Do not destroy entities!
        self.entities = list()
<commit_msg>add type (ignore) annotations<commit_after># Copyright (c) 2019-2021, Manfred Moitzi
# License: MIT License
from typing import (
    Optional,
    Iterable,
    Tuple,
    TYPE_CHECKING,
    Dict,
    Set,
    List,
    Iterator,
)
from contextlib import contextmanager
from ezdxf.tools.handle import HandleGenerator
from ezdxf.lldxf.types import is_valid_handle
from ezdxf.entities.dxfentity import DXFEntity
from ezdxf.audit import AuditError, Auditor
from ezdxf.lldxf.const import DXFInternalEzdxfError
from ezdxf.entities import factory

if TYPE_CHECKING:
    from ezdxf.eztypes import TagWriter

DATABASE_EXCLUDE = {
    "SECTION",
    "ENDSEC",
    "EOF",
    "TABLE",
    "ENDTAB",
    "CLASS",
    "ACDSRECORD",
    "ACDSSCHEMA",
}

class EntityDB:
    """A simple key/entity database.

    Every entity/object, except tables and sections, are represented as
    DXFEntity or inherited types, this entities are stored in the
    DXF document database, database-key is the `handle` as string.

    """

    class Trashcan:
        """Store handles to entities which should be deleted later."""

        def __init__(self, db: "EntityDB"):
            self._database = db._database
            self._handles: Set[str] = set()

        def add(self, handle: str):
            """Put handle into trashcan to delete the entity later, this is
            required for deleting entities while iterating the database.
            """
            self._handles.add(handle)

        def clear(self):
            """Remove handles in trashcan from database and destroy entities if
            still alive.
            """
            db = self._database
            for handle in self._handles:
                entity = db.get(handle)
                if entity and entity.is_alive:
                    entity.destroy()

                if handle in db:
                    del db[handle]

            self._handles.clear()

    def __init__(self):
        self._database: Dict[str, DXFEntity] = {}
        # DXF handles of entities to delete later:
        self.handles = HandleGenerator()
        self.locked: bool = False  # used only for debugging

    def __getitem__(self, handle: str) -> DXFEntity:
        """Get entity by `handle`, does not filter destroyed entities nor
        entities in the trashcan.
        """
        return self._database[handle]

    def __setitem__(self, handle: str, entity: DXFEntity) -> None:
        """Set `entity` for `handle`."""
        assert isinstance(handle, str), type(handle)
        assert isinstance(entity, DXFEntity), type(entity)
        assert entity.is_alive, "Can not store destroyed entity."
        if self.locked:
            raise DXFInternalEzdxfError("Locked entity database.")

        if handle == "0" or not is_valid_handle(handle):
            raise ValueError(f"Invalid handle {handle}.")
        self._database[handle] = entity

    def __delitem__(self, handle: str) -> None:
        """Delete entity by `handle`. Removes entity only from database, does
        not destroy the entity.
        """
        if self.locked:
            raise DXFInternalEzdxfError("Locked entity database.")
        del self._database[handle]

    def __contains__(self, handle: str) -> bool:
        """``True`` if database contains `handle`."""
        if handle is None:
            return False
        assert isinstance(handle, str), type(handle)
        return handle in self._database

    def __len__(self) -> int:
        """Count of database items."""
        return len(self._database)

    def __iter__(self) -> Iterator[str]:
        """Iterable of all handles, does filter destroyed entities but not
        entities in the trashcan.
        """
        return self.keys()  # type: ignore

    def get(self, handle: str) -> Optional[DXFEntity]:
        """Returns entity for `handle` or ``None`` if no entry exist, does
        not filter destroyed entities.
        """
        return self._database.get(handle)

    def next_handle(self) -> str:
        """Returns next unique handle."""
        while True:
            handle = self.handles.next()
            if handle not in self._database:
                return handle

    def keys(self) -> Iterable[str]:
        """Iterable of all handles, does filter destroyed entities."""
        return (handle for handle, entity in self.items())

    def values(self) -> Iterable[DXFEntity]:
        """Iterable of all entities, does filter destroyed entities."""
        return (entity for handle, entity in self.items())

    def items(self) -> Iterable[Tuple[str, DXFEntity]]:
        """Iterable of all (handle, entities) pairs, does filter destroyed
        entities.
        """
        return (
            (handle, entity)
            for handle, entity in self._database.items()
            if entity.is_alive
        )

    def add(self, entity: DXFEntity) -> None:
        """Add `entity` to database, assigns a new handle to the `entity`
        if :attr:`entity.dxf.handle` is ``None``. Adding the same entity
        multiple times is possible and creates only a single database entry.

        """
        if entity.dxftype() in DATABASE_EXCLUDE:
            if entity.dxf.handle is not None:
                # Mark existing entity handle as used to avoid
                # reassigning the same handle again.
                self[entity.dxf.handle] = entity
            return
        handle: str = entity.dxf.handle
        if handle is None:
            handle = self.next_handle()
            entity.update_handle(handle)
        self[handle] = entity

        # Add sub entities ATTRIB, VERTEX and SEQEND to database:
        # Add linked MTEXT columns to database:
        if hasattr(entity, "add_sub_entities_to_entitydb"):
            entity.add_sub_entities_to_entitydb(self)  # type: ignore

    def delete_entity(self, entity: DXFEntity) -> None:
        """Remove `entity` from database and destroy the `entity`."""
        if entity.is_alive:
            del self[entity.dxf.handle]
            entity.destroy()

    def discard(self, entity: DXFEntity) -> None:
        """Discard `entity` from database without destroying the `entity`."""
        if entity.is_alive:
            if hasattr(entity, "process_sub_entities"):
                entity.process_sub_entities(lambda e: self.discard(e))  # type: ignore

            handle = entity.dxf.handle
            try:
                del self._database[handle]
                entity.dxf.handle = None
            except KeyError:
                pass

    def duplicate_entity(self, entity: DXFEntity) -> DXFEntity:
        """Duplicates `entity` and its sub entities (VERTEX, ATTRIB, SEQEND)
        and store them with new handles in the entity database.
        Graphical entities have to be added to a layout by
        :meth:`~ezdxf.layouts.BaseLayout.add_entity`.

        To import DXF entities from another drawing use the
        :class:`~ezdxf.addons.importer.Importer` add-on.

        A new owner handle will be set by adding the duplicated entity to a
        layout.

        """
        new_entity: DXFEntity = entity.copy()
        new_entity.dxf.handle = self.next_handle()
        factory.bind(new_entity, entity.doc)  # type: ignore
        return new_entity

    def audit(self, auditor: "Auditor"):
        """Restore database integrity:

        - restore database entries with modified handles (key != entity.dxf.handle)
        - remove entities with invalid handles
        - empty trashcan - destroy all entities in the trashcan
        - removes destroyed database entries (purge)

        """
        assert self.locked is False, "Database is locked!"
        add_entities = []

        with self.trashcan() as trash:  # type: ignore
            for handle, entity in self.items():
                # Destroyed entities are already filtered!
                if not is_valid_handle(handle):
                    auditor.fixed_error(
                        code=AuditError.INVALID_ENTITY_HANDLE,
                        message=f"Removed entity {entity.dxftype()} with invalid "
                        f'handle "{handle}" from entity database.',
                    )
                    trash.add(handle)
                if handle != entity.dxf.get("handle"):
                    # database handle != stored entity handle
                    # prevent entity from being destroyed:
                    self._database[handle] = None  # type: ignore
                    trash.add(handle)
                    add_entities.append(entity)

        # Remove all destroyed entities from database:
        self.purge()

        for entity in add_entities:
            handle = entity.dxf.get("handle")
            if handle is None:
                auditor.fixed_error(
                    code=AuditError.INVALID_ENTITY_HANDLE,
                    message=f"Removed entity {entity.dxftype()} without handle "
                    f"from entity database.",
                )
                continue
            if not is_valid_handle(handle) or handle == "0":
                auditor.fixed_error(
                    code=AuditError.INVALID_ENTITY_HANDLE,
                    message=f"Removed entity {entity.dxftype()} with invalid "
                    f'handle "{handle}" from entity database.',
                )
                continue
            self[handle] = entity

    def new_trashcan(self) -> "EntityDB.Trashcan":
        """Returns a new trashcan, empty trashcan manually by: :
        func:`Trashcan.clear()`.
        """
        return EntityDB.Trashcan(self)

    @contextmanager  # type: ignore
    def trashcan(self) -> "EntityDB.Trashcan":  # type: ignore
        """Returns a new trashcan in context manager mode, trashcan will be
        emptied when leaving context.
        """
        trashcan_ = self.new_trashcan()
        yield trashcan_
        # try ... finally is not required, in case of an exception the database
        # is maybe already in an unreliable state.
        trashcan_.clear()

    def purge(self) -> None:
        """Remove all destroyed entities from database, but does not empty the
        trashcan.
        """
        # Important: operate on underlying data structure:
        db = self._database
        dead_handles = [
            handle for handle, entity in db.items() if not entity.is_alive
        ]
        for handle in dead_handles:
            del db[handle]

    def dxf_types_in_use(self) -> Set[str]:
        return set(entity.dxftype() for entity in self.values())

class EntitySpace:
    """
    An :class:`EntitySpace` is a collection of :class:`~ezdxf.entities.DXFEntity`
    objects, that stores only  references to :class:`DXFEntity` objects.

    The :class:`~ezdxf.layouts.Modelspace`, any :class:`~ezdxf.layouts.Paperspace`
    layout and :class:`~ezdxf.layouts.BlockLayout` objects have an
    :class:`EntitySpace` container to store their entities.

    """

    def __init__(self, entities: Iterable[DXFEntity] = None):
        self.entities: List[DXFEntity] = (
            list(e for e in entities if e.is_alive) if entities else []
        )

    def __iter__(self) -> Iterator[DXFEntity]:
        """Iterable of all entities, filters destroyed entities."""
        return (e for e in self.entities if e.is_alive)

    def __getitem__(self, index) -> DXFEntity:
        """Get entity at index `item`

        :class:`EntitySpace` has a standard Python list like interface,
        therefore `index` can be any valid list indexing or slicing term, like
        a single index ``layout[-1]`` to get the last entity, or an index slice
        ``layout[:10]`` to get the first 10 or less entities as
        ``List[DXFEntity]``. Does not filter destroyed entities.

        """
        return self.entities[index]

    def __len__(self) -> int:
        """Count of entities including destroyed entities."""
        return len(self.entities)

    def has_handle(self, handle: str) -> bool:
        """``True`` if `handle` is present, does filter destroyed entities."""
        assert isinstance(handle, str), type(handle)
        return any(e.dxf.handle == handle for e in self)

    def purge(self):
        """Remove all destroyed entities from entity space."""
        self.entities = list(self)

    def add(self, entity: DXFEntity) -> None:
        """Add `entity`."""
        assert isinstance(entity, DXFEntity), type(entity)
        assert entity.is_alive, "Can not store destroyed entities"
        self.entities.append(entity)

    def extend(self, entities: Iterable[DXFEntity]) -> None:
        """Add multiple `entities`."""
        for entity in entities:
            self.add(entity)

    def export_dxf(self, tagwriter: "TagWriter") -> None:
        """Export all entities into DXF file by `tagwriter`.

        (internal API)
        """
        for entity in iter(self):
            entity.export_dxf(tagwriter)

    def remove(self, entity: DXFEntity) -> None:
        """Remove `entity`."""
        self.entities.remove(entity)

    def clear(self) -> None:
        """Remove all entities."""
        # Do not destroy entities!
        self.entities = list()
<|endoftext|>


<commit_before>#!/usr/bin/env python

import ast

# All the filters
from src.filters import *

class FilterManager:

    def __init__(self):
        # Build filters
        self.filters = dict()
        self.filters['cds_length_range'] = CDSLengthRangeFilter()
        self.filters['exon_length_range'] = ExonLengthRangeFilter()
        self.filters['intron_length_range'] = IntronLengthRangeFilter()

        # Build args
        self.filter_args = dict()
        for filt_name, filt in self.filters.items():
            self.filter_args[filt_name] = [attr for attr in dir(filt) if not callable(getattr(filt, attr)) and not attr.startswith("__")]

        # Starts out dirty
        self.dirty = False

    def apply_filters(self, seq):
        for filt in self.filters.values():
            filt.apply(seq)

    def set_filter_arg(self, filter_name, filter_arg, val):
        val = ast.literal_eval(val)
        if self.get_filter_arg(filter_name, filter_arg) != val:
            self.dirty = True
        setattr(self.filters[filter_name], filter_arg, val)

    def get_filter_arg(self, filter_name, filter_arg):
        return getattr(self.filters[filter_name], filter_arg)

    def get_filter_help(self, filter_name = ""):
        # If there is no filter name, supply all of the filters' args
        if filter_name == "":
            args_help = ''
            for filt_name, args in self.filter_args.items():
                args_help += filt_name+": " + ", ".join(args)
            return args_help
        elif filter_name in self.filters:
            return filter_name+": " + ", ".join(self.filter_args[filter_name])
        else:
            return "No such filter: "+filter_name

<commit_msg>FilterManager.get_filter_help now puts in newlines like it should<commit_after>#!/usr/bin/env python

import ast

# All the filters
from src.filters import *

class FilterManager:

    def __init__(self):
        # Build filters
        self.filters = dict()
        self.filters['cds_length_range'] = CDSLengthRangeFilter()
        self.filters['exon_length_range'] = ExonLengthRangeFilter()
        self.filters['intron_length_range'] = IntronLengthRangeFilter()

        # Build args
        self.filter_args = dict()
        for filt_name, filt in self.filters.items():
            self.filter_args[filt_name] = [attr for attr in dir(filt) if not callable(getattr(filt, attr)) and not attr.startswith("__")]

        # Starts out dirty
        self.dirty = False

    def apply_filters(self, seq):
        for filt in self.filters.values():
            filt.apply(seq)

    def set_filter_arg(self, filter_name, filter_arg, val):
        val = ast.literal_eval(val)
        if self.get_filter_arg(filter_name, filter_arg) != val:
            self.dirty = True
        setattr(self.filters[filter_name], filter_arg, val)

    def get_filter_arg(self, filter_name, filter_arg):
        return getattr(self.filters[filter_name], filter_arg)

    def get_filter_help(self, filter_name = ""):
        # If there is no filter name, supply all of the filters' args
        if filter_name == "":
            args_help = ''
            for filt_name, args in self.filter_args.items():
                args_help += filt_name+": " + ", ".join(args)+'\n'
            return args_help
        elif filter_name in self.filters:
            return filter_name+": " + ", ".join(self.filter_args[filter_name])
        else:
            return "No such filter: "+filter_name

<|endoftext|>


<commit_before>import logging
import socket
import time

import requests

log = logging.getLogger(__name__)

class FMEServer(object):
    def __init__(self, server_name, instance_id, api_token):
        self.api_token = api_token
        self.instance_id = instance_id
        self.server_name = server_name

    def _in_dns(self) -> bool:
        """
        Checks if `self.server_name` is in DNS / name resolves to IP
        :return: bool
        """
        try:
            socket.gethostbyname(self.server_name.split('//')[-1])
            log.debug('DNS is available for server')
            return True
        except socket.gaierror:
            log.warning("No DNS available for server")
            return False

    def _headers(self) -> dict:
        """
        Returns the auth header
        :return: dict
        """
        return {'Authorization': 'Bearer {FME_SERVER_API}'.format(FME_SERVER_API=self.api_token)}

    def _url(self, path=None) -> str:
        """
        Returns the FME server management uinstance url
        :param path:
        :return:
        """
        return 'https://api.fmecloud.safe.com/v1/instances/{}{}'.format(self.instance_id, path or "")

    def get_status(self) -> str:
        res = requests.get(self._url(), headers=self._headers())
        res.raise_for_status()

        status = res.json()['state']
        log.debug("Current server status: %s", status)
        return status

    def start(self):
        """
        Start the FME server instance
        :return:
        """
        if self.get_status() != "RUNNING":
            # Only start the service when it is not running
            log.info("Starting server %s", self.server_name)
            res = requests.put(self._url("/start"), headers=self._headers())
            res.raise_for_status()

            while self.get_status() in ['PENDING', 'STOPPING', 'PAUSED']:
                time.sleep(1)

            if self.get_status() == 'RUNNING':
                log.debug("Already running")
                return

            while self.get_status() != 'RUNNING':
                time.sleep(10)

            log.debug("Waiting for DNS availability of server")

            while not self._in_dns():
                time.sleep(2)

        log.info("Server started")

    def stop(self):
        """
        Stop the FME server instance
        :return:
        """
        log.info("Stopping server")
        while self.get_status() in ['PENDING', 'STOPPING']:
            time.sleep(1)

        if self.get_status() != "RUNNING":
            log.debug("Not running, cannot stop")
            return

        res = requests.put(self._url("/pause"), headers=self._headers())
        res.raise_for_status()

        while self.get_status() != 'PAUSED':
            time.sleep(10)

        log.info("Server paused")
<commit_msg>Naar aanleiding op security change van Safe.com aanpassing op: 'Authorization': 'bearer {FME_SERVER_API}'.format(FME_SERVER_API=self.api_token) namelijk bearer change in capital 'B' terug gedraaid naar bearer.<commit_after>import logging
import socket
import time

import requests

log = logging.getLogger(__name__)

class FMEServer(object):
    def __init__(self, server_name, instance_id, api_token):
        self.api_token = api_token
        self.instance_id = instance_id
        self.server_name = server_name

    def _in_dns(self) -> bool:
        """
        Checks if `self.server_name` is in DNS / name resolves to IP
        :return: bool
        """
        try:
            socket.gethostbyname(self.server_name.split('//')[-1])
            log.debug('DNS is available for server')
            return True
        except socket.gaierror:
            log.warning("No DNS available for server")
            return False

    def _headers(self) -> dict:
        """
        Returns the auth header
        :return: dict
        """
        return {'Authorization': 'bearer {FME_SERVER_API}'.format(FME_SERVER_API=self.api_token)}

    def _url(self, path=None) -> str:
        """
        Returns the FME server management uinstance url
        :param path:
        :return:
        """
        return 'https://api.fmecloud.safe.com/v1/instances/{}{}'.format(self.instance_id, path or "")

    def get_status(self) -> str:
        res = requests.get(self._url(), headers=self._headers())
        res.raise_for_status()

        status = res.json()['state']
        log.debug("Current server status: %s", status)
        return status

    def start(self):
        """
        Start the FME server instance
        :return:
        """
        if self.get_status() != "RUNNING":
            # Only start the service when it is not running
            log.info("Starting server %s", self.server_name)
            res = requests.put(self._url("/start"), headers=self._headers())
            res.raise_for_status()

            while self.get_status() in ['PENDING', 'STOPPING', 'PAUSED']:
                time.sleep(1)

            if self.get_status() == 'RUNNING':
                log.debug("Already running")
                return

            while self.get_status() != 'RUNNING':
                time.sleep(10)

            log.debug("Waiting for DNS availability of server")

            while not self._in_dns():
                time.sleep(2)

        log.info("Server started")

    def stop(self):
        """
        Stop the FME server instance
        :return:
        """
        log.info("Stopping server")
        while self.get_status() in ['PENDING', 'STOPPING']:
            time.sleep(1)

        if self.get_status() != "RUNNING":
            log.debug("Not running, cannot stop")
            return

        res = requests.put(self._url("/pause"), headers=self._headers())
        res.raise_for_status()

        while self.get_status() != 'PAUSED':
            time.sleep(10)

        log.info("Server paused")
<|endoftext|>


<commit_before>import asyncio
import logging
import pytest
import re

from aioredis import (
    RedisPool,
    ReplyError,
    PoolClosedError,
    ConnectionClosedError,
    )

def _assert_defaults(pool):
    assert isinstance(pool, RedisPool)
    assert pool.minsize == 1
    assert pool.maxsize == 10
    assert pool.size == 1
    assert pool.freesize == 1

def test_connect(pool):
    _assert_defaults(pool)

def test_global_loop(create_pool, loop, server):
    asyncio.set_event_loop(loop)

    pool = loop.run_until_complete(create_pool(
        server.tcp_address))
    _assert_defaults(pool)

@pytest.mark.run_loop
def test_clear(pool):
    _assert_defaults(pool)

    yield from pool.clear()
    assert pool.freesize == 0

@pytest.mark.run_loop
@pytest.mark.parametrize('minsize', [None, -100, 0.0, 100])
def test_minsize(minsize, create_pool, loop, server):

    with pytest.raises(AssertionError):
        yield from create_pool(
            server.tcp_address,
            minsize=minsize, maxsize=10, loop=loop)

@pytest.mark.run_loop
@pytest.mark.parametrize('maxsize', [None, -100, 0.0, 1])
def test_maxsize(maxsize, create_pool, loop, server):

    with pytest.raises(AssertionError):
        yield from create_pool(
            server.tcp_address,
            minsize=2, maxsize=maxsize, loop=loop)

def test_no_yield_from(pool):
    with pytest.raises(RuntimeError):
        with pool:
            pass    # pragma: no cover

@pytest.mark.run_loop
def test_simple_command(create_pool, loop, server):
    pool = yield from create_pool(
        server.tcp_address,
        minsize=10, loop=loop)

    with (yield from pool) as conn:
        msg = yield from conn.echo('hello')
        assert msg == b'hello'
        assert pool.size == 10
        assert pool.freesize == 9
    assert pool.size == 10
    assert pool.freesize == 10

@pytest.mark.run_loop
def test_create_new(create_pool, loop, server):
    pool = yield from create_pool(
        server.tcp_address,
        minsize=1, loop=loop)
    assert pool.size == 1
    assert pool.freesize == 1

    with (yield from pool):
        assert pool.size == 1
        assert pool.freesize == 0

        with (yield from pool):
            assert pool.size == 2
            assert pool.freesize == 0

    assert pool.size == 2
    assert pool.freesize == 2

@pytest.mark.run_loop
def test_create_constraints(create_pool, loop, server):
    pool = yield from create_pool(
        server.tcp_address,
        minsize=1, maxsize=1, loop=loop)
    assert pool.size == 1
    assert pool.freesize == 1

    with (yield from pool):
        assert pool.size == 1
        assert pool.freesize == 0

        with pytest.raises(asyncio.TimeoutError):
            yield from asyncio.wait_for(pool.acquire(),
                                        timeout=0.2,
                                        loop=loop)

@pytest.mark.run_loop
def test_create_no_minsize(create_pool, loop, server):
    pool = yield from create_pool(
        server.tcp_address,
        minsize=0, maxsize=1, loop=loop)
    assert pool.size == 0
    assert pool.freesize == 0

    with (yield from pool):
        assert pool.size == 1
        assert pool.freesize == 0

        with pytest.raises(asyncio.TimeoutError):
            yield from asyncio.wait_for(pool.acquire(),
                                        timeout=0.2,
                                        loop=loop)
    assert pool.size == 1
    assert pool.freesize == 1

@pytest.mark.run_loop
def test_release_closed(create_pool, loop, server):
    pool = yield from create_pool(
        server.tcp_address,
        minsize=1, loop=loop)
    assert pool.size == 1
    assert pool.freesize == 1

    with (yield from pool) as redis:
        redis.close()
        yield from redis.wait_closed()
    assert pool.size == 0
    assert pool.freesize == 0

@pytest.mark.run_loop
def test_release_bad_connection(create_pool, create_redis, loop, server):
    pool = yield from create_pool(
        server.tcp_address,
        loop=loop)
    conn = yield from pool.acquire()
    other_conn = yield from create_redis(
        server.tcp_address,
        loop=loop)
    with pytest.raises(AssertionError):
        pool.release(other_conn)

    pool.release(conn)
    other_conn.close()
    yield from other_conn.wait_closed()

@pytest.mark.run_loop
def test_select_db(create_pool, loop, server):
    pool = yield from create_pool(
        server.tcp_address,
        loop=loop)

    yield from pool.select(1)
    with (yield from pool) as redis:
        assert redis.db == 1

@pytest.mark.run_loop
def test_change_db(create_pool, loop, server):
    pool = yield from create_pool(
        server.tcp_address,
        minsize=1, db=0,
        loop=loop)
    assert pool.size == 1
    assert pool.freesize == 1

    with (yield from pool) as redis:
        yield from redis.select(1)
    assert pool.size == 0
    assert pool.freesize == 0

    with (yield from pool) as redis:
        assert pool.size == 1
        assert pool.freesize == 0

        yield from pool.select(1)
        assert pool.db == 1
        assert pool.size == 1
        assert pool.freesize == 0
    assert pool.size == 0
    assert pool.freesize == 0
    assert pool.db == 1

@pytest.mark.run_loop
def test_change_db_errors(create_pool, loop, server):
    pool = yield from create_pool(
        server.tcp_address,
        minsize=1, db=0,
        loop=loop)

    with pytest.raises(TypeError):
        yield from pool.select(None)
    assert pool.db == 0

    with (yield from pool):
        pass
    assert pool.size == 1
    assert pool.freesize == 1

    with pytest.raises(TypeError):
        yield from pool.select(None)
    assert pool.db == 0
    with pytest.raises(ValueError):
        yield from pool.select(-1)
    assert pool.db == 0
    with pytest.raises(ReplyError):
        yield from pool.select(100000)
    assert pool.db == 0

@pytest.mark.run_loop
def test_select_and_create(create_pool, loop, server):
    # trying to model situation when select and acquire
    # called simultaneously
    # but acquire freezes on _wait_select and
    # then continues with propper db
    @asyncio.coroutine
    def test():
        pool = yield from create_pool(
            server.tcp_address,
            minsize=1, db=0,
            loop=loop)
        db = 0
        while True:
            db = (db + 1) & 1
            _, conn = yield from asyncio.gather(pool.select(db),
                                                pool.acquire(),
                                                loop=loop)
            assert pool.db == db
            pool.release(conn)
            if conn.db == db:
                break
    yield from asyncio.wait_for(test(), 1, loop=loop)

@pytest.mark.run_loop
def test_response_decoding(create_pool, loop, server):
    pool = yield from create_pool(
        server.tcp_address,
        encoding='utf-8', loop=loop)

    assert pool.encoding == 'utf-8'
    with (yield from pool) as redis:
        yield from redis.set('key', 'value')
    with (yield from pool) as redis:
        res = yield from redis.get('key')
        assert res == 'value'

@pytest.mark.run_loop
def test_hgetall_response_decoding(create_pool, loop, server):
    pool = yield from create_pool(
        server.tcp_address,
        encoding='utf-8', loop=loop)

    assert pool.encoding == 'utf-8'
    with (yield from pool) as redis:
        yield from redis.delete('key1')
        yield from redis.hmset('key1', 'foo', 'bar')
        yield from redis.hmset('key1', 'baz', 'zap')
    with (yield from pool) as redis:
        res = yield from redis.hgetall('key1')
        assert res == {'foo': 'bar', 'baz': 'zap'}

@pytest.mark.run_loop
def test_crappy_multiexec(create_pool, loop, server):
    pool = yield from create_pool(
        server.tcp_address,
        encoding='utf-8', loop=loop,
        minsize=1, maxsize=1)

    with (yield from pool) as redis:
        yield from redis.set('abc', 'def')
        yield from redis.connection.execute('multi')
        yield from redis.set('abc', 'fgh')
    assert redis.closed is True
    with (yield from pool) as redis:
        value = yield from redis.get('abc')
    assert value == 'def'

@pytest.mark.run_loop
def test_pool_size_growth(create_pool, server, loop):
    pool = yield from create_pool(
        server.tcp_address,
        loop=loop,
        minsize=1, maxsize=1)

    done = set()
    tasks = []

    @asyncio.coroutine
    def task1(i):
        with (yield from pool):
            assert pool.size <= pool.maxsize
            assert pool.freesize == 0
            yield from asyncio.sleep(0.2, loop=loop)
            done.add(i)

    @asyncio.coroutine
    def task2():
        with (yield from pool):
            assert pool.size <= pool.maxsize
            assert pool.freesize >= 0
            assert done == {0, 1}

    for _ in range(2):
        tasks.append(asyncio.async(task1(_), loop=loop))
    tasks.append(asyncio.async(task2(), loop=loop))
    yield from asyncio.gather(*tasks, loop=loop)

@pytest.mark.run_loop
def test_pool_with_closed_connections(create_pool, server, loop):
    pool = yield from create_pool(
        server.tcp_address,
        loop=loop,
        minsize=1, maxsize=2)
    assert 1 == pool.freesize
    conn1 = pool._pool[0]
    conn1.close()
    assert conn1.closed is True
    assert 1 == pool.freesize
    with (yield from pool) as conn2:
        assert conn2.closed is False
        assert conn1 is not conn2

@pytest.mark.run_loop
def test_pool_close(create_pool, server, loop):
    pool = yield from create_pool(
        server.tcp_address, loop=loop)

    assert pool.closed is False

    with (yield from pool) as redis:
        assert (yield from redis.ping()) == b'PONG'

    pool.close()
    yield from pool.wait_closed()
    assert pool.closed is True

    with pytest.raises(PoolClosedError):
        with (yield from pool) as redis:
            assert (yield from redis.ping()) == b'PONG'

@pytest.mark.run_loop
def test_pool_close__used(create_pool, server, loop):
    pool = yield from create_pool(
        server.tcp_address, loop=loop)

    assert pool.closed is False

    with (yield from pool) as redis:
        pool.close()
        yield from pool.wait_closed()
        assert pool.closed is True

        with pytest.raises(ConnectionClosedError):
            yield from redis.ping()

@pytest.mark.run_loop
def test_pool_check_closed_when_exception(create_pool, server, loop):
    exception = False

    class TrackExceptionRecord(logging.Handler):
        def __init__(self):
            logging.Handler.__init__(self)
            self.has_closed_msg = False

        def shouldFlush(self, record):
            return True

        def emit(self, record):
            if re.match("Closed [0-9]+ connections", record.getMessage()):
                self.has_closed_msg = True

        def flush(self):
            pass

        def close(self):
            pass

    handler = TrackExceptionRecord()
    logger = logging.getLogger('aioredis')
    logger.setLevel(logging.DEBUG)
    logger.addHandler(handler)

    try:
        yield from create_pool(address=('127.3.2.1', 9999), loop=loop)
    except:
        exception = True

    assert exception is True

    yield from asyncio.sleep(0, result=None, loop=loop)
    assert handler.has_closed_msg is True

    logger.removeHandler(handler)
<commit_msg>Fix converage checks (pool_test)<commit_after>import asyncio
import logging
import pytest
import re

from aioredis import (
    RedisPool,
    ReplyError,
    PoolClosedError,
    ConnectionClosedError,
    )

def _assert_defaults(pool):
    assert isinstance(pool, RedisPool)
    assert pool.minsize == 1
    assert pool.maxsize == 10
    assert pool.size == 1
    assert pool.freesize == 1

def test_connect(pool):
    _assert_defaults(pool)

def test_global_loop(create_pool, loop, server):
    asyncio.set_event_loop(loop)

    pool = loop.run_until_complete(create_pool(
        server.tcp_address))
    _assert_defaults(pool)

@pytest.mark.run_loop
def test_clear(pool):
    _assert_defaults(pool)

    yield from pool.clear()
    assert pool.freesize == 0

@pytest.mark.run_loop
@pytest.mark.parametrize('minsize', [None, -100, 0.0, 100])
def test_minsize(minsize, create_pool, loop, server):

    with pytest.raises(AssertionError):
        yield from create_pool(
            server.tcp_address,
            minsize=minsize, maxsize=10, loop=loop)

@pytest.mark.run_loop
@pytest.mark.parametrize('maxsize', [None, -100, 0.0, 1])
def test_maxsize(maxsize, create_pool, loop, server):

    with pytest.raises(AssertionError):
        yield from create_pool(
            server.tcp_address,
            minsize=2, maxsize=maxsize, loop=loop)

def test_no_yield_from(pool):
    with pytest.raises(RuntimeError):
        with pool:
            pass    # pragma: no cover

@pytest.mark.run_loop
def test_simple_command(create_pool, loop, server):
    pool = yield from create_pool(
        server.tcp_address,
        minsize=10, loop=loop)

    with (yield from pool) as conn:
        msg = yield from conn.echo('hello')
        assert msg == b'hello'
        assert pool.size == 10
        assert pool.freesize == 9
    assert pool.size == 10
    assert pool.freesize == 10

@pytest.mark.run_loop
def test_create_new(create_pool, loop, server):
    pool = yield from create_pool(
        server.tcp_address,
        minsize=1, loop=loop)
    assert pool.size == 1
    assert pool.freesize == 1

    with (yield from pool):
        assert pool.size == 1
        assert pool.freesize == 0

        with (yield from pool):
            assert pool.size == 2
            assert pool.freesize == 0

    assert pool.size == 2
    assert pool.freesize == 2

@pytest.mark.run_loop
def test_create_constraints(create_pool, loop, server):
    pool = yield from create_pool(
        server.tcp_address,
        minsize=1, maxsize=1, loop=loop)
    assert pool.size == 1
    assert pool.freesize == 1

    with (yield from pool):
        assert pool.size == 1
        assert pool.freesize == 0

        with pytest.raises(asyncio.TimeoutError):
            yield from asyncio.wait_for(pool.acquire(),
                                        timeout=0.2,
                                        loop=loop)

@pytest.mark.run_loop
def test_create_no_minsize(create_pool, loop, server):
    pool = yield from create_pool(
        server.tcp_address,
        minsize=0, maxsize=1, loop=loop)
    assert pool.size == 0
    assert pool.freesize == 0

    with (yield from pool):
        assert pool.size == 1
        assert pool.freesize == 0

        with pytest.raises(asyncio.TimeoutError):
            yield from asyncio.wait_for(pool.acquire(),
                                        timeout=0.2,
                                        loop=loop)
    assert pool.size == 1
    assert pool.freesize == 1

@pytest.mark.run_loop
def test_release_closed(create_pool, loop, server):
    pool = yield from create_pool(
        server.tcp_address,
        minsize=1, loop=loop)
    assert pool.size == 1
    assert pool.freesize == 1

    with (yield from pool) as redis:
        redis.close()
        yield from redis.wait_closed()
    assert pool.size == 0
    assert pool.freesize == 0

@pytest.mark.run_loop
def test_release_bad_connection(create_pool, create_redis, loop, server):
    pool = yield from create_pool(
        server.tcp_address,
        loop=loop)
    conn = yield from pool.acquire()
    other_conn = yield from create_redis(
        server.tcp_address,
        loop=loop)
    with pytest.raises(AssertionError):
        pool.release(other_conn)

    pool.release(conn)
    other_conn.close()
    yield from other_conn.wait_closed()

@pytest.mark.run_loop
def test_select_db(create_pool, loop, server):
    pool = yield from create_pool(
        server.tcp_address,
        loop=loop)

    yield from pool.select(1)
    with (yield from pool) as redis:
        assert redis.db == 1

@pytest.mark.run_loop
def test_change_db(create_pool, loop, server):
    pool = yield from create_pool(
        server.tcp_address,
        minsize=1, db=0,
        loop=loop)
    assert pool.size == 1
    assert pool.freesize == 1

    with (yield from pool) as redis:
        yield from redis.select(1)
    assert pool.size == 0
    assert pool.freesize == 0

    with (yield from pool) as redis:
        assert pool.size == 1
        assert pool.freesize == 0

        yield from pool.select(1)
        assert pool.db == 1
        assert pool.size == 1
        assert pool.freesize == 0
    assert pool.size == 0
    assert pool.freesize == 0
    assert pool.db == 1

@pytest.mark.run_loop
def test_change_db_errors(create_pool, loop, server):
    pool = yield from create_pool(
        server.tcp_address,
        minsize=1, db=0,
        loop=loop)

    with pytest.raises(TypeError):
        yield from pool.select(None)
    assert pool.db == 0

    with (yield from pool):
        pass
    assert pool.size == 1
    assert pool.freesize == 1

    with pytest.raises(TypeError):
        yield from pool.select(None)
    assert pool.db == 0
    with pytest.raises(ValueError):
        yield from pool.select(-1)
    assert pool.db == 0
    with pytest.raises(ReplyError):
        yield from pool.select(100000)
    assert pool.db == 0

@pytest.mark.run_loop
def test_select_and_create(create_pool, loop, server):
    # trying to model situation when select and acquire
    # called simultaneously
    # but acquire freezes on _wait_select and
    # then continues with propper db
    @asyncio.coroutine
    def test():
        pool = yield from create_pool(
            server.tcp_address,
            minsize=1, db=0,
            loop=loop)
        db = 0
        while True:
            db = (db + 1) & 1
            _, conn = yield from asyncio.gather(pool.select(db),
                                                pool.acquire(),
                                                loop=loop)
            assert pool.db == db
            pool.release(conn)
            if conn.db == db:
                break
    yield from asyncio.wait_for(test(), 1, loop=loop)

@pytest.mark.run_loop
def test_response_decoding(create_pool, loop, server):
    pool = yield from create_pool(
        server.tcp_address,
        encoding='utf-8', loop=loop)

    assert pool.encoding == 'utf-8'
    with (yield from pool) as redis:
        yield from redis.set('key', 'value')
    with (yield from pool) as redis:
        res = yield from redis.get('key')
        assert res == 'value'

@pytest.mark.run_loop
def test_hgetall_response_decoding(create_pool, loop, server):
    pool = yield from create_pool(
        server.tcp_address,
        encoding='utf-8', loop=loop)

    assert pool.encoding == 'utf-8'
    with (yield from pool) as redis:
        yield from redis.delete('key1')
        yield from redis.hmset('key1', 'foo', 'bar')
        yield from redis.hmset('key1', 'baz', 'zap')
    with (yield from pool) as redis:
        res = yield from redis.hgetall('key1')
        assert res == {'foo': 'bar', 'baz': 'zap'}

@pytest.mark.run_loop
def test_crappy_multiexec(create_pool, loop, server):
    pool = yield from create_pool(
        server.tcp_address,
        encoding='utf-8', loop=loop,
        minsize=1, maxsize=1)

    with (yield from pool) as redis:
        yield from redis.set('abc', 'def')
        yield from redis.connection.execute('multi')
        yield from redis.set('abc', 'fgh')
    assert redis.closed is True
    with (yield from pool) as redis:
        value = yield from redis.get('abc')
    assert value == 'def'

@pytest.mark.run_loop
def test_pool_size_growth(create_pool, server, loop):
    pool = yield from create_pool(
        server.tcp_address,
        loop=loop,
        minsize=1, maxsize=1)

    done = set()
    tasks = []

    @asyncio.coroutine
    def task1(i):
        with (yield from pool):
            assert pool.size <= pool.maxsize
            assert pool.freesize == 0
            yield from asyncio.sleep(0.2, loop=loop)
            done.add(i)

    @asyncio.coroutine
    def task2():
        with (yield from pool):
            assert pool.size <= pool.maxsize
            assert pool.freesize >= 0
            assert done == {0, 1}

    for _ in range(2):
        tasks.append(asyncio.async(task1(_), loop=loop))
    tasks.append(asyncio.async(task2(), loop=loop))
    yield from asyncio.gather(*tasks, loop=loop)

@pytest.mark.run_loop
def test_pool_with_closed_connections(create_pool, server, loop):
    pool = yield from create_pool(
        server.tcp_address,
        loop=loop,
        minsize=1, maxsize=2)
    assert 1 == pool.freesize
    conn1 = pool._pool[0]
    conn1.close()
    assert conn1.closed is True
    assert 1 == pool.freesize
    with (yield from pool) as conn2:
        assert conn2.closed is False
        assert conn1 is not conn2

@pytest.mark.run_loop
def test_pool_close(create_pool, server, loop):
    pool = yield from create_pool(
        server.tcp_address, loop=loop)

    assert pool.closed is False

    with (yield from pool) as redis:
        assert (yield from redis.ping()) == b'PONG'

    pool.close()
    yield from pool.wait_closed()
    assert pool.closed is True

    with pytest.raises(PoolClosedError):
        with (yield from pool) as redis:
            assert (yield from redis.ping()) == b'PONG'

@pytest.mark.run_loop
def test_pool_close__used(create_pool, server, loop):
    pool = yield from create_pool(
        server.tcp_address, loop=loop)

    assert pool.closed is False

    with (yield from pool) as redis:
        pool.close()
        yield from pool.wait_closed()
        assert pool.closed is True

        with pytest.raises(ConnectionClosedError):
            yield from redis.ping()

@pytest.mark.run_loop
def test_pool_check_closed_when_exception(create_pool, server, loop):
    exception = False

    class TrackExceptionRecord(logging.Handler):
        def __init__(self):
            logging.Handler.__init__(self)
            self.has_closed_msg = False

        def emit(self, record):
            if re.match("Closed [0-9]+ connections", record.getMessage()):
                self.has_closed_msg = True

    handler = TrackExceptionRecord()
    logger = logging.getLogger('aioredis')
    logger.setLevel(logging.DEBUG)
    logger.addHandler(handler)

    try:
        yield from create_pool(address=('127.3.2.1', 9999), loop=loop)
    except:
        exception = True

    assert exception is True

    yield from asyncio.sleep(0, result=None, loop=loop)
    assert handler.has_closed_msg is True

    logger.removeHandler(handler)
<|endoftext|>


<commit_before>import asyncio
import logging
import pytest
import re

from aioredis import (
    RedisPool,
    ReplyError,
    PoolClosedError,
    ConnectionClosedError,
    )

def _assert_defaults(pool):
    assert isinstance(pool, RedisPool)
    assert pool.minsize == 1
    assert pool.maxsize == 10
    assert pool.size == 1
    assert pool.freesize == 1

def test_connect(pool):
    _assert_defaults(pool)

def test_global_loop(create_pool, loop, server):
    asyncio.set_event_loop(loop)

    pool = loop.run_until_complete(create_pool(
        server.tcp_address))
    _assert_defaults(pool)

@pytest.mark.run_loop
def test_clear(pool):
    _assert_defaults(pool)

    yield from pool.clear()
    assert pool.freesize == 0

@pytest.mark.run_loop
@pytest.mark.parametrize('minsize', [None, -100, 0.0, 100])
def test_minsize(minsize, create_pool, loop, server):

    with pytest.raises(AssertionError):
        yield from create_pool(
            server.tcp_address,
            minsize=minsize, maxsize=10, loop=loop)

@pytest.mark.run_loop
@pytest.mark.parametrize('maxsize', [None, -100, 0.0, 1])
def test_maxsize(maxsize, create_pool, loop, server):

    with pytest.raises(AssertionError):
        yield from create_pool(
            server.tcp_address,
            minsize=2, maxsize=maxsize, loop=loop)

def test_no_yield_from(pool):
    with pytest.raises(RuntimeError):
        with pool:
            pass    # pragma: no cover

@pytest.mark.run_loop
def test_simple_command(create_pool, loop, server):
    pool = yield from create_pool(
        server.tcp_address,
        minsize=10, loop=loop)

    with (yield from pool) as conn:
        msg = yield from conn.echo('hello')
        assert msg == b'hello'
        assert pool.size == 10
        assert pool.freesize == 9
    assert pool.size == 10
    assert pool.freesize == 10

@pytest.mark.run_loop
def test_create_new(create_pool, loop, server):
    pool = yield from create_pool(
        server.tcp_address,
        minsize=1, loop=loop)
    assert pool.size == 1
    assert pool.freesize == 1

    with (yield from pool):
        assert pool.size == 1
        assert pool.freesize == 0

        with (yield from pool):
            assert pool.size == 2
            assert pool.freesize == 0

    assert pool.size == 2
    assert pool.freesize == 2

@pytest.mark.run_loop
def test_create_constraints(create_pool, loop, server):
    pool = yield from create_pool(
        server.tcp_address,
        minsize=1, maxsize=1, loop=loop)
    assert pool.size == 1
    assert pool.freesize == 1

    with (yield from pool):
        assert pool.size == 1
        assert pool.freesize == 0

        with pytest.raises(asyncio.TimeoutError):
            yield from asyncio.wait_for(pool.acquire(),
                                        timeout=0.2,
                                        loop=loop)

@pytest.mark.run_loop
def test_create_no_minsize(create_pool, loop, server):
    pool = yield from create_pool(
        server.tcp_address,
        minsize=0, maxsize=1, loop=loop)
    assert pool.size == 0
    assert pool.freesize == 0

    with (yield from pool):
        assert pool.size == 1
        assert pool.freesize == 0

        with pytest.raises(asyncio.TimeoutError):
            yield from asyncio.wait_for(pool.acquire(),
                                        timeout=0.2,
                                        loop=loop)
    assert pool.size == 1
    assert pool.freesize == 1

@pytest.mark.run_loop
def test_release_closed(create_pool, loop, server):
    pool = yield from create_pool(
        server.tcp_address,
        minsize=1, loop=loop)
    assert pool.size == 1
    assert pool.freesize == 1

    with (yield from pool) as redis:
        redis.close()
        yield from redis.wait_closed()
    assert pool.size == 0
    assert pool.freesize == 0

@pytest.mark.run_loop
def test_release_bad_connection(create_pool, create_redis, loop, server):
    pool = yield from create_pool(
        server.tcp_address,
        loop=loop)
    conn = yield from pool.acquire()
    other_conn = yield from create_redis(
        server.tcp_address,
        loop=loop)
    with pytest.raises(AssertionError):
        pool.release(other_conn)

    pool.release(conn)
    other_conn.close()
    yield from other_conn.wait_closed()

@pytest.mark.run_loop
def test_select_db(create_pool, loop, server):
    pool = yield from create_pool(
        server.tcp_address,
        loop=loop)

    yield from pool.select(1)
    with (yield from pool) as redis:
        assert redis.db == 1

@pytest.mark.run_loop
def test_change_db(create_pool, loop, server):
    pool = yield from create_pool(
        server.tcp_address,
        minsize=1, db=0,
        loop=loop)
    assert pool.size == 1
    assert pool.freesize == 1

    with (yield from pool) as redis:
        yield from redis.select(1)
    assert pool.size == 0
    assert pool.freesize == 0

    with (yield from pool) as redis:
        assert pool.size == 1
        assert pool.freesize == 0

        yield from pool.select(1)
        assert pool.db == 1
        assert pool.size == 1
        assert pool.freesize == 0
    assert pool.size == 0
    assert pool.freesize == 0
    assert pool.db == 1

@pytest.mark.run_loop
def test_change_db_errors(create_pool, loop, server):
    pool = yield from create_pool(
        server.tcp_address,
        minsize=1, db=0,
        loop=loop)

    with pytest.raises(TypeError):
        yield from pool.select(None)
    assert pool.db == 0

    with (yield from pool):
        pass
    assert pool.size == 1
    assert pool.freesize == 1

    with pytest.raises(TypeError):
        yield from pool.select(None)
    assert pool.db == 0
    with pytest.raises(ValueError):
        yield from pool.select(-1)
    assert pool.db == 0
    with pytest.raises(ReplyError):
        yield from pool.select(100000)
    assert pool.db == 0

@pytest.mark.run_loop
def test_select_and_create(create_pool, loop, server):
    # trying to model situation when select and acquire
    # called simultaneously
    # but acquire freezes on _wait_select and
    # then continues with propper db
    @asyncio.coroutine
    def test():
        pool = yield from create_pool(
            server.tcp_address,
            minsize=1, db=0,
            loop=loop)
        db = 0
        while True:
            db = (db + 1) & 1
            _, conn = yield from asyncio.gather(pool.select(db),
                                                pool.acquire(),
                                                loop=loop)
            assert pool.db == db
            pool.release(conn)
            if conn.db == db:
                break
    yield from asyncio.wait_for(test(), 1, loop=loop)

@pytest.mark.run_loop
def test_response_decoding(create_pool, loop, server):
    pool = yield from create_pool(
        server.tcp_address,
        encoding='utf-8', loop=loop)

    assert pool.encoding == 'utf-8'
    with (yield from pool) as redis:
        yield from redis.set('key', 'value')
    with (yield from pool) as redis:
        res = yield from redis.get('key')
        assert res == 'value'

@pytest.mark.run_loop
def test_hgetall_response_decoding(create_pool, loop, server):
    pool = yield from create_pool(
        server.tcp_address,
        encoding='utf-8', loop=loop)

    assert pool.encoding == 'utf-8'
    with (yield from pool) as redis:
        yield from redis.delete('key1')
        yield from redis.hmset('key1', 'foo', 'bar')
        yield from redis.hmset('key1', 'baz', 'zap')
    with (yield from pool) as redis:
        res = yield from redis.hgetall('key1')
        assert res == {'foo': 'bar', 'baz': 'zap'}

@pytest.mark.run_loop
def test_crappy_multiexec(create_pool, loop, server):
    pool = yield from create_pool(
        server.tcp_address,
        encoding='utf-8', loop=loop,
        minsize=1, maxsize=1)

    with (yield from pool) as redis:
        yield from redis.set('abc', 'def')
        yield from redis.connection.execute('multi')
        yield from redis.set('abc', 'fgh')
    assert redis.closed is True
    with (yield from pool) as redis:
        value = yield from redis.get('abc')
    assert value == 'def'

@pytest.mark.run_loop
def test_pool_size_growth(create_pool, server, loop):
    pool = yield from create_pool(
        server.tcp_address,
        loop=loop,
        minsize=1, maxsize=1)

    done = set()
    tasks = []

    @asyncio.coroutine
    def task1(i):
        with (yield from pool):
            assert pool.size <= pool.maxsize
            assert pool.freesize == 0
            yield from asyncio.sleep(0.2, loop=loop)
            done.add(i)

    @asyncio.coroutine
    def task2():
        with (yield from pool):
            assert pool.size <= pool.maxsize
            assert pool.freesize >= 0
            assert done == {0, 1}

    for _ in range(2):
        tasks.append(asyncio.async(task1(_), loop=loop))
    tasks.append(asyncio.async(task2(), loop=loop))
    yield from asyncio.gather(*tasks, loop=loop)

@pytest.mark.run_loop
def test_pool_with_closed_connections(create_pool, server, loop):
    pool = yield from create_pool(
        server.tcp_address,
        loop=loop,
        minsize=1, maxsize=2)
    assert 1 == pool.freesize
    conn1 = pool._pool[0]
    conn1.close()
    assert conn1.closed is True
    assert 1 == pool.freesize
    with (yield from pool) as conn2:
        assert conn2.closed is False
        assert conn1 is not conn2

@pytest.mark.run_loop
def test_pool_close(create_pool, server, loop):
    pool = yield from create_pool(
        server.tcp_address, loop=loop)

    assert pool.closed is False

    with (yield from pool) as redis:
        assert (yield from redis.ping()) == b'PONG'

    pool.close()
    yield from pool.wait_closed()
    assert pool.closed is True

    with pytest.raises(PoolClosedError):
        with (yield from pool) as redis:
            assert (yield from redis.ping()) == b'PONG'

@pytest.mark.run_loop
def test_pool_close__used(create_pool, server, loop):
    pool = yield from create_pool(
        server.tcp_address, loop=loop)

    assert pool.closed is False

    with (yield from pool) as redis:
        pool.close()
        yield from pool.wait_closed()
        assert pool.closed is True

        with pytest.raises(ConnectionClosedError):
            yield from redis.ping()

@pytest.mark.run_loop
def test_pool_check_closed_when_exception(create_pool, server, loop):
    exception = False

    class TrackExceptionRecord(logging.Handler):
        def __init__(self):
            logging.Handler.__init__(self)
            self.has_closed_msg = False

        def emit(self, record):
            if re.match("Closed [0-9]+ connections", record.getMessage()):
                self.has_closed_msg = True

    handler = TrackExceptionRecord()
    logger = logging.getLogger('aioredis')
    logger.setLevel(logging.DEBUG)
    logger.addHandler(handler)

    try:
        yield from create_pool(address=('127.3.2.1', 9999), loop=loop)
    except:
        exception = True

    assert exception is True

    yield from asyncio.sleep(0, result=None, loop=loop)
    assert handler.has_closed_msg is True

    logger.removeHandler(handler)
<commit_msg>Use pytest.raises instead of try/except construct<commit_after>import asyncio
import logging
import pytest
import re

from aioredis import (
    RedisPool,
    ReplyError,
    PoolClosedError,
    ConnectionClosedError,
    )

def _assert_defaults(pool):
    assert isinstance(pool, RedisPool)
    assert pool.minsize == 1
    assert pool.maxsize == 10
    assert pool.size == 1
    assert pool.freesize == 1

def test_connect(pool):
    _assert_defaults(pool)

def test_global_loop(create_pool, loop, server):
    asyncio.set_event_loop(loop)

    pool = loop.run_until_complete(create_pool(
        server.tcp_address))
    _assert_defaults(pool)

@pytest.mark.run_loop
def test_clear(pool):
    _assert_defaults(pool)

    yield from pool.clear()
    assert pool.freesize == 0

@pytest.mark.run_loop
@pytest.mark.parametrize('minsize', [None, -100, 0.0, 100])
def test_minsize(minsize, create_pool, loop, server):

    with pytest.raises(AssertionError):
        yield from create_pool(
            server.tcp_address,
            minsize=minsize, maxsize=10, loop=loop)

@pytest.mark.run_loop
@pytest.mark.parametrize('maxsize', [None, -100, 0.0, 1])
def test_maxsize(maxsize, create_pool, loop, server):

    with pytest.raises(AssertionError):
        yield from create_pool(
            server.tcp_address,
            minsize=2, maxsize=maxsize, loop=loop)

def test_no_yield_from(pool):
    with pytest.raises(RuntimeError):
        with pool:
            pass    # pragma: no cover

@pytest.mark.run_loop
def test_simple_command(create_pool, loop, server):
    pool = yield from create_pool(
        server.tcp_address,
        minsize=10, loop=loop)

    with (yield from pool) as conn:
        msg = yield from conn.echo('hello')
        assert msg == b'hello'
        assert pool.size == 10
        assert pool.freesize == 9
    assert pool.size == 10
    assert pool.freesize == 10

@pytest.mark.run_loop
def test_create_new(create_pool, loop, server):
    pool = yield from create_pool(
        server.tcp_address,
        minsize=1, loop=loop)
    assert pool.size == 1
    assert pool.freesize == 1

    with (yield from pool):
        assert pool.size == 1
        assert pool.freesize == 0

        with (yield from pool):
            assert pool.size == 2
            assert pool.freesize == 0

    assert pool.size == 2
    assert pool.freesize == 2

@pytest.mark.run_loop
def test_create_constraints(create_pool, loop, server):
    pool = yield from create_pool(
        server.tcp_address,
        minsize=1, maxsize=1, loop=loop)
    assert pool.size == 1
    assert pool.freesize == 1

    with (yield from pool):
        assert pool.size == 1
        assert pool.freesize == 0

        with pytest.raises(asyncio.TimeoutError):
            yield from asyncio.wait_for(pool.acquire(),
                                        timeout=0.2,
                                        loop=loop)

@pytest.mark.run_loop
def test_create_no_minsize(create_pool, loop, server):
    pool = yield from create_pool(
        server.tcp_address,
        minsize=0, maxsize=1, loop=loop)
    assert pool.size == 0
    assert pool.freesize == 0

    with (yield from pool):
        assert pool.size == 1
        assert pool.freesize == 0

        with pytest.raises(asyncio.TimeoutError):
            yield from asyncio.wait_for(pool.acquire(),
                                        timeout=0.2,
                                        loop=loop)
    assert pool.size == 1
    assert pool.freesize == 1

@pytest.mark.run_loop
def test_release_closed(create_pool, loop, server):
    pool = yield from create_pool(
        server.tcp_address,
        minsize=1, loop=loop)
    assert pool.size == 1
    assert pool.freesize == 1

    with (yield from pool) as redis:
        redis.close()
        yield from redis.wait_closed()
    assert pool.size == 0
    assert pool.freesize == 0

@pytest.mark.run_loop
def test_release_bad_connection(create_pool, create_redis, loop, server):
    pool = yield from create_pool(
        server.tcp_address,
        loop=loop)
    conn = yield from pool.acquire()
    other_conn = yield from create_redis(
        server.tcp_address,
        loop=loop)
    with pytest.raises(AssertionError):
        pool.release(other_conn)

    pool.release(conn)
    other_conn.close()
    yield from other_conn.wait_closed()

@pytest.mark.run_loop
def test_select_db(create_pool, loop, server):
    pool = yield from create_pool(
        server.tcp_address,
        loop=loop)

    yield from pool.select(1)
    with (yield from pool) as redis:
        assert redis.db == 1

@pytest.mark.run_loop
def test_change_db(create_pool, loop, server):
    pool = yield from create_pool(
        server.tcp_address,
        minsize=1, db=0,
        loop=loop)
    assert pool.size == 1
    assert pool.freesize == 1

    with (yield from pool) as redis:
        yield from redis.select(1)
    assert pool.size == 0
    assert pool.freesize == 0

    with (yield from pool) as redis:
        assert pool.size == 1
        assert pool.freesize == 0

        yield from pool.select(1)
        assert pool.db == 1
        assert pool.size == 1
        assert pool.freesize == 0
    assert pool.size == 0
    assert pool.freesize == 0
    assert pool.db == 1

@pytest.mark.run_loop
def test_change_db_errors(create_pool, loop, server):
    pool = yield from create_pool(
        server.tcp_address,
        minsize=1, db=0,
        loop=loop)

    with pytest.raises(TypeError):
        yield from pool.select(None)
    assert pool.db == 0

    with (yield from pool):
        pass
    assert pool.size == 1
    assert pool.freesize == 1

    with pytest.raises(TypeError):
        yield from pool.select(None)
    assert pool.db == 0
    with pytest.raises(ValueError):
        yield from pool.select(-1)
    assert pool.db == 0
    with pytest.raises(ReplyError):
        yield from pool.select(100000)
    assert pool.db == 0

@pytest.mark.run_loop
def test_select_and_create(create_pool, loop, server):
    # trying to model situation when select and acquire
    # called simultaneously
    # but acquire freezes on _wait_select and
    # then continues with propper db
    @asyncio.coroutine
    def test():
        pool = yield from create_pool(
            server.tcp_address,
            minsize=1, db=0,
            loop=loop)
        db = 0
        while True:
            db = (db + 1) & 1
            _, conn = yield from asyncio.gather(pool.select(db),
                                                pool.acquire(),
                                                loop=loop)
            assert pool.db == db
            pool.release(conn)
            if conn.db == db:
                break
    yield from asyncio.wait_for(test(), 1, loop=loop)

@pytest.mark.run_loop
def test_response_decoding(create_pool, loop, server):
    pool = yield from create_pool(
        server.tcp_address,
        encoding='utf-8', loop=loop)

    assert pool.encoding == 'utf-8'
    with (yield from pool) as redis:
        yield from redis.set('key', 'value')
    with (yield from pool) as redis:
        res = yield from redis.get('key')
        assert res == 'value'

@pytest.mark.run_loop
def test_hgetall_response_decoding(create_pool, loop, server):
    pool = yield from create_pool(
        server.tcp_address,
        encoding='utf-8', loop=loop)

    assert pool.encoding == 'utf-8'
    with (yield from pool) as redis:
        yield from redis.delete('key1')
        yield from redis.hmset('key1', 'foo', 'bar')
        yield from redis.hmset('key1', 'baz', 'zap')
    with (yield from pool) as redis:
        res = yield from redis.hgetall('key1')
        assert res == {'foo': 'bar', 'baz': 'zap'}

@pytest.mark.run_loop
def test_crappy_multiexec(create_pool, loop, server):
    pool = yield from create_pool(
        server.tcp_address,
        encoding='utf-8', loop=loop,
        minsize=1, maxsize=1)

    with (yield from pool) as redis:
        yield from redis.set('abc', 'def')
        yield from redis.connection.execute('multi')
        yield from redis.set('abc', 'fgh')
    assert redis.closed is True
    with (yield from pool) as redis:
        value = yield from redis.get('abc')
    assert value == 'def'

@pytest.mark.run_loop
def test_pool_size_growth(create_pool, server, loop):
    pool = yield from create_pool(
        server.tcp_address,
        loop=loop,
        minsize=1, maxsize=1)

    done = set()
    tasks = []

    @asyncio.coroutine
    def task1(i):
        with (yield from pool):
            assert pool.size <= pool.maxsize
            assert pool.freesize == 0
            yield from asyncio.sleep(0.2, loop=loop)
            done.add(i)

    @asyncio.coroutine
    def task2():
        with (yield from pool):
            assert pool.size <= pool.maxsize
            assert pool.freesize >= 0
            assert done == {0, 1}

    for _ in range(2):
        tasks.append(asyncio.async(task1(_), loop=loop))
    tasks.append(asyncio.async(task2(), loop=loop))
    yield from asyncio.gather(*tasks, loop=loop)

@pytest.mark.run_loop
def test_pool_with_closed_connections(create_pool, server, loop):
    pool = yield from create_pool(
        server.tcp_address,
        loop=loop,
        minsize=1, maxsize=2)
    assert 1 == pool.freesize
    conn1 = pool._pool[0]
    conn1.close()
    assert conn1.closed is True
    assert 1 == pool.freesize
    with (yield from pool) as conn2:
        assert conn2.closed is False
        assert conn1 is not conn2

@pytest.mark.run_loop
def test_pool_close(create_pool, server, loop):
    pool = yield from create_pool(
        server.tcp_address, loop=loop)

    assert pool.closed is False

    with (yield from pool) as redis:
        assert (yield from redis.ping()) == b'PONG'

    pool.close()
    yield from pool.wait_closed()
    assert pool.closed is True

    with pytest.raises(PoolClosedError):
        with (yield from pool) as redis:
            assert (yield from redis.ping()) == b'PONG'

@pytest.mark.run_loop
def test_pool_close__used(create_pool, server, loop):
    pool = yield from create_pool(
        server.tcp_address, loop=loop)

    assert pool.closed is False

    with (yield from pool) as redis:
        pool.close()
        yield from pool.wait_closed()
        assert pool.closed is True

        with pytest.raises(ConnectionClosedError):
            yield from redis.ping()

@pytest.mark.run_loop
def test_pool_check_closed_when_exception(create_pool, server, loop):
    class TrackExceptionRecord(logging.Handler):
        def __init__(self):
            logging.Handler.__init__(self)
            self.has_closed_msg = False

        def emit(self, record):
            if re.match("Closed [0-9]+ connections", record.getMessage()):
                self.has_closed_msg = True

    handler = TrackExceptionRecord()
    logger = logging.getLogger('aioredis')
    logger.setLevel(logging.DEBUG)
    logger.addHandler(handler)

    with pytest.raises(Exception):
        yield from create_pool(address=('127.3.2.1', 9999), loop=loop)

    yield from asyncio.sleep(0, result=None, loop=loop)
    assert handler.has_closed_msg is True

    logger.removeHandler(handler)
<|endoftext|>


<commit_before>#!/usr/bin/env python
"""Tests for the tulip.spec subpackage."""
import logging
logging.basicConfig(level=logging.DEBUG)
logging.getLogger('ltl_parser_log').setLevel(logging.ERROR)
import nose.tools as nt
from tulip.spec import ast, lexyacc
from tulip.spec.parser import parse

def parse_parse_check(formula, expected_length):
    # If expected_length is None, then the formula is malformed, and
    # thus we expect parsing to fail.
    if expected_length is not None:
        assert len(parse(formula)) == expected_length
    else:
        nt.assert_raises(Exception, parse, formula)

def parse_parse_test():
    for (formula, expected_len) in [("G p", 2),
                                    ("p G", None)]:
        yield parse_parse_check, formula, expected_len

def full_name_operators_test():
    formulas = {
        'always eventually p': '( G ( F p ) )',
        'ALwaYs EvenTUAlly(p)': '( G ( F p ) )',
        ('(p and q) UNtIl (q or ((p -> w) and '
         'not (z implies b))) and always next g'):
        ('( ( ( p & q ) U ( q | ( ( p -> w ) & '
         '( ! ( z -> b ) ) ) ) ) & ( G ( X g ) ) )')}

    for f, correct in formulas.iteritems():
        tree = parse(f, full_operators=True)
        # g.write('hehe.png')
        assert tree.flatten() == correct, tree.flatten()

def test_ast_nodes():
    nodes = ast.make_nodes()
    # test Terminal
    t = nodes.Terminal('a')
    assert t.value == 'a'
    assert repr(t) == "Terminal('a')"
    assert str(t) == 'a'
    assert len(t) == 1
    t1 = nodes.Terminal('a')
    assert t == t1
    t2 = nodes.Terminal('b')
    assert t != t2
    assert t.flatten() == 'a'
    # values and operators must be strings
    nt.assert_raises(TypeError, nodes.Terminal, 2)
    nt.assert_raises(TypeError, nodes.Unary, 2, 'v')
    nt.assert_raises(TypeError, nodes.Binary, 2, 'v')
    # test Unary
    u = nodes.Unary('!', t)
    assert u.operator == '!'
    assert u.operands[0] is t
    assert repr(u) == "Unary('!', Terminal('a'))"
    print(u)
    assert str(u) == '(! a)'
    assert len(u) == 2
    assert u.flatten() == '( ! a )'
    # test Binary
    v = nodes.Binary('+', t, t2)
    assert v.operator == '+'
    assert v.operands[0] is t
    assert v.operands[1] is t2
    assert repr(v) == "Binary('+', Terminal('a'), Terminal('b'))"
    assert len(v) == 3
    assert v.flatten() == "( a + b )"
    # different operator map
    opmap = {'!': '!', '+': '+'}
    nodes = ast.make_nodes(opmap)
    assert nodes.Node.opmap is opmap
    assert nodes.Terminal.opmap is opmap
    assert nodes.Unary.opmap is opmap
    assert nodes.Binary.opmap is opmap

def test_fol_nodes():
    nodes = ast.make_fol_nodes()
    # test Var
    v = nodes.Var('a')
    assert v.value == 'a'
    nt.assert_raises(TypeError, nodes.Var, 2)
    # test Bool
    b = nodes.Bool('TRue')
    print(b.value)
    assert b.value == 'True'
    assert b.flatten() == 'True'
    nt.assert_raises(TypeError, nodes.Bool, 2)
    nt.assert_raises(TypeError, nodes.Bool, 'bee')

def test_lex():
    # catch token precedence errors
    # for example if "EVENTUALLY" is defined before "FALSE",
    # then "False" is parsed as "EVENTUALLY", "NAME", where
    # "NAME" is equal to "alse".
    s = 'False'
    lexer = lexyacc.Lexer()
    lexer.lexer.input(s)
    r = list(lexer.lexer)
    assert len(r) == 1
    (tok, ) = r
    assert tok.value == 'False'
    # another case, which needs that "NAME" be defined
    # before "NEXT"
    s = 'X0reach'
    lexer.lexer.input(s)
    r = list(lexer.lexer)
    assert len(r) == 1
    (tok, ) = r
    assert tok.value == 'X0reach'

def lexer_token_precedence_test():
    s = 'False'
    r = parse(s)
    assert isinstance(r, ast.nodes.Bool)
    assert r.value == 'False'
    s = 'a'
    r = parse(s)
    assert isinstance(r, ast.nodes.Var)
    assert r.value == 'a'
    s = '"a"'
    r = parse(s)
    assert isinstance(r, ast.nodes.Str)
    assert r.value == 'a'
    s = '1'
    r = parse(s)
    assert isinstance(r, ast.nodes.Num)
    assert r.value == '1'
    s = '[] a'
    r = parse(s)
    assert isinstance(r, ast.nodes.Unary)
    assert r.operator == 'G'
    assert isinstance(r.operands[0], ast.nodes.Var)
    assert r.operands[0].value == 'a'
    s = 'a U b'
    r = parse(s)
    assert isinstance(r, ast.nodes.Binary)
    assert r.operator == 'U'
    assert isinstance(r.operands[0], ast.nodes.Var)
    assert r.operands[0].value == 'a'
    assert isinstance(r.operands[1], ast.nodes.Var)
    assert r.operands[1].value == 'b'
    s = '( a )'
    r = parse(s)
    assert isinstance(r, ast.nodes.Var)
    s = "(a ' = 1)"
    r = parse(s)
    assert isinstance(r, ast.nodes.Comparator)
    assert r.operator == '='
    x = r.operands[0]
    assert isinstance(x, ast.nodes.Unary)
    assert x.operator == 'X'
    assert isinstance(x.operands[0], ast.nodes.Var)
    assert x.operands[0].value == 'a'
    y = r.operands[1]
    assert isinstance(y, ast.nodes.Num)
    assert y.value == '1'
<commit_msg>use well-formed formulae in spec test<commit_after>#!/usr/bin/env python
"""Tests for the tulip.spec subpackage."""
import logging
logging.basicConfig(level=logging.DEBUG)
logging.getLogger('ltl_parser_log').setLevel(logging.ERROR)
import nose.tools as nt
from tulip.spec import ast, lexyacc
from tulip.spec.parser import parse

def parse_parse_check(formula, expected_length):
    # If expected_length is None, then the formula is malformed, and
    # thus we expect parsing to fail.
    if expected_length is not None:
        assert len(parse(formula)) == expected_length
    else:
        nt.assert_raises(Exception, parse, formula)

def parse_parse_test():
    for (formula, expected_len) in [("G p", 2),
                                    ("p G", None)]:
        yield parse_parse_check, formula, expected_len

def full_name_operators_test():
    formulas = {
        'always eventually p': '( G ( F p ) )',
        'ALwaYs EvenTUAlly(p)': '( G ( F p ) )',
        ('(p and q) UNtIl (q or ((p -> w) and '
         'not (z implies b))) and always next g'):
        ('( ( ( p & q ) U ( q | ( ( p -> w ) & '
         '( ! ( z -> b ) ) ) ) ) & ( G ( X g ) ) )')}

    for f, correct in formulas.iteritems():
        tree = parse(f, full_operators=True)
        # g.write('hehe.png')
        assert tree.flatten() == correct, tree.flatten()

def test_ast_nodes():
    nodes = ast.make_nodes()
    # test Terminal
    t = nodes.Terminal('a')
    assert t.value == 'a'
    assert repr(t) == "Terminal('a')"
    assert str(t) == 'a'
    assert len(t) == 1
    t1 = nodes.Terminal('a')
    assert t == t1
    t2 = nodes.Terminal('b')
    assert t != t2
    assert t.flatten() == 'a'
    # values and operators must be strings
    nt.assert_raises(TypeError, nodes.Terminal, 2)
    nt.assert_raises(TypeError, nodes.Unary, 2, 'v')
    nt.assert_raises(TypeError, nodes.Binary, 2, 'v')
    # test Unary
    u = nodes.Unary('!', t)
    assert u.operator == '!'
    assert u.operands[0] is t
    assert repr(u) == "Unary('!', Terminal('a'))"
    print(u)
    assert str(u) == '(! a)'
    assert len(u) == 2
    assert u.flatten() == '( ! a )'
    # test Binary
    v = nodes.Binary('+', t, t2)
    assert v.operator == '+'
    assert v.operands[0] is t
    assert v.operands[1] is t2
    assert repr(v) == "Binary('+', Terminal('a'), Terminal('b'))"
    assert len(v) == 3
    assert v.flatten() == "( a + b )"
    # different operator map
    opmap = {'!': '!', '+': '+'}
    nodes = ast.make_nodes(opmap)
    assert nodes.Node.opmap is opmap
    assert nodes.Terminal.opmap is opmap
    assert nodes.Unary.opmap is opmap
    assert nodes.Binary.opmap is opmap

def test_fol_nodes():
    nodes = ast.make_fol_nodes()
    # test Var
    v = nodes.Var('a')
    assert v.value == 'a'
    nt.assert_raises(TypeError, nodes.Var, 2)
    # test Bool
    b = nodes.Bool('TRue')
    print(b.value)
    assert b.value == 'True'
    assert b.flatten() == 'True'
    nt.assert_raises(TypeError, nodes.Bool, 2)
    nt.assert_raises(TypeError, nodes.Bool, 'bee')

def test_lex():
    # catch token precedence errors
    # for example if "EVENTUALLY" is defined before "FALSE",
    # then "False" is parsed as "EVENTUALLY", "NAME", where
    # "NAME" is equal to "alse".
    s = 'False'
    lexer = lexyacc.Lexer()
    lexer.lexer.input(s)
    r = list(lexer.lexer)
    assert len(r) == 1
    (tok, ) = r
    assert tok.value == 'False'
    # another case, which needs that "NAME" be defined
    # before "NEXT"
    s = 'X0reach'
    lexer.lexer.input(s)
    r = list(lexer.lexer)
    assert len(r) == 1
    (tok, ) = r
    assert tok.value == 'X0reach'

def lexer_token_precedence_test():
    s = 'False'
    r = parse(s)
    assert isinstance(r, ast.nodes.Bool)
    assert r.value == 'False'
    s = 'a'
    r = parse(s)
    assert isinstance(r, ast.nodes.Var)
    assert r.value == 'a'
    s = 'x = "a"'
    r = parse(s)
    assert isinstance(r, ast.nodes.Binary)
    assert r.operator == '='
    x, a = r.operands
    assert isinstance(x, ast.nodes.Var)
    assert x.value == 'x'
    assert isinstance(a, ast.nodes.Str)
    assert a.value == 'a'
    s = 'y = 1'
    r = parse(s)
    assert isinstance(r, ast.nodes.Binary)
    assert r.operator == '='
    y, n = r.operands
    assert isinstance(y, ast.nodes.Var)
    assert y.value == 'y'
    assert isinstance(n, ast.nodes.Num)
    assert n.value == '1'
    s = '[] a'
    r = parse(s)
    assert isinstance(r, ast.nodes.Unary)
    assert r.operator == 'G'
    assert isinstance(r.operands[0], ast.nodes.Var)
    assert r.operands[0].value == 'a'
    s = 'a U b'
    r = parse(s)
    assert isinstance(r, ast.nodes.Binary)
    assert r.operator == 'U'
    assert isinstance(r.operands[0], ast.nodes.Var)
    assert r.operands[0].value == 'a'
    assert isinstance(r.operands[1], ast.nodes.Var)
    assert r.operands[1].value == 'b'
    s = '( a )'
    r = parse(s)
    assert isinstance(r, ast.nodes.Var)
    s = "(a ' = 1)"
    r = parse(s)
    assert isinstance(r, ast.nodes.Comparator)
    assert r.operator == '='
    x = r.operands[0]
    assert isinstance(x, ast.nodes.Unary)
    assert x.operator == 'X'
    assert isinstance(x.operands[0], ast.nodes.Var)
    assert x.operands[0].value == 'a'
    y = r.operands[1]
    assert isinstance(y, ast.nodes.Num)
    assert y.value == '1'
<|endoftext|>


<commit_before>import re

TIMESTAMP_PATTERN = re.compile('(\d+):(\d{2}):(\d{2})[.,](\d{3})')

class Caption(object):
    """
    Represents a caption.
    """
    def __init__(self, start='00:00:00.000', end='00:00:00.000', text=None):
        self._start = self._parse_timestamp(start)
        self._end = self._parse_timestamp(end)

        # If lines is a string convert to a list
        if text and isinstance(text, str):
            text = text.splitlines()

        self._lines = text or []

    def add_line(self, line):
        self.lines.append(line)

    def _to_seconds(self, hours, minutes, seconds, milliseconds):
        return hours * 3600 + minutes * 60 + seconds + milliseconds / 1000

    def _parse_timestamp(self, timestamp):
        res = re.match(TIMESTAMP_PATTERN, timestamp)
        return self._to_seconds(
            int(res.group(1)),  # hours
            int(res.group(2)),  # minutes
            int(res.group(3)),  # seconds
            int(res.group(4))  # milliseconds
        )

    def _to_timestamp(self, total_seconds):
        hours = int(total_seconds / 3600)
        minutes = int(total_seconds / 60 - hours * 60)
        seconds = total_seconds - hours * 3600 - minutes * 60
        return '{:02d}:{:02d}:{:06.3f}'.format(int(hours), int(minutes), seconds)

    @property
    def start_in_seconds(self):
        return self._start

    @property
    def end_in_seconds(self):
        return self._end

    @property
    def start(self):
        return self._to_timestamp(self._start)

    @start.setter
    def start(self, value):
        self._start = self._parse_timestamp(value)

    @property
    def end(self):
        return self._to_timestamp(self._end)

    @end.setter
    def end(self, value):
        self._end = self._parse_timestamp(value)

    @property
    def lines(self):
        return self._lines

    @property
    def text(self):
        """Returns the captions lines as a text"""
        return '\n'.join(self.lines)

    @text.setter
    def text(self, value):
        if not isinstance(value, str):
            raise AttributeError('String value expected but received {}.'.format(type(value)))

        self._lines = value.splitlines()

class GenericParser(object):
    """
    A generic parent class for all parsers.
    """
    def __init__(self):
        self.captions = []

    def _parse(self, content):
        # method to be overwritten by child classes
        pass

    def _read_content(self, file):
        # method to be overwritten by child classes
        return

    def _validate(self, content):
        # method to be overwritten by child classes
        pass

    def read(self, file):
        """Reads the captions file."""
        self.captions = []

        content = self._read_content(file)
        self._validate(content)
        self._parse(content)

        return self<commit_msg>Encapsulate captions with a property.<commit_after>import re

TIMESTAMP_PATTERN = re.compile('(\d+):(\d{2}):(\d{2})[.,](\d{3})')

class Caption(object):
    """
    Represents a caption.
    """
    def __init__(self, start='00:00:00.000', end='00:00:00.000', text=None):
        self._start = self._parse_timestamp(start)
        self._end = self._parse_timestamp(end)

        # If lines is a string convert to a list
        if text and isinstance(text, str):
            text = text.splitlines()

        self._lines = text or []

    def add_line(self, line):
        self.lines.append(line)

    def _to_seconds(self, hours, minutes, seconds, milliseconds):
        return hours * 3600 + minutes * 60 + seconds + milliseconds / 1000

    def _parse_timestamp(self, timestamp):
        res = re.match(TIMESTAMP_PATTERN, timestamp)
        return self._to_seconds(
            int(res.group(1)),  # hours
            int(res.group(2)),  # minutes
            int(res.group(3)),  # seconds
            int(res.group(4))  # milliseconds
        )

    def _to_timestamp(self, total_seconds):
        hours = int(total_seconds / 3600)
        minutes = int(total_seconds / 60 - hours * 60)
        seconds = total_seconds - hours * 3600 - minutes * 60
        return '{:02d}:{:02d}:{:06.3f}'.format(int(hours), int(minutes), seconds)

    @property
    def start_in_seconds(self):
        return self._start

    @property
    def end_in_seconds(self):
        return self._end

    @property
    def start(self):
        return self._to_timestamp(self._start)

    @start.setter
    def start(self, value):
        self._start = self._parse_timestamp(value)

    @property
    def end(self):
        return self._to_timestamp(self._end)

    @end.setter
    def end(self, value):
        self._end = self._parse_timestamp(value)

    @property
    def lines(self):
        return self._lines

    @property
    def text(self):
        """Returns the captions lines as a text"""
        return '\n'.join(self.lines)

    @text.setter
    def text(self, value):
        if not isinstance(value, str):
            raise AttributeError('String value expected but received {}.'.format(type(value)))

        self._lines = value.splitlines()

class GenericParser(object):
    """
    A generic parent class for all parsers.
    """
    def __init__(self):
        self._captions = []

    def _parse(self, content):
        # method to be overwritten by child classes
        pass

    def _read_content(self, file):
        # method to be overwritten by child classes
        return

    def _validate(self, content):
        # method to be overwritten by child classes
        pass

    def read(self, file):
        """Reads the captions file."""
        self._captions = []

        content = self._read_content(file)
        self._validate(content)
        self._parse(content)

        return self

    @property
    def captions(self):
        return self._captions<|endoftext|>