# -*- coding: utf-8 -*-
from setuptools import setup

packages = \
['python_redis_orm', 'python_redis_orm.tests']

package_data = \
{'': ['*']}

install_requires = \
['pytz>=2021,<2022', 'redis>=3.5.3,<4.0.0']

setup_kwargs = {
    'name': 'python-redis-orm',
    'version': '0.4.8',
    'description': 'Python Redis ORM library that gives redis easy-to-use objects with fields and speeds a development up, inspired by Django ORM',
    'long_description': '# python-redis-orm\n\n## **Python Redis ORM library that gives redis easy-to-use objects with fields and speeds a development up, inspired by Django ORM**\n\n\n[![Full test](https://github.com/gh0st-work/python_redis_orm/actions/workflows/python-app.yml/badge.svg?event=push)](https://github.com/gh0st-work/python_redis_orm/actions/workflows/python-app.yml)\n\nFor one project, I needed to work with redis, but redis-py provides a minimum level of work with redis. I didn\'t find any Django-like ORM for redis, so I wrote this library, then there will be a port to Django.\n\n### Working with this library, you are expected:\n\n- Fully works in 2021\n- Django-like architecture\n- Easy adaptation to your needs\n- Adequate informational messages and error messages\n- Built-in RedisRoot class that stores specified models, with:\n    - **redis_instance** setting - your redis connection (from redis-py)\n    - **prefix** setting - prefix of this RedisRoot to be stored in redis\n    - **ignore_deserialization_errors** setting - do not raise errors, while deserializing data\n    - **save_consistency** setting - show structure-first data\n    - **economy** setting - to not return full data and save some requests (usually, speeds up your app on 80%)\n- 13 built-in types of fields:\n    - **RedisField** - base class for nesting\n    - **RedisString** - string\n    - **RedisNumber** - int or float\n    - **RedisId** - instances IDs\n    - **RedisBool** - bool\n    - **RedisDecimal** - working accurately with numbers via decimal\n    - **RedisJson** - for data, that can be JSONed\n    - **RedisList** - list\n    - **RedisDict** - dict\n    - **RedisDateTime** - for work with date and time, via python datetime.datetime\n    - **RedisDate** - for work with date, via python datetime.data\n    - **RedisForeignKey** - for link to other instance\n    - **RedisManyToMany** - for links to other instances\n- All fields supports:\n    - Automatically serialization\n    - Automatically deserialization\n    - TTL (Time To Live) setting\n    - Default values\n    - Providing functions without call, to call, while need\n    - Allow null values setting\n    - Choices\n    - Filtering (and deep filtering):\n        - **exact** - equality\n        - **iexact** - case-independent equality\n        - **contains** - is filter string in the value string\n        - **icontains** - is filter string case-independent in the value string\n        - **in** - is value in the provided list\n        - **gt** - is value greater\n        - **gte** - is value greater or equals\n        - **lt** - is value less\n        - **lte** - is value less or equals\n        - **startswith** - is string starts with\n        - **istartswith** - is string case-independent starts with\n        - **endswith** - is string ends with\n        - **iendswith** - is string case-independent ends wth\n        - **range** - is value in provided range\n        - **isnull** - is value in ["null", None]\n- Built-in RedisModel class, with:\n    - All fields that you want\n    - TTL (Time To Live)\n- CRUD (Create Read Update Delete)\n- Non-blocking usage! Any operation gives the same result as the default, but it just creates an asyncio task in the background instead of write inside the call\n\n\n# Installation\n`pip install python-redis-orm`\n\n[Here is PyPI](https://pypi.org/project/python-redis-orm/)\n\nObviously, you need to install and run redis server on your machine, we support v3+ \n\n\n# Usage\n\n1. Create **RedisRoot** with params:\n    - **prefix** (str) - prefix for your redis root\n    - **connection_pool** (redis.ConnectionPool) - redis-py redis.ConnectionPool instance, with decode_responses=True\n    - **ignore_deserialization_errors** (bool) - to ignore deserialization errors or raise exception\n    - **save_consistency** (bool) - to use structure-first data\n    - **economy** (bool) - if True, all update requests will return only instance id \n    - **use_keys** (bool) - to use Redis keys command (uses memory instead of CPU) instead of scan\n2. Create your models\n3. Call **register_models()** on your RedisRoot instance and provide list with your models\n4. Use our CRUD\n\n\n# CRUD\n```python\nexample_instance = ExampleModel(example_field=\'example_data\').save() # - to create an instance and get its data dict\n# or:\nexample_instance = redis_root.create(ExampleModel, example_field=\'example_data\')\nfiltered_example_instances = redis_root.get(ExampleModel, example_field=\'example_data\') # - to get all ExampleModel instances with example_field filter and get its data dict\nordered_instances = redis_root.order(filtered_example_instances, \'-id\') # - to get ordered filtered_example_instances by id (\'-\' for reverse)\nupdated_example_instances = redis_root.update(ExampleModel, ordered_instances, example_field=\'another_example_data\') # - to update all ordered_instances example_field with value \'another_example_data\' and get its data dict\nredis_root.delete(ExampleModel, updated_example_instances) # - to delete updated_example_instances\n\n# Non-blocking funcs are the same, just add "_nb" to the end:\n# ExampleModel(...).save_nb()\n# redis_root.create_nb(...)\n# redis_root.update_nb(...)\n# redis_root.delete_nb(...)\n\n```\n\n\n# Example usage\n\nAll features:\n\n[full_test.py](https://github.com/gh0st-work/python_redis_orm/blob/master/python_redis_orm/tests/full_test.py)\n```python\nimport random\nimport sys\nfrom time import sleep\nimport asyncio\nimport os\n\nfrom python_redis_orm.core import *\n\n\ndef generate_token(chars_count):\n    allowed_chars = \'QWERTYUIOPASDFGHJKLZXCVBNM1234567890\'\n    token = f\'{"".join([random.choice(allowed_chars) for i in range(chars_count)])}\'\n    return token\n\n\ndef generate_token_12_chars():\n    return generate_token(12)\n\n\nclass BotSession(RedisModel):\n    session_token = RedisString(default=generate_token_12_chars)\n    created = RedisDateTime(default=datetime.datetime.now)\n\n\nclass TaskChallenge(RedisModel):\n    bot_session = RedisForeignKey(model=BotSession)\n    task_id = RedisNumber(default=0, null=False)\n    status = RedisString(default=\'in_work\', choices={\n        \'in_work\': \'В работе\',\n        \'completed\': \'Завершён успешно\',\n        \'completed_frozen_points\': \'Завершён успешно, получил поинты в холде\',\n        \'completed_points\': \'Завершён успешно, получил поинты\',\n        \'completed_decommissioning\': \'Завершён успешно, поинты списаны\',\n        \'failed_bot\': \'Зафейлил бот\',\n        \'failed_task_creator\': \'Зафейлил создатель задания\',\n    }, null=False)\n    account_checks_count = RedisNumber(default=0)\n    created = RedisDateTime(default=datetime.datetime.now)\n\n\nclass TtlCheckModel(RedisModel):\n    redis_number_with_ttl = RedisNumber(default=0, null=False)\n\n\nclass MetaTtlCheckModel(RedisModel):\n    redis_number = RedisNumber(default=0, null=False)\n    \n    class Meta:\n        ttl = 5\n\n\nclass DictCheckModel(RedisModel):\n    redis_dict = RedisDict()\n\n\nclass ListCheckModel(RedisModel):\n    redis_list = RedisList()\n\n\nclass ForeignKeyCheckModel(RedisModel):\n    task_challenge = RedisForeignKey(model=TaskChallenge)\n\n\nclass ManyToManyCheckModel(RedisModel):\n    task_challenges = RedisManyToMany(model=TaskChallenge)\n\n\nclass ModelWithOverriddenSave(RedisModel):\n    multiplied_max_field = RedisNumber()\n    \n    def save(self):\n        redis_root = self.get(\'redis_root\')  # get value of any field\n        new_value = 1\n        all_instances = redis_root.get(ModelWithOverriddenSave)\n        if all_instances:\n            max_value = max(list(map(lambda instance: instance[\'multiplied_max_field\'], all_instances)))\n            new_value = max_value * 2\n        self.set(multiplied_max_field=new_value)\n        return super().save()\n\n\ndef clean_db_after_test(connection_pool, prefix):\n    redis_instance = redis.Redis(connection_pool=connection_pool)\n    for key in redis_instance.keys(f\'{prefix}:*\'):\n        redis_instance.delete(key)\n\n\ndef basic_test(connection_pool, prefix):\n    try:\n        redis_root = RedisRoot(\n            prefix=prefix,\n            connection_pool=connection_pool,\n            ignore_deserialization_errors=True\n        )\n        redis_root.register_models([\n            TaskChallenge,\n        ])\n        for i in range(5):\n            TaskChallenge(\n                redis_root=redis_root,\n                status=\'in_work\',\n            ).save()\n        task_challenges_without_keys = redis_root.get(TaskChallenge)\n        task_challenges_with_keys = redis_root.get(TaskChallenge, return_dict=True)\n        have_exception = False\n        if not len(task_challenges_without_keys):\n            have_exception = True\n        if not task_challenges_with_keys:\n            have_exception = True\n        else:\n            if not task_challenges_with_keys.keys():\n                have_exception = True\n            else:\n                if len(list(task_challenges_with_keys.keys())) != len(task_challenges_without_keys):\n                    have_exception = True\n    except BaseException as ex:\n        have_exception = True\n    clean_db_after_test(connection_pool, prefix)\n    return have_exception\n\n\ndef auto_reg_test(connection_pool, prefix):\n    redis_root = RedisRoot(\n        prefix=prefix,\n        connection_pool=connection_pool,\n        ignore_deserialization_errors=True\n    )\n    task_challenge_1 = TaskChallenge(\n        redis_root=redis_root,\n        status=\'in_work\',\n    ).save()\n    try:\n        task_challenges = redis_root.get(TaskChallenge)\n        have_exception = False\n    except BaseException as ex:\n        have_exception = True\n    clean_db_after_test(connection_pool, prefix)\n    return have_exception\n\n\ndef no_connection_pool_test(*args, **kwargs):\n    try:\n        redis_root = RedisRoot(\n            ignore_deserialization_errors=True\n        )\n        task_challenge_1 = TaskChallenge(\n            redis_root=redis_root,\n            status=\'in_work\',\n        )\n        task_challenge_1.save()\n        task_challenges = redis_root.get(TaskChallenge)\n        have_exception = False\n        connection_pool = redis.ConnectionPool(\n            host=os.environ[\'REDIS_HOST\'],\n            port=os.environ[\'REDIS_PORT\'],\n            db=0,\n            decode_responses=True\n        )\n        clean_db_after_test(connection_pool, redis_root.prefix)\n    except BaseException as ex:\n        have_exception = True\n    return have_exception\n\n\ndef choices_test(connection_pool, prefix):\n    redis_root = RedisRoot(\n        prefix=prefix,\n        connection_pool=connection_pool,\n        ignore_deserialization_errors=True\n    )\n    task_challenge_1 = TaskChallenge(\n        redis_root=redis_root,\n        status=\'bruh\',\n    )\n    try:\n        save_result = task_challenge_1.save()\n        task_challenges = redis_root.get(TaskChallenge)\n        have_exception = True\n    except BaseException as ex:\n        have_exception = False\n    clean_db_after_test(connection_pool, prefix)\n    return have_exception\n\n\ndef order_test(connection_pool, prefix):\n    redis_root = RedisRoot(\n        prefix=prefix,\n        connection_pool=connection_pool,\n        ignore_deserialization_errors=True\n    )\n    for i in range(3):\n        TaskChallenge(\n            redis_root=redis_root\n        ).save()\n    have_exception = True\n    try:\n        task_challenges = redis_root.get(TaskChallenge)\n        first_task_challenge = redis_root.order(task_challenges, \'id\')[0]\n        last_task_challenge = redis_root.order(task_challenges, \'-id\')[0]\n        if first_task_challenge[\'id\'] == 1 and last_task_challenge[\'id\'] == len(task_challenges):\n            have_exception = False\n    except BaseException as ex:\n        pass\n    clean_db_after_test(connection_pool, prefix)\n    return have_exception\n\n\ndef filter_test(connection_pool, prefix):\n    redis_root = RedisRoot(\n        prefix=prefix,\n        connection_pool=connection_pool,\n        ignore_deserialization_errors=True\n    )\n    have_exception = True\n    try:\n        same_tokens_count = 2\n        random_tokens_count = 8\n        same_token = generate_token(50)\n        random_tokens = [generate_token(50) for i in range(random_tokens_count)]\n        for i in range(same_tokens_count):\n            BotSession(redis_root, session_token=same_token).save()\n        for random_token in random_tokens:\n            BotSession(redis_root, session_token=random_token).save()\n        task_challenges_with_same_token = redis_root.get(BotSession, session_token=same_token)\n        if len(task_challenges_with_same_token) == same_tokens_count:\n            have_exception = False\n    except BaseException as ex:\n        print(ex)\n    \n    clean_db_after_test(connection_pool, prefix)\n    return have_exception\n\n\ndef update_test(connection_pool, prefix):\n    redis_root = RedisRoot(\n        prefix=prefix,\n        connection_pool=connection_pool,\n        ignore_deserialization_errors=True\n    )\n    have_exception = True\n    try:\n        bot_session_1 = BotSession(redis_root, session_token=\'123\').save()\n        bot_session_1_id = bot_session_1[\'id\']\n        redis_root.update(BotSession, bot_session_1, session_token=\'234\')\n        bot_sessions_filtered = redis_root.get(BotSession, id=bot_session_1_id)\n        if len(bot_sessions_filtered) == 1:\n            bot_session_1_new = bot_sessions_filtered[0]\n            if \'session_token\' in bot_session_1_new.keys():\n                if bot_session_1_new[\'session_token\'] == \'234\':\n                    have_exception = False\n    except BaseException as ex:\n        print(ex)\n    \n    clean_db_after_test(connection_pool, prefix)\n    return have_exception\n\n\ndef functions_like_defaults_test(connection_pool, prefix):\n    redis_root = RedisRoot(\n        prefix=prefix,\n        connection_pool=connection_pool,\n        ignore_deserialization_errors=True\n    )\n    have_exception = False\n    try:\n        bot_session_1 = BotSession(redis_root).save()\n        bot_session_2 = BotSession(redis_root).save()\n        if bot_session_1.session_token == bot_session_2.session_token:\n            have_exception = True\n    except BaseException as ex:\n        pass\n    \n    clean_db_after_test(connection_pool, prefix)\n    return have_exception\n\n\ndef redis_foreign_key_test(connection_pool, prefix):\n    redis_root = RedisRoot(\n        prefix=prefix,\n        connection_pool=connection_pool,\n        ignore_deserialization_errors=True\n    )\n    have_exception = True\n    try:\n        bot_session_1 = BotSession(\n            redis_root=redis_root,\n        ).save()\n        task_challenge_1 = TaskChallenge(\n            redis_root=redis_root,\n            bot_session=bot_session_1\n        ).save()\n        bot_sessions = redis_root.get(BotSession)\n        bot_session = redis_root.order(bot_sessions, \'-id\')[0]\n        task_challenges = redis_root.get(TaskChallenge)\n        task_challenge = redis_root.order(task_challenges, \'-id\')[0]\n        if type(task_challenge[\'bot_session\']) == dict:\n            if task_challenge[\'bot_session\'] == bot_session:\n                have_exception = False\n    except BaseException as ex:\n        print(ex)\n    \n    clean_db_after_test(connection_pool, prefix)\n    return have_exception\n\n\ndef delete_test(connection_pool, prefix):\n    redis_root = RedisRoot(\n        prefix=prefix,\n        connection_pool=connection_pool,\n        ignore_deserialization_errors=True\n    )\n    have_exception = True\n    try:\n        bot_session_1 = BotSession(\n            redis_root=redis_root,\n        ).save()\n        task_challenge_1 = TaskChallenge(\n            redis_root=redis_root,\n            bot_session=bot_session_1\n        ).save()\n        redis_root.delete(BotSession, bot_session_1)\n        redis_root.delete(TaskChallenge, task_challenge_1)\n        bot_sessions = redis_root.get(BotSession)\n        task_challenges = redis_root.get(TaskChallenge)\n        if len(bot_sessions) == 0 and len(task_challenges) == 0:\n            have_exception = False\n    except BaseException as ex:\n        print(ex)\n    \n    clean_db_after_test(connection_pool, prefix)\n    return have_exception\n\n\ndef save_consistency_test(connection_pool, prefix):\n    redis_root = RedisRoot(\n        prefix=prefix,\n        connection_pool=connection_pool,\n        ignore_deserialization_errors=True,\n        save_consistency=True,\n    )\n    have_exception = True\n    try:\n        ttl_check_model_1 = TtlCheckModel(\n            redis_root=redis_root,\n        ).save()\n        ttl_check_models = redis_root.get(TtlCheckModel)\n        if len(ttl_check_models):\n            ttl_check_model = ttl_check_models[0]\n            if \'redis_number_with_ttl\' in ttl_check_model.keys():\n                sleep(6)\n                ttl_check_models = redis_root.get(TtlCheckModel)\n                if len(ttl_check_models):\n                    ttl_check_model = ttl_check_models[0]\n                    if \'redis_number_with_ttl\' in ttl_check_model.keys():  # because consistency is saved\n                        have_exception = False\n    except BaseException as ex:\n        print(ex)\n    \n    clean_db_after_test(connection_pool, prefix)\n    return have_exception\n\n\ndef meta_ttl_test(connection_pool, prefix):\n    redis_root = RedisRoot(\n        prefix=prefix,\n        connection_pool=connection_pool,\n        ignore_deserialization_errors=True,\n    )\n    have_exception = True\n    try:\n        meta_ttl_check_model_1 = MetaTtlCheckModel(\n            redis_root=redis_root,\n        ).save()\n        meta_ttl_check_models = redis_root.get(MetaTtlCheckModel)\n        if len(meta_ttl_check_models):\n            meta_ttl_check_model = meta_ttl_check_models[0]\n            if \'redis_number\' in meta_ttl_check_model.keys():\n                sleep(6)\n                meta_ttl_check_models = redis_root.get(MetaTtlCheckModel)\n                if not len(meta_ttl_check_models):\n                    have_exception = False\n    except BaseException as ex:\n        print(ex)\n    \n    clean_db_after_test(connection_pool, prefix)\n    return have_exception\n\n\ndef use_keys_test(connection_pool, prefix):\n    have_exception = True\n    try:\n        \n        redis_root = RedisRoot(\n            prefix=prefix,\n            connection_pool=connection_pool,\n            ignore_deserialization_errors=True,\n            use_keys=True\n        )\n        started_in_keys = datetime.datetime.now()\n        tests_count = 100\n        for i in range(tests_count):\n            task_challenge_1 = TaskChallenge(\n                redis_root=redis_root,\n                status=\'in_work\',\n            ).save()\n            redis_root.update(TaskChallenge, task_challenge_1, account_checks_count=1)\n        ended_in_keys = datetime.datetime.now()\n        keys_time = (ended_in_keys - started_in_keys).total_seconds()\n        clean_db_after_test(connection_pool, prefix)\n        \n        redis_root = RedisRoot(\n            prefix=prefix,\n            connection_pool=connection_pool,\n            ignore_deserialization_errors=True,\n            use_keys=False\n        )\n        started_in_no_keys = datetime.datetime.now()\n        for i in range(tests_count):\n            task_challenge_1 = TaskChallenge(\n                redis_root=redis_root,\n                status=\'in_work\',\n            ).save()\n            redis_root.update(TaskChallenge, task_challenge_1, account_checks_count=1)\n        ended_in_no_keys = datetime.datetime.now()\n        no_keys_time = (ended_in_no_keys - started_in_no_keys).total_seconds()\n        clean_db_after_test(connection_pool, prefix)\n        keys_percent = round((no_keys_time / keys_time - 1) * 100, 2)\n        keys_symbol = (\'+\' if keys_percent > 0 else \'\')\n        print(f\'Keys usage gives {keys_symbol}{keys_percent}% efficiency\')\n        have_exception = False\n    except BaseException as ex:\n        print(ex)\n    \n    clean_db_after_test(connection_pool, prefix)\n    return have_exception\n\n\ndef dict_test(connection_pool, prefix):\n    redis_root = RedisRoot(\n        prefix=prefix,\n        connection_pool=connection_pool,\n        ignore_deserialization_errors=True\n    )\n    have_exception = True\n    try:\n        some_dict = {\n            \'age\': 19,\n            \'weed\': True\n        }\n        DictCheckModel(\n            redis_root=redis_root,\n            redis_dict=some_dict\n        ).save()\n        dict_check_model_instance = redis_root.get(DictCheckModel)[0]\n        if \'redis_dict\' in dict_check_model_instance.keys():\n            if dict_check_model_instance[\'redis_dict\'] == some_dict:\n                have_exception = False\n    except BaseException as ex:\n        print(ex)\n    \n    clean_db_after_test(connection_pool, prefix)\n    return have_exception\n\n\ndef list_test(connection_pool, prefix):\n    redis_root = RedisRoot(\n        prefix=prefix,\n        connection_pool=connection_pool,\n        ignore_deserialization_errors=True\n    )\n    have_exception = True\n    try:\n        some_list = [5, 9, \'s\', 4.5, False]\n        ListCheckModel(\n            redis_root=redis_root,\n            redis_list=some_list\n        ).save()\n        list_check_model_instance = redis_root.get(ListCheckModel)[0]\n        if \'redis_list\' in list_check_model_instance.keys():\n            if list_check_model_instance[\'redis_list\'] == some_list:\n                have_exception = False\n    except BaseException as ex:\n        print(ex)\n    \n    clean_db_after_test(connection_pool, prefix)\n    return have_exception\n\n\ndef non_blocking_test(connection_pool, prefix):\n    have_exception = True\n    \n    # try:\n    \n    def task(data_count, use_non_blocking):\n        connection_pool = redis.ConnectionPool(\n            host=os.environ[\'REDIS_HOST\'],\n            port=os.environ[\'REDIS_PORT\'],\n            db=0,\n            decode_responses=True\n        )\n        redis_root = RedisRoot(\n            prefix=prefix,\n            connection_pool=connection_pool,\n            ignore_deserialization_errors=True\n        )\n        \n        for i in range(data_count):\n            redis_root.create(\n                ListCheckModel,\n                redis_list=[\'update_list\']\n            )\n            redis_root.create(\n                ListCheckModel,\n                redis_list=[\'delete_list\']\n            )\n        \n        def create_list():\n            if use_non_blocking:\n                list_check_model_instance = redis_root.create_nb(\n                    ListCheckModel,\n                    redis_list=[\'create_list\']\n                )\n            else:\n                list_check_model_instance = redis_root.create(\n                    ListCheckModel,\n                    redis_list=[\'create_list\']\n                )\n        \n        def update_list():\n            to_update = redis_root.get(\n                ListCheckModel,\n                redis_list=[\'update_list\']\n            )\n            if use_non_blocking:\n                updated_instance = redis_root.update_nb(\n                    ListCheckModel,\n                    to_update,\n                    redis_list=[\'now_updated_list\']\n                )\n            else:\n                updated_instance = redis_root.update(\n                    ListCheckModel,\n                    to_update,\n                    redis_list=[\'now_updated_list\']\n                )\n        \n        def delete_list():\n            to_delete = redis_root.get(\n                ListCheckModel,\n                redis_list=[\'delete_list\']\n            )\n            if use_non_blocking:\n                redis_root.delete_nb(\n                    ListCheckModel,\n                    to_delete,\n                )\n            else:\n                redis_root.delete(\n                    ListCheckModel,\n                    to_delete,\n                )\n        \n        tests = [\n            create_list,\n            update_list,\n            delete_list,\n        ]\n        for test in tests:\n            for i in range(data_count):\n                test()\n    \n    data_count = 100\n    clean_db_after_test(connection_pool, prefix)\n    nb_started_in = datetime.datetime.now()\n    task(data_count, True)\n    nb_ended_in = datetime.datetime.now()\n    nb_time = (nb_ended_in - nb_started_in).total_seconds()\n    clean_db_after_test(connection_pool, prefix)\n    b_started_in = datetime.datetime.now()\n    task(data_count, False)\n    b_ended_in = datetime.datetime.now()\n    b_time = (b_ended_in - b_started_in).total_seconds()\n    clean_db_after_test(connection_pool, prefix)\n    \n    nb_percent = round((nb_time / b_time - 1) * 100, 2)\n    nb_symbol = (\'+\' if nb_percent > 0 else \'\')\n    print(f\'Non blocking gives {nb_symbol}{nb_percent}% efficiency\')\n    have_exception = False\n    # except BaseException as ex:\n    #     print(ex)\n    \n    clean_db_after_test(connection_pool, prefix)\n    return have_exception\n\n\ndef foreign_key_test(connection_pool, prefix):\n    redis_root = RedisRoot(\n        prefix=prefix,\n        connection_pool=connection_pool,\n        ignore_deserialization_errors=True,\n    )\n    have_exception = False\n    try:\n        task_id = 12345\n        task_challenge = TaskChallenge(\n            redis_root=redis_root,\n            task_id=task_id\n        ).save()\n        foreign_key_check_instance = redis_root.create(\n            ForeignKeyCheckModel,\n            task_challenge=task_challenge\n        )\n        # Check really created\n        task_challenge_qs = redis_root.get(TaskChallenge, task_id=task_id)\n        if len(task_challenge_qs) != 1:\n            have_exception = True\n        else:\n            task_challenge = task_challenge_qs[0]\n            foreign_key_check_instance_qs = redis_root.get(ForeignKeyCheckModel, task_challenge=task_challenge)\n            if len(foreign_key_check_instance_qs) != 1:\n                have_exception = True\n            else:\n                foreign_key_check_instance = foreign_key_check_instance_qs[0]\n                if foreign_key_check_instance[\'task_challenge\'][\'task_id\'] != task_id:\n                    have_exception = True\n    except BaseException as ex:\n        print(ex)\n        have_exception = True\n    \n    clean_db_after_test(connection_pool, prefix)\n    return have_exception\n\n\ndef many_to_many_test(connection_pool, prefix):\n    redis_root = RedisRoot(\n        prefix=prefix,\n        connection_pool=connection_pool,\n        ignore_deserialization_errors=True,\n    )\n    have_exception = False\n    try:\n        tasks_ids = set([random.randrange(0, 100) for i in range(10)])\n        task_challenges = [\n            TaskChallenge(\n                redis_root=redis_root,\n                task_id=task_id\n            ).save()\n            for task_id in tasks_ids\n        ]\n        many_to_many_check_instance = redis_root.create(\n            ManyToManyCheckModel,\n            task_challenges=task_challenges\n        )\n        # Check really created\n        many_to_many_check_instances_qs = redis_root.get(ManyToManyCheckModel)\n        if len(many_to_many_check_instances_qs) != 1:\n            have_exception = True\n        else:\n            many_to_many_check_instance = many_to_many_check_instances_qs[0]\n            if many_to_many_check_instance[\'task_challenges\'] != task_challenges:\n                have_exception = True\n    except BaseException as ex:\n        print(ex)\n        have_exception = True\n    \n    clean_db_after_test(connection_pool, prefix)\n    return have_exception\n\n\ndef save_override_test(connection_pool, prefix):\n    redis_root = RedisRoot(\n        prefix=prefix,\n        connection_pool=connection_pool,\n        ignore_deserialization_errors=True,\n    )\n    have_exception = False\n    try:\n        instance_1 = redis_root.create(ModelWithOverriddenSave)\n        instance_2 = redis_root.create(ModelWithOverriddenSave)\n        if instance_1[\'multiplied_max_field\'] * 2 != instance_2[\'multiplied_max_field\']:\n            have_exception = True\n    except BaseException as ex:\n        print(ex)\n        have_exception = True\n    \n    clean_db_after_test(connection_pool, prefix)\n    return have_exception\n\n\ndef performance_test(connection_pool, prefix):\n    have_exception = False\n    try:\n        \n        def run_test(count, model):\n            \n            def test(count, model, **test_params):\n                real_test_params = {\n                    \'use_keys\': True,\n                    \'use_non_blocking\': True\n                }\n                for key in real_test_params.copy():\n                    if key in test_params.keys():\n                        real_test_params[key] = test_params[key]\n                \n                def create_instances(redis_root, count, use_non_blocking, model):\n                    \n                    if use_non_blocking:\n                        started_in = datetime.datetime.now()\n                        results = [\n                            redis_root.create_nb(model)\n                            for i in range(count)\n                        ]\n                        ended_in = datetime.datetime.now()\n                    else:\n                        started_in = datetime.datetime.now()\n                        results = [\n                            redis_root.create(model)\n                            for i in range(count)\n                        ]\n                        ended_in = datetime.datetime.now()\n                    \n                    time_took = (ended_in - started_in).total_seconds()\n                    fields_count = len(results[0].keys()) * count\n                    clean_db_after_test(connection_pool, prefix)\n                    return [time_took, count, fields_count]\n                \n                redis_root = RedisRoot(\n                    prefix=prefix,\n                    connection_pool=connection_pool,\n                    ignore_deserialization_errors=True,\n                    use_keys=real_test_params[\'use_keys\']\n                )\n                \n                test_result = create_instances(redis_root, count, real_test_params[\'use_non_blocking\'], model)\n                \n                return test_result\n            \n            test_confs = [\n                {\n                    \'use_keys\': False,\n                    \'use_non_blocking\': False,\n                },\n                {\n                    \'use_keys\': False,\n                    \'use_non_blocking\': True,\n                },\n                {\n                    \'use_keys\': True,\n                    \'use_non_blocking\': False,\n                },\n                {\n                    \'use_keys\': True,\n                    \'use_non_blocking\': True,\n                },\n            \n            ]\n            \n            test_confs_results = [\n                test(count, model, **test_conf)\n                for test_conf in test_confs\n            ]\n            \n            print(f\'\\n\\n\\n\'\n                  f\'Performance test results on your machine:\\n\'\n                  f\'Every test creates {test_confs_results[0][1]} instances ({test_confs_results[0][2]} fields) of {model.__name__} model,\\n\'\n                  f\'Here is the results:\\n\'\n                  f\'\\n\')\n            \n            min_time = min(list(map(lambda result: result[0], test_confs_results)))\n            min_conf_text = \'\'\n            for i, test_confs_result in enumerate(test_confs_results):\n                test_conf_text = ", ".join([f"{k} = {v}" for k, v in test_confs[i].items()])\n                print(f\'Configuration: {test_conf_text} took {test_confs_result[0]}s\')\n                if test_confs_result[0] == min_time:\n                    min_conf_text = test_conf_text\n            print(f\'\\n\\n\'\n                  f\'The best configuration: {min_conf_text}\\n\')\n        \n        count = 1000\n        model = TaskChallenge\n        run_test(count, model)\n    except BaseException as ex:\n        print(ex)\n        have_exception = True\n    \n    clean_db_after_test(connection_pool, prefix)\n    return have_exception\n\n\ndef run_tests():\n    connection_pool = redis.ConnectionPool(\n        host=os.environ[\'REDIS_HOST\'],\n        port=os.environ[\'REDIS_PORT\'],\n        db=0,\n        decode_responses=True\n    )\n    tests = [\n        basic_test,\n        auto_reg_test,\n        no_connection_pool_test,\n        choices_test,\n        order_test,\n        filter_test,\n        functions_like_defaults_test,\n        redis_foreign_key_test,\n        update_test,\n        delete_test,\n        save_consistency_test,\n        meta_ttl_test,\n        use_keys_test,\n        list_test,\n        dict_test,\n        non_blocking_test,\n        foreign_key_test,\n        many_to_many_test,\n        save_override_test,\n        performance_test,\n    ]\n    results = []\n    started_in = datetime.datetime.now()\n    print(\'STARTING TESTS\\n\')\n    for i, test in enumerate(tests):\n        print(f\'Starting {int(i + 1)} test: {test.__name__.replace("_", " ")}\')\n        test_started_in = datetime.datetime.now()\n        result = not test(connection_pool, test.__name__)\n        test_ended_in = datetime.datetime.now()\n        test_time = (test_ended_in - test_started_in).total_seconds()\n        print(f\'{result = } / {test_time}s\\n\')\n        results.append(result)\n    ended_in = datetime.datetime.now()\n    time = (ended_in - started_in).total_seconds()\n    success_message = \'SUCCESS\' if all(results) else \'FAILED\'\n    print(\'\\n\'\n          f\'{success_message}!\\n\')\n    results_success_count = 0\n    for i, result in enumerate(results):\n        result_message = \'SUCCESS\' if result else \'FAILED\'\n        print(f\'Test {(i + 1)}/{len(results)}: {result_message} ({tests[i].__name__.replace("_", " ")})\')\n        if result:\n            results_success_count += 1\n    print(f\'\\n\'\n          f\'{results_success_count} / {len(results)} tests ran successfully\\n\'\n          f\'All tests completed in {time}s\\n\')\n    \n    return all(results)\n\n\nif __name__ == \'__main__\':\n    results = run_tests()\n    if not results:\n        sys.exit(1)\n\n```\n\n\n### Output\n\n```\nSTARTING TESTS\n\nStarting 1 test: basic test\nresult = True / 0.017655s\n\nStarting 2 test: auto reg test\nresult = True / 0.002688s\n\nStarting 3 test: no connection pool test\n2021-09-17 13:33:42.915213 - RedisRoot: No connection_pool provided, trying default config...\nresult = True / 0.003571s\n\nStarting 4 test: choices test\nresult = True / 0.001307s\n\nStarting 5 test: order test\nresult = True / 0.005999s\n\nStarting 6 test: filter test\nresult = True / 0.019395s\n\nStarting 7 test: functions like defaults test\nresult = True / 0.003462s\n\nStarting 8 test: redis foreign key test\nresult = True / 0.006697s\n\nStarting 9 test: update test\nresult = True / 0.003751s\n\nStarting 10 test: delete test\nresult = True / 0.006936s\n\nStarting 11 test: save consistency test\nresult = True / 6.009583s\n\nStarting 12 test: meta ttl test\nresult = True / 6.010543s\n\nStarting 13 test: use keys test\nKeys usage gives +32.47% efficiency\nresult = True / 1.348907s\n\nStarting 14 test: list test\nresult = True / 0.002379s\n\nStarting 15 test: dict test\nresult = True / 0.002316s\n\nStarting 16 test: non blocking test\nNon blocking gives +195.91% efficiency\nresult = True / 13.0517s\n\nStarting 17 test: foreign key test\nresult = True / 0.00598s\n\nStarting 18 test: many to many test\nresult = True / 0.033524s\n\nStarting 19 test: save override test\nresult = True / 0.003881s\n\nStarting 20 test: performance test\n\n\n\nPerformance test results on your machine:\nEvery test creates 1000 instances (6000 fields) of TaskChallenge model,\nHere is the results:\n\n\nConfiguration: use_keys = False, use_non_blocking = False took 40.92255s\nConfiguration: use_keys = False, use_non_blocking = True took 0.234719s\nConfiguration: use_keys = True, use_non_blocking = False took 31.373307s\nConfiguration: use_keys = True, use_non_blocking = True took 0.242484s\n\n\nThe best configuration: use_keys = False, use_non_blocking = True\n\nresult = True / 73.106303s\n\n\nSUCCESS!\n\nTest 1/20: SUCCESS (basic test)\nTest 2/20: SUCCESS (auto reg test)\nTest 3/20: SUCCESS (no connection pool test)\nTest 4/20: SUCCESS (choices test)\nTest 5/20: SUCCESS (order test)\nTest 6/20: SUCCESS (filter test)\nTest 7/20: SUCCESS (functions like defaults test)\nTest 8/20: SUCCESS (redis foreign key test)\nTest 9/20: SUCCESS (update test)\nTest 10/20: SUCCESS (delete test)\nTest 11/20: SUCCESS (save consistency test)\nTest 12/20: SUCCESS (meta ttl test)\nTest 13/20: SUCCESS (use keys test)\nTest 14/20: SUCCESS (list test)\nTest 15/20: SUCCESS (dict test)\nTest 16/20: SUCCESS (non blocking test)\nTest 17/20: SUCCESS (foreign key test)\nTest 18/20: SUCCESS (many to many test)\nTest 19/20: SUCCESS (save override test)\nTest 20/20: SUCCESS (performance test)\n\n20 / 20 tests ran successfully\nAll tests completed in 99.646971s\n\n```',
    'author': 'Anton Nechaev',
    'author_email': 'antonnechaev990@gmail.com',
    'maintainer': None,
    'maintainer_email': None,
    'url': 'https://github.com/gh0st-work/python_redis_orm',
    'packages': packages,
    'package_data': package_data,
    'install_requires': install_requires,
    'python_requires': '>=3.6,<4.0',
}


setup(**setup_kwargs)
