Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"]
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
Expand Down
27 changes: 20 additions & 7 deletions obniz/obniz/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import asyncio

from .libs.utils.eventloop import ensure_future, get_event_loop
from .obniz_uis import ObnizUIs

name = "obniz"
Expand All @@ -15,6 +16,7 @@ def __init__(self, id, options=None):
def repeat(self, callback, interval=100):
if self.looper:
self.looper = callback
self.repeatInterval = interval
return

self.looper = callback
Expand All @@ -28,12 +30,23 @@ def _call_on_connect(self):
self.loop()

def loop(self):
if self.looper:
prom = self.looper()
if prom:
prom()
if not self.looper:
return

ret = self.looper()
if asyncio.iscoroutine(ret):
# wait for the async callback to finish before scheduling the next run
async def _run():
await ret
self._schedule_next_loop()

asyncio.get_event_loop().call_later(1, self.loop)
ensure_future(_run())
else:
self._schedule_next_loop()

def _schedule_next_loop(self):
if self.looper:
get_event_loop().call_later(self.repeatInterval / 1000, self.loop)

def ws_on_close(self):
super().ws_on_close()
Expand All @@ -43,7 +56,7 @@ def ws_on_close(self):
def message(self, target, message):
targets = []
if type(target) is str:
targets.push(target)
targets.append(target)
else:
targets = target

Expand All @@ -66,7 +79,7 @@ def notify_to_module(self, obj):
self.error({"alert": "error", "message": msg})

if self.ondebug:
self.ondebug(obj.debug)
self.ondebug(obj["debug"])

#######################
# ReadParts
Expand Down
5 changes: 2 additions & 3 deletions obniz/obniz/libs/embeds/ble/ble_attribute_abstract.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from pyee import AsyncIOEventEmitter
import asyncio

from .ble_helper import BleHelper
from ...utils.eventloop import get_event_loop
from ...utils.util import ObnizUtil


Expand Down Expand Up @@ -119,8 +119,7 @@ def write_number(self, val, need_response=False):
# }

def read_wait(self):
# get_running_loop() function is preferred on Python >= 3.7
future = asyncio.get_event_loop().create_future()
future = get_event_loop().create_future()
def cb(params):
if params["result"] == "success":
future.set_result(params["data"])
Expand Down
8 changes: 3 additions & 5 deletions obniz/obniz/libs/embeds/switch.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import asyncio
from ..utils.eventloop import get_event_loop

class ObnizSwitch:
def __init__(self, obniz):
Expand All @@ -19,17 +19,15 @@ def add_observer(self, future):
self.observers.append(future)

def get_wait(self):
# get_running_loop() function is preferred on Python >= 3.7
future = asyncio.get_event_loop().create_future()
future = get_event_loop().create_future()
self.add_observer(future)
obj = {}
obj["switch"] = "get"
self.obniz.send(obj)
return future

def state_wait(self, is_pressed):
# get_running_loop() function is preferred on Python >= 3.7
future = asyncio.get_event_loop().create_future()
future = get_event_loop().create_future()
def on_change_for_state_wait(pressed):
def noop(*args):
pass
Expand Down
5 changes: 2 additions & 3 deletions obniz/obniz/libs/io_peripherals/ad.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import asyncio
from ..utils.eventloop import get_event_loop

class PeripheralAD:
def __init__(self, obniz, id):
Expand All @@ -23,8 +23,7 @@ def start(self, callback=None):
return self.value

def get_wait(self):
# get_running_loop() function is preferred on Python >= 3.7
future = asyncio.get_event_loop().create_future()
future = get_event_loop().create_future()
self.add_observer(future)
obj = {}
obj["ad" + str(self.id)] = {"stream": False}
Expand Down
4 changes: 2 additions & 2 deletions obniz/obniz/libs/io_peripherals/i2c.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from ..utils.eventloop import get_event_loop
from ..utils.util import ObnizUtil

import asyncio

class PeripheralI2C:
def __init__(self, obniz, id):
Expand Down Expand Up @@ -146,7 +146,7 @@ def read_wait(self, address, length):
if length > 1024:
raise Exception("i2c: data length should be under 1024 bytes")

future = asyncio.get_event_loop().create_future()
future = get_event_loop().create_future()
self.add_observer(future)
obj = {}
obj["i2c" + str(self.id)] = {"address": address, "read": length}
Expand Down
4 changes: 2 additions & 2 deletions obniz/obniz/libs/io_peripherals/io.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import asyncio
from ..utils.eventloop import get_event_loop

class PeripheralIO:
def __init__(self, obniz, id):
Expand Down Expand Up @@ -66,7 +66,7 @@ def input(self, callback):
return self.value

def input_wait(self):
future = asyncio.get_event_loop().create_future()
future = get_event_loop().create_future()
self.add_observer(future)
obj = {}
obj["io" + str(self.id)] = {"direction": "input", "stream": False}
Expand Down
5 changes: 3 additions & 2 deletions obniz/obniz/libs/io_peripherals/io_.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import asyncio
import datetime

from ..utils.eventloop import get_event_loop


class PeripheralIO_: # noqa: N801
def __init__(self, obniz):
Expand Down Expand Up @@ -53,8 +55,7 @@ def repeat_wait(self, array, repeat):
if (type(repeat) is not int):
raise Exception("please provide integer number like 1, 2, 3,,,")

# get_running_loop() function is preferred on Python >= 3.7
future = asyncio.get_event_loop().create_future()
future = get_event_loop().create_future()
name = "_repeatwait" + datetime.datetime.now().strftime("%Y%m%d%H%M%S")
if self._animation_identifier + 1 > 1000:
self._animation_identifier = 0
Expand Down
7 changes: 4 additions & 3 deletions obniz/obniz/libs/io_peripherals/spi.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import semver

from ..utils.eventloop import get_event_loop
from ..utils.util import ObnizUtil

import asyncio
import semver

class PeripheralSPI:
def __init__(self, obniz, id):
Expand Down Expand Up @@ -99,7 +100,7 @@ def write_wait(self, data):
+ ". Please update obniz firmware"
)

future = asyncio.get_event_loop().create_future()
future = get_event_loop().create_future()
self.add_observer(future)
obj = {}
obj["spi" + str(self.id)] = {"data": data, "read": True}
Expand Down
3 changes: 2 additions & 1 deletion obniz/obniz/libs/measurements/logicanalyzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ def __init__(self, obniz):

def _reset(self):
self.onmeasured = None
self.measured = None

def start(self, params):
err = ObnizUtil._required_keys(params, ["io", "interval", "duration"])
Expand Down Expand Up @@ -47,4 +48,4 @@ def notified(self, obj):
if not self.measured:
self.measured = []

self.measured.push(obj["data"])
self.measured.append(obj["data"])
33 changes: 33 additions & 0 deletions obniz/obniz/libs/utils/eventloop.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import asyncio
import warnings


def get_event_loop():
"""Return a usable event loop on all supported Python versions.

Prefers the running loop. Otherwise returns the loop registered for the
current thread, creating and registering a new one when none exists —
Python 3.14 removed the implicit creation from asyncio.get_event_loop(),
and 3.10-3.13 emit DeprecationWarning for it.
"""
try:
return asyncio.get_running_loop()
except RuntimeError:
pass

try:
with warnings.catch_warnings():
warnings.simplefilter("ignore", DeprecationWarning)
loop = asyncio.get_event_loop()
if loop.is_closed():
raise RuntimeError("event loop is closed")
return loop
except RuntimeError:
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
return loop


def ensure_future(coro):
"""Schedule a coroutine on the loop returned by get_event_loop()."""
return asyncio.ensure_future(coro, loop=get_event_loop())
19 changes: 10 additions & 9 deletions obniz/obniz/obniz_connection.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import asyncio
import inspect
import json

from pyee import AsyncIOEventEmitter
import websockets

from .__version__ import __version__
from .libs.utils.eventloop import ensure_future, get_event_loop
from .libs.utils.util import ObnizUtil

class ObnizConnection:
Expand Down Expand Up @@ -104,7 +105,7 @@ def cb(connected):
self.emitter.once("closed", lambda: cb(False))

if timeout:
asyncio.get_event_loop().call_later(timeout, lambda: cb(False))
get_event_loop().call_later(timeout, lambda: cb(False))

self.connect()

Expand Down Expand Up @@ -177,7 +178,7 @@ async def connecting():
self.error(e)
break

asyncio.ensure_future(connecting())
ensure_future(connecting())

# _connectLocal(host) {
# const url = 'ws://' + host
Expand Down Expand Up @@ -285,7 +286,7 @@ def close(self):
if self.socket.open:
# Connecting & Connected
self.connection_state = 'closing'
self.socket.close(1000, "close")
ensure_future(self.socket.close(1000, "close"))

self.clear_socket(self.socket)
self.socket = None
Expand All @@ -312,8 +313,8 @@ def _call_on_connect(self):
if should_call:
if self.onconnect:
try:
if asyncio.iscoroutinefunction(self.onconnect):
asyncio.ensure_future(self.onconnect(self))
if inspect.iscoroutinefunction(self.onconnect):
ensure_future(self.onconnect(self))
else:
self.onconnect(self)
except Exception as e:
Expand Down Expand Up @@ -348,7 +349,7 @@ def send(self, obj, options=None):
self._send_routed(send_data)

def _send_routed(self, data):
if self.socket_local and self.socket_local.on and type(data) is not str:
if self.socket_local is not None and type(data) is not str:
self.print_debug("send via local")
self.socket_local.send(data)
if self.socket_local.buffered_amount > self.bufferd_amound_warn_bytes:
Expand All @@ -358,7 +359,7 @@ def _send_routed(self, data):
return

if self.socket and self.socket.open:
asyncio.ensure_future(self.socket.send(data))
ensure_future(self.socket.send(data))
# if self.socket.buffered_amount > self.bufferd_amound_warn_bytes:
# self.warning(
# f'over {self.socket.buffered_amount} bytes queued'
Expand Down Expand Up @@ -435,7 +436,7 @@ def handle_ws_command(self, ws_obj):
self.print_debug("WS connection changed to " + server)

# close current ws immidiately
self.socket.close(1000, "close")
ensure_future(self.socket.close(1000, "close"))
self.clear_socket(self.socket)
self.socket = None

Expand Down
5 changes: 3 additions & 2 deletions obniz/obniz/obniz_system_methods.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
from random import random
from time import time
import asyncio

from .libs.utils.eventloop import get_event_loop
from .obniz_components import ObnizComponents


Expand Down Expand Up @@ -57,8 +59,7 @@ def ping_wait(self, unixtime=None, rand=None, force_global_network=None):
obj = {"system": {"ping": {"key": buf}}}

self.send(obj, {"local_connect": not force_global_network})
# get_running_loop() function is preferred on Python >= 3.7
future = asyncio.get_event_loop().create_future()
future = get_event_loop().create_future()
def cb(system_obj):
for i, b in enumerate(buf):
if b != system_obj["pong"]["key"][i]:
Expand Down
8 changes: 4 additions & 4 deletions obniz/parts/DistanceSensor/HC-SR04/__init__.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import math

from attrdict import AttrDefault

import asyncio
from obniz.obniz.libs.utils.eventloop import get_event_loop

import math

class HCSR04:
def __init__(self):
Expand Down Expand Up @@ -62,8 +63,7 @@ def anonym_func(edges):
)

def measure_wait(self):
# get_running_loop() function is preferred on Python >= 3.7
future = asyncio.get_event_loop().create_future()
future = get_event_loop().create_future()
self.measure(future=future)
return future

Expand Down
6 changes: 3 additions & 3 deletions obniz/parts/MovementSensor/Button/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from attrdict import AttrDefault

import asyncio
from obniz.obniz.libs.utils.eventloop import get_event_loop


class Button:
def __init__(self):
Expand Down Expand Up @@ -33,8 +34,7 @@ async def is_pressed_wait(self):
return ret == False

def state_wait(self, is_pressed):
# get_running_loop() function is preferred on Python >= 3.7
future = asyncio.get_event_loop().create_future()
future = get_event_loop().create_future()
def onpress(pressed):
def nothing(*args):
pass
Expand Down
2 changes: 2 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@
'Programming Language :: Python :: 3.10',
'Programming Language :: Python :: 3.11',
'Programming Language :: Python :: 3.12',
'Programming Language :: Python :: 3.13',
'Programming Language :: Python :: 3.14',
"Operating System :: OS Independent",
],
)
Loading
Loading