Compare commits

..

16 Commits

Author SHA1 Message Date
1daed542a3 Merge pull request 'v4.3.0' (#220) from dev into main
All checks were successful
Analysis / SonarCloud (push) Successful in 1m0s
Tests / Build and Test (3.11) (push) Successful in 1m23s
Tests / Build and Test (3.12) (push) Successful in 1m20s
Tests / Build and Test (3.13) (push) Successful in 1m11s
Upload Python Package / release-build (release) Successful in 26s
Upload Python Package / gitea-publish (release) Successful in 14s
Upload Python Package / pypi-publish (release) Successful in 15s
Reviewed-on: #220
2025-07-08 01:34:43 +03:00
af74e3fdd0 Merge pull request 'v4.2.0' (#207) from dev into main
All checks were successful
Analysis / SonarCloud (push) Successful in 51s
Tests / Build and Test (3.11) (push) Successful in 1m11s
Tests / Build and Test (3.12) (push) Successful in 1m7s
Tests / Build and Test (3.13) (push) Successful in 1m6s
Upload Python Package / release-build (release) Successful in 21s
Upload Python Package / gitea-publish (release) Successful in 22s
Upload Python Package / pypi-publish (release) Successful in 13s
Reviewed-on: #207
2025-05-18 18:38:49 +03:00
fdb8db4782 Merge pull request 'v4.1.0' (#189) from dev into main
Some checks failed
Analysis / SonarCloud (push) Successful in 46s
Tests / Build and Test (3.11) (push) Successful in 1m9s
Tests / Build and Test (3.12) (push) Successful in 1m7s
Tests / Build and Test (3.13) (push) Successful in 1m4s
Upload Python Package / release-build (release) Successful in 18s
Upload Python Package / gitea-publish (release) Failing after 20s
Upload Python Package / pypi-publish (release) Failing after 11s
Reviewed-on: #189
2025-02-16 18:37:38 +02:00
508c48d22b Merge pull request 'v4.0.2' (#172) from dev into main
All checks were successful
Analysis / SonarCloud (push) Successful in 46s
Tests / Build and Test (3.11) (push) Successful in 1m16s
Tests / Build and Test (3.12) (push) Successful in 1m24s
Tests / Build and Test (3.13) (push) Successful in 1m24s
Upload Python Package / release-build (release) Successful in 18s
Upload Python Package / gitea-publish (release) Successful in 9s
Upload Python Package / pypi-publish (release) Successful in 15s
Reviewed-on: #172
2025-01-02 15:04:26 +02:00
bfcb067ba5 Merge pull request 'v4.0.1' (#170) from dev into main
All checks were successful
Analysis / SonarCloud (push) Successful in 39s
Tests / Build and Test (3.11) (push) Successful in 1m18s
Tests / Build and Test (3.12) (push) Successful in 1m35s
Tests / Build and Test (3.13) (push) Successful in 1m21s
Reviewed-on: #170
2024-12-29 17:18:48 +02:00
a38b55d270 Merge pull request 'v4.0.0' (#169) from dev into main
Some checks failed
Analysis / SonarCloud (push) Successful in 42s
Tests / Build and Test (3.10) (push) Failing after 57s
Tests / Build and Test (3.11) (push) Successful in 1m4s
Tests / Build and Test (3.12) (push) Successful in 1m13s
Tests / Build and Test (3.9) (push) Failing after 57s
Reviewed-on: #169
2024-12-26 19:59:34 +02:00
9907cc50f1 Merge pull request 'v3.3.1' (#160) from dev into main
All checks were successful
Analysis / SonarCloud (push) Successful in 46s
Tests / Build and Test (3.10) (push) Successful in 1m3s
Tests / Build and Test (3.11) (push) Successful in 1m1s
Tests / Build and Test (3.12) (push) Successful in 1m8s
Tests / Build and Test (3.9) (push) Successful in 1m3s
Reviewed-on: #160
2024-12-16 23:57:08 +02:00
1b60257bc5 Merge pull request 'v3.3.0' (#159) from dev into main
All checks were successful
Analysis / SonarCloud (push) Successful in 43s
Tests / Build and Test (3.10) (push) Successful in 1m2s
Tests / Build and Test (3.11) (push) Successful in 1m2s
Tests / Build and Test (3.12) (push) Successful in 1m8s
Tests / Build and Test (3.9) (push) Successful in 1m4s
Reviewed-on: #159
2024-12-16 23:48:07 +02:00
171e36a491 Merge pull request 'v3.2.3' (#118) from dev into main
All checks were successful
Tests / test (3.10) (push) Successful in 1m2s
Tests / test (3.11) (push) Successful in 58s
Tests / test (3.8) (push) Successful in 1m4s
Tests / test (3.9) (push) Successful in 1m1s
Reviewed-on: #118
2024-07-10 00:07:54 +03:00
c419c684aa Merge pull request 'v3.2.2' (#107) from dev into main
All checks were successful
Tests / test (3.10) (push) Successful in 1m10s
Tests / test (3.11) (push) Successful in 1m4s
Tests / test (3.8) (push) Successful in 1m6s
Tests / test (3.9) (push) Successful in 1m7s
Reviewed-on: #107
2024-05-26 22:44:18 +03:00
748b2b2abb Merge pull request 'v3.2.1' (#106) from dev into main
All checks were successful
Tests / test (3.10) (push) Successful in 1m3s
Tests / test (3.11) (push) Successful in 1m23s
Tests / test (3.8) (push) Successful in 1m7s
Tests / test (3.9) (push) Successful in 1m7s
Reviewed-on: #106
2024-05-26 17:53:00 +03:00
52c2e5cc13 Merge pull request 'v3.2.0' (#105) from dev into main
All checks were successful
Tests / test (3.10) (push) Successful in 1m4s
Tests / test (3.11) (push) Successful in 1m2s
Tests / test (3.8) (push) Successful in 1m29s
Tests / test (3.9) (push) Successful in 1m4s
Reviewed-on: #105
2024-05-26 17:29:44 +03:00
55c61e3fce Merge pull request 'v3.1.0' (#102) from dev into main
All checks were successful
Tests / test (3.10) (push) Successful in 55s
Tests / test (3.11) (push) Successful in 55s
Tests / test (3.8) (push) Successful in 57s
Tests / test (3.9) (push) Successful in 1m27s
Reviewed-on: #102
2024-05-19 16:22:17 +03:00
b9550032ba Merge pull request 'Update to 3.0.1' (#98) from dev into main
All checks were successful
Tests / test (3.10) (push) Successful in 57s
Tests / test (3.11) (push) Successful in 54s
Tests / test (3.8) (push) Successful in 1m8s
Tests / test (3.9) (push) Successful in 55s
Reviewed-on: #98
2024-05-15 00:19:03 +03:00
5ba763246b Merge pull request 'Update to 3.0.0' (#52) from dev into main
All checks were successful
Tests / test (3.10) (push) Successful in 1m15s
Tests / test (3.11) (push) Successful in 1m14s
Tests / test (3.8) (push) Successful in 1m14s
Tests / test (3.9) (push) Successful in 1m22s
Reviewed-on: #52
2024-01-04 00:06:50 +02:00
f0ffdf096d Merge pull request 'Pycord support initial release' (#48) from dev into main
All checks were successful
Tests / test (3.10) (push) Successful in 1m8s
Tests / test (3.11) (push) Successful in 1m5s
Tests / test (3.8) (push) Successful in 1m43s
Tests / test (3.9) (push) Successful in 1m3s
Reviewed-on: #48
2023-12-27 15:00:41 +02:00
13 changed files with 200 additions and 197 deletions

View File

@@ -1,2 +1,2 @@
aiofiles>=23.0.0 aiofiles>=23.0.0
typing-extensions~=4.15.0 typing-extensions~=4.14.0

View File

@@ -1,2 +1,2 @@
pymemcache~=4.0.0 pymemcache~=4.0.0
redis~=6.4.0 redis~=6.2.0

View File

@@ -1,12 +1,12 @@
black==25.1.0 black==25.1.0
build==1.3.0 build==1.2.2.post1
isort==5.13.2 isort==5.13.2
mypy==1.17.1 mypy==1.16.1
pylint==3.3.8 pylint==3.3.7
pytest-asyncio==1.1.0 pytest-asyncio==1.0.0
pytest-cov==6.2.1 pytest-cov==6.2.1
pytest==8.4.1 pytest==8.4.1
tox==4.29.0 tox==4.27.0
twine==6.1.0 twine==6.1.0
types-aiofiles==24.1.0.20250822 types-aiofiles==24.1.0.20250606
types-ujson==5.10.0.20250822 types-ujson==5.10.0.20250326

View File

@@ -1 +1 @@
ujson~=5.11.0 ujson~=5.10.0

View File

@@ -1,4 +1,4 @@
__version__ = "4.4.0" __version__ = "4.3.0"
__license__ = "GPL3" __license__ = "GPL3"
__author__ = "Profitroll" __author__ = "Profitroll"

View File

@@ -23,13 +23,13 @@ class CacheMemcached(Cache):
logger.info("Initialized Memcached for caching") logger.info("Initialized Memcached for caching")
@classmethod @classmethod
def from_config(cls, engine_config: Dict[str, Any], prefix: Optional[str] = None, default_ttl_seconds: Optional[int] = None) -> "CacheMemcached": def from_config(cls, engine_config: Dict[str, Any], prefix: Optional[str] = None) -> "CacheMemcached":
if "uri" not in engine_config: if "uri" not in engine_config:
raise KeyError( raise KeyError(
"Cache configuration is invalid. Please check if all keys are set (engine: memcached)" "Cache configuration is invalid. Please check if all keys are set (engine: memcached)"
) )
return cls(Client(engine_config["uri"], default_noreply=True), prefix=prefix, default_ttl_seconds=default_ttl_seconds) return cls(Client(engine_config["uri"], default_noreply=True), prefix=prefix)
def _get_prefixed_key(self, key: str) -> str: def _get_prefixed_key(self, key: str) -> str:
return key if self.prefix is None else f"{self.prefix}_{key}" return key if self.prefix is None else f"{self.prefix}_{key}"

View File

@@ -23,13 +23,13 @@ class CacheRedis(Cache):
logger.info("Initialized Redis for caching") logger.info("Initialized Redis for caching")
@classmethod @classmethod
def from_config(cls, engine_config: Dict[str, Any], prefix: Optional[str] = None, default_ttl_seconds: Optional[int] = None) -> Any: def from_config(cls, engine_config: Dict[str, Any], prefix: Optional[str] = None) -> Any:
if "uri" not in engine_config: if "uri" not in engine_config:
raise KeyError( raise KeyError(
"Cache configuration is invalid. Please check if all keys are set (engine: memcached)" "Cache configuration is invalid. Please check if all keys are set (engine: memcached)"
) )
return cls(Redis.from_url(engine_config["uri"]), prefix=prefix, default_ttl_seconds=default_ttl_seconds) return cls(Redis.from_url(engine_config["uri"]), prefix=prefix)
def _get_prefixed_key(self, key: str) -> str: def _get_prefixed_key(self, key: str) -> str:
return key if self.prefix is None else f"{self.prefix}_{key}" return key if self.prefix is None else f"{self.prefix}_{key}"

View File

@@ -7,19 +7,7 @@ def create_cache_client(
config: Dict[str, Any], config: Dict[str, Any],
engine: Literal["memcached", "redis"] | None = None, engine: Literal["memcached", "redis"] | None = None,
prefix: Optional[str] = None, prefix: Optional[str] = None,
default_ttl_seconds: Optional[int] = None,
) -> CacheMemcached | CacheRedis: ) -> CacheMemcached | CacheRedis:
"""Create a cache client of a provided type.
Args:
config (Dict[str, Any]): Cache client configuration.
engine (Literal["memcached", "redis"] | None): Cache engine to use. Defaults to None.
prefix (:obj:`str`, optional): Prefix used for each key-value pair. Defaults to None (no prefix).
default_ttl_seconds (:obj:`int`, optional): Default TTL for values (in seconds). Defaults to None (does not expire).
Returns:
CacheMemcached | CacheRedis: Cache client.
"""
if engine not in ["memcached", "redis"] or engine is None: if engine not in ["memcached", "redis"] or engine is None:
raise KeyError(f"Incorrect cache engine provided. Expected 'memcached' or 'redis', got '{engine}'") raise KeyError(f"Incorrect cache engine provided. Expected 'memcached' or 'redis', got '{engine}'")
@@ -30,8 +18,8 @@ def create_cache_client(
match engine: match engine:
case "memcached": case "memcached":
return CacheMemcached.from_config(config["cache"][engine], prefix=prefix, default_ttl_seconds=default_ttl_seconds) return CacheMemcached.from_config(config["cache"][engine], prefix=prefix)
case "redis": case "redis":
return CacheRedis.from_config(config["cache"][engine], prefix=prefix, default_ttl_seconds=default_ttl_seconds) return CacheRedis.from_config(config["cache"][engine], prefix=prefix)
case _: case _:
raise KeyError(f"Cache implementation for the engine '{engine}' is not present.") raise KeyError(f"Cache implementation for the engine '{engine}' is not present.")

View File

@@ -18,16 +18,16 @@ def _(
locale: str | None = "en", locale: str | None = "en",
locales_root: str | Path = Path("locale"), locales_root: str | Path = Path("locale"),
) -> Any: ) -> Any:
"""Get value of locale string. """Get value of locale string
Args: ### Args:
key (str): The last key of the locale's keys path. * key (`str`): The last key of the locale's keys path.
*args (str): Path to key like: `dict[args][key]`. * *args (`str`): Path to key like: `dict[args][key]`.
locale (str | None): Locale to looked up in. Defaults to "en". * locale (`str | None`): Locale to looked up in. Defaults to `"en"`.
locales_root (str | Path, optional): Folder where locales are located. Defaults to Path("locale"). * locales_root (`str | Path`, *optional*): Folder where locales are located. Defaults to `Path("locale")`.
Returns: ### Returns:
Any: Value of provided locale key. Is usually `str`, `Dict[str, Any]` or `List[Any]`. * `Any`: Value of provided locale key. Is usually `str`, `Dict[str, Any]` or `List[Any]`
""" """
if locale is None: if locale is None:
locale: str = config_get("locale") locale: str = config_get("locale")
@@ -58,16 +58,16 @@ async def _(
locale: str | None = "en", locale: str | None = "en",
locales_root: str | Path = Path("locale"), locales_root: str | Path = Path("locale"),
) -> Any: ) -> Any:
"""Get value of locale string. """Get value of locale string
Args: ### Args:
key (str): The last key of the locale's keys path. * key (`str`): The last key of the locale's keys path.
*args (str): Path to key like: `dict[args][key]`. * *args (`str`): Path to key like: `dict[args][key]`.
locale (str | None): Locale to looked up in. Defaults to "en". * locale (`str | None`): Locale to looked up in. Defaults to `"en"`.
locales_root (str | Path, optional): Folder where locales are located. Defaults to Path("locale"). * locales_root (`str | Path`, *optional*): Folder where locales are located. Defaults to `Path("locale")`.
Returns: ### Returns:
Any: Value of provided locale key. Is usually `str`, `Dict[str, Any]` or `List[Any]`. * `Any`: Value of provided locale key. Is usually `str`, `Dict[str, Any]` or `List[Any]`
""" """
locale: str = config_get("locale") if locale is None else locale locale: str = config_get("locale") if locale is None else locale
@@ -94,15 +94,15 @@ async def _(
@asyncable @asyncable
def in_all_locales(key: str, *args: str, locales_root: str | Path = Path("locale")) -> List[Any]: def in_all_locales(key: str, *args: str, locales_root: str | Path = Path("locale")) -> List[Any]:
"""Get value of the provided key and path in all available locales. """Get value of the provided key and path in all available locales
Args: ### Args:
key (str): The last key of the locale's keys path. * key (`str`): The last key of the locale's keys path.
*args (str): Path to key like: `dict[args][key]`. * *args (`str`): Path to key like: `dict[args][key]`.
locales_root (str | Path, optional): Folder where locales are located. Defaults to `Path("locale")`. * locales_root (`str | Path`, *optional*): Folder where locales are located. Defaults to `Path("locale")`.
Returns: ### Returns:
List[Any]: List of values in all locales. * `List[Any]`: List of values in all locales
""" """
output: List[Any] = [] output: List[Any] = []
@@ -128,15 +128,15 @@ def in_all_locales(key: str, *args: str, locales_root: str | Path = Path("locale
@in_all_locales.asynchronous @in_all_locales.asynchronous
async def in_all_locales(key: str, *args: str, locales_root: str | Path = Path("locale")) -> List[Any]: async def in_all_locales(key: str, *args: str, locales_root: str | Path = Path("locale")) -> List[Any]:
"""Get value of the provided key and path in all available locales. """Get value of the provided key and path in all available locales
Args: ### Args:
key (str): The last key of the locale's keys path. * key (`str`): The last key of the locale's keys path.
*args (str): Path to key like: `dict[args][key]`. * *args (`str`): Path to key like: `dict[args][key]`.
locales_root (str | Path, optional): Folder where locales are located. Defaults to Path("locale"). * locales_root (`str | Path`, *optional*): Folder where locales are located. Defaults to `Path("locale")`.
Returns: ### Returns:
List[Any]: List of values in all locales. * `List[Any]`: List of values in all locales
""" """
output: List[Any] = [] output: List[Any] = []
@@ -164,15 +164,15 @@ async def in_all_locales(key: str, *args: str, locales_root: str | Path = Path("
def in_every_locale( def in_every_locale(
key: str, *args: str, locales_root: str | Path = Path("locale") key: str, *args: str, locales_root: str | Path = Path("locale")
) -> Dict[str, Any]: ) -> Dict[str, Any]:
"""Get value of the provided key and path in every available locale with locale tag. """Get value of the provided key and path in every available locale with locale tag
Args: ### Args:
key (str): The last key of the locale's keys path. * key (`str`): The last key of the locale's keys path.
*args (str): Path to key like: `dict[args][key]`. * *args (`str`): Path to key like: `dict[args][key]`.
locales_root (str | Path, optional): Folder where locales are located. Defaults to Path("locale"). * locales_root (`str | Path`, *optional*): Folder where locales are located. Defaults to `Path("locale")`.
Returns: ### Returns:
Dict[str, Any]: Locale is a key, and it's value from locale file is a value. * `Dict[str, Any]`: Locale is a key, and it's value from locale file is a value
""" """
output: Dict[str, Any] = {} output: Dict[str, Any] = {}
@@ -200,15 +200,15 @@ def in_every_locale(
async def in_every_locale( async def in_every_locale(
key: str, *args: str, locales_root: str | Path = Path("locale") key: str, *args: str, locales_root: str | Path = Path("locale")
) -> Dict[str, Any]: ) -> Dict[str, Any]:
"""Get value of the provided key and path in every available locale with locale tag. """Get value of the provided key and path in every available locale with locale tag
Args: ### Args:
key (str): The last key of the locale's keys path. * key (`str`): The last key of the locale's keys path.
*args (str): Path to key like: `dict[args][key]`. * *args (`str`): Path to key like: `dict[args][key]`.
locales_root (str | Path, optional): Folder where locales are located. Defaults to Path("locale"). * locales_root (`str | Path`, *optional*): Folder where locales are located. Defaults to `Path("locale")`.
Returns: ### Returns:
Dict[str, Any]: Locale is a key, and it's value from locale file is a value. * `Dict[str, Any]`: Locale is a key, and it's value from locale file is a value
""" """
output: Dict[str, Any] = {} output: Dict[str, Any] = {}

View File

@@ -14,11 +14,6 @@ class BotLocale:
default_locale: str | None = "en", default_locale: str | None = "en",
locales_root: str | Path = Path("locale"), locales_root: str | Path = Path("locale"),
) -> None: ) -> None:
"""
Args:
default_locale (str | None, optional): Default locale. Defaults to "en".
locales_root (str | Path, optional): Path to a directory with locale files. Defaults to Path("locale").
"""
if isinstance(locales_root, str): if isinstance(locales_root, str):
locales_root = Path(locales_root) locales_root = Path(locales_root)
elif not isinstance(locales_root, Path): elif not isinstance(locales_root, Path):
@@ -35,15 +30,15 @@ class BotLocale:
self.locales[locale] = json_read(Path(f"{locales_root}/{locale}.json")) self.locales[locale] = json_read(Path(f"{locales_root}/{locale}.json"))
def _(self, key: str, *args: str, locale: str | None = None) -> Any: def _(self, key: str, *args: str, locale: str | None = None) -> Any:
"""Get value of locale string. """Get value of locale string
Args: ### Args:
key (str): The last key of the locale's keys path. * key (`str`): The last key of the locale's keys path
*args (str): Path to key like: `dict[args][key]`. * *args (`str`): Path to key like: `dict[args][key]`
locale (str | None, optional): Locale to looked up in. Defaults to config's `"locale"` value. * locale (`str | None`, *optional*): Locale to looked up in. Defaults to config's `"locale"` value
Returns: ### Returns:
Any: Value of provided locale key. Is usually `str`, `Dict[str, Any]` or `List[Any]`. * `Any`: Value of provided locale key. Is usually `str`, `Dict[str, Any]` or `List[Any]`
""" """
if locale is None: if locale is None:
locale: str = self.default locale: str = self.default
@@ -69,14 +64,14 @@ class BotLocale:
return f'⚠️ Locale in config is invalid: could not get "{key}" in {args} from locale "{locale}"' return f'⚠️ Locale in config is invalid: could not get "{key}" in {args} from locale "{locale}"'
def in_all_locales(self, key: str, *args: str) -> List[Any]: def in_all_locales(self, key: str, *args: str) -> List[Any]:
"""Get value of the provided key and path in all available locales. """Get value of the provided key and path in all available locales
Args: ### Args:
key (str): The last key of the locale's keys path. * key (`str`): The last key of the locale's keys path.
*args (str): Path to key like: `dict[args][key]`. * *args (`str`): Path to key like: `dict[args][key]`.
Returns: ### Returns:
List[Any]: List of values in all locales. * `List[Any]`: List of values in all locales
""" """
output: List[Any] = [] output: List[Any] = []
@@ -99,14 +94,14 @@ class BotLocale:
return output return output
def in_every_locale(self, key: str, *args: str) -> Dict[str, Any]: def in_every_locale(self, key: str, *args: str) -> Dict[str, Any]:
"""Get value of the provided key and path in every available locale with locale tag. """Get value of the provided key and path in every available locale with locale tag
Args: ### Args:
key (str): The last key of the locale's keys path. * key (`str`): The last key of the locale's keys path.
*args (str): Path to key like: `dict[args][key]`. * *args (`str`): Path to key like: `dict[args][key]`.
Returns: ### Returns:
Dict[str, Any]: Locale is a key, and it's value from locale file is a value. * `Dict[str, Any]`: Locale is a key, and it's value from locale file is a value
""" """
output: Dict[str, Any] = {} output: Dict[str, Any] = {}

View File

@@ -15,24 +15,34 @@ DEFAULT_CONFIG_LOCATION: str = "config.json"
@asyncable @asyncable
def config_get(key: str, *path: str, config_file: str | Path = DEFAULT_CONFIG_LOCATION) -> Any: def config_get(key: str, *path: str, config_file: str | Path = DEFAULT_CONFIG_LOCATION) -> Any:
"""Get a value of the config key by its path provided. """Get a value of the config key by its path provided
For example, `foo.bar.key` has a path of `"foo", "bar"` and the key `"key"`. For example, `foo.bar.key` has a path of `"foo", "bar"` and the key `"key"`
Args: ### Args:
key (str): Key that contains the value * key (`str`): Key that contains the value
*path (str): Path to the key that contains the value (pass *[] or don't pass anything at all to get on the top/root level) * *path (`str`): Path to the key that contains the value (pass *[] or don't pass anything at all to get on the top/root level)
config_file (str | Path, optional): Path-like object or path as a string of a location of the config file. Defaults to `"config.json"` * config_file (`str | Path`, *optional*): Path-like object or path as a string of a location of the config file. Defaults to `"config.json"`
Returns: ### Returns:
Any: Key's value * `Any`: Key's value
Example: ### Example:
Get the "salary" of "Pete" from this JSON structure: `{"users": {"Pete": {"salary": 10.0}}}` Get the "salary" of "Pete" from this JSON structure:
```json
{
"users": {
"Pete": {
"salary": 10.0
}
}
}
```
This can be easily done with the following code: This can be easily done with the following code:
```python
>>> import libbot import libbot
salary: float = libbot.sync.config_get("salary", "users", "Pete") salary = libbot.sync.config_get("salary", "users", "Pete")
```
""" """
this_key: Dict[str, Any] = json_read(config_file) this_key: Dict[str, Any] = json_read(config_file)
@@ -44,24 +54,34 @@ def config_get(key: str, *path: str, config_file: str | Path = DEFAULT_CONFIG_LO
@config_get.asynchronous @config_get.asynchronous
async def config_get(key: str, *path: str, config_file: str | Path = DEFAULT_CONFIG_LOCATION) -> Any: async def config_get(key: str, *path: str, config_file: str | Path = DEFAULT_CONFIG_LOCATION) -> Any:
"""Get a value of the config key by its path provided. """Get a value of the config key by its path provided
For example, `foo.bar.key` has a path of `"foo", "bar"` and the key `"key"`. For example, `foo.bar.key` has a path of `"foo", "bar"` and the key `"key"`
Args: ### Args:
key (str): Key that contains the value * key (`str`): Key that contains the value
*path (str): Path to the key that contains the value (pass *[] or don't pass anything at all to get on the top/root level) * *path (`str`): Path to the key that contains the value (pass *[] or don't pass anything at all to get on the top/root level)
config_file (str | Path, optional): Path-like object or path as a string of a location of the config file. Defaults to `"config.json"` * config_file (`str | Path`, *optional*): Path-like object or path as a string of a location of the config file. Defaults to `"config.json"`
Returns: ### Returns:
Any: Key's value * `Any`: Key's value
Example: ### Example:
Get the "salary" of "Pete" from this JSON structure: `{"users": {"Pete": {"salary": 10.0}}}` Get the "salary" of "Pete" from this JSON structure:
```json
{
"users": {
"Pete": {
"salary": 10.0
}
}
}
```
This can be easily done with the following code: This can be easily done with the following code:
```python
>>> import libbot import libbot
salary: float = libbot.sync.config_get("salary", "users", "Pete") salary = await libbot.config_get("salary", "users", "Pete")
```
""" """
this_key: Dict[str, Any] = await json_read(config_file) this_key: Dict[str, Any] = await json_read(config_file)
@@ -73,16 +93,16 @@ async def config_get(key: str, *path: str, config_file: str | Path = DEFAULT_CON
@asyncable @asyncable
def config_set(key: str, value: Any, *path: str, config_file: str | Path = DEFAULT_CONFIG_LOCATION) -> None: def config_set(key: str, value: Any, *path: str, config_file: str | Path = DEFAULT_CONFIG_LOCATION) -> None:
"""Set config's key by its path to the value. """Set config's key by its path to the value
Args: ### Args:
key (str): Key that leads to the value. * key (`str`): Key that leads to the value
value (Any): Any JSON-serializable data. * value (`Any`): Any JSON serializable data
*path (str): Path to the key of the target (pass *[] or don't pass anything at all to set on the top/root level). * *path (`str`): Path to the key of the target (pass *[] or don't pass anything at all to set on the top/root level)
config_file (str | Path, optional): Path-like object or path as a string of a location of the config file. Defaults to "config.json". * config_file (`str | Path`, *optional*): Path-like object or path as a string of a location of the config file. Defaults to `"config.json"`
Raises: ### Raises:
KeyError: Key was not found under the provided path. * `KeyError`: Key is not found under path provided
""" """
json_write(nested_set(json_read(config_file), value, *(*path, key)), config_file) json_write(nested_set(json_read(config_file), value, *(*path, key)), config_file)
@@ -91,16 +111,16 @@ def config_set(key: str, value: Any, *path: str, config_file: str | Path = DEFAU
async def config_set( async def config_set(
key: str, value: Any, *path: str, config_file: str | Path = DEFAULT_CONFIG_LOCATION key: str, value: Any, *path: str, config_file: str | Path = DEFAULT_CONFIG_LOCATION
) -> None: ) -> None:
"""Set config's key by its path to the value. """Set config's key by its path to the value
Args: ### Args:
key (str): Key that leads to the value. * key (`str`): Key that leads to the value
value (Any): Any JSON-serializable data. * value (`Any`): Any JSON serializable data
*path (str): Path to the key of the target (pass *[] or don't pass anything at all to set on the top/root level). * *path (`str`): Path to the key of the target (pass *[] or don't pass anything at all to set on the top/root level)
config_file (str | Path, optional): Path-like object or path as a string of a location of the config file. Defaults to "config.json". * config_file (`str | Path`, *optional*): Path-like object or path as a string of a location of the config file. Defaults to `"config.json"`
Raises: ### Raises:
KeyError: Key was not found under the provided path. * `KeyError`: Key is not found under path provided
""" """
await json_write(nested_set(await json_read(config_file), value, *(*path, key)), config_file) await json_write(nested_set(await json_read(config_file), value, *(*path, key)), config_file)
@@ -112,16 +132,16 @@ def config_delete(
missing_ok: bool = False, missing_ok: bool = False,
config_file: str | Path = DEFAULT_CONFIG_LOCATION, config_file: str | Path = DEFAULT_CONFIG_LOCATION,
) -> None: ) -> None:
"""Delete config's key by its path. """Set config's key by its path
Args: ### Args:
key (str): Key to delete. * key (`str`): Key to delete
*path (str): Path to the key of the target (pass *[] or don't pass anything at all to delete on the top/root level) * *path (`str`): Path to the key of the target (pass *[] or don't pass anything at all to delete on the top/root level)
missing_ok (bool): Do not raise an exception if the key is missing. Defaults to False. * missing_ok (`bool`): Do not raise an exception if the key is missing. Defaults to `False`
config_file (str | Path, optional): Path-like object or path as a string of a location of the config file. Defaults to "config.json". * config_file (`str | Path`, *optional*): Path-like object or path as a string of a location of the config file. Defaults to `"config.json"`
Raises: ### Raises:
KeyError: Key is not found under path provided and `missing_ok` is False. * `KeyError`: Key is not found under path provided and `missing_ok` is `False`
""" """
config_data: Dict[str, Any] = json_read(config_file) config_data: Dict[str, Any] = json_read(config_file)
@@ -141,16 +161,16 @@ async def config_delete(
missing_ok: bool = False, missing_ok: bool = False,
config_file: str | Path = DEFAULT_CONFIG_LOCATION, config_file: str | Path = DEFAULT_CONFIG_LOCATION,
) -> None: ) -> None:
"""Delete config's key by its path. """Set config's key by its path
Args: ### Args:
key (str): Key to delete. * key (`str`): Key to delete
*path (str): Path to the key of the target (pass *[] or don't pass anything at all to delete on the top/root level) * *path (`str`): Path to the key of the target (pass *[] or don't pass anything at all to delete on the top/root level)
missing_ok (bool): Do not raise an exception if the key is missing. Defaults to False. * missing_ok (`bool`): Do not raise an exception if the key is missing. Defaults to `False`
config_file (str | Path, optional): Path-like object or path as a string of a location of the config file. Defaults to "config.json". * config_file (`str | Path`, *optional*): Path-like object or path as a string of a location of the config file. Defaults to `"config.json"`
Raises: ### Raises:
KeyError: Key is not found under path provided and `missing_ok` is False. * `KeyError`: Key is not found under path provided and `missing_ok` is `False`
""" """
config_data: Dict[str, Any] = await json_read(config_file) config_data: Dict[str, Any] = await json_read(config_file)

View File

@@ -14,13 +14,13 @@ except ImportError:
@asyncable @asyncable
def json_read(path: str | Path) -> Any: def json_read(path: str | Path) -> Any:
"""Read contents of a JSON file and return it. """Read contents of a JSON file
Args: ### Args:
path (str | Path): Path-like object or path to the file as a string. * path (`str | Path`): Path-like object or path as a string
Returns: ### Returns:
Any: File contents. * `Any`: File contents
""" """
with open(str(path), mode="r", encoding="utf-8") as f: with open(str(path), mode="r", encoding="utf-8") as f:
data = f.read() data = f.read()
@@ -30,13 +30,13 @@ def json_read(path: str | Path) -> Any:
@json_read.asynchronous @json_read.asynchronous
async def json_read(path: str | Path) -> Any: async def json_read(path: str | Path) -> Any:
"""Read contents of a JSON file and return it. """Read contents of a JSON file
Args: ### Args:
path (str | Path): Path-like object or path to the file as a string. * path (`str | Path`): Path-like object or path as a string
Returns: ### Returns:
Any: File contents. * `Any`: File contents
""" """
async with aiofiles.open(str(path), mode="r", encoding="utf-8") as f: async with aiofiles.open(str(path), mode="r", encoding="utf-8") as f:
data = await f.read() data = await f.read()
@@ -46,11 +46,11 @@ async def json_read(path: str | Path) -> Any:
@asyncable @asyncable
def json_write(data: Any, path: str | Path) -> None: def json_write(data: Any, path: str | Path) -> None:
"""Write contents to a JSON file. """Write contents to a JSON file
Args: ### Args:
data (Any): Contents to write. Must be a JSON-serializable object. * data (`Any`): Contents to write. Must be a JSON serializable
path (str | Path): Path-like object or path to the file as a string. * path (`str | Path`): Path-like object or path as a string of a destination
""" """
with open(str(path), mode="w", encoding="utf-8") as f: with open(str(path), mode="w", encoding="utf-8") as f:
f.write( f.write(
@@ -62,11 +62,11 @@ def json_write(data: Any, path: str | Path) -> None:
@json_write.asynchronous @json_write.asynchronous
async def json_write(data: Any, path: str | Path) -> None: async def json_write(data: Any, path: str | Path) -> None:
"""Write contents to a JSON file. """Write contents to a JSON file
Args: ### Args:
data (Any): Contents to write. Must be a JSON-serializable object. * data (`Any`): Contents to write. Must be a JSON serializable
path (str | Path): Path-like object or path to the file as a string. * path (`str | Path`): Path-like object or path as a string of a destination
""" """
async with aiofiles.open(str(path), mode="w", encoding="utf-8") as f: async with aiofiles.open(str(path), mode="w", encoding="utf-8") as f:
await f.write( await f.write(

View File

@@ -4,14 +4,14 @@ from typing import Callable
def supports_argument(func: Callable[..., Any], arg_name: str) -> bool: def supports_argument(func: Callable[..., Any], arg_name: str) -> bool:
"""Check whether a function has a specific argument. """Check whether a function has a specific argument
Args: ### Args:
func (Callable[..., Any]): Function to be inspected. * func (`Callable[..., Any]`): Function to be inspected
arg_name (str): Argument to be checked. * arg_name (`str`): Argument to be checked
Returns: ### Returns:
bool: True if argument is supported and False if not. * `bool`: `True` if argument is supported and `False` if not
""" """
if hasattr(func, "__code__"): if hasattr(func, "__code__"):
return arg_name in inspect.signature(func).parameters return arg_name in inspect.signature(func).parameters
@@ -29,17 +29,17 @@ def nested_set(
) -> Dict[str, Any]: ) -> Dict[str, Any]:
"""Set the key by its path to the value """Set the key by its path to the value
Args: ### Args:
target (Dict[str, Any]): Dictionary to perform the modification on. * target (`Dict[str, Any]`): Dictionary to perform modifications on
value (Any): New value. * value (`Any`): Any data
*path (str): Path to the key. * *path (`str`): Path to the key of the target
create_missing (:obj:`bool`, optional): Create keys on the way if they're missing. Defaults to True. * create_missing (`bool`, *optional*): Create keys on the way if they're missing. Defaults to `True`
Raises: ### Raises:
KeyError: Key is not found under the provided path. * `KeyError`: Key is not found under path provided
Returns: ### Returns:
Dict[str, Any]: Modified dictionary. * `Dict[str, Any]`: Changed dictionary
""" """
target_copy: Dict[str, Any] = target target_copy: Dict[str, Any] = target
@@ -60,16 +60,16 @@ def nested_set(
def nested_delete(target: Dict[str, Any], *path: str) -> Dict[str, Any]: def nested_delete(target: Dict[str, Any], *path: str) -> Dict[str, Any]:
"""Delete the key by its path. """Delete the key by its path
Args: ### Args:
target (Dict[str, Any]): Dictionary to perform the modification on. * target (`Dict[str, Any]`): Dictionary to perform modifications on
Raises: ### Raises:
KeyError: Key is not found under the provided path. * `KeyError`: Key is not found under path provided
Returns: ### Returns:
Dict[str, Any]: Modified dictionary. `Dict[str, Any]`: Changed dictionary
""" """
target_copy: Dict[str, Any] = target target_copy: Dict[str, Any] = target