Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
9baa615
Increment Version to 0.8.5
JarbasAl Mar 11, 2026
685ea9e
feat: AsyncFakeBus alongside FakeBus (#371)
JarbasAl May 18, 2026
04f5050
Increment Version to 0.9.0a1
JarbasAl May 18, 2026
90e48d6
Update Changelog
JarbasAl May 18, 2026
5b436b2
feat: migrate ovos-utils onto ovos-spec-tools (#373)
JarbasAl May 22, 2026
decfeb3
Increment Version to 0.10.0a1
JarbasAl May 22, 2026
16169be
Update Changelog
JarbasAl May 22, 2026
20e8445
feat: fakebus Message subclasses ovos_spec_tools.Message — no API bre…
JarbasAl May 25, 2026
a88d996
Increment Version to 0.11.0a1
JarbasAl May 25, 2026
6903f95
Update Changelog
JarbasAl May 25, 2026
5291187
fix: standardize_lang_tag macro=True preserves region (restore langco…
JarbasAl May 25, 2026
c9966ff
Increment Version to 0.11.1a1
JarbasAl May 25, 2026
9df555f
Update Changelog
JarbasAl May 25, 2026
fc9d84f
fix: allow json-database 1.x (#379)
JarbasAl Jun 20, 2026
ad75228
Increment Version to 0.11.2a1
JarbasAl Jun 20, 2026
f13a977
Update Changelog
JarbasAl Jun 20, 2026
cc68e60
feat: FakeBus mirrors the legacy<->ovos.* namespace migration (#381)
JarbasAl Jun 25, 2026
fd6b6e0
Increment Version to 0.12.0a1
JarbasAl Jun 25, 2026
e9f3b17
Update Changelog
JarbasAl Jun 25, 2026
08febad
fix: raise ovos-spec-tools floor to 0.10.0a1 for NamespaceTranslator …
JarbasAl Jun 25, 2026
ae73d69
Increment Version to 0.12.1a1
JarbasAl Jun 25, 2026
5c47256
Update Changelog
JarbasAl Jun 25, 2026
9b02d99
fix: translate mirrored payload onto counterpart topic in FakeBus (#385)
JarbasAl Jun 27, 2026
acb4e1e
Increment Version to 0.12.2a1
JarbasAl Jun 27, 2026
d846054
Update Changelog
JarbasAl Jun 27, 2026
a2404f9
feat: AsyncFakeBus namespace migration + env/config flag parity (#387)
JarbasAl Jun 27, 2026
232aecc
Increment Version to 0.13.0a1
JarbasAl Jun 27, 2026
6ddc21e
Update Changelog
JarbasAl Jun 27, 2026
3b208ff
fix: target a real shape-changing pair in namespace-migration tests (…
JarbasAl Jun 29, 2026
7750b4f
Increment Version to 0.13.1a1
JarbasAl Jun 29, 2026
2866a88
Update Changelog
JarbasAl Jun 29, 2026
283dac7
fix: drop deprecated make_default from FakeBus default-session sync (…
JarbasAl Jun 29, 2026
6d14fb9
Increment Version to 0.13.2a1
JarbasAl Jun 29, 2026
49e9f20
Update Changelog
JarbasAl Jun 29, 2026
9522042
fix: FakeBus folds the default session like any other (drop owner-onl…
JarbasAl Jun 29, 2026
8f38d55
Increment Version to 0.13.3a1
JarbasAl Jun 29, 2026
1c6ad40
Update Changelog
JarbasAl Jun 29, 2026
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
4 changes: 2 additions & 2 deletions .github/workflows/coverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@ jobs:
with:
python_version: '3.11'
coverage_source: 'ovos_utils'
test_path: 'test/'
install_extras: ''
test_path: 'test/unittests'
install_extras: '.[extras]'
min_coverage: 0
86 changes: 66 additions & 20 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,54 +1,100 @@
# Changelog

## [0.8.5a4](https://github.com/OpenVoiceOS/ovos-utils/tree/0.8.5a4) (2026-03-11)
## [0.13.3a1](https://github.com/OpenVoiceOS/ovos-utils/tree/0.13.3a1) (2026-06-29)

[Full Changelog](https://github.com/OpenVoiceOS/ovos-utils/compare/0.8.6a2...0.8.5a4)
[Full Changelog](https://github.com/OpenVoiceOS/ovos-utils/compare/0.13.2a1...0.13.3a1)

**Merged pull requests:**

- chore: Add comprehensive test suites and documentation [\#362](https://github.com/OpenVoiceOS/ovos-utils/pull/362) ([JarbasAl](https://github.com/JarbasAl))
- fix: make the stopwatch test less strict [\#360](https://github.com/OpenVoiceOS/ovos-utils/pull/360) ([PureTryOut](https://github.com/PureTryOut))
- fix: FakeBus folds the default session like any other \(drop owner-only\) [\#393](https://github.com/OpenVoiceOS/ovos-utils/pull/393) ([JarbasAl](https://github.com/JarbasAl))

## [0.8.6a2](https://github.com/OpenVoiceOS/ovos-utils/tree/0.8.6a2) (2026-02-02)
## [0.13.2a1](https://github.com/OpenVoiceOS/ovos-utils/tree/0.13.2a1) (2026-06-29)

[Full Changelog](https://github.com/OpenVoiceOS/ovos-utils/compare/0.8.6a1...0.8.6a2)
[Full Changelog](https://github.com/OpenVoiceOS/ovos-utils/compare/0.13.1a1...0.13.2a1)

**Merged pull requests:**

- chore\(deps\): update actions/setup-python action to v6 [\#353](https://github.com/OpenVoiceOS/ovos-utils/pull/353) ([renovate[bot]](https://github.com/apps/renovate))
- chore\(deps\): update actions/checkout action to v6 [\#352](https://github.com/OpenVoiceOS/ovos-utils/pull/352) ([renovate[bot]](https://github.com/apps/renovate))
- fix: drop deprecated make\_default from FakeBus default-session sync [\#389](https://github.com/OpenVoiceOS/ovos-utils/pull/389) ([JarbasAl](https://github.com/JarbasAl))

## [0.8.6a1](https://github.com/OpenVoiceOS/ovos-utils/tree/0.8.6a1) (2026-02-02)
## [0.13.1a1](https://github.com/OpenVoiceOS/ovos-utils/tree/0.13.1a1) (2026-06-29)

[Full Changelog](https://github.com/OpenVoiceOS/ovos-utils/compare/0.8.5a3...0.8.6a1)
[Full Changelog](https://github.com/OpenVoiceOS/ovos-utils/compare/0.13.0a1...0.13.1a1)

**Merged pull requests:**

- fix: prevent handler errors from aborting FakeBus emit [\#358](https://github.com/OpenVoiceOS/ovos-utils/pull/358) ([JarbasAl](https://github.com/JarbasAl))
- fix: target a real shape-changing pair in namespace-migration tests [\#390](https://github.com/OpenVoiceOS/ovos-utils/pull/390) ([JarbasAl](https://github.com/JarbasAl))

## [0.8.5a3](https://github.com/OpenVoiceOS/ovos-utils/tree/0.8.5a3) (2025-12-19)
## [0.13.0a1](https://github.com/OpenVoiceOS/ovos-utils/tree/0.13.0a1) (2026-06-27)

[Full Changelog](https://github.com/OpenVoiceOS/ovos-utils/compare/0.8.5a2...0.8.5a3)
[Full Changelog](https://github.com/OpenVoiceOS/ovos-utils/compare/0.12.2a1...0.13.0a1)

**Merged pull requests:**

- chore\(deps\): update dependency python to 3.14 [\#348](https://github.com/OpenVoiceOS/ovos-utils/pull/348) ([renovate[bot]](https://github.com/apps/renovate))
- feat: AsyncFakeBus namespace migration + env/config flag parity [\#387](https://github.com/OpenVoiceOS/ovos-utils/pull/387) ([JarbasAl](https://github.com/JarbasAl))

## [0.8.5a2](https://github.com/OpenVoiceOS/ovos-utils/tree/0.8.5a2) (2025-12-18)
## [0.12.2a1](https://github.com/OpenVoiceOS/ovos-utils/tree/0.12.2a1) (2026-06-27)

[Full Changelog](https://github.com/OpenVoiceOS/ovos-utils/compare/0.8.5a1...0.8.5a2)
[Full Changelog](https://github.com/OpenVoiceOS/ovos-utils/compare/0.12.1a1...0.12.2a1)

**Merged pull requests:**

- chore: Configure Renovate [\#347](https://github.com/OpenVoiceOS/ovos-utils/pull/347) ([renovate[bot]](https://github.com/apps/renovate))
- fix: translate mirrored payload onto counterpart topic in FakeBus [\#385](https://github.com/OpenVoiceOS/ovos-utils/pull/385) ([JarbasAl](https://github.com/JarbasAl))

## [0.8.5a1](https://github.com/OpenVoiceOS/ovos-utils/tree/0.8.5a1) (2025-11-07)
## [0.12.1a1](https://github.com/OpenVoiceOS/ovos-utils/tree/0.12.1a1) (2026-06-25)

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

**Merged pull requests:**

- fix: use timezone-aware datetime functions and update scheduler event names [\#343](https://github.com/OpenVoiceOS/ovos-utils/pull/343) ([JarbasAl](https://github.com/JarbasAl))
- fix: raise ovos-spec-tools floor to 0.10.0a1 for NamespaceTranslator [\#383](https://github.com/OpenVoiceOS/ovos-utils/pull/383) ([JarbasAl](https://github.com/JarbasAl))

## [0.12.0a1](https://github.com/OpenVoiceOS/ovos-utils/tree/0.12.0a1) (2026-06-25)

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

**Merged pull requests:**

- feat: FakeBus mirrors the legacy\<-\>ovos.\* namespace migration [\#381](https://github.com/OpenVoiceOS/ovos-utils/pull/381) ([JarbasAl](https://github.com/JarbasAl))

## [0.11.2a1](https://github.com/OpenVoiceOS/ovos-utils/tree/0.11.2a1) (2026-06-20)

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

**Merged pull requests:**

- fix: allow json-database 1.x [\#379](https://github.com/OpenVoiceOS/ovos-utils/pull/379) ([JarbasAl](https://github.com/JarbasAl))

## [0.11.1a1](https://github.com/OpenVoiceOS/ovos-utils/tree/0.11.1a1) (2026-05-25)

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

**Merged pull requests:**

- fix: standardize\_lang\_tag macro=True preserves region \(restore langcodes semantics\) [\#377](https://github.com/OpenVoiceOS/ovos-utils/pull/377) ([JarbasAl](https://github.com/JarbasAl))

## [0.11.0a1](https://github.com/OpenVoiceOS/ovos-utils/tree/0.11.0a1) (2026-05-25)

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

**Merged pull requests:**

- feat: fakebus Message subclasses ovos\_spec\_tools.Message — no API break [\#375](https://github.com/OpenVoiceOS/ovos-utils/pull/375) ([JarbasAl](https://github.com/JarbasAl))

## [0.10.0a1](https://github.com/OpenVoiceOS/ovos-utils/tree/0.10.0a1) (2026-05-22)

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

**Merged pull requests:**

- feat: migrate ovos-utils onto ovos-spec-tools [\#373](https://github.com/OpenVoiceOS/ovos-utils/pull/373) ([JarbasAl](https://github.com/JarbasAl))

## [0.9.0a1](https://github.com/OpenVoiceOS/ovos-utils/tree/0.9.0a1) (2026-05-18)

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

**Merged pull requests:**

- feat: AsyncFakeBus alongside FakeBus [\#371](https://github.com/OpenVoiceOS/ovos-utils/pull/371) ([JarbasAl](https://github.com/JarbasAl))



Expand Down
59 changes: 59 additions & 0 deletions docs/fakebus.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,65 @@ bus.emit(FakeMessage("recognizer_loop:utterance", {"utterances": ["hello"]}))
| `close()` | Calls `on_close()` |
| `create_client()` | Returns `self` |

For asyncio-native code, see [`AsyncFakeBus`](#asyncfakebus) below.

---

## `AsyncFakeBus`

`AsyncFakeBus` — `ovos_utils/fakebus.py:351`

In-process stand-in for `AsyncMessageBusClient` (from `ovos-bus-client`). Use this when your code is asyncio-native and needs a drop-in fake bus without a WebSocket connection. The API surface mirrors the real async client: coroutine methods keep you inside the event loop, while handler registration stays synchronous to match `pyee` and the real client's contract.

```python
import asyncio
from ovos_utils.fakebus import AsyncFakeBus, FakeMessage

async def main():
bus = AsyncFakeBus()

received = []

def on_ping(message):
received.append(message)

bus.on("test:ping", on_ping)

await bus.emit(FakeMessage("test:ping", {"n": 1}))
print(received) # [FakeMessage("test:ping", ...)]

await bus.close()

asyncio.run(main())
```

### Coroutine vs sync split

| Sync (handler registration) | Async (I/O surface) |
|---|---|
| `on(msg_type, handler)` | `connect(*args, **kwargs)` |
| `once(msg_type, handler)` | `close()` |
| `remove(msg_type, handler)` | `emit(message)` |
| `remove_all_listeners(event_name)` | `wait_for_message(message_type, timeout)` |
| | `wait_for_response(message, reply_type, timeout)` |

### Key Methods

| Method | Description | Source |
|---|---|---|
| `connect()` | No-op; sets `connected_event` and `started_running = True` | `fakebus.py:409` |
| `close()` | Clears `connected_event`, calls `on_close()` | `fakebus.py:418` |
| `emit(message)` | Injects session, dispatches to `pyee` emitter | `fakebus.py:426` |
| `wait_for_message(message_type, timeout)` | Awaits a single message of that type | `fakebus.py:489` |
| `wait_for_response(message, reply_type, timeout)` | Emits a message and awaits the reply | `fakebus.py:513` |
| `create_client()` | Returns `self` (backwards-compat shim) | `fakebus.py:543` |
| `run_forever()` | Sets `started_running = True` (backwards-compat shim) | `fakebus.py:546` |
| `run_in_thread()` | Calls `run_forever()` (backwards-compat shim) | `fakebus.py:549` |

### Session Handling

Session injection side effects are identical to `FakeBus`: `emit()` populates `message.context["session"]` from `SessionManager`, and `on_message()` feeds incoming messages back through `Session.from_message()` / `SessionManager.update()`. Both imports are lazy so the class works without `ovos-bus-client` installed.

---

## `FakeMessage`
Expand Down
4 changes: 2 additions & 2 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Shared utility library used by all OVOS components. Provides logging, process li
|---|---|
| `ovos_utils.log` | `LOG` — OVOS-wide logging class with optional file rotation |
| `ovos_utils.process_utils` | `ProcessStatus`, `RuntimeRequirements`, `PIDLock`, `MonotonicEvent` |
| `ovos_utils.fakebus` | `FakeBus`, `FakeMessage` — in-process bus for testing without a live WebSocket |
| `ovos_utils.fakebus` | `FakeBus`, `AsyncFakeBus`, `FakeMessage` — in-process bus for testing without a live WebSocket |
| `ovos_utils.events` | `EventContainer`, `EventSchedulerInterface`, handler wrappers |
| `ovos_utils.file_utils` | Resource resolution, vocab loading, `FileWatcher` |
| `ovos_utils.network_utils` | `get_ip()`, `is_connected_dns()`, `is_connected_http()`, `check_captive_portal()` |
Expand Down Expand Up @@ -60,6 +60,6 @@ pip install ovos-utils

- [Logging](log.md) — `LOG`, `init_service_logger()`, `log_deprecation()`, `deprecated` decorator
- [Process Utilities](process-utils.md) — `ProcessStatus`, `RuntimeRequirements`, `PIDLock`, `MonotonicEvent`
- [FakeBus](fakebus.md) — `FakeBus`, `FakeMessage` — in-process message bus for testing
- [FakeBus](fakebus.md) — `FakeBus`, `AsyncFakeBus`, `FakeMessage` — in-process message bus for testing
- [Events](events.md) — `EventContainer`, `EventSchedulerInterface`, handler wrappers
- [Utilities](utilities.md) — file, network, sound, threading, XDG helpers
Empty file added ovos_logs_console_script
Empty file.
59 changes: 25 additions & 34 deletions ovos_utils/bracket_expansion.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,43 +2,34 @@
import re
from typing import List, Dict
import warnings
from ovos_utils.log import deprecated

from ovos_spec_tools import expand as _spec_expand

def expand_template(template: str) -> List[str]:
def expand_optional(text):
"""Replace [optional] with two options: one with and one without."""
return re.sub(r"\[([^\[\]]+)\]", lambda m: f"({m.group(1)}|)", text)

def expand_alternatives(text):
"""Expand (alternative|choices) into a list of choices."""
parts = []
for segment in re.split(r"(\([^\(\)]+\))", text):
if segment.startswith("(") and segment.endswith(")"):
options = segment[1:-1].split("|")
parts.append(options)
else:
parts.append([segment])
return itertools.product(*parts)

def fully_expand(texts):
"""Iteratively expand alternatives until all possibilities are covered."""
result = set(texts)
while True:
expanded = set()
for text in result:
options = list(expand_alternatives(text))
expanded.update(["".join(option).strip() for option in options])
if expanded == result: # No new expansions found
break
result = expanded
return sorted(result) # Return a sorted list for consistency
from ovos_utils.log import deprecated
from ovos_utils.version import VERSION_MAJOR

# Expand optional items first
template = expand_optional(template)

# Fully expand all combinations of alternatives
return fully_expand([template])
@deprecated("import 'expand' from 'ovos_spec_tools' instead",
f"{VERSION_MAJOR + 1}.0.0")
def expand_template(template: str) -> List[str]:
"""Expand a sentence template to its sample set.

Resolves ``(a|b)`` alternatives and ``[optional]`` segments; named
``{slot}`` placeholders are carried through unchanged. The samples are
returned sorted.

.. deprecated::
Import :func:`expand` from ``ovos_spec_tools`` directly — it is the
single conformant OVOS-INTENT-1 expander, and what this now delegates
to. A template the specification rejects as malformed (a single-branch
group, an empty sample, a slot-only template, …) raises
:class:`ovos_spec_tools.MalformedTemplate`.
"""
# stacklevel=3: warn() -> expand_template body -> @deprecated wrapper -> caller
warnings.warn("expand_template is deprecated; import 'expand' from "
"'ovos_spec_tools' instead",
DeprecationWarning, stacklevel=3)
return sorted(_spec_expand(template))


def expand_slots(template: str, slots: Dict[str, List[str]]) -> List[str]:
Expand All @@ -53,7 +44,7 @@ def expand_slots(template: str, slots: Dict[str, List[str]]) -> List[str]:
list[str]: A list of all expanded combinations.
"""
# Expand alternatives and optional components
base_expansions = expand_template(template)
base_expansions = sorted(_spec_expand(template))

# Process slots
all_sentences = []
Expand Down
29 changes: 26 additions & 3 deletions ovos_utils/dialog.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,31 @@
import os
import random
import re
import warnings
from os.path import join
from pathlib import Path
from typing import Optional

from ovos_utils.bracket_expansion import expand_template
from ovos_spec_tools import expand

from ovos_utils.file_utils import resolve_resource_file
from ovos_utils.lang import translate_word
from ovos_utils.log import LOG, log_deprecation
from ovos_utils.log import LOG, deprecated, log_deprecation
from ovos_utils.version import VERSION_MAJOR


class MustacheDialogRenderer:
"""A dialog template renderer based on the mustache templating language."""

@deprecated("use the OVOS-INTENT-2 §4.2 dialog renderer in "
"'ovos_spec_tools' ('render' / 'DialogRenderer')",
f"{VERSION_MAJOR + 1}.0.0")
def __init__(self):
warnings.warn(
"MustacheDialogRenderer is deprecated; use the OVOS-INTENT-2 §4.2 "
"dialog renderer in 'ovos_spec_tools' ('render' / "
"'DialogRenderer')",
DeprecationWarning, stacklevel=3)
self.templates = {}
self.recent_phrases = []

Expand Down Expand Up @@ -92,7 +103,7 @@ def render(self, template_name, context=None, index=None):
line = template_functions[index % len(template_functions)]
# Replace {key} in line with matching values from context
line = line.format(**context)
line = random.choice(expand_template(line))
line = random.choice(sorted(expand(line)))

# Here's where we keep track of what we've said recently. Remember,
# this is by line in the .dialog file, not by exact phrase
Expand All @@ -104,6 +115,8 @@ def render(self, template_name, context=None, index=None):
return line


@deprecated("use 'ovos_spec_tools.LocaleResources' to load .dialog resources",
f"{VERSION_MAJOR + 1}.0.0")
def load_dialogs(dialog_dir: str,
renderer: Optional[MustacheDialogRenderer] = None) -> \
MustacheDialogRenderer:
Expand All @@ -116,6 +129,10 @@ def load_dialogs(dialog_dir: str,
Returns:
a loaded instance of a dialog renderer
"""
warnings.warn(
"load_dialogs is deprecated; use 'ovos_spec_tools.LocaleResources' "
"to load .dialog resources",
DeprecationWarning, stacklevel=3)
if renderer is None:
renderer = MustacheDialogRenderer()

Expand All @@ -132,6 +149,8 @@ def load_dialogs(dialog_dir: str,
return renderer


@deprecated("use the OVOS-INTENT-2 §4.2 dialog renderer in 'ovos_spec_tools' "
"('render' / 'DialogRenderer')", f"{VERSION_MAJOR + 1}.0.0")
def get_dialog(phrase: str, lang: str = None,
context: Optional[dict] = None) -> str:
"""
Expand All @@ -149,6 +168,10 @@ def get_dialog(phrase: str, lang: str = None,
str: a randomized and/or translated version of the phrase
"""

warnings.warn(
"get_dialog is deprecated; use the OVOS-INTENT-2 §4.2 dialog renderer "
"in 'ovos_spec_tools' ('render' / 'DialogRenderer')",
DeprecationWarning, stacklevel=3)
if not lang:
log_deprecation("Expected a string lang and got None.", "0.1.0")
try:
Expand Down
Loading
Loading