Compare commits

..

No commits in common. "3062378a7bbf8f560731088a8d42a15da10e53da" and "b3f9566b8b8863ec85a00ce424d77c8e19576c44" have entirely different histories.

12 changed files with 145 additions and 185 deletions

51
.github/workflows/publish_assets.yaml vendored Normal file
View file

@ -0,0 +1,51 @@
# CI Code for generating and publishing beta assets
name: publish_assets
on:
release:
types: [published]
jobs:
generate_assets:
runs-on: ubuntu-latest
steps:
- name: Checkout Moonraker
uses: actions/checkout@v2
with:
fetch-depth: 0
ref: ${{ github.ref }}
path: moonraker
- name: Checkout Klipper
uses: actions/checkout@v2
with:
fetch-depth: 0
repository: Klipper3d/klipper
path: klipper
- name: Build Beta Assets
if: ${{ github.event.release.prerelease }}
run: >
./moonraker/scripts/build-zip-release.sh -b
-o ${{ github.workspace }}
-k ${{ github.workspace }}/klipper
- name: Build Stable Assets
if: ${{ !github.event.release.prerelease }}
run: >
./moonraker/scripts/build-zip-release.sh
-o ${{ github.workspace }}
-k ${{ github.workspace }}/klipper
- name: Upload assets
run: |
cd moonraker
gh release upload ${{ env.TAG }} ${{ env.FILES }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
FILES: >
${{ github.workspace }}/moonraker.zip
${{ github.workspace }}/klipper.zip
${{ github.workspace }}/RELEASE_INFO
${{ github.workspace }}/COMMIT_LOG
TAG: ${{ github.event.release.tag_name }}

6
.gitignore vendored
View file

@ -8,13 +8,7 @@ __pycache__/
venv
start_moonraker
*.env
*.egg-info
.pdm-python
build
dist
share
.vscode
.mypy_cache
.pdm-build
.pytest_cache

View file

@ -21,10 +21,6 @@ from ..common import (
KlippyState
)
from ..utils import json_wrapper as jsonw
try:
from paho.mqtt.reasoncodes import ReasonCode
except ImportError:
from paho.mqtt.reasoncodes import ReasonCodes as ReasonCode
# Annotation imports
from typing import (
@ -45,7 +41,6 @@ if TYPE_CHECKING:
from ..common import JsonRPC, APIDefinition
from ..eventloop import FlexTimer
from .klippy_apis import KlippyAPI
from paho.mqtt.properties import Properties
FlexCallback = Callable[[bytes], Optional[Coroutine]]
RPCCallback = Callable[..., Coroutine]
@ -78,7 +73,7 @@ class ExtPahoClient(paho_mqtt.Client):
"remaining_count": [],
"remaining_mult": 1,
"remaining_length": 0,
"packet": b"", # type: ignore
"packet": b"",
"to_process": 0,
"pos": 0
}
@ -105,7 +100,7 @@ class ExtPahoClient(paho_mqtt.Client):
self._last_msg_out = paho_mqtt.time_func()
self._ping_t = 0
self._state = paho_mqtt.mqtt_cs_new # type: ignore
self._state = paho_mqtt.mqtt_cs_new
self._sock_close()
@ -154,16 +149,16 @@ class ExtPahoClient(paho_mqtt.Client):
if self._transport == "websockets":
sock.settimeout(self._keepalive)
sock = paho_mqtt.WebsocketWrapper( # type: ignore
sock = paho_mqtt.WebsocketWrapper(
sock, self._host, self._port, self._ssl,
self._websocket_path, self._websocket_extra_headers
) # type: ignore
self._sock = sock # type: ignore
self._sock = sock
assert self._sock is not None
self._sock.setblocking(False)
self._registered_write = False
self._call_socket_open() # type: ignore
self._call_socket_open()
return self._send_connect(self._keepalive)
@ -238,7 +233,7 @@ class BrokerAckLogger:
def __call__(self, fut: asyncio.Future) -> None:
if self.action == "subscribe":
res: Union[List[int], List[ReasonCode]]
res: Union[List[int], List[paho_mqtt.ReasonCodes]]
res = fut.result()
log_msg = "MQTT Subscriptions Acknowledged"
if len(res) != len(self.topics):
@ -248,7 +243,7 @@ class BrokerAckLogger:
else:
for topic, qos in zip(self.topics, res):
log_msg += f"\n Topic: {topic} | "
if isinstance(qos, ReasonCode):
if isinstance(qos, paho_mqtt.ReasonCodes):
log_msg += qos.getName()
else:
log_msg += f"Granted QoS {qos}"
@ -270,43 +265,40 @@ class AIOHelper:
self.client.on_socket_open = self._on_socket_open
self.client.on_socket_close = self._on_socket_close
self.client._on_socket_register_write = self._on_socket_register_write
self.client._on_socket_unregister_write = self._on_socket_unregister_write
self.client._on_socket_unregister_write = \
self._on_socket_unregister_write
self.misc_task: Optional[asyncio.Task] = None
def _on_socket_open(
self,
def _on_socket_open(self,
client: paho_mqtt.Client,
userdata: Any,
sock: socket.socket | paho_mqtt.SocketLike
sock: socket.socket
) -> None:
logging.info("MQTT Socket Opened")
self.loop.add_reader(sock, client.loop_read)
self.misc_task = self.loop.create_task(self.misc_loop())
def _on_socket_close(
self,
def _on_socket_close(self,
client: paho_mqtt.Client,
userdata: Any,
sock: socket.socket | paho_mqtt.SocketLike
sock: socket.socket
) -> None:
logging.info("MQTT Socket Closed")
self.loop.remove_reader(sock)
if self.misc_task is not None:
self.misc_task.cancel()
def _on_socket_register_write(
self,
def _on_socket_register_write(self,
client: paho_mqtt.Client,
userdata: Any,
sock: socket.socket | paho_mqtt.SocketLike
sock: socket.socket
) -> None:
self.loop.add_writer(sock, client.loop_write)
def _on_socket_unregister_write(
self,
def _on_socket_unregister_write(self,
client: paho_mqtt.Client,
userdata: Any,
sock: socket.socket | paho_mqtt.SocketLike
sock: socket.socket
) -> None:
self.loop.remove_writer(sock)
@ -362,9 +354,7 @@ class MQTTClient(APITransport):
config.getboolean("publish_split_status", False)
client_id: str = config.get("client_id", "")
if PAHO_MQTT_VERSION < (2, 0):
self.client = ExtPahoClient(
client_id, protocol=self.protocol # type: ignore
)
self.client = ExtPahoClient(client_id, protocol=self.protocol)
else:
self.client = ExtPahoClient(
paho_mqtt.CallbackAPIVersion.VERSION1, client_id, # type: ignore
@ -482,7 +472,7 @@ class MQTTClient(APITransport):
self._publish_status_update(payload, self.last_status_time)
def _on_message(self,
client: str | paho_mqtt.Client,
client: str,
user_data: Any,
message: paho_mqtt.MQTTMessage
) -> None:
@ -501,8 +491,8 @@ class MQTTClient(APITransport):
client: paho_mqtt.Client,
user_data: Any,
flags: Dict[str, Any],
reason_code: Union[int, ReasonCode],
properties: Optional[Properties] = None
reason_code: Union[int, paho_mqtt.ReasonCodes],
properties: Optional[paho_mqtt.Properties] = None
) -> None:
logging.info("MQTT Client Connected")
if reason_code == 0:
@ -531,7 +521,7 @@ class MQTTClient(APITransport):
client: paho_mqtt.Client,
user_data: Any,
reason_code: int,
properties: Optional[Properties] = None
properties: Optional[paho_mqtt.Properties] = None
) -> None:
if self.disconnect_evt is not None:
self.disconnect_evt.set()
@ -557,8 +547,8 @@ class MQTTClient(APITransport):
client: paho_mqtt.Client,
user_data: Any,
msg_id: int,
flex: Union[List[int], List[ReasonCode]],
properties: Optional[Properties] = None
flex: Union[List[int], List[paho_mqtt.ReasonCodes]],
properties: Optional[paho_mqtt.Properties] = None
) -> None:
sub_fut = self.pending_acks.pop(msg_id, None)
if sub_fut is not None and not sub_fut.done():
@ -568,8 +558,8 @@ class MQTTClient(APITransport):
client: paho_mqtt.Client,
user_data: Any,
msg_id: int,
properties: Optional[Properties] = None,
reasoncodes: Optional[ReasonCode] = None
properties: Optional[paho_mqtt.Properties] = None,
reasoncodes: Optional[paho_mqtt.ReasonCodes] = None
) -> None:
unsub_fut = self.pending_acks.pop(msg_id, None)
if unsub_fut is not None and not unsub_fut.done():

View file

@ -9,9 +9,7 @@ import asyncio
import logging
import re
import contextlib
import base64
import tornado.websocket as tornado_ws
from tornado.httpclient import HTTPRequest
from tornado import version_info as tornado_version
from ..common import RequestType, HistoryFieldData
from ..utils import json_wrapper as jsonw
@ -88,23 +86,6 @@ class SpoolManager:
self.spoolman_url = f"{scheme}://{host}/api"
self.ws_url = f"{ws_scheme}://{host}/api/v1/spool"
headers_raw = config.get("headers", "")
self.http_headers = {}
for c in headers_raw.split(";"):
c = c.strip()
if not c:
continue
c_parts = c.split(":", 1)
if len(c_parts) != 2:
raise config.error(f"Section [spoolman], Option headers: {c}: Invalid header format")
self.http_headers[c_parts[0]] = c_parts[1]
username = config.get("http_username", None)
password = config.get("http_password", None)
if username and password:
creds = base64.b64encode(f"{username}:{password}".encode()).decode()
self.http_headers["Authorization"] = "Basic " + creds
def _register_notifications(self):
self.server.register_notification("spoolman:active_spool_set")
self.server.register_notification("spoolman:spoolman_status_changed")
@ -149,10 +130,10 @@ class SpoolManager:
if log_connect:
logging.info(f"Connecting To Spoolman: {self.ws_url}")
log_connect = False
request = HTTPRequest(url=self.ws_url, headers=self.http_headers, connect_timeout=5.)
try:
self.spoolman_ws = await tornado_ws.websocket_connect(
request,
self.ws_url,
connect_timeout=5.,
ping_interval=None if tornado_version < (6, 5) else 20.
)
setattr(self.spoolman_ws, "on_ping", self._on_ws_ping)
@ -236,7 +217,7 @@ class SpoolManager:
if self.spool_id is not None:
response = await self.http_client.get(
f"{self.spoolman_url}/v1/spool/{self.spool_id}",
connect_timeout=1., request_timeout=2., headers=self.http_headers
connect_timeout=1., request_timeout=2.
)
if response.status_code == 404:
logging.info(f"Spool ID {self.spool_id} not found, setting to None")
@ -327,8 +308,7 @@ class SpoolManager:
response = await self.http_client.request(
method="PUT",
url=f"{self.spoolman_url}/v1/spool/{spool_id}/use",
body={"use_length": used_length},
headers=self.http_headers
body={"use_length": used_length}
)
if response.has_error():
if response.status_code == 404:
@ -390,7 +370,6 @@ class SpoolManager:
method=method,
url=full_url,
body=body,
headers=self.http_headers
)
if not use_v2_response:
response.raise_for_status()

View file

@ -392,9 +392,6 @@ class PackageKitTransaction:
summary: str
) -> None:
info = PkEnum.Info.from_index(info_code & 0xFFFF)
severity = PkEnum.Info.from_index((info_code >> 16) & 0xFFFF)
if info == PkEnum.Info.UNKNOWN:
info = severity
if self._role in self.GET_PKG_ROLES:
pkg_data = {
'package_id': package_id,

View file

@ -4,7 +4,6 @@
#
# This file may be distributed under the terms of the GNU GPLv3 license
from __future__ import annotations
import os
import shlex
import re
import pathlib
@ -62,22 +61,15 @@ class SysDepsParser:
version = distro_info.get("distro_version")
if version:
self.distro_version = _convert_version(version)
self.vendor: str = os.getenv("MOONRAKER_VENDOR", "")
if not self.vendor and pathlib.Path("/etc/rpi-issue").is_file():
self.vendor: str = ""
if pathlib.Path("/etc/rpi-issue").is_file():
self.vendor = "raspberry-pi"
exclusions = os.getenv("MOONRAKER_EXCLUDED_PKGS", "")
self.exclusions: List[str] = [
excl.strip() for excl in exclusions.split() if excl.strip()
]
def _parse_spec(self, full_spec: str) -> str | None:
parts = full_spec.split(";", maxsplit=1)
pkg_name = parts[0].strip()
if pkg_name in self.exclusions or not pkg_name:
logging.info(f"Package '{full_spec}' excluded by environment")
return None
if len(parts) == 1:
return pkg_name
return full_spec
pkg_name = parts[0].strip()
expressions = re.split(r"( and | or )", parts[1].strip())
if not len(expressions) & 1:
# There should always be an odd number of expressions. Each

View file

@ -6,22 +6,23 @@ authors = [
{name = "Eric Callahan", email = "arksine.code@gmail.com"},
]
dependencies = [
"tornado>=6.2.0, <=6.5.4",
"tornado>=6.2.0, <=6.5.1",
"pyserial==3.4",
"pillow>=9.5.0, <=12.1.0",
"pillow>=9.5.0, <=11.1.0",
"streaming-form-data>=1.11.0, <=1.19.1",
"distro==1.9.0",
"inotify-simple==2.0.1",
"inotify-simple==1.3.5",
"libnacl==2.1.0",
"paho-mqtt==2.1.0",
"zeroconf>=0.131.0, <=0.148.0",
"paho-mqtt==1.6.1",
"zeroconf==0.131.0",
"preprocess-cancellation==0.2.1",
"jinja2==3.1.6",
"dbus-fast>=2.21.3, <=3.1.2",
"apprise>=1.9.3, <=1.9.6",
"jinja2==3.1.5",
"dbus-fast>=2.21.3, <=2.44.1",
"apprise==1.9.2",
"ldap3==2.9.1",
"python-periphery==2.4.1",
"importlib_metadata>=6.7.0, <=8.7.1"
"importlib_metadata==6.7.0 ; python_version=='3.7'",
"importlib_metadata==8.2.0 ; python_version>='3.8'"
]
requires-python = ">=3.7"
readme = "README.md"
@ -37,8 +38,6 @@ classifiers = [
"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"
]
[project.urls]
@ -54,7 +53,7 @@ speedups = [
"msgspec>=0.18.4 ; python_version>='3.8'",
"uvloop>=0.17.0"
]
dev = ["pre-commit", "build", "mypy", "ruff", "twine"]
dev = ["pre-commit"]
[tool.pdm.version]
source = "scm"

View file

@ -12,8 +12,6 @@ LOG_PATH="${MOONRAKER_LOG_PATH}"
DATA_PATH="${MOONRAKER_DATA_PATH}"
INSTANCE_ALIAS="${MOONRAKER_ALIAS:-moonraker}"
SPEEDUPS="${MOONRAKER_SPEEDUPS:-n}"
DEV_INSTALL="${MOONRAKER_DEV_INSTALL:-n}"
PY_INST_TYPE="${MOONRAKER_PYTHON_INSTALL_TYPE:-venv}"
SERVICE_VERSION="1"
DISTRIBUTION=""
DISTRO_VERSION=""
@ -27,14 +25,6 @@ if [ ! -z "${MOONRAKER_FORCE_DEFAULTS}" ]; then
FORCE_SYSTEM_INSTALL=$MOONRAKER_FORCE_DEFAULTS
fi
# Check if this is a dev container, apply dev defaults if not set by environment
if [ "${MOONRAKER_VENDOR}" = "vscode-dev" ]; then
echo "VSCode Dev Container detected..."
[ -z "${MOONRAKER_DEV_INSTALL}" ] && DEV_INSTALL="y"
[ -z "${MOONRAKER_PYTHON_INSTALL_TYPE}" ] && PY_INST_TYPE="user"
[ -z "${MOONRAKER_DISABLE_SYSTEMCTL}" ] && DISABLE_SYSTEMCTL="y"
fi
# Force script to exit if an error occurs
set -e
@ -44,7 +34,6 @@ SRCDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )"/.. && pwd )"
# Determine if Moonraker is to be installed from source
if [ -f "${SRCDIR}/moonraker/__init__.py" ]; then
echo "Installing from Moonraker source..."
cd $SRCDIR
IS_SRC_DIST="y"
fi
@ -59,7 +48,6 @@ detect_distribution() {
# *** AUTO GENERATED OS PACKAGE SCRIPT START ***
get_pkgs_script=$(cat << EOF
from __future__ import annotations
import os
import shlex
import re
import pathlib
@ -113,22 +101,15 @@ class SysDepsParser:
version = distro_info.get("distro_version")
if version:
self.distro_version = _convert_version(version)
self.vendor: str = os.getenv("MOONRAKER_VENDOR", "")
if not self.vendor and pathlib.Path("/etc/rpi-issue").is_file():
self.vendor: str = ""
if pathlib.Path("/etc/rpi-issue").is_file():
self.vendor = "raspberry-pi"
exclusions = os.getenv("MOONRAKER_EXCLUDED_PKGS", "")
self.exclusions: List[str] = [
excl.strip() for excl in exclusions.split() if excl.strip()
]
def _parse_spec(self, full_spec: str) -> str | None:
parts = full_spec.split(";", maxsplit=1)
pkg_name = parts[0].strip()
if pkg_name in self.exclusions or not pkg_name:
logging.info(f"Package '{full_spec}' excluded by environment")
return None
if len(parts) == 1:
return pkg_name
return full_spec
pkg_name = parts[0].strip()
expressions = re.split(r"( and | or )", parts[1].strip())
if not len(expressions) & 1:
logging.info(
@ -156,7 +137,7 @@ class SysDepsParser:
continue
elif last_logical_op is None:
logging.info(
f"Requirement specifier contains two sequential expressions "
f"Requirement specifier contains two seqential expressions "
f"without a logical operator: {full_spec}")
return None
dep_parts = re.split(r"(==|!=|<=|>=|<|>)", exp.strip())
@ -289,12 +270,6 @@ install_packages()
# Step 4: Create python virtual environment
create_virtualenv()
{
if [ $PY_INST_TYPE = "system" ]; then
pip_inst="pip install"
elif [ $PY_INST_TYPE = "user" ]; then
pip_inst="pip install --user"
else
pip_inst="${PYTHONDIR}/bin/pip install"
report_status "Installing python virtual environment..."
# If venv exists and user prompts a rebuild, then do so
@ -304,37 +279,29 @@ create_virtualenv()
fi
if [ ! -d ${PYTHONDIR} ]; then
virtualenv -p python3 ${PYTHONDIR}
virtualenv -p /usr/bin/python3 ${PYTHONDIR}
#GET_PIP="${HOME}/get-pip.py"
#curl https://bootstrap.pypa.io/pip/3.6/get-pip.py -o ${GET_PIP}
#${PYTHONDIR}/bin/python ${GET_PIP}
#rm ${GET_PIP}
fi
fi
echo "Using pip install command '${pip_inst}'..."
# Install/update dependencies
export SKIP_CYTHON=1
if [ $IS_SRC_DIST = "y" ]; then
report_status "Installing Moonraker python dependencies..."
$pip_inst -r ${SRCDIR}/scripts/moonraker-requirements.txt
${PYTHONDIR}/bin/pip install -r ${SRCDIR}/scripts/moonraker-requirements.txt
if [ $DEV_INSTALL = "y" ]; then
report_status "Installing dev requirements..."
$pip_inst -r ${SRCDIR}/scripts/moonraker-speedups.txt
$pip_inst -r ${SRCDIR}/scripts/moonraker-dev-reqs.txt
$pip_inst -r ${SRCDIR}/docs/doc-requirements.txt
elif [ $SPEEDUPS = "y" ]; then
if [ ${SPEEDUPS} = "y" ]; then
report_status "Installing Speedups..."
$pip_inst -r ${SRCDIR}/scripts/moonraker-speedups.txt
${PYTHONDIR}/bin/pip install -r ${SRCDIR}/scripts/moonraker-speedups.txt
fi
else
report_status "Installing Moonraker package via Pip..."
if [ $DEV_INSTALL = "y" ]; then
$pip_inst -U moonraker[speedups,dev]
elif [ $SPEEDUPS = "y" ]; then
$pip_inst -U moonraker[speedups]
if [ ${SPEEDUPS} = "y" ]; then
${PYTHONDIR}/bin/pip install -U moonraker[speedups]
else
$pip_inst -U moonraker
${PYTHONDIR}/bin/pip install -U moonraker
fi
fi
}
@ -382,13 +349,9 @@ EOF
# Step 6: Install startup script
install_script()
{
if [ ! -d $SYSTEMDDIR ]; then
report_status "Systemd not detected, aborting service installation"
fi
# Create systemd service file
ENV_FILE="${DATA_PATH}/systemd/moonraker.env"
if [ ! -f $ENV_FILE ] || [ $FORCE_SYSTEM_INSTALL = "y" ]; then
report_status "Creating systemd environment file ${ENV_FILE}..."
rm -f $ENV_FILE
env_vars="MOONRAKER_DATA_PATH=\"${DATA_PATH}\""
[ -n "${CONFIG_PATH}" ] && env_vars="${env_vars}\nMOONRAKER_CONFIG_PATH=\"${CONFIG_PATH}\""
@ -398,9 +361,7 @@ install_script()
echo -e $env_vars > $ENV_FILE
fi
[ -f $SERVICE_FILE ] && [ $FORCE_SYSTEM_INSTALL = "n" ] && return
report_status "Installing systemd service unit..."
python_bin="${PYTHONDIR}/bin/python"
[ $PY_INST_TYPE != "venv" ] && python_bin="python3"
report_status "Installing system start script..."
sudo groupadd -f moonraker-admin
sudo /bin/sh -c "cat > ${SERVICE_FILE}" << EOF
# systemd service file for moonraker
@ -418,7 +379,7 @@ User=$USER
SupplementaryGroups=moonraker-admin
RemainAfterExit=yes
EnvironmentFile=${ENV_FILE}
ExecStart=${python_bin} \$MOONRAKER_ARGS
ExecStart=${PYTHONDIR}/bin/python \$MOONRAKER_ARGS
Restart=always
RestartSec=10
EOF

View file

@ -1,5 +1 @@
pre-commit
build
mypy
ruff
twine

View file

@ -1,18 +1,19 @@
# Python dependencies for Moonraker
--find-links=python_wheels
tornado>=6.2.0, <=6.5.4
tornado>=6.2.0, <=6.5.1
pyserial==3.4
pillow>=9.5.0, <=12.1.0
pillow>=9.5.0, <=11.1.0
streaming-form-data>=1.11.0, <=1.19.1
distro==1.9.0
inotify-simple==2.0.1
inotify-simple==1.3.5
libnacl==2.1.0
paho-mqtt==2.1.0
zeroconf>=0.131.0, <=0.148.0
paho-mqtt==1.6.1
zeroconf==0.131.0
preprocess-cancellation==0.2.1
jinja2==3.1.6
dbus-fast>=2.21.3, <=3.1.2
apprise>=1.9.3, <=1.9.6
jinja2==3.1.5
dbus-fast>=2.21.3, <=2.44.1
apprise==1.9.2
ldap3==2.9.1
python-periphery==2.4.1
importlib_metadata>=6.7.0, <=8.7.1
importlib_metadata==6.7.0 ; python_version=='3.7'
importlib_metadata==8.2.0 ; python_version>='3.8'