{"id":23312474,"url":"https://github.com/attumm/redis-dict","last_synced_at":"2026-03-17T22:04:10.744Z","repository":{"id":21601317,"uuid":"93274511","full_name":"Attumm/redis-dict","owner":"Attumm","description":"Python dictionary with Redis as backend, built for large datasets. Simplifies Redis operations for large-scale and distributed systems. Supports various data types, namespacing, pipelining, and expiration.","archived":false,"fork":false,"pushed_at":"2025-03-08T10:16:30.000Z","size":5512,"stargazers_count":72,"open_issues_count":3,"forks_count":13,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-02T10:14:27.844Z","etag":null,"topics":["dictionary","distributed-computing","distributed-systems","python","pythonic","redis-dict"],"latest_commit_sha":null,"homepage":"https://attumm.github.io/redis-dict/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Attumm.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-06-03T21:20:36.000Z","updated_at":"2025-03-28T04:42:09.000Z","dependencies_parsed_at":"2022-08-07T10:00:56.852Z","dependency_job_id":"9ee3932b-cf53-4122-a382-91409c5803d0","html_url":"https://github.com/Attumm/redis-dict","commit_stats":{"total_commits":59,"total_committers":8,"mean_commits":7.375,"dds":"0.38983050847457623","last_synced_commit":"392eb70f1e8fe0b340eb25bca1e70473202bbf03"},"previous_names":["attumm/redisdict"],"tags_count":23,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Attumm%2Fredis-dict","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Attumm%2Fredis-dict/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Attumm%2Fredis-dict/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Attumm%2Fredis-dict/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Attumm","download_url":"https://codeload.github.com/Attumm/redis-dict/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246828495,"owners_count":20840474,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dictionary","distributed-computing","distributed-systems","python","pythonic","redis-dict"],"created_at":"2024-12-20T14:32:58.608Z","updated_at":"2026-03-17T22:04:10.737Z","avatar_url":"https://github.com/Attumm.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Redis-dict\n\n[![PyPI](https://img.shields.io/pypi/v/redis-dict.svg)](https://pypi.org/project/redis-dict/)\n[![CI](https://github.com/Attumm/redis-dict/actions/workflows/ci.yml/badge.svg)](https://github.com/Attumm/redis-dict/actions/workflows/ci.yml)\n[![codecov](https://codecov.io/gh/Attumm/redis-dict/graph/badge.svg?token=Lqs7McQGEs)](https://codecov.io/gh/Attumm/redis-dict)\n[![Documentation](https://img.shields.io/badge/docs-sphinx-blue.svg)](https://attumm.github.io/redis-dict/)\n[![Supports Valkey](https://github.com/Attumm/redis-dict/actions/workflows/valkey_support.yml/badge.svg)](https://github.com/Attumm/redis-dict/actions/workflows/valkey_support.yml)\n[![Downloads](https://static.pepy.tech/badge/redis-dict/month)](https://pepy.tech/project/redis-dict)\n\nRedisDict is a Python library that offers a convenient and familiar interface for interacting with Redis, treating it as if it were a Python dictionary. Its goal is to help developers write clean, Pythonic code while using Redis as a storage solution for seamless distributed computing. Redis-Dict utilizes Redis as a key-value store and supports various data types, including strings, integers, floats, booleans, lists, and dictionaries. Additionally, developers can extend RedisDict to work with custom objects.\n\nThe library includes utility functions for more complex use cases such as caching, batching, and more. By leveraging Redis for efficient key-value storage, RedisDict enables high-performance data management, maintaining efficiency even with large datasets and Redis instances.\n\n\n[Usage](#Usage) |  [Types](#Types) | [Expiration](#Expiration) | [Batching](#Batching) | [Custom Types](#Custom-Types) | [Security](#Security)\n\n---\n\n## Features\n\n* Dictionary-like interface: Use familiar Python dictionary syntax to interact with Redis.\n* Data Type Support: Comprehensive support for various data types.\n* Pipelining support: Use pipelines for batch operations to improve performance.\n* Expiration Support: Enables the setting of expiration times either globally or individually per key, through the use of context managers.\n* Efficiency and Scalability: RedisDict is designed for use with large datasets and is optimized for efficiency. It retrieves only the data needed for a particular operation, ensuring efficient memory usage and fast performance.\n* Namespace Management: Provides simple and efficient namespace handling to help organize and manage data in Redis, streamlining data access and manipulation.\n* Distributed Computing: With its ability to seamlessly connect to other instances or servers with access to the same Redis instance, RedisDict enables easy distributed computing.\n* Custom data: types: Add custom types encoding/decoding to store your data types.\n* Encryption: allows for storing data encrypted, while retaining the simple dictionary interface.\n\n## Usage\n\n```bash\npip install redis-dict\n```\n\n```python\n\u003e\u003e\u003e from redis_dict import RedisDict\n\u003e\u003e\u003e dic = RedisDict()\n\u003e\u003e\u003e dic['foo'] = 42\n\u003e\u003e\u003e dic['foo']\n42\n\u003e\u003e\u003e 'foo' in dic\nTrue\n\u003e\u003e\u003e dic[\"baz\"] = \"hello world\"\n\u003e\u003e\u003e dic\n{'foo': 42, 'baz': 'hello world'}\n\u003e\u003e\u003e from datetime import datetime\n\u003e\u003e\u003e dic[\"datetime\"] = datetime.now()\n```\nIn Redis our example looks like this.\n```\n127.0.0.1:6379\u003e KEYS \"*\"\n1) \"main:foo\"\n2) \"main:baz\"\n127.0.0.1:6379\u003e GET \"main:foo\"\n\"int:42\"\n127.0.0.1:6379\u003e GET \"main:baz\"\n\"str:hello world\"\n127.0.0.1:6379\u003e GET \"main:datetime\"\n\"datetime:2025-02-20T19:37:54.214274\"\n```\n\n## Types\n\n### Standard types\nRedisDict supports a range of Python data types, from basic types to nested structures.\nBasic types are handled natively, while complex data types like lists and dictionaries, RedisDict uses JSON serialization, specifically avoiding [pickle](https://docs.python.org/3/library/pickle.html) due to its security vulnerabilities within distributed computing contexts.\nAlthough the library supports nested structures, the recommended best practice is to use RedisDict as a shallow dictionary.\nThis approach optimizes Redis database performance and efficiency by ensuring that each set and get operation efficiently maps to Redis's key-value storage capabilities, while still preserving the library's Pythonic interface.\nFollowing types are supported: \n`str, int, float, bool, NoneType, list, dict, tuple, set, datetime, date, time, timedelta, Decimal, complex, bytes, UUID, OrderedDict, defaultdict, frozenset`\n```python\nfrom redis_dict import RedisDict\n\nfrom uuid import UUID\nfrom decimal import Decimal\nfrom collections import OrderedDict, defaultdict\nfrom datetime import datetime, date, time, timedelta\n\ndic = RedisDict()\n\ndic[\"string\"] = \"Hello World\"\ndic[\"number\"] = 42\ndic[\"float\"] = 3.14\ndic[\"bool\"] = True\ndic[\"None\"] = None\n\ndic[\"list\"] = [1, 2, 3]\ndic[\"dict\"] = {\"a\": 1, \"b\": 2}\ndic[\"tuple\"] = (1, 2, 3)\ndic[\"set\"] = {1, 2, 3}\n\ndic[\"datetime\"] = datetime.date(2024, 1, 1, 12, 30, 45)\ndic[\"date\"] = date(2024, 1, 1)\ndic[\"time\"] = time(12, 30, 45)\ndic[\"delta\"] = timedelta(days=1, hours=2)\n\ndic[\"decimal\"] = Decimal(\"3.14159\")\ndic[\"complex\"] = complex(1, 2)\ndic[\"bytes\"] = bytes([72, 101, 108, 108, 111])\ndic[\"uuid\"] = UUID('12345678-1234-5678-1234-567812345678')\n\ndic[\"ordered\"] = OrderedDict([('a', 1), ('b', 2)])\ndic[\"default\"] = defaultdict(int, {'a': 1, 'b': 2})\ndic[\"frozen\"] = frozenset([1, 2, 3])\n```\n\n### Namespaces\nActing as an identifier for your dictionary across different systems, RedisDict employs namespaces for organized data management. When a namespace isn't specified, \"main\" becomes the default. Thus allowing for data organization across systems and projects with the same redis instance.\nThis approach also minimizes the risk of key collisions between different applications, preventing hard-to-debug issues. By leveraging namespaces, RedisDict ensures a cleaner and more maintainable data management experience for developers working on multiple projects.\n\n## Advanced Features\n\n### Expiration\n\nRedis provides a valuable feature that enables keys to expire. RedisDict supports this feature in the following ways:\n1. Set a default expiration time when creating a RedisDict instance. In this example, the keys will have a default expiration time of 10 seconds. Use seconds with an integer or pass a datetime timedelta.\n\n```python\ndic = RedisDict(expire=10)\ndic['gone'] = 'in ten seconds'\n```\nOr, for a more Pythonic approach, use a timedelta.\n```python\nfrom datetime import timedelta\n\ndic = RedisDict(expire=timedelta(minutes=1))\ndic['gone'] = 'in a minute'\n```\n\n2. Temporarily set the default expiration time within the scope using a context manager. In this example, the key 'gone' will expire after 60 seconds. The default expiration time for other keys outside the context manager remains unchanged. Either pass an integer or a timedelta.\n\n```python\ndic = RedisDict()\n\nseconds = 60\nwith dic.expire_at(seconds):\n    dic['gone'] = 'in sixty seconds'\n```\n\n3. Updating keys while preserving the initial timeout In certain situations, there is a need to update the value while keeping the expiration intact. This is achievable by setting the 'preserve_expiration' to true.\n\n```python\nimport time\n\ndic = RedisDict(expire=10, preserve_expiration=True)\ndic['gone'] = 'in ten seconds'\n\ntime.sleep(5)\ndic['gone'] = 'gone in 5 seconds'\n\n```\n\n### Batching\nEfficiently batch your requests using the Pipeline feature, which can be easily utilized with a context manager.\n\n```python\ndic = RedisDict(namespace=\"example\")\n\n# one round trip to redis\nwith dic.pipeline():\n    for index in range(100):\n        dic[str(index)] = index\n```\n\n### Distributed computing\nYou can use RedisDict for distributed computing by starting multiple RedisDict instances on different servers or instances that have access to the same Redis instance:\n```python\n# On server 1\nfrom redis_dict import RedisDict\n\ndic = RedisDict(namespace=\"example\")\ndic[\"foo\"] = \"bar\"\n\n# On server 2\nfrom redis_dict import RedisDict\n\ndic = RedisDict(namespace=\"example\")\nprint(dic[\"foo\"]) # outputs \"bar\"\n```\n\n## Additional Examples\n\n### Caching made simple\n```python\nimport time\nfrom datetime import timedelta\nfrom redis_dict import RedisDict\n\ndef expensive_function(x):\n    time.sleep(x)\n    return x * 2\n\ncache = RedisDict(namespace=\"cache\", expire=timedelta(minutes=60))\n\ndef cached_expensive_function(x):\n    if x not in cache:\n        cache[x] = expensive_function(x)\n    return cache[x]\n\nstart_time = time.time()\nprint(cached_expensive_function(5))  # Takes around 5 seconds to compute and caches the result.\nprint(f\"Time taken: {time.time() - start_time:.2f} seconds\")\n\nstart_time = time.time()\nprint(cached_expensive_function(5))  # Fetches the result from the cache, taking almost no time.\nprint(f\"Time taken: {time.time() - start_time:.2f} seconds\")\n```\n\n### Redis-dict as dictionary\n```python\nfrom redis_dict import RedisDict\n\n# Create a RedisDict instance with a namespace\ndic = RedisDict(namespace=\"example\")\n\n# Set key-value pairs\ndic[\"name\"] = \"John Doe\"\ndic[\"age\"] = 32\ndic[\"city\"] = \"Amsterdam\"\n\n# Get value by key, from any instance connected to the same redis/namespace\nprint(dic[\"name\"])  # Output: John Doe\n\n# Update value by key, got a year older\ndic[\"age\"] = 33\n\n# Check if key exists\nprint(\"name\" in dic)  # Output: True\nprint(\"country\" in dic)  # Output: False\n\n# Get value with a default value if the key doesn't exist\nprint(dic.get(\"country\", \"NL\"))  # Output: NL\n\n# Get the length (number of keys) of the RedisDict\nprint(len(dic))  # Output: 3\n\n# Iterate over keys\nfor key in dic:\n    print(key, dic[key])\n\n# Delete a key-value pair\ndel dic[\"city\"]\n\n# Clear all key-value pairs in the RedisDict\ndic.clear()\n\n# Get the length (number of keys) of the RedisDict\nprint(len(dic))  # Output: 0\n\n# Update RedisDict with multiple key-value pairs\ndic.update({\"a\": 1, \"b\": 2, \"c\": 3})\n\n# Use methods of a normal dict\nprint(list(dic.keys()))   # Output: ['a', 'b', 'c']\nprint(list(dic.values()))  # Output: [1, 2, 3]\nprint(list(dic.items()))  # Output: [('a', 1), ('b', 2), ('c', 3)]\n\n# Using pop() and popitem() methods\nvalue = dic.pop(\"a\")\nprint(value)  # Output: 1\n\nkey, value = dic.popitem()\nprint(key, value)  # Output: 'c' 3 (example)\n\n# Using setdefault() method\ndic.setdefault(\"d\", 4)\nprint(dic[\"d\"])  # Output: 4\n\nfrom datetime import datetime, timedelta\n\n# Redis dict support datetime\ndic[\"now\"] = datetime.now()\nprint(dic[\"now\"])  # 2025-02-20 19:25:38.835816\n\n# SRedis dict support timedelta and more types\ndic[\"time\"] = timedelta(days=1)\nprint(dic[\"time\"])  # 1 day, 0:00:00\n\nprint(dic)\n{'now': datetime.datetime(2025, 2, 20, 19, 25, 38, 835816), 'time': datetime.timedelta(days=1), 'b': 2, 'd': 4}\n```\n\n### Additional Examples\nFor more advanced examples of RedisDict, please refer to the unit-test files in the repository. All features and functionalities are thoroughly tested in [unit tests (here)](https://github.com/Attumm/redis-dict/blob/main/tests/unit/tests.py#L1) Or take a look at load test for batching [load test](https://github.com/Attumm/redis-dict/blob/main/tests/load/tests_load.py#L1).\nThe unit-tests can be as used as a starting point.\n\n### Nested types\nRedisDict supports nested structures with mixed types through JSON serialization. The feature works by utilizing JSON encoding and decoding under the hood. While this represents an upgrade in functionality, the feature is not fully implemented and should be used with caution. For optimal performance, using shallow dictionaries is recommended.\n```python\nfrom datetime import datetime, timedelta\n\ndic[\"mixed\"] = [1, \"foobar\", 3.14, [1, 2, 3], datetime.now()]\n\ndic['dic'] = {\"elapsed_time\": timedelta(hours=60)}\n```\n\n### JSON Encoding - Decoding\nThe nested type support in RedisDict is implemented using custom JSON encoders and decoders. These JSON encoders and decoders are built on top of RedisDict's own encoding and decoding functionality, extending it for JSON compatibility. Since JSON serialization was a frequently requested feature, these enhanced encoders and decoders are available for use in other projects:\n```python\nimport json\nfrom datetime import datetime\nfrom redis_dict import RedisDictJSONDecoder, RedisDictJSONEncoder\n\ndata = [1, \"foobar\", 3.14, [1, 2, 3], datetime.now()]\nencoded = json.dumps(data, cls=RedisDictJSONEncoder)\nresult = json.loads(encoded, cls=RedisDictJSONDecoder)\n```\n\n## Custom Types\n### Extending RedisDict with Custom Types\n\nRedisDict supports custom type serialization. Here's how to add a new type:\n\n```python\nimport json\n\nclass Person:\n    def __init__(self, name, age):\n        self.name = name\n        self.age = age\n\n    def encode(self) -\u003e str:\n        return json.dumps(self.__dict__)\n\n    @classmethod\n    def decode(cls, encoded_str: str) -\u003e 'Person':\n        return cls(**json.loads(encoded_str))\n\nredis_dict = RedisDict()\n\n# Extend redis dict with the new type\nredis_dict.extends_type(Person)\n\n# RedisDict can now seamlessly handle Person instances.\nperson = Person(name=\"John\", age=32)\nredis_dict[\"person1\"] = person\n\nresult = redis_dict[\"person1\"]\n\nassert result.name == person.name\nassert result.age == person.age\n```\n\n### Insertion Order\nFor insertion order, use the PythonRedisDict. This class is focused on Python dictionary behavior one-to-one.\nIt will eventually become a drop-in replacement for dictionary. Currently, nested types and typed keys are not yet supported but will be added in the future.\n\n```python\nfrom redis_dict import PythonRedisDict\n\ndic = PythonRedisDict()\ndic[\"1\"] = \"one\"\ndic[\"2\"] = \"two\"\ndic[\"3\"] = \"three\"\n\nassert list(dic.keys()) == [\"1\", \"2\", \"3\"]\n```\n\nFor more information on [extending types](https://github.com/Attumm/redis-dict/blob/main/tests/unit/tests_extend_types.py).\n\n## Security\n\nSecurity is an important aspect of production projects. Redis-dict was developed within a strict compliance environment.\nBest practice in Redis is to use passwords and network encryption through TLS. However, Redis-dict offers an additional feature by using extended types.\nIt is possible to store the values of keys encrypted. The values are encrypted with AES GCM, which is currently considered best practice for security.\n\n### Storage Encryption\nFor storing data values encrypted can be achieved using encrypted values, more documentation on that later.\nFor now code example within this test file. [encrypted test](https://github.com/Attumm/redis-dict/blob/main/tests/unit/tests_encrypt.py).\n\n### Encryption Network\nSetup guide for configuring and utilizing encrypted Redis TLS for redis-dict.\n[Setup guide](https://github.com/Attumm/redis-dict/blob/main/docs/tutorials/encrypted_redis.MD)\n\n### Tests\nThe RedisDict library includes a comprehensive suite of tests that ensure its correctness and resilience. The test suite covers various data types, edge cases, and error handling scenarios. It also employs the Hypothesis library for property-based testing, which provides fuzz testing to evaluate the implementation\n\n### Redis config\nTo configure RedisDict using your Redis config.\n\nConfigure both the host and port. Or configuration with a setting dictionary.\n```python\ndic = RedisDict(host='127.0.0.1', port=6380)\n\nredis_config = {\n    'host': '127.0.0.1',\n    'port': 6380,\n}\n\nconfig_dic = RedisDict(**redis_config)\n```\n\n## Installation\n```sh\npip install redis-dict\n```\n\n### Note\n* Please be aware that this project is currently being utilized by various organizations in their production environments. If you have any questions or concerns, feel free to raise issues\n* This project only uses redis as dependency\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fattumm%2Fredis-dict","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fattumm%2Fredis-dict","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fattumm%2Fredis-dict/lists"}