Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
7990ab4
Update downstream dependencies for ovos-utils
github-actions[bot] Jun 9, 2025
2e8b733
Update downstream dependencies for ovos-utils
github-actions[bot] Jun 10, 2025
3294783
Update downstream dependencies for ovos-utils
github-actions[bot] Jun 11, 2025
f9bf8ed
Update downstream dependencies for ovos-utils
github-actions[bot] Jun 13, 2025
d5f76fe
Update downstream dependencies for ovos-utils
github-actions[bot] Jun 14, 2025
30682a3
Update downstream dependencies for ovos-utils
github-actions[bot] Jun 15, 2025
7e1d2e2
Update downstream dependencies for ovos-utils
github-actions[bot] Jun 16, 2025
6af3ae2
Update downstream dependencies for ovos-utils
github-actions[bot] Jun 17, 2025
b97b820
Update downstream dependencies for ovos-utils
github-actions[bot] Jun 18, 2025
d191375
Update downstream dependencies for ovos-utils
github-actions[bot] Jun 19, 2025
387d2cb
Update downstream dependencies for ovos-utils
github-actions[bot] Jun 20, 2025
577a44c
Update downstream dependencies for ovos-utils
github-actions[bot] Jun 22, 2025
763106b
Update downstream dependencies for ovos-utils
github-actions[bot] Jun 27, 2025
3708d47
Update downstream dependencies for ovos-utils
github-actions[bot] Jul 8, 2025
3164201
Update downstream dependencies for ovos-utils
github-actions[bot] Jul 9, 2025
c24c2e4
Update downstream dependencies for ovos-utils
github-actions[bot] Jul 12, 2025
666ee51
Update downstream dependencies for ovos-utils
github-actions[bot] Jul 13, 2025
5ec0ece
Update downstream dependencies for ovos-utils
github-actions[bot] Jul 23, 2025
d815250
Update downstream dependencies for ovos-utils
github-actions[bot] Jul 31, 2025
6b05796
Update downstream dependencies for ovos-utils
github-actions[bot] Aug 1, 2025
97b97f0
Update downstream dependencies for ovos-utils
github-actions[bot] Aug 2, 2025
ab6e931
Update downstream dependencies for ovos-utils
github-actions[bot] Aug 3, 2025
f5278e8
Update downstream dependencies for ovos-utils
github-actions[bot] Aug 4, 2025
ca993a4
Update downstream dependencies for ovos-utils
github-actions[bot] Aug 5, 2025
cb1d02f
Update downstream dependencies for ovos-utils
github-actions[bot] Aug 9, 2025
bbfb4f5
Update downstream dependencies for ovos-utils
github-actions[bot] Aug 28, 2025
33cda06
fix: make orjson optional (#335)
JarbasAl Sep 5, 2025
997ceab
Increment Version to 0.8.2a1
JarbasAl Sep 5, 2025
4033d67
Update Changelog
JarbasAl Sep 5, 2025
59a0140
Update downstream dependencies for ovos-utils
github-actions[bot] Sep 6, 2025
9b2fbe3
Update downstream dependencies for ovos-utils
github-actions[bot] Sep 7, 2025
fd22209
Update downstream dependencies for ovos-utils
github-actions[bot] Sep 8, 2025
01184f6
Update downstream dependencies for ovos-utils
github-actions[bot] Sep 9, 2025
af8b484
Update downstream dependencies for ovos-utils
github-actions[bot] Sep 25, 2025
0556b2c
Update downstream dependencies for ovos-utils
github-actions[bot] Sep 29, 2025
d742d3e
Update downstream dependencies for ovos-utils
github-actions[bot] Sep 30, 2025
dfef4fe
Update downstream dependencies for ovos-utils
github-actions[bot] Oct 1, 2025
1f0bb77
Update downstream dependencies for ovos-utils
github-actions[bot] Oct 6, 2025
1443c7b
Update downstream dependencies for ovos-utils
github-actions[bot] Oct 8, 2025
2b594d6
Update downstream dependencies for ovos-utils
github-actions[bot] Oct 9, 2025
5f78341
Update downstream dependencies for ovos-utils
github-actions[bot] Oct 10, 2025
5b4d6f4
fix: fail safe when used in applications with conflicting watchdog mo…
JarbasAl Oct 13, 2025
f4f64d8
Increment Version to 0.8.3a1
JarbasAl Oct 13, 2025
730698a
Update Changelog
JarbasAl Oct 13, 2025
cbcdc93
fix: handle issues in NVDA python stdlib (#341)
JarbasAl Oct 13, 2025
9442551
Increment Version to 0.8.4a1
JarbasAl Oct 13, 2025
feeda5f
Update Changelog
JarbasAl Oct 13, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,29 @@
# Changelog

## [0.8.4a1](https://github.com/OpenVoiceOS/ovos-utils/tree/0.8.4a1) (2025-10-13)

[Full Changelog](https://github.com/OpenVoiceOS/ovos-utils/compare/0.8.3a1...0.8.4a1)

**Merged pull requests:**

- fix: handle issues in NVDA python stdlib [\#341](https://github.com/OpenVoiceOS/ovos-utils/pull/341) ([JarbasAl](https://github.com/JarbasAl))

## [0.8.3a1](https://github.com/OpenVoiceOS/ovos-utils/tree/0.8.3a1) (2025-10-13)

[Full Changelog](https://github.com/OpenVoiceOS/ovos-utils/compare/0.8.2a1...0.8.3a1)

**Merged pull requests:**

- fix: fail safe when used in applications with conflicting watchdog [\#338](https://github.com/OpenVoiceOS/ovos-utils/pull/338) ([JarbasAl](https://github.com/JarbasAl))

## [0.8.2a1](https://github.com/OpenVoiceOS/ovos-utils/tree/0.8.2a1) (2025-09-05)

[Full Changelog](https://github.com/OpenVoiceOS/ovos-utils/compare/0.8.1...0.8.2a1)

**Merged pull requests:**

- fix: make orjson optional [\#335](https://github.com/OpenVoiceOS/ovos-utils/pull/335) ([JarbasAl](https://github.com/JarbasAl))



\* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)*
1,645 changes: 779 additions & 866 deletions downstream_report.txt

Large diffs are not rendered by default.

54 changes: 54 additions & 0 deletions ovos_utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,70 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
import dataclasses
import datetime
import json
import warnings
from time import sleep
from typing import Dict, Any

from ovos_utils.decorators import classproperty, timed_lru_cache
from ovos_utils.list_utils import flatten_list, rotate_list
from ovos_utils.log import LOG, log_deprecation
from ovos_utils.text_utils import camel_case_split
from ovos_utils.thread_utils import wait_for_exit_signal, threaded_timeout, create_killable_daemon, create_daemon

try:
import orjson
except ImportError:
orjson = None


def json_dumps(payload: Any) -> str:
"""
Serializes an object to a JSON string using `orjson` if available,
with a fallback to the standard `json` library.

This function provides a significant performance boost when `orjson` is
installed, while ensuring compatibility by gracefully falling back. It also
handles dataclass serialization for both implementations.

Args:
payload (Any): The object to be serialized. This can be a built-in
type, a dictionary, or a dataclass instance.

Returns:
str: The serialized JSON string.
"""
if orjson is None:
# handle dataclasses
if dataclasses.is_dataclass(payload):
payload = dataclasses.asdict(payload)
return json.dumps(payload, ensure_ascii=False)
else:
# orjson.dumps has native dataclass support and returns bytes
return orjson.dumps(payload).decode("utf-8")


def json_loads(payload: str) -> Dict[str, Any]:
"""
Deserializes a JSON string into a dictionary using `orjson` if available,
with a fallback to the standard `json` library.

This function provides a significant performance boost for deserialization
when `orjson` is installed.

Args:
payload (str): The JSON string to be deserialized.

Returns:
Dict[str, Any]: The deserialized data as a dictionary.
"""
if orjson is None:
return json.loads(payload)
else:
return orjson.loads(payload)


def create_loop(target, interval, args=(), kwargs=None):
"""
Expand Down
90 changes: 50 additions & 40 deletions ovos_utils/file_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,6 @@
from threading import RLock
from typing import Optional, List

from watchdog.events import FileSystemEventHandler
from watchdog.observers import Observer

from ovos_utils.bracket_expansion import expand_template
from ovos_utils.log import LOG, log_deprecation

Expand Down Expand Up @@ -371,6 +368,9 @@ def __init__(self, files: List[str], callback: callable,
@param recursive: If true, recursively include directory contents
@param ignore_creation: If true, ignore file creation events
"""
# here in case watchdog module is not available
# just let the import fail with clear trace
from watchdog.observers import Observer
self.observer = Observer()
self.handlers = []
for file_path in files:
Expand All @@ -390,40 +390,50 @@ def shutdown(self):
self.observer.unschedule_all()
self.observer.stop()


class FileEventHandler(FileSystemEventHandler):
def __init__(self, file_path: str, callback: callable,
ignore_creation: bool = False):
"""
Create a handler for file change events
@param file_path: file_path being watched Unused(?)
@param callback: function to call on file change with modified file path
@param ignore_creation: if True, only track file modification events
"""
super().__init__()
self._callback = callback
self._file_path = file_path
if ignore_creation:
self._events = ('modified')
else:
self._events = ('created', 'modified')
self._changed_files = []
self._lock = RLock()

def on_any_event(self, event):
if event.is_directory:
return
with self._lock:
if event.event_type == "closed":
if event.src_path in self._changed_files:
self._changed_files.remove(event.src_path)
# fire event, it is now safe
try:
self._callback(event.src_path)
except:
LOG.exception("An error occurred handling file "
"change event callback")

elif event.event_type in self._events:
if event.src_path not in self._changed_files:
self._changed_files.append(event.src_path)
try:
# NOTE: technically a dependency, but often not used outside OVOS itself
# conflicts have been detected with NVDA when using ovos-plugin-manager
# this allows standalone usage of ovos-utils outside OVOS but
# ensures a ImportError happens if using FileWatcher class
from watchdog.events import FileSystemEventHandler

class FileEventHandler(FileSystemEventHandler):
def __init__(self, file_path: str, callback: callable,
ignore_creation: bool = False):
"""
Create a handler for file change events
@param file_path: file_path being watched Unused(?)
@param callback: function to call on file change with modified file path
@param ignore_creation: if True, only track file modification events
"""
super().__init__()
self._callback = callback
self._file_path = file_path
if ignore_creation:
self._events = ('modified')
else:
self._events = ('created', 'modified')
self._changed_files = []
self._lock = RLock()

def on_any_event(self, event):
if event.is_directory:
return
with self._lock:
if event.event_type == "closed":
if event.src_path in self._changed_files:
self._changed_files.remove(event.src_path)
# fire event, it is now safe
try:
self._callback(event.src_path)
except:
LOG.exception("An error occurred handling file "
"change event callback")

elif event.event_type in self._events:
if event.src_path not in self._changed_files:
self._changed_files.append(event.src_path)

except ImportError:
LOG.error("Failed to import watchdog module. FileWatcher will not be available. "
"Are you using ovos-utils inside an application containing a file named 'watchdog.py'?")
23 changes: 15 additions & 8 deletions ovos_utils/log.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
import logging
import os
import sys
from logging.handlers import RotatingFileHandler
from os.path import join
from pathlib import Path
from typing import Optional, List, Set
Expand Down Expand Up @@ -110,13 +109,21 @@ def create_logger(cls, name, tostdout=True):
logger.addHandler(stdout_handler)
# log to file
if cls.base_path != "stdout":
os.makedirs(cls.base_path, exist_ok=True)
path = join(cls.base_path,
cls.name.lower().strip() + ".log")
handler = RotatingFileHandler(path, maxBytes=cls.max_bytes,
backupCount=cls.backup_count)
handler.setFormatter(cls.formatter)
logger.addHandler(handler)
try:
# HACK: noticed in NVDA, the bundled python appears to be a
# stripped down version with missing stdlib components
# `logging.handlers` confirmed to be missing under that setup
from logging.handlers import RotatingFileHandler
os.makedirs(cls.base_path, exist_ok=True)
path = join(cls.base_path,
cls.name.lower().strip() + ".log")
handler = RotatingFileHandler(path, maxBytes=cls.max_bytes,
backupCount=cls.backup_count)
handler.setFormatter(cls.formatter)
logger.addHandler(handler)
except ImportError:
logger.exception("Unable to import 'logging.handlers.RotatingFileHandler'. You seem to be running in a stripped down python version.")

logger.setLevel(cls.level)
cls._loggers[name] = logger
return logger
Expand Down
10 changes: 5 additions & 5 deletions ovos_utils/ocp.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from enum import IntEnum
from typing import Optional, Tuple, List, Union
import warnings
import orjson
from ovos_utils import json_dumps, json_loads

from ovos_utils.log import LOG, deprecated

Expand Down Expand Up @@ -228,8 +228,8 @@ def as_dict(self) -> dict:
"""
Return a dict representation of this MediaEntry
"""
# orjson handles dataclasses directly
return orjson.loads(orjson.dumps(self).decode("utf-8"))
# json_dumps handles dataclasses directly
return json_loads(json_dumps(self))

@staticmethod
def from_dict(track: dict) -> 'MediaEntry':
Expand Down Expand Up @@ -326,8 +326,8 @@ def as_dict(self) -> dict:
"""
Return a dict representation of this MediaEntry
"""
# orjson handles dataclasses directly
return orjson.loads(orjson.dumps(self).decode("utf-8"))
# json_dumps handles dataclasses directly
return json_loads(json_dumps(self))

@staticmethod
def from_dict(track: dict) -> 'PluginStream':
Expand Down
4 changes: 2 additions & 2 deletions ovos_utils/version.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# START_VERSION_BLOCK
VERSION_MAJOR = 0
VERSION_MINOR = 8
VERSION_BUILD = 1
VERSION_ALPHA = 0
VERSION_BUILD = 4
VERSION_ALPHA = 1
# END_VERSION_BLOCK
1 change: 1 addition & 0 deletions requirements/extras.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ ovos_bus_client>=0.0.8
langcodes
timezonefinder
oauthlib~=3.2
orjson
3 changes: 1 addition & 2 deletions requirements/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,4 @@ watchdog
pyee>=8.0.0
combo-lock~=0.2
rich-click~=1.7
rich~=13.7
orjson
rich~=13.7