add timed_cache()
This commit is contained in:
parent
b4ef56f260
commit
303e9e2b2d
7 changed files with 57 additions and 11 deletions
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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'
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -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'
|
||||||
)
|
)
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
16
src/suou/obsolete/__init__.py
Normal file
16
src/suou/obsolete/__init__.py
Normal 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.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
@ -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)
|
||||||
|
|
@ -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])
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue