Esempio n. 1
0
def test_hash():
    d = {
        Query().key1 == 2: True,
        Query().key1.key2.key3.exists(): True,
        Query().key1.exists() & Query().key2.exists(): True,
        Query().key1.exists() | Query().key2.exists(): True,
    }

    assert (Query().key1 == 2) in d
    assert (Query().key1.key2.key3.exists()) in d
    assert (Query()['key1.key2'].key3.exists()) not in d

    # Commutative property of & and |
    assert (Query().key1.exists() & Query().key2.exists()) in d
    assert (Query().key2.exists() & Query().key1.exists()) in d
    assert (Query().key1.exists() | Query().key2.exists()) in d
    assert (Query().key2.exists() | Query().key1.exists()) in d
Esempio n. 2
0
def test_repr():
    Fruit = Query()

    assert repr(Fruit) == "Query()"
    assert repr(Fruit.type == 'peach') == "QueryImpl('==', ('type',), 'peach')"
Esempio n. 3
0
from os import name
from tinydb import TinyDB
from datetime import datetime, time
from dataclasses import dataclass
from tinydb.queries import Query
import json
db: TinyDB = TinyDB("./newsdb.json")
q: Query = Query()


@dataclass
class NewsObject():
    title: str
    description: str
    added_by: str
    time_created: str = datetime.now().ctime()
    timestamp: str = datetime.now().__str__()

    def tojson(self):
        return {
            "title": self.title,
            "description": self.description,
            "added_by": self.added_by,
            "time_created": self.time_created,
            "timestamp": self.timestamp,
        }


ListOfNewsType = [
    NewsObject(title="news title",
               description="news Description",
Esempio n. 4
0
def test_one_of():
    query = Query().key1.one_of(['value 1', 'value 2'])
    assert query({'key1': 'value 1'})
    assert query({'key1': 'value 2'})
    assert not query({'key1': 'value 3'})
Esempio n. 5
0
def test_equality():
    q = Query()
    assert (q.foo == 2) != 0
    assert (q.foo == 'yes') != ''
Esempio n. 6
0
def test_eq():
    query = Query().value == 1
    assert query({'value': 1})
    assert not query({'value': 2})
Esempio n. 7
0
def test_le():
    query = Query().value <= 1
    assert query({'value': 0})
    assert query({'value': 1})
    assert not query({'value': 2})
Esempio n. 8
0
def test_has_key():
    query = Query().val3.exists()

    assert query({'val3': 1})
    assert not query({'val1': 1, 'val2': 2})
    assert hash(query)
Esempio n. 9
0
def test_ne():
    query = Query().value != 1
    assert query({'value': 2})
    assert not query({'value': 1})
Esempio n. 10
0
def test_lt():
    query = Query().value < 1
    assert query({'value': 0})
    assert not query({'value': 1})
Esempio n. 11
0
class DataType(TinyDB):
    """Base for all the model types, be it template or model

    Internally it uses the TinyDB database"""
    __weakref__ = ("_current_index", "_keys_cache")

    DEFAULT_TABLE: str = 'data'
    parents: Dict[str, "DataType"] = {}
    query: Query = Query()
    raws: list = []

    data_name: str = "DataType"

    dependencies: List[str]
    data_group: "Group"
    data_core: "DataCore"
    wrapper: "GroupWrapper"

    _current_index: int = -1
    _keys_cache: List[str] = []

    def __new__(cls, *_, db_file: Optional[PathType] = None, **__):
        """Method that handles the instancing of the models and templates, this
        is necessary because dataclasses create a custom __init__ method. Which
        we doesn't use at all if a raw file is supplied."""
        if not hasattr(cls, "dataclass_args"):
            raise PCDNeedsToBeInherited(
                "You can't create a DataType instance "
                "directly without inheriting it first.")

        instanced = object.__new__(cls)
        instanced.dataclass_instanced = not isinstance(db_file, str)

        return instanced

    def __getattr__(self, attr_name: str) -> Any:
        """This is here just to make this method back to the default that was
        overwritten by TinyDB

        :param name: name of the attribute to get"""
        raise AttributeError(f"type object '{type(self).__name__}' has no "
                             f"attribute '{attr_name}'")

    __setattr__ = object.__setattr__

    def __repr__(self) -> str:
        return_value = []
        fields_list = fields(self)
        fields_list = [
            "\t\t{}({}) = {}".format(current_field.name, current_field.type,
                                     getattr(self, current_field.name))
            for current_field in fields_list
        ]

        if self.data_name != type(self).__name__:
            return_value.append(
                f"DataType {self.data_name} ({type(self).__name__}):")
        else:
            return_value.append(f"DataType {self.data_name}:")

        return_value.append("\tFields:")
        return_value.append('\n'.join(fields_list))

        # if any(self.parents):
        #    checked_parents = [self.data_name,]
        #    return_value.append("\tParents:")
        #    for parent_name, parent_value in self.parents.items():
        #        if parent_name in checked_parents:
        #            continue
        #        checked_parents.append(parent_name)
        #        return_value.append("\t\t" + parent_name + "\n\t\t\t" +
        # repr(parent_value).replace("\n", "\n\t\t\t"))

        return "\n".join(return_value)

    def __bool__(self) -> bool:
        return self.is_instanced

    # mapping helpers ----------------------------------------------------------
    def _in_fields(self, key: str):
        if key in self:
            return getattr(self, key)
        raise PCDKeyError(key)

    def _set_field(self, key: str, value: Any):
        if key in self:
            return setattr(self, key, value)
        raise PCDKeyError(key)

    def _build_keys(self) -> List[str]:
        keys_list = []
        for key in self.__dict__:
            if key in self:
                keys_list.append(key)

        return keys_list

    # mapping methods ----------------------------------------------------------
    def __iter__(self):
        self._current_index = -1
        self._keys_cache = self._build_keys()
        yield from self._keys_cache

    def __next__(self):
        self._current_index += 1
        if self._current_index < len(self._keys_cache):
            return self._keys_cache[self._current_index]
        raise StopIteration()

    def __getitem__(self, key: Union[str, int]) -> Any:
        if isinstance(key, int) and key < len(self):
            return list(self.values())[key]
        return self._in_fields(key)

    def __setitem__(self, key: str, value: Any):
        self._set_field(key, value)

    def __len__(self) -> int:
        return len(self.__dict__) - 3

    def __delitem__(self, key: str) -> Any:
        if key in self:
            del self.__dataclass_fields__[key]
            del self.__annotations__[key]
            return self.__dict__.pop(key)
        raise PCDKeyError(key)

    def __contains__(self, key: str):
        return (key not in [
            "dataclass_instanced", "_current_index", "_keys_cache"
        ] and key in self.__dict__)

    def values(self):
        for key in self:
            yield self[key]

    def items(self):
        for key in self:
            yield key, self[key]

    def clear(self):
        while True:
            try:
                del self[next(self)]
            except StopIteration:
                break

    pop = __delitem__
    keys = __iter__

    # creation helpers ---------------------------------------------------------
    @staticmethod
    def _get_core(core_name: str) -> 'panda_core_data.DataCore':
        from . import data_core

        if isinstance(data_core, dict) and not core_name:
            return data_core["DEFAULT"]

        if isinstance(data_core, dict) and core_name:
            return data_core[core_name]

        # This return statement is necessary, however, for some reason the
        # coverage doesn't detect it
        return data_core  # pragma: no cover

    @staticmethod
    def _add_into(data_type: 'DataType', data_type_dict, **kwargs):
        """You can use the prefix `dataclass_` with a dataclass parameter to
        configure it. They can be found in this link:
        https://docs.python.org/3/library/dataclasses.html#dataclasses.dataclass

        For example:

        .. code:: python

            from panda_core_data.model import Model

            class ModelName(Model, dataclass_init=False):
                out: int

                def __init__(self, num):
                    self.out = num ** num

        Will make the library not create a `__init__` method and use yours
        instead.

        :param data_type: class type to be added
        :param template_name: The name of the template, if not supplied, the
                              class name is used.
        :type template_name: None or str
        :param dependency_list: :class:`Template` to be used as dependency.
        :type dependency_list: list[str]"""
        from .data_core_bases import GroupWrapper

        if not hasattr(data_type, "dataclass_args"):
            generate_dataclass_args(data_type, **kwargs)
            data_type = _process_class(data_type, **data_type.dataclass_args)

            def new_init(self,
                         *init_args,
                         db_file: Optional[str] = None,
                         default_table: str = DataType.DEFAULT_TABLE,
                         **init_kwargs):
                from .model import Model
                if db_file:
                    self.load_db(db_file,
                                 *init_args,
                                 default_table=default_table,
                                 **init_kwargs)
                elif self.original_init:
                    self.original_init(*init_args, **init_kwargs)

                if isinstance(self, Model):
                    self.wrapper.instances.append(self)
                else:
                    self.wrapper.instances = self

                if hasattr(self, "__post_init__"):
                    self.__post_init__(*init_args, **init_kwargs)

            if (hasattr(data_type, "__init__")
                    and data_type.__init__ is not new_init):
                if data_type.__init__ is not TinyDB.__init__:
                    data_type.original_init = data_type.__init__

                data_type.__init__ = new_init

        replace = kwargs.pop("replace", False)
        data_name = kwargs.pop("data_name", data_type.__name__)
        data_type.dependencies = kwargs.pop("dependencies", [])

        data_type.data_name = data_name
        data_type.data_type_dict = data_type_dict
        data_type.wrapper = GroupWrapper(data_type)

        if data_name not in data_type_dict or replace:
            data_type_dict[data_name] = data_type
        else:
            raise PCDDuplicatedTypeName(
                f"There's already a {type(data_type)} with the name "
                f"{data_name}")

    @property
    def has_dependencies(self) -> bool:
        """If the model has any dependencies

        :return: If the instance have dependencies or not."""
        check_if_valid_instance(self, DataType)
        return any(self.dependencies)

    # def load_inner_dependencies(self, dependency):
    #    check_if_valid_instance(dependency, DataType)
    #
    #    tmp_dependencies = {dependency.data_name: dependency}
    #
    #    for current_data in dependency.parents.values():
    #        tmp_dependencies.update(self.load_inner_dependencies(current_data))
    #
    #    return tmp_dependencies

    @property
    def is_instanced(self) -> bool:
        return isinstance(self, type(self))

    @classmethod
    def instance_from_raw(cls, raw_file) -> 'DataType':
        return cls(db_file=raw_file)

    def load_db(self,
                db_file: PathType,
                *init_args,
                default_table: str = DEFAULT_TABLE,
                **kwargs):
        """Method that load raw files and assign each field to an attribute.

        :param db_file: Path to a raw file
        :param tinydb.storages.Storage storage: storage class to be used.
        :param default_table: default main field in the raw file."""
        check_if_valid_instance(self, DataType)

        db_file = auto_convert_to_pathlib(db_file)
        extension = get_extension(db_file)
        storage = get_storage_from_extension(extension)

        TinyDB.__init__(self,
                        db_file,
                        *init_args,
                        storage=storage,
                        default_table=default_table,
                        **kwargs)

        for current_field in self.all():
            setattr(self,
                    list(current_field.keys())[0],
                    list(current_field.values())[0])

    def all(self, *arg, **kwargs):
        return self._table.all(*arg, **kwargs)

    def add_dependencies(self):
        "Add all dependencies for the model"
        for current_dependency in self.dependencies:
            dependency = self.data_core.get_template_type(current_dependency)
            dependency = dependency.instanced()
            # if dependency.has_dependencies:
            #    self.parents.update(self.load_inner_dependencies(dependency))

            self.parents[current_dependency] = dependency

    # context methods ----------------------------------------------------------
    def save_to_file(self, *_):
        "Save fields instance into the raw"
        to_write = {self.DEFAULT_TABLE: []}
        for the_field in fields(self):
            to_write[self.DEFAULT_TABLE].append(
                {the_field.name: getattr(self, the_field.name)})

        self._storage.write(to_write)
        self.close()

    __exit__ = save_to_file
Esempio n. 12
0
    def getIdByUname(self, uname: str):
        db = self.createObj()
        row = db.tbl.get(Query().username == uname)
        db.close()

        return row
Esempio n. 13
0
def test_empty_query_error():
    with pytest.raises(RuntimeError, match='Empty query was evaluated'):
        Query()({})
Esempio n. 14
0
def test_no_path():
    with pytest.raises(ValueError):
        _ = Query() == 2
Esempio n. 15
0
def test_gt():
    query = Query().value > 1
    assert query({'value': 2})
    assert not query({'value': 1})
Esempio n. 16
0
def test_ge():
    query = Query().value >= 1
    assert query({'value': 2})
    assert query({'value': 1})
    assert not query({'value': 0})
    assert hash(query)
Esempio n. 17
0
def test_and():
    query = ((Query().val1 == 1) & (Query().val2 == 2))
    assert query({'val1': 1, 'val2': 2})
    assert not query({'val1': 1})
    assert not query({'val2': 2})
    assert not query({'val1': '', 'val2': ''})
Esempio n. 18
0
def test_has():
    query = Query().key1.key2.exists()
    str(query)  # This used to cause a bug...

    assert query({'key1': {'key2': {'key3': 1}}})
    assert query({'key1': {'key2': 1}})
    assert not query({'key1': 3})
    assert not query({'key1': {'key1': 1}})
    assert not query({'key2': {'key1': 1}})
    assert hash(query)

    query = Query().key1.key2 == 1

    assert query({'key1': {'key2': 1}})
    assert not query({'key1': {'key2': 2}})
    assert hash(query)

    # Nested has: key exists
    query = Query().key1.key2.key3.exists()
    assert query({'key1': {'key2': {'key3': 1}}})
    # Not a dict
    assert not query({'key1': 1})
    assert not query({'key1': {'key2': 1}})
    # Wrong key
    assert not query({'key1': {'key2': {'key0': 1}}})
    assert not query({'key1': {'key0': {'key3': 1}}})
    assert not query({'key0': {'key2': {'key3': 1}}})

    assert hash(query)

    # Nested has: check for value
    query = Query().key1.key2.key3 == 1
    assert query({'key1': {'key2': {'key3': 1}}})
    assert not query({'key1': {'key2': {'key3': 0}}})
    assert hash(query)

    # Test special methods: regex matches
    query = Query().key1.value.matches(r'\d+')
    assert query({'key1': {'value': '123'}})
    assert not query({'key2': {'value': '123'}})
    assert not query({'key2': {'value': 'abc'}})
    assert hash(query)

    # Test special methods: regex contains
    query = Query().key1.value.search(r'\d+')
    assert query({'key1': {'value': 'a2c'}})
    assert not query({'key2': {'value': 'a2c'}})
    assert not query({'key2': {'value': 'abc'}})
    assert hash(query)

    # Test special methods: nested has and regex matches
    query = Query().key1.x.y.matches(r'\d+')
    assert query({'key1': {'x': {'y': '123'}}})
    assert not query({'key1': {'x': {'y': 'abc'}}})
    assert hash(query)

    # Test special method: nested has and regex contains
    query = Query().key1.x.y.search(r'\d+')
    assert query({'key1': {'x': {'y': 'a2c'}}})
    assert not query({'key1': {'x': {'y': 'abc'}}})
    assert hash(query)

    # Test special methods: custom test
    query = Query().key1.int.test(lambda x: x == 3)
    assert query({'key1': {'int': 3}})
    assert hash(query)
Esempio n. 19
0
def test_noop():
    query = Query().noop()

    assert query({'foo': True})
    assert query({'foo': None})
    assert query({})