From 303e9e2b2dbc63a1541bce149a4eec916d2a769b Mon Sep 17 00:00:00 2001 From: Yusur Princeps Date: Tue, 22 Jul 2025 22:15:11 +0200 Subject: [PATCH] add timed_cache() --- CHANGELOG.md | 5 ++++ src/suou/__init__.py | 6 ++--- src/suou/functools.py | 30 +++++++++++++++++++--- src/suou/markdown.py | 6 ++--- src/suou/obsolete/__init__.py | 16 ++++++++++++ src/suou/{ => obsolete}/configparsev0_3.py | 4 +-- tests/test_codecs.py | 1 + 7 files changed, 57 insertions(+), 11 deletions(-) create mode 100644 src/suou/obsolete/__init__.py rename src/suou/{ => obsolete}/configparsev0_3.py (98%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a52d1d..d959872 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## 0.5.0 + ++ Add `timed_cache()` ++ Move obsolete stuff to `obsolete` package + ## 0.4.0 + `pydantic` is now a hard dependency diff --git a/src/suou/__init__.py b/src/suou/__init__.py index 98834c2..d2f7b76 100644 --- a/src/suou/__init__.py +++ b/src/suou/__init__.py @@ -21,7 +21,7 @@ from .codecs import (StringCase, cb32encode, cb32decode, b32lencode, b32ldecode, 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 .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 .itertools import makelist, kwargs_prefix, ltuple, rtuple, additem from .i18n import I18n, JsonI18n, TomlI18n @@ -29,7 +29,7 @@ from .snowflake import Snowflake, SnowflakeGen from .lex import symbol_table, lex, ilex from .strtools import PrefixIdentifier -__version__ = "0.4.0" +__version__ = "0.5.0-dev29" __all__ = ( 'ConfigOptions', 'ConfigParserConfigSource', 'ConfigSource', 'ConfigValue', @@ -41,5 +41,5 @@ __all__ = ( 'deprecated', 'ilex', 'join_bits', 'jsonencode', 'kwargs_prefix', 'lex', 'ltuple', 'makelist', 'mask_shift', 'mod_ceil', 'mod_floor', 'not_implemented', 'rtuple', 'split_bits', 'ssv_list', 'symbol_table', - 'want_bytes', 'want_str', 'want_urlsafe' + 'timed_cache', 'want_bytes', 'want_str', 'want_urlsafe' ) diff --git a/src/suou/functools.py b/src/suou/functools.py index 9041f92..a34f023 100644 --- a/src/suou/functools.py +++ b/src/suou/functools.py @@ -14,15 +14,17 @@ This software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. """ +import math +import time from typing import Callable import warnings -from functools import wraps +from functools import wraps, lru_cache try: from warnings import deprecated except ImportError: # Python <=3.12 does not implement warnings.deprecated - def deprecated(message: str, /, *, category=DeprecationWarning, stacklevel:int=1): + def deprecated(message: str, /, *, category=DeprecationWarning, stacklevel: int = 1): """ Backport of PEP 702 for Python <=3.12. The stack_level stuff is not reimplemented on purpose because @@ -64,6 +66,28 @@ def not_implemented(msg: Callable | str | None = None): return decorator(msg) 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__ = ( - 'deprecated', 'not_implemented' + 'deprecated', 'not_implemented', 'timed_cache' ) \ No newline at end of file diff --git a/src/suou/markdown.py b/src/suou/markdown.py index 58ce15d..acd4ab5 100644 --- a/src/suou/markdown.py +++ b/src/suou/markdown.py @@ -43,9 +43,9 @@ class SpoilerExtension(markdown.extensions.Extension): """ Add spoiler tags to text, using >!Reddit syntax!<. - XXX remember to call SpoilerExtension.patch_blockquote_processor() - to clear conflicts with the blockquote processor and allow - spoiler tags to start at beginning of line. + If blockquotes interfer with rendered markup, you might want to call + SpoilerExtension.patch_blockquote_processor() to clear conflicts with + the blockquote processor and allow spoiler tags to start at beginning of line. """ def extendMarkdown(self, md: markdown.Markdown, md_globals=None): md.inlinePatterns.register(SimpleTagInlineProcessor(r'()>!(.*?)!<', 'span class="spoiler"'), 'spoiler', 14) diff --git a/src/suou/obsolete/__init__.py b/src/suou/obsolete/__init__.py new file mode 100644 index 0000000..108c334 --- /dev/null +++ b/src/suou/obsolete/__init__.py @@ -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. +""" + diff --git a/src/suou/configparsev0_3.py b/src/suou/obsolete/configparsev0_3.py similarity index 98% rename from src/suou/configparsev0_3.py rename to src/suou/obsolete/configparsev0_3.py index 282e248..1563813 100644 --- a/src/suou/configparsev0_3.py +++ b/src/suou/obsolete/configparsev0_3.py @@ -28,8 +28,8 @@ from typing import Any, Callable, Iterator from collections import OrderedDict import warnings -from .functools import deprecated -from .exceptions import MissingConfigError, MissingConfigWarning +from ..functools import deprecated +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'\ 'Do not use unless you know what you are doing.', DeprecationWarning) diff --git a/tests/test_codecs.py b/tests/test_codecs.py index 7716aa8..035a605 100644 --- a/tests/test_codecs.py +++ b/tests/test_codecs.py @@ -35,6 +35,7 @@ class TestCodecs(unittest.TestCase): self.assertEqual(b64decode('6RgpyyfClq7ehg'), B3) self.assertEqual(b64decode('ByO8WGlGLEB8e77jDHqoyw'), B4) self.assertEqual(b64decode('__init__'), B5) + self.assertEqual(b64decode('//init//'), B5) self.assertEqual(b64decode('TvC0ww'), B1[:4]) self.assertEqual(b64decode('AE7wtMM'), b'\0' + B1[:4]) self.assertEqual(b64decode('AAAAAABO8LTD'), b'\0\0\0\0\0' + B1[:4])