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 venv
start_moonraker start_moonraker
*.env *.env
*.egg-info
.pdm-python .pdm-python
build build
dist dist
share share
.vscode
.mypy_cache
.pdm-build
.pytest_cache

View file

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

View file

@ -9,9 +9,7 @@ import asyncio
import logging import logging
import re import re
import contextlib import contextlib
import base64
import tornado.websocket as tornado_ws import tornado.websocket as tornado_ws
from tornado.httpclient import HTTPRequest
from tornado import version_info as tornado_version from tornado import version_info as tornado_version
from ..common import RequestType, HistoryFieldData from ..common import RequestType, HistoryFieldData
from ..utils import json_wrapper as jsonw from ..utils import json_wrapper as jsonw
@ -88,23 +86,6 @@ class SpoolManager:
self.spoolman_url = f"{scheme}://{host}/api" self.spoolman_url = f"{scheme}://{host}/api"
self.ws_url = f"{ws_scheme}://{host}/api/v1/spool" 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): def _register_notifications(self):
self.server.register_notification("spoolman:active_spool_set") self.server.register_notification("spoolman:active_spool_set")
self.server.register_notification("spoolman:spoolman_status_changed") self.server.register_notification("spoolman:spoolman_status_changed")
@ -149,10 +130,10 @@ class SpoolManager:
if log_connect: if log_connect:
logging.info(f"Connecting To Spoolman: {self.ws_url}") logging.info(f"Connecting To Spoolman: {self.ws_url}")
log_connect = False log_connect = False
request = HTTPRequest(url=self.ws_url, headers=self.http_headers, connect_timeout=5.)
try: try:
self.spoolman_ws = await tornado_ws.websocket_connect( 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. ping_interval=None if tornado_version < (6, 5) else 20.
) )
setattr(self.spoolman_ws, "on_ping", self._on_ws_ping) setattr(self.spoolman_ws, "on_ping", self._on_ws_ping)
@ -236,7 +217,7 @@ class SpoolManager:
if self.spool_id is not None: if self.spool_id is not None:
response = await self.http_client.get( response = await self.http_client.get(
f"{self.spoolman_url}/v1/spool/{self.spool_id}", 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: if response.status_code == 404:
logging.info(f"Spool ID {self.spool_id} not found, setting to None") 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( response = await self.http_client.request(
method="PUT", method="PUT",
url=f"{self.spoolman_url}/v1/spool/{spool_id}/use", url=f"{self.spoolman_url}/v1/spool/{spool_id}/use",
body={"use_length": used_length}, body={"use_length": used_length}
headers=self.http_headers
) )
if response.has_error(): if response.has_error():
if response.status_code == 404: if response.status_code == 404:
@ -390,7 +370,6 @@ class SpoolManager:
method=method, method=method,
url=full_url, url=full_url,
body=body, body=body,
headers=self.http_headers
) )
if not use_v2_response: if not use_v2_response:
response.raise_for_status() response.raise_for_status()

View file

@ -392,9 +392,6 @@ class PackageKitTransaction:
summary: str summary: str
) -> None: ) -> None:
info = PkEnum.Info.from_index(info_code & 0xFFFF) 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: if self._role in self.GET_PKG_ROLES:
pkg_data = { pkg_data = {
'package_id': package_id, 'package_id': package_id,

View file

@ -4,7 +4,6 @@
# #
# This file may be distributed under the terms of the GNU GPLv3 license # This file may be distributed under the terms of the GNU GPLv3 license
from __future__ import annotations from __future__ import annotations
import os
import shlex import shlex
import re import re
import pathlib import pathlib
@ -62,22 +61,15 @@ class SysDepsParser:
version = distro_info.get("distro_version") version = distro_info.get("distro_version")
if version: if version:
self.distro_version = _convert_version(version) self.distro_version = _convert_version(version)
self.vendor: str = os.getenv("MOONRAKER_VENDOR", "") self.vendor: str = ""
if not self.vendor and pathlib.Path("/etc/rpi-issue").is_file(): if pathlib.Path("/etc/rpi-issue").is_file():
self.vendor = "raspberry-pi" 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: def _parse_spec(self, full_spec: str) -> str | None:
parts = full_spec.split(";", maxsplit=1) 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: if len(parts) == 1:
return pkg_name return full_spec
pkg_name = parts[0].strip()
expressions = re.split(r"( and | or )", parts[1].strip()) expressions = re.split(r"( and | or )", parts[1].strip())
if not len(expressions) & 1: if not len(expressions) & 1:
# There should always be an odd number of expressions. Each # 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"}, {name = "Eric Callahan", email = "arksine.code@gmail.com"},
] ]
dependencies = [ dependencies = [
"tornado>=6.2.0, <=6.5.4", "tornado>=6.2.0, <=6.5.1",
"pyserial==3.4", "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", "streaming-form-data>=1.11.0, <=1.19.1",
"distro==1.9.0", "distro==1.9.0",
"inotify-simple==2.0.1", "inotify-simple==1.3.5",
"libnacl==2.1.0", "libnacl==2.1.0",
"paho-mqtt==2.1.0", "paho-mqtt==1.6.1",
"zeroconf>=0.131.0, <=0.148.0", "zeroconf==0.131.0",
"preprocess-cancellation==0.2.1", "preprocess-cancellation==0.2.1",
"jinja2==3.1.6", "jinja2==3.1.5",
"dbus-fast>=2.21.3, <=3.1.2", "dbus-fast>=2.21.3, <=2.44.1",
"apprise>=1.9.3, <=1.9.6", "apprise==1.9.2",
"ldap3==2.9.1", "ldap3==2.9.1",
"python-periphery==2.4.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" requires-python = ">=3.7"
readme = "README.md" readme = "README.md"
@ -37,8 +38,6 @@ classifiers = [
"Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14"
] ]
[project.urls] [project.urls]
@ -54,7 +53,7 @@ speedups = [
"msgspec>=0.18.4 ; python_version>='3.8'", "msgspec>=0.18.4 ; python_version>='3.8'",
"uvloop>=0.17.0" "uvloop>=0.17.0"
] ]
dev = ["pre-commit", "build", "mypy", "ruff", "twine"] dev = ["pre-commit"]
[tool.pdm.version] [tool.pdm.version]
source = "scm" source = "scm"

View file

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

View file

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

View file

@ -1,18 +1,19 @@
# Python dependencies for Moonraker # Python dependencies for Moonraker
--find-links=python_wheels --find-links=python_wheels
tornado>=6.2.0, <=6.5.4 tornado>=6.2.0, <=6.5.1
pyserial==3.4 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 streaming-form-data>=1.11.0, <=1.19.1
distro==1.9.0 distro==1.9.0
inotify-simple==2.0.1 inotify-simple==1.3.5
libnacl==2.1.0 libnacl==2.1.0
paho-mqtt==2.1.0 paho-mqtt==1.6.1
zeroconf>=0.131.0, <=0.148.0 zeroconf==0.131.0
preprocess-cancellation==0.2.1 preprocess-cancellation==0.2.1
jinja2==3.1.6 jinja2==3.1.5
dbus-fast>=2.21.3, <=3.1.2 dbus-fast>=2.21.3, <=2.44.1
apprise>=1.9.3, <=1.9.6 apprise==1.9.2
ldap3==2.9.1 ldap3==2.9.1
python-periphery==2.4.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'