Skip to content

Commit

Permalink
Merge branch 'master' into order_kwargs
Browse files Browse the repository at this point in the history
  • Loading branch information
Borda committed Feb 2, 2024
2 parents 3ccc094 + 3dbc43f commit 2c96e3b
Show file tree
Hide file tree
Showing 21 changed files with 179 additions and 120 deletions.
14 changes: 7 additions & 7 deletions .codecov.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,16 @@ coverage:
project: false

comment:
layout: header, changes, diff
behavior: default
require_changes: false
branches: null
flags: null
paths: null
layout: "header, changes, diff"
behavior: default
require_changes: false
branches: null
flags: null
paths: null

ignore:
- "versioneer.py"
- "tests"
- "**/_version.py"
- "**/__init__.py"
- "cachier/scripts"
- "cachier/scripts"
1 change: 1 addition & 0 deletions .flake8
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
[flake8]
max-line-length = 120
extend-ignore = E203,C901
exclude =
.git,
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/checkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ on:
push:

jobs:
build:
checkdocs:
runs-on: ${{ matrix.os }}
environment:
name: checkdocs
Expand All @@ -26,7 +26,7 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install -e ".[test]"
python -m pip install -e . -r tests/requirements.txt
- name: Check Docs
run: |
python setup.py checkdocs
37 changes: 0 additions & 37 deletions .github/workflows/lint.yml

This file was deleted.

16 changes: 10 additions & 6 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,16 @@ on:
- cron: '0 0 * * 0'

jobs:
build:
test:
runs-on: ${{ matrix.os }}
environment:
name: test
strategy:
fail-fast: false
matrix:
python-version: [3.8, 3.9, '3.10', '3.11', '3.12']
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12']
os: [ubuntu-latest, macOS-latest, windows-latest]
backend: ['local', 'db']

steps:
- uses: actions/[email protected]
Expand All @@ -30,16 +31,19 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install -e ".[test]"
- name: Unit tests
python -m pip install -e . -r tests/requirements.txt
- name: Unit tests (local)
if: matrix.backend == 'local'
run: pytest -m "not mongo"
- name: Unit tests (DB)
if: matrix.backend == 'db'
env:
CACHIER_TEST_HOST: ${{ secrets.CACHIER_TEST_HOST }}
CACHIER_TEST_DB: ${{ secrets.CACHIER_TEST_DB }}
CACHIER_TEST_USERNAME: ${{ secrets.CACHIER_TEST_USERNAME }}
CACHIER_TEST_PASSWORD: ${{ secrets.CACHIER_TEST_PASSWORD }}
CACHIER_TEST_VS_LIVE_MONGO: ${{ secrets.CACHIER_TEST_VS_LIVE_MONGO }}
run: |
pytest
run: pytest -m "mongo"
- name: "Upload coverage to Codecov"
uses: codecov/codecov-action@v3
with:
Expand Down
7 changes: 3 additions & 4 deletions .ignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
*/versioneer.py
versioneer.py
*versioneer.py
*.ipynb
.venv
.venv/*
.venv/
.idea/
.vscode/
71 changes: 71 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
default_language_version:
python: python3

ci:
autofix_prs: true
autoupdate_commit_msg: "[pre-commit.ci] pre-commit suggestions"
autoupdate_schedule: quarterly
# submodules: true

repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0
hooks:
- id: end-of-file-fixer
- id: trailing-whitespace
- id: check-json
- id: check-yaml
- id: check-toml
- id: check-docstring-first
- id: check-executables-have-shebangs
- id: check-case-conflict
- id: check-added-large-files
- id: detect-private-key

- repo: https://github.com/crate-ci/typos
rev: v1.16.26
hooks:
- id: typos
# empty to do not write fixes
args: []
exclude: versioneer.py

# - repo: https://github.com/PyCQA/docformatter
# rev: v1.7.5
# hooks:
# - id: docformatter
# additional_dependencies: [tomli]
# args: ["--in-place"]

# - repo: https://github.com/psf/black-pre-commit-mirror
# rev: 24.1.1
# hooks:
# - id: black
# name: Format code
# args:
# - "--skip-string-normalization"

# todo: unify formatting all GH actions files
# - repo: https://github.com/pre-commit/mirrors-prettier
# rev: v3.1.0
# hooks:
# - id: prettier
# # https://prettier.io/docs/en/options.html#print-width
# args: ["--print-width=120"]

# - repo: https://github.com/pre-commit/mirrors-mypy
# rev: v1.8.0
# hooks:
# - id: mypy

- repo: https://github.com/pycqa/flake8
rev: 7.0.0
hooks:
- id: flake8

# todo: replacement for flake8
# - repo: https://github.com/astral-sh/ruff-pre-commit
# rev: v0.1.9
# hooks:
# - id: ruff
# args: ["--fix"]
14 changes: 7 additions & 7 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Persistent, stale-free, local and cross-machine caching for Python functions.
@cachier(stale_after=datetime.timedelta(days=3))
def foo(arg1, arg2):
"""foo now has a persistent cache, trigerring recalculation for values stored more than 3 days."""
"""foo now has a persistent cache, triggering recalculation for values stored more than 3 days."""
return {'arg1': arg1, 'arg2': arg2}
Expand Down Expand Up @@ -97,18 +97,18 @@ Class and object methods can also be cached. Cachier will automatically ignore t
# Instance method does not depend on object's internal state, so good to cache
@cachier()
def good_usage_1(self, arg_1, arg_2)
def good_usage_1(self, arg_1, arg_2):
return arg_1 + arg_2
# Instance method is calling external service, probably okay to cache
@cachier()
def good_usage_2(self, arg_1, arg_2)
def good_usage_2(self, arg_1, arg_2):
result = self.call_api(arg_1, arg_2)
return result
# Instance method relies on object attribute, NOT good to cache
@cachier()
def bad_usage(self, arg_1, arg_2)
def bad_usage(self, arg_1, arg_2):
return arg_1 + arg_2 + self.arg_3
Expand Down Expand Up @@ -150,7 +150,7 @@ The current defaults can be fetched by calling `get_default_params`.
Threads Limit
~~~~~~~~~~~~~

To limit the number of threads Cachier is allowed to spawn, set the ``CACHIER_MAX_WORKERS`` with the desired number. The defeault is 8, so to enable Cachier to spawn even more threads, you'll have to set a higher limit explicitly.
To limit the number of threads Cachier is allowed to spawn, set the ``CACHIER_MAX_WORKERS`` with the desired number. The default is 8, so to enable Cachier to spawn even more threads, you'll have to set a higher limit explicitly.


Global Enable/Disable
Expand Down Expand Up @@ -363,7 +363,7 @@ Install in development mode with test dependencies:
.. code-block:: bash
cd cachier
pip install -e ".[test]"
pip install -e . -r tests/requirements.txt
Running the tests
Expand Down Expand Up @@ -406,7 +406,7 @@ Running MongoDB tests against a live MongoDB instance

**Note to developers:** By default, all MongoDB tests are run against a mocked MongoDB instance, provided by the ``pymongo_inmemory`` package. To run them against a live MongoDB instance, the ``CACHIER_TEST_VS_LIVE_MONGO`` environment variable is set to ``True`` in the ``test`` environment of this repository (and additional environment variables are populated with the appropriate credentials), used by the GitHub Action running tests on every commit and pull request.

Contributers are not expected to run these tests against a live MongoDB instance when developing, as credentials for the testing instance used will NOT be shared, but rather use the testing against the in-memory MongoDB instance as a good proxy.
Contributors are not expected to run these tests against a live MongoDB instance when developing, as credentials for the testing instance used will NOT be shared, but rather use the testing against the in-memory MongoDB instance as a good proxy.

**HOWEVER, the tests run against a live MongoDB instance when you submit a PR are the determining tests for deciding whether your code functions correctly against MongoDB.**

Expand Down
2 changes: 1 addition & 1 deletion cachier/_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command):
# TAG-NUM-gHEX
mo = re.search(r'^(.+)-(\d+)-g([0-9a-f]+)$', git_describe)
if not mo:
# unparseable. Maybe git-describe is misbehaving?
# unparsable. Maybe git-describe is misbehaving?
pieces["error"] = ("unable to parse git-describe output: '%s'"
% describe_out)
return pieces
Expand Down
4 changes: 2 additions & 2 deletions cachier/base_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ def __init__(self, hash_func, default_params):

def set_func(self, func):
"""Sets the function this core will use. This has to be set before any
method is called. Also determine if the funtion is an object method."""
method is called. Also determine if the function is an object method."""
func_params = list(inspect.signature(func).parameters)
self.func_is_method = func_params and func_params[0] == 'self'
self.func = func
Expand All @@ -48,7 +48,7 @@ def precache_value(self, args, kwds, value_to_cache):
return value_to_cache

def check_calc_timeout(self, time_spent):
"""Raise an exception if a recalulation is needed."""
"""Raise an exception if a recalculation is needed."""
if self.wait_for_calc_timeout is not None:
calc_timeout = self.wait_for_calc_timeout
else:
Expand Down
41 changes: 23 additions & 18 deletions cachier/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,17 @@ def _default_hash_func(args, kwds):
return hash.hexdigest()


def _convert_args_kwargs(func, _is_method: bool, args: tuple, kwds: dict) -> dict:
"""Convert mix of positional and keyword arguments to aggregated kwargs."""
args_as_kw = dict(
zip(func.__code__.co_varnames[1:], args[1:])
if _is_method else
zip(func.__code__.co_varnames, args)
)
# merge args expanded as kwargs and the original kwds
return OrderedDict(**args_as_kw, **kwds)


class MissingMongetter(ValueError):
"""Thrown when the mongetter keyword argument is missing."""

Expand Down Expand Up @@ -253,24 +264,23 @@ def func_wrapper(*args, **kwds): # pylint: disable=C0111,R0911
ignore_cache = kwds.pop('ignore_cache', False)
overwrite_cache = kwds.pop('overwrite_cache', False)
verbose_cache = kwds.pop('verbose_cache', False)
# merge args expanded as kwargs and the original kwds
kwargs = _convert_args_kwargs(func, _is_method=core.func_is_method, args=args, kwds=kwds)

_print = lambda x: None # skipcq: FLK-E731 # noqa: E731
if verbose_cache:
_print = print
if ignore_cache or not _default_params['caching_enabled']:
return func(*args, **kwds)
kwds_ordered = OrderedDict(sorted(kwds.items()))
if core.func_is_method:
key, entry = core.get_entry(args[1:], kwds_ordered)
else:
key, entry = core.get_entry(args, kwds_ordered)
return func(**kwargs)
key, entry = core.get_entry(tuple(), kwargs)
if overwrite_cache:
return _calc_entry(core, key, func, args, kwds)
if entry is not None: # pylint: disable=R0101
_print('Entry found.')
if (_allow_none or entry.get('value', None) is not None):
_print('Cached result found.')
local_stale_after = stale_after if stale_after is not None else _default_params['stale_after'] # noqa: E501
local_next_time = next_time if next_time is not None else _default_params['next_time'] # noqa: E501
local_stale_after = stale_after or _default_params['stale_after'] # noqa: E501
local_next_time = next_time or _default_params['next_time'] # noqa: E501
now = datetime.datetime.now()
if now - entry['time'] > local_stale_after:
_print('But it is stale... :(')
Expand All @@ -282,20 +292,13 @@ def func_wrapper(*args, **kwds): # pylint: disable=C0111,R0911
try:
return core.wait_on_entry_calc(key)
except RecalculationNeeded:
return _calc_entry(
core, key, func, args, kwds
)
return _calc_entry(core, key, func, args, kwds)
if local_next_time:
_print('Async calc and return stale')
try:
core.mark_entry_being_calculated(key)
_get_executor().submit(
_function_thread,
core,
key,
func,
args,
kwds,
_function_thread, core, key, func, args, kwds,
)
finally:
core.mark_entry_not_calculated(key)
Expand Down Expand Up @@ -336,7 +339,9 @@ def precache_value(*args, value_to_cache, **kwds):
value : any
entry to be written into the cache
"""
return core.precache_value(args, kwds, value_to_cache)
# merge args expanded as kwargs and the original kwds
kwargs = _convert_args_kwargs(func, _is_method=core.func_is_method, args=args, kwds=kwds)
return core.precache_value(tuple(), kwargs, value_to_cache)

func_wrapper.clear_cache = clear_cache
func_wrapper.clear_being_calculated = clear_being_calculated
Expand Down
Loading

0 comments on commit 2c96e3b

Please sign in to comment.