Skip to content

Commit

Permalink
Docs refactor WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
Galtozzy committed Sep 22, 2024
1 parent 2427b03 commit a4fc4ce
Show file tree
Hide file tree
Showing 35 changed files with 1,612 additions and 324 deletions.
6 changes: 4 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ site_build
venv
docs.zip
archive.zip

.zed
.zed/*
# vim temporary files
*~
.*.sw?
Expand Down Expand Up @@ -56,6 +57,8 @@ wheels/
*.egg-info/
.installed.cfg
*.egg
pyrightconfig.json
.zed

# PyInstaller
# Usually these files are written by a python script from a template
Expand Down Expand Up @@ -320,4 +323,3 @@ Control.system-ca-bundle
# Visual Studio Code #
.vscode/
.history

140 changes: 106 additions & 34 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,47 +1,79 @@
# Py-Cachify

[![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://opensource.org/licenses/MIT)
[![PyPI version](https://badge.fury.io/py/py-cachify.svg)](https://badge.fury.io/py/py-cachify)
[![Documentation Status](https://readthedocs.org/projects/py-cachify/badge/?version=latest)](https://py-cachify.readthedocs.io/en/latest/?badge=latest)
[![Build Status](https://github.com/EzyGang/py-cachify/actions/workflows/checks.yml/badge.svg)]()
[![Tests Status](https://github.com/EzyGang/py-cachify/actions/workflows/integration-tests.yml/badge.svg)]()
[![Coverage Status](https://coveralls.io/repos/github/EzyGang/py-cachify/badge.png?branch=main)](https://coveralls.io/github/EzyGang/py-cachify?branch=main)
[![Reliability Rating](https://sonarcloud.io/api/project_badges/measure?project=EzyGang_py-cachify&metric=reliability_rating)](https://sonarcloud.io/summary/new_code?id=EzyGang_py-cachify)
<p align="center">
<a href="https://opensource.org/licenses/MIT" target="_blank">
<img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="License">
</a>
<a href="https://badge.fury.io/py/py-cachify" target="_blank">
<img src="https://badge.fury.io/py/py-cachify.svg" alt="PyPI version">
</a>
<a href="https://py-cachify.readthedocs.io/en/latest/?badge=latest" target="_blank">
<img src="https://readthedocs.org/projects/py-cachify/badge/?version=latest" alt="Documentation Status">
</a>
</p>
<p align="center">
<a href="https://github.com/EzyGang/py-cachify/actions/workflows/checks.yml/badge.svg" target="_blank">
<img src="https://github.com/EzyGang/py-cachify/actions/workflows/checks.yml/badge.svg" alt="Pre-build checks and Tests">
</a>
<a href="https://github.com/EzyGang/py-cachify/actions/workflows/integration-tests.yml/badge.svg" target="_blank">
<img src="https://github.com/EzyGang/py-cachify/actions/workflows/integration-tests.yml/badge.svg" alt="Tests Status">
</a>
<a href="https://coveralls.io/github/EzyGang/py-cachify?branch=main" target="_blank">
<img src="https://coveralls.io/repos/github/EzyGang/py-cachify/badge.png?branch=main" alt="Coverage Status">
</a>
<a href="https://sonarcloud.io/summary/new_code?id=EzyGang_py-cachify" target="_blank">
<img src="https://sonarcloud.io/api/project_badges/measure?project=EzyGang_py-cachify&metric=reliability_rating" alt="Reliability Rating">
</a>
</p>

---

**Documentation**: <a href="https://py-cachify.readthedocs.io/latest/" target="_blank">https://py-cachify.readthedocs.io/latest/</a>

**Source Code**: <a href="https://github.com/EzyGang/py-cachify" target="_blank">https://github.com/EzyGang/py-cachify</a>

---

py-cachify is a small library that provides useful cache based utilities.

py-cachify works well in both sync and async environments, has 100% test coverage, fully type annotated,
is backend agnostic (you can provide your own client as long as it matches the signature), and supports Python from 3.8 and upward.

py-cachify is a library that provides small but useful cache utilities.
It offers distributed (cache based) locks and decorators to lock function executions and storing their results in the cache.

Some parts were heavily inspired by [douglasfarinelli's python-cachelock](https://github.com/douglasfarinelli/python-cachelock) lib,
which is sadly no longer maintained.
The key features are:

py-cachify works well in both sync and async environments, has 100% test coverage,
is backend agnostic (you can provide your own client as long as it matches the signature), and supports Python from 3.8 and upward.
* **Intuitive to write**: Great editor support. When applying decorators your IDE will still be able to autocomplete and highlight inline errors for applied functions.
* **Fully type annotated**: You don't have to constantly look into the docs. Everything is type annotated and easy to understand out of the box.
* **Short**: Minimize code duplication. Add just one line of code to implement a cache or lock on your function.
* **Start simple**: The simplest example adds only a couple of lines of code: initialize a library and use the provided utils.
* **Backend agnostic**: Use whatever cache-backend you want to use. Py-cachify is not forcing you into anything.
* **Test coverage**: Has 100% test coverage and supports Python 3.8+

It offers distributed (cache based) locks and decorators for securing function executions and storing their results in the cache.
---

## Table of Contents

- [Documentation](#documentation)
- [Installation](#installation)
- [Examples](#examples)
- [How to use](#how-to-use)
- [Contributing](#contributing)
- [License](#license)

## Documentation

Detailed documentation can be found at https://py-cachify.readthedocs.io/en/latest/.

## Installation
To install:

<!-- termynal -->
```bash
pip install py-cachify
$ pip install py-cachify

# or if using poetry
poetry add py-cachify
---> 100%
Successfully installed py-cachify
```

## Examples
## How to use

To start working with it, you'll have to initialize it using `init_cachify`:
You can read more about everything in here in the tutorial

First, to start working with it, you'll have to initialize it using `init_cachify`:
```python
from py_cachify import init_cachify

Expand All @@ -59,26 +91,66 @@ init_cachify(sync_client=from_url(redis_url), async_client=async_from_url(async_
```
Normally you wouldn't have to use both sync and async clients since an application usually works in a single mode i.e. sync/async.

Once initialized you can use everything that the library provides straight up without being worried about managing the cache yourself.
Once initialized you can use everything that the library provides straight up without being worried about managing the cache yourself.

If you forgot to call `init_cachify` the `CachifyInitError` will be raised during runtime.

Let's write the simple `@cached` example and utilize the flexibility of a dynamic key:
```python
from py_cachify import once
import asyncio

from py_cachify import init_cachify, cached


# Cache the result of the following function with dynamic key
@cached(key='sum_two-{original}-{inc_by}')
async def sum_two(a: int, b: int) -> int:
# Let's put print here to see what was the function called with
print(f'Called with {a} {b}')
return a + b


async def main() -> None:
# init the library
init_cachify()

# Call the function
print(f'First call result: {await sum_two(5, 5)}')

# And we will call it again to make sure it's not called but the result is the same
print(f'Second call result: {await sum_two(5, 5)}')

# Now we will call it with different args to make sure the function is indeed called for another set of arguments
print(f'Third call result: {await sum_two(5, 10)}')


if __name__ == '__main__':
asyncio.run(main())
```

Run the example:
<!-- termynal -->
```bash
// Run our example
$ python main.py

// The ouput should be
Called with 5 5
First call result: 10
Second call result: 10
Called with 5 10
Third call result: 15

# Make sure there is just one copy of a function running at a time
@once(key='long_running_function')
async def long_running_function() -> str:
# Executing long-running operation...
pass
```
As you can see the first call was cached, and the second call got the result out of the cache,
but the third call was performed again since the arguments have changed.

For more detailed documentation and examples please visit https://py-cachify.readthedocs.io/en/latest/.
For more detailed tutorial visit #.

## Contributing

If you'd like to contribute, please first discuss the changes using Issues, and then don't hesitate to shoot a PR which will be reviewed shortly.

## License

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
This project is licensed under the MIT License - see the [LICENSE](https://github.com/EzyGang/py-cachify/blob/main/LICENSE) file for details.
Binary file added docs/img/type-annotations-2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/img/type-annotations-3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/img/type-annotations.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
77 changes: 57 additions & 20 deletions docs/index.md
Original file line number Diff line number Diff line change
@@ -1,31 +1,68 @@
# py-cachify
# Py-Cachify

[![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://opensource.org/licenses/MIT)
[![PyPI version](https://badge.fury.io/py/py-cachify.svg)](https://badge.fury.io/py/py-cachify)
[![Coverage Status](https://coveralls.io/repos/github/EzyGang/py-cachify/badge.png?branch=main)](https://coveralls.io/github/EzyGang/py-cachify?branch=main)
<p align="center">
<a href="https://opensource.org/licenses/MIT" target="_blank">
<img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="License">
</a>
<a href="https://badge.fury.io/py/py-cachify" target="_blank">
<img src="https://badge.fury.io/py/py-cachify.svg" alt="PyPI version">
</a>
<a href="https://py-cachify.readthedocs.io/en/latest/?badge=latest" target="_blank">
<img src="https://readthedocs.org/projects/py-cachify/badge/?version=latest" alt="Documentation Status">
</a>
</p>
<p align="center">
<a href="https://github.com/EzyGang/py-cachify/actions/workflows/checks.yml/badge.svg" target="_blank">
<img src="https://github.com/EzyGang/py-cachify/actions/workflows/checks.yml/badge.svg" alt="Pre-build checks and Tests">
</a>
<a href="https://github.com/EzyGang/py-cachify/actions/workflows/integration-tests.yml/badge.svg" target="_blank">
<img src="https://github.com/EzyGang/py-cachify/actions/workflows/integration-tests.yml/badge.svg" alt="Tests Status">
</a>
<a href="https://coveralls.io/github/EzyGang/py-cachify?branch=main" target="_blank">
<img src="https://coveralls.io/repos/github/EzyGang/py-cachify/badge.png?branch=main" alt="Coverage Status">
</a>
<a href="https://sonarcloud.io/summary/new_code?id=EzyGang_py-cachify" target="_blank">
<img src="https://sonarcloud.io/api/project_badges/measure?project=EzyGang_py-cachify&metric=reliability_rating" alt="Reliability Rating">
</a>
</p>

[![Homepage](https://github.githubassets.com/favicons/favicon-dark.png)](https://github.com/EzyGang/py-cachify)
---

py-cachify is a library that provides small but useful cache-based utilities.
**Documentation**: <a href="https://py-cachify.readthedocs.io/latest/" target="_blank">https://py-cachify.readthedocs.io/latest/</a>

Some parts were heavily inspired by [douglasfarinelli's python-cachelock](https://github.com/douglasfarinelli/python-cachelock) lib,
which is sadly no longer maintained.
**Source Code**: <a href="https://github.com/EzyGang/py-cachify" target="_blank">https://github.com/EzyGang/py-cachify</a>

## Features
* Offers distributed cache-based locks and decorators around them
* Offers convenient decorators to store function results in the cache
* Works well in both sync and async environments
* Fully type annotated
* Has 100% test coverage
* Has integration tests in place for the common scenarios
* Backend agnostic (you can provide your own client as long as it matches the signature)
* Supports Python from 3.8 and upward
---

py-cachify is a small library that provides useful cache based utilities (caching, locks).

py-cachify works well in both sync and async environments, has 100% test coverage, fully type annotated,
is backend agnostic (you can provide your own client as long as it matches the signature), and supports Python from 3.8 and upward.

It offers distributed (cache based) locks and decorators to lock function executions and storing their results in the cache.

The key features are:

* **Intuitive to write**: Great editor support. When applying decorators your IDE will still be able to autocomplete and highlight inline errors for applied functions.
* **Fully type annotated**: You don't have to constantly look into the docs. Everything is type annotated and easy to understand out of the box.
* **Short**: Minimize code duplication. Add just one line of code to implement a cache or lock on your function.
* **Start simple**: The simplest example adds only a couple of lines of code: initialize a library and use the needed utility.
* **Backend agnostic**: Use whatever cache-backend you want to use. Py-cachify is not forcing you into anything.
* **Test coverage**: Has 100% test coverage and supports Python 3.8+

---

## Quick navigation
To help you get started, see [Initial setup](setup.md).
To help you get started, see [Initial setup](setup.md) and [Tutorial](tutorial/index.md).

Examples can be found [here](examples.md).

More information about [`cached` decorator](cached-decorator.md), [`once` decorator](once-decorator.md),
and [locks](locks.md) can be found on linked pages.
For a detailed API reference [API]

## Contributing

If you'd like to contribute, please first discuss the changes using Issues, and then don't hesitate to shoot a PR which will be reviewed shortly.

## License

This project is licensed under the MIT License - see the [LICENSE](https://github.com/EzyGang/py-cachify/blob/main/LICENSE) file for details.
15 changes: 15 additions & 0 deletions docs/scripts/hooks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import re

from mkdocs import plugins
from mkdocs.config.defaults import MkDocsConfig
from mkdocs.structure.nav import Navigation
from paginate import Page


@plugins.event_priority(-100)
def on_post_page(output_content: str, page: Page, config: MkDocsConfig) -> str:

return re.sub(
r'(\/(.+\(\))\/)', r'<code style="background-color: var(--md-code-bg-color);padding: 0 .2941176471em;">\2</code>',
output_content,
)
74 changes: 74 additions & 0 deletions docs/tutorial/cached-decorator/dynamic-cache-keys.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# Cached - Dynamic cache key arguments

Continuing our previous example, we'll customize the key in the decorator.

The full code will look like this:

```python
import asyncio

from py_cachify import init_cachify, cached


# here we are initializing py-cachify to use an in-memory cache
init_cachify()


# notice that we now have {a} and {b} in the cache key
@cached(key='sum_two-{a}-{b}')
async def sum_two(a: int, b: int) -> int:
# Let's put print here to see what was the function called with
print(f'Called with {a} {b}')
return a + b


async def main() -> None:
# Call the function first time with (5, 5)
print(f'First call result: {await sum_two(5, 5)}')

# And we will call it again to make sure it's not called but the result is the same
print(f'Second call result: {await sum_two(5, 5)}')

# Now we will call it with different args to make sure the function is indeed called for another set of arguments
print(f'Third call result: {await sum_two(5, 10)}')


if __name__ == '__main__':
asyncio.run(main())
```


## Understanding what has changed

As you can see, we now have `{a}` and `{b}` inside our key,
what it allows py-cachify to do is dynamically craft a key for a function the decorator is being applied to.

This way it will cache the result for each set of arguments instead of creating just one key.

Note, that in this current example key `'sum_two-{}-{}'` will have the same effect.
Providing a not named placeholders is supported to allow creating dynamic cache keys even for the functions that accept `*args, **kwargs` as its arguments.

We have also modified our main function to showcase the introduced changes.

## Let's run our code

After running the example:
<!-- termynal -->
```bash
# Run our example
$ python main.py

# The ouput will be
Called with 5 5
First call result: 10
Second call result: 10
Called with 5 10
Third call result: 15

```

As you can see, the function result is being cached based on the arguments provided.

## What's next

In the next chapter we'll learn what other parameters `@cached()` decorator has.
Loading

0 comments on commit a4fc4ce

Please sign in to comment.