add timed_cache()

This commit is contained in:
Yusur 2025-07-22 22:15:11 +02:00
parent b4ef56f260
commit 303e9e2b2d
7 changed files with 57 additions and 11 deletions

View file

@ -1,5 +1,10 @@
# Changelog # Changelog
## 0.5.0
+ Add `timed_cache()`
+ Move obsolete stuff to `obsolete` package
## 0.4.0 ## 0.4.0
+ `pydantic` is now a hard dependency + `pydantic` is now a hard dependency

View file

@ -21,7 +21,7 @@ from .codecs import (StringCase, cb32encode, cb32decode, b32lencode, b32ldecode,
jsonencode, want_bytes, want_str, ssv_list, want_urlsafe) jsonencode, want_bytes, want_str, ssv_list, want_urlsafe)
from .bits import count_ones, mask_shift, split_bits, join_bits, mod_ceil, mod_floor from .bits import count_ones, mask_shift, split_bits, join_bits, mod_ceil, mod_floor
from .configparse import MissingConfigError, MissingConfigWarning, ConfigOptions, ConfigParserConfigSource, ConfigSource, DictConfigSource, ConfigValue, EnvConfigSource from .configparse import MissingConfigError, MissingConfigWarning, ConfigOptions, ConfigParserConfigSource, ConfigSource, DictConfigSource, ConfigValue, EnvConfigSource
from .functools import deprecated, not_implemented from .functools import deprecated, not_implemented, timed_cache
from .classtools import Wanted, Incomplete from .classtools import Wanted, Incomplete
from .itertools import makelist, kwargs_prefix, ltuple, rtuple, additem from .itertools import makelist, kwargs_prefix, ltuple, rtuple, additem
from .i18n import I18n, JsonI18n, TomlI18n from .i18n import I18n, JsonI18n, TomlI18n
@ -29,7 +29,7 @@ from .snowflake import Snowflake, SnowflakeGen
from .lex import symbol_table, lex, ilex from .lex import symbol_table, lex, ilex
from .strtools import PrefixIdentifier from .strtools import PrefixIdentifier
__version__ = "0.4.0" __version__ = "0.5.0-dev29"
__all__ = ( __all__ = (
'ConfigOptions', 'ConfigParserConfigSource', 'ConfigSource', 'ConfigValue', 'ConfigOptions', 'ConfigParserConfigSource', 'ConfigSource', 'ConfigValue',
@ -41,5 +41,5 @@ __all__ = (
'deprecated', 'ilex', 'join_bits', 'jsonencode', 'kwargs_prefix', 'lex', 'deprecated', 'ilex', 'join_bits', 'jsonencode', 'kwargs_prefix', 'lex',
'ltuple', 'makelist', 'mask_shift', 'mod_ceil', 'mod_floor', 'ltuple', 'makelist', 'mask_shift', 'mod_ceil', 'mod_floor',
'not_implemented', 'rtuple', 'split_bits', 'ssv_list', 'symbol_table', 'not_implemented', 'rtuple', 'split_bits', 'ssv_list', 'symbol_table',
'want_bytes', 'want_str', 'want_urlsafe' 'timed_cache', 'want_bytes', 'want_str', 'want_urlsafe'
) )

View file

@ -14,9 +14,11 @@ This software is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
""" """
import math
import time
from typing import Callable from typing import Callable
import warnings import warnings
from functools import wraps from functools import wraps, lru_cache
try: try:
from warnings import deprecated from warnings import deprecated
@ -64,6 +66,28 @@ def not_implemented(msg: Callable | str | None = None):
return decorator(msg) return decorator(msg)
return decorator return decorator
def timed_cache(ttl: int, maxsize: int = 128, typed: bool = False) -> Callable[[Callable], Callable]:
"""
LRU cache which expires after the TTL in seconds passed as argument.
"""
def decorator(func):
start_time = None
@lru_cache(maxsize, typed)
def inner_wrapper(ttl_period: int, *a, **k):
return func(*a, **k)
@wraps(func)
def wrapper(*a, **k):
nonlocal start_time
if not start_time:
start_time = int(time.time())
return inner_wrapper(math.floor((time.time() - start_time) // ttl), *a, **k)
return wrapper
return decorator
__all__ = ( __all__ = (
'deprecated', 'not_implemented' 'deprecated', 'not_implemented', 'timed_cache'
) )

View file

@ -43,9 +43,9 @@ class SpoilerExtension(markdown.extensions.Extension):
""" """
Add spoiler tags to text, using >!Reddit syntax!<. Add spoiler tags to text, using >!Reddit syntax!<.
XXX remember to call SpoilerExtension.patch_blockquote_processor() If blockquotes interfer with rendered markup, you might want to call
to clear conflicts with the blockquote processor and allow SpoilerExtension.patch_blockquote_processor() to clear conflicts with
spoiler tags to start at beginning of line. the blockquote processor and allow spoiler tags to start at beginning of line.
""" """
def extendMarkdown(self, md: markdown.Markdown, md_globals=None): def extendMarkdown(self, md: markdown.Markdown, md_globals=None):
md.inlinePatterns.register(SimpleTagInlineProcessor(r'()>!(.*?)!<', 'span class="spoiler"'), 'spoiler', 14) md.inlinePatterns.register(SimpleTagInlineProcessor(r'()>!(.*?)!<', 'span class="spoiler"'), 'spoiler', 14)

View file

@ -0,0 +1,16 @@
"""
This stuff might still be good, but it's out of support.
---
Copyright (c) 2025 Sakuragasaki46.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
See LICENSE for the specific language governing permissions and
limitations under the License.
This software is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""

View file

@ -28,8 +28,8 @@ from typing import Any, Callable, Iterator
from collections import OrderedDict from collections import OrderedDict
import warnings import warnings
from .functools import deprecated from ..functools import deprecated
from .exceptions import MissingConfigError, MissingConfigWarning from ..exceptions import MissingConfigError, MissingConfigWarning
warnings.warn('This module will be removed in 0.5.0 and is kept only in case new implementation breaks!\n'\ warnings.warn('This module will be removed in 0.5.0 and is kept only in case new implementation breaks!\n'\
'Do not use unless you know what you are doing.', DeprecationWarning) 'Do not use unless you know what you are doing.', DeprecationWarning)

View file

@ -35,6 +35,7 @@ class TestCodecs(unittest.TestCase):
self.assertEqual(b64decode('6RgpyyfClq7ehg'), B3) self.assertEqual(b64decode('6RgpyyfClq7ehg'), B3)
self.assertEqual(b64decode('ByO8WGlGLEB8e77jDHqoyw'), B4) self.assertEqual(b64decode('ByO8WGlGLEB8e77jDHqoyw'), B4)
self.assertEqual(b64decode('__init__'), B5) self.assertEqual(b64decode('__init__'), B5)
self.assertEqual(b64decode('//init//'), B5)
self.assertEqual(b64decode('TvC0ww'), B1[:4]) self.assertEqual(b64decode('TvC0ww'), B1[:4])
self.assertEqual(b64decode('AE7wtMM'), b'\0' + B1[:4]) self.assertEqual(b64decode('AE7wtMM'), b'\0' + B1[:4])
self.assertEqual(b64decode('AAAAAABO8LTD'), b'\0\0\0\0\0' + B1[:4]) self.assertEqual(b64decode('AAAAAABO8LTD'), b'\0\0\0\0\0' + B1[:4])