Skip to content
Open
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
4 changes: 2 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v2.3.0
rev: v6.0.0
hooks:
- id: check-yaml
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/psf/black
rev: 22.3.0
rev: 26.3.1
hooks:
- id: black
- repo: https://github.com/pre-commit/pre-commit-hooks
Expand Down
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
- pip install setuptools==60.9.0 # https://github.com/pypa/setuptools/issues/3293
- pip install OctoPrint # Need OctoPrint to satisfy req's of `__init__.py`
- pip install coverage coveralls
- pip install -r requirements.txt
- pip install -e .
script:
- coverage run -m unittest discover -p "*_test.py"
after_success:
Expand Down
86 changes: 86 additions & 0 deletions Taskfile.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@

# Taskfile to be used with task: https://taskfile.dev
#
# A copy of task gets automatically installed as a "develop" dependency this plugin:
#
# pip install .[develop]
# go-task --list-all
#

version: "3"

env:
LOCALES: [] # list your included locales here, e.g. ["de", "fr"]
TRANSLATIONS: "continuousprint/translations" # translations folder


tasks:
install:
desc: Installs the plugin into the current venv
cmds:
- "python -m pip install -e .[develop]"

### Build related

build:
desc: Builds sdist & wheel
cmds:
- python -m build --sdist --wheel

build-sdist:
desc: Builds sdist
cmds:
- python -m build --sdist

build-wheel:
desc: Builds wheel
cmds:
- python -m build --wheel

### Translation related

babel-new:
desc: Create a new translation for a locale
cmds:
- task: babel-extract
- pybabel init --input-file=translations/messages.pot --output-dir=translations --locale="{{ .CLI_ARGS }}"

babel-extract:
desc: Update pot file from source
cmds:
- pybabel extract --mapping-file=babel.cfg --output-file=translations/messages.pot --msgid-bugs-address=i18n@octoprint.org --copyright-holder="The OctoPrint Project" .

babel-update:
desc: Update translation files from pot file
cmds:
- for:
var: LOCALES
cmd: pybabel update --input-file=translations/messages.pot --output-dir=translations --locale={{ .ITEM }}

babel-refresh:
desc: Update translation files from source
cmds:
- task: babel-extract
- task: babel-update

babel-compile:
desc: Compile translation files
cmds:
- pybabel compile --directory=translations

babel-bundle:
desc: Bundle translations
preconditions:
- test -d {{ .TRANSLATIONS }}
cmds:
- for:
var: LOCALES
cmd: |
locale="{{ .ITEM }}"
source="translations/${locale}"
target="{{ .TRANSLATIONS }}/${locale}"

[ ! -d "${target}" ] || rm -r "${target}"

echo "Copying translations for locale ${locale} from ${source} to ${target}..."
cp -r "${source}" "${target}"
33 changes: 31 additions & 2 deletions continuousprint/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ def get_blueprint_kwargs(self):
def is_blueprint_protected(self):
return self._plugin.is_blueprint_protected()

def is_blueprint_csrf_protected(self):
return True

def get_blueprint_api_prefixes(self):
return self._plugin.get_blueprint_api_prefixes()

Expand Down Expand Up @@ -70,8 +73,6 @@ def on_after_startup(self):
self._settings.get_all_data()
)
)
self._plugin.patchCommJobReader()
self._plugin.patchComms()
self._plugin.start()

# It's possible to miss events or for some weirdness to occur in conditionals. Adding a watchdog
Expand Down Expand Up @@ -122,6 +123,9 @@ def get_template_vars(self):
def get_template_configs(self):
return TEMPLATES

def is_template_autoescaped(self):
return True

# -------------------- End TemplatePlugin ----------------

# -------------------- Begin AssetPlugin ----------------
Expand All @@ -144,6 +148,30 @@ def resume_action_handler(self, comm, line, action, *args, **kwargs):
return
self._plugin.resume_action()

def gcode_sending_handler(
self,
comm_instance,
phase,
cmd,
cmd_type,
gcode,
subcode=None,
tags=None,
*args,
**kwargs,
):
return self._plugin.gcode_sending_handler(
comm_instance,
phase,
cmd,
cmd_type,
gcode,
subcode=subcode,
tags=tags,
*args,
**kwargs,
)

def support_gjob_format(*args, **kwargs):
return dict(machinecode=dict(gjob=["gjob"]))

Expand All @@ -161,5 +189,6 @@ def __plugin_load__():
"octoprint.plugin.softwareupdate.check_config": __plugin_implementation__.get_update_information,
"octoprint.access.permissions": __plugin_implementation__.add_permissions,
"octoprint.comm.protocol.action": __plugin_implementation__.resume_action_handler,
"octoprint.comm.protocol.gcode.sending": __plugin_implementation__.gcode_sending_handler,
"octoprint.filemanager.extension_tree": __plugin_implementation__.support_gjob_format,
}
6 changes: 4 additions & 2 deletions continuousprint/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import json
from .storage import queries
from .storage.database import DEFAULT_QUEUE
from .data import CustomEvents
from .driver import Action as DA
from abc import ABC, abstractmethod

Expand Down Expand Up @@ -97,6 +96,9 @@ def cpq_permission_wrapper(*args, **kwargs):


class ContinuousPrintAPI(ABC, octoprint.plugin.BlueprintPlugin):
def is_blueprint_csrf_protected(self):
return True

@abstractmethod
def _update(self, a: DA):
pass
Expand Down Expand Up @@ -319,7 +321,7 @@ def get_queues(self):
@cpq_permission(Permission.EDITQUEUES)
def edit_queues(self):
queues = json.loads(flask.request.form.get("json"))
(absent_names, added) = queries.assignQueues(queues)
absent_names, added = queries.assignQueues(queues)
self._commit_queues(added, absent_names)
return json.dumps("OK")

Expand Down
10 changes: 5 additions & 5 deletions continuousprint/api_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import logging
from .driver import Action as DA
from unittest.mock import patch, MagicMock, call, PropertyMock
import imp
import importlib
from flask import Flask
from .api import Permission, cpq_permission
import continuousprint.api
Expand Down Expand Up @@ -44,15 +44,15 @@ def setUp(self): # , plugin, restrict):
# octoprint internal state.
def kill_patches():
patch.stopall()
imp.reload(continuousprint.api)
importlib.reload(continuousprint.api)

self.addCleanup(kill_patches)
patch(
"continuousprint.api.octoprint.server.util.flask.restricted_access",
lambda x: x,
).start()

imp.reload(continuousprint.api)
importlib.reload(continuousprint.api)
self.perm = patch("continuousprint.api.Permissions").start()
patch.object(
continuousprint.api.ContinuousPrintAPI, "__abstractmethods__", set()
Expand Down Expand Up @@ -94,8 +94,8 @@ def test_role_access_denied(self):
num_handlers_tested = len(set([tc[1] for tc in testcases]))
handlers = [
f
for f in dir(self.api)
if hasattr(getattr(self.api, f), "_blueprint_rules")
for f in dir(type(self.api))
if hasattr(getattr(type(self.api), f), "_blueprint_rules")
]
self.assertEqual(num_handlers_tested, len(handlers))

Expand Down
20 changes: 10 additions & 10 deletions continuousprint/data/data_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def test_referential_integrity(self):
self.assertNotEqual(GCODE_SCRIPTS.get(v["defaults"][s]), None)


def test_preprocessor(name):
def _preprocessor_case(name):
def preprocessor_decorator(func):
def testcase(self):
def runInterp(symtable):
Expand Down Expand Up @@ -81,17 +81,17 @@ def test_has_all_fields(self):
sorted(["body", "name"]),
)

@test_preprocessor("If the bed temperature is >40C")
@_preprocessor_case("If the bed temperature is >40C")
def test_bed_temp(self, pp):
self.assertEqual(pp(dict(current=dict(bed_temp=40)))[0], False)
self.assertEqual(pp(dict(current=dict(bed_temp=41)))[0], True)

@test_preprocessor('If print filename ends in "_special.gcode"')
@_preprocessor_case('If print filename ends in "_special.gcode"')
def test_filename_special(self, pp):
self.assertEqual(pp(dict(current=dict(path="foo.gcode")))[0], False)
self.assertEqual(pp(dict(current=dict(path="foo_special.gcode")))[0], True)

@test_preprocessor("If print will be at least 10mm high")
@_preprocessor_case("If print will be at least 10mm high")
def test_print_height(self, pp):
self.assertEqual(
pp(dict(metadata=dict(analysis=dict(dimensions=dict(height=9)))))[0], False
Expand All @@ -100,7 +100,7 @@ def test_print_height(self, pp):
pp(dict(metadata=dict(analysis=dict(dimensions=dict(height=10)))))[0], True
)

@test_preprocessor("If print takes on average over an hour to complete")
@_preprocessor_case("If print takes on average over an hour to complete")
def test_avg_print_time(self, pp):
self.assertEqual(
pp(
Expand All @@ -121,7 +121,7 @@ def test_avg_print_time(self, pp):
True,
)

@test_preprocessor("If print has failed more than 10% of the time")
@_preprocessor_case("If print has failed more than 10% of the time")
def test_failure_rate(self, pp):
history = [dict(success=False)]
for i in range(10):
Expand All @@ -130,19 +130,19 @@ def test_failure_rate(self, pp):
history.append(dict(success=False))
self.assertEqual(pp(dict(metadata=dict(history=history)))[0], True)

@test_preprocessor("Also notify of bed temperature")
@_preprocessor_case("Also notify of bed temperature")
def test_notify(self, pp):
result, stdout = pp(dict(current=dict(bed_temp=1)))
stdout.seek(0)
self.assertEqual(stdout.read(), "Preprocessor says the bed temperature is 1\n")

@test_preprocessor("Error and pause if bed is >60C")
@_preprocessor_case("Error and pause if bed is >60C")
def test_error(self, pp):
self.assertEqual(pp(dict(current=dict(bed_temp=1)))[0], True)
with self.assertRaisesRegex(Exception, "600C"):
pp(dict(current=dict(bed_temp=600)))

@test_preprocessor("If starting from idle (first run, or ran finished script)")
@_preprocessor_case("If starting from idle (first run, or ran finished script)")
def test_from_idle(self, pp):
self.assertEqual(
pp(
Expand All @@ -169,7 +169,7 @@ def test_from_idle(self, pp):
True,
)

@test_preprocessor("If externally set variable is True")
@_preprocessor_case("If externally set variable is True")
def test_extern(self, pp):
self.assertEqual(pp(dict(external=dict(testval=True)))[0], True)
self.assertEqual(pp(dict(external=dict(testval=False)))[0], False)
3 changes: 1 addition & 2 deletions continuousprint/driver_test.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import unittest
import datetime
import time
from unittest.mock import MagicMock, ANY
from unittest.mock import MagicMock
from .driver import Driver, Action as DA, Printer as DP
from .data import CustomEvents
import logging
import traceback

# logging.basicConfig(level=logging.DEBUG)

Expand Down
14 changes: 6 additions & 8 deletions continuousprint/integration_test.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import unittest
import datetime
import time
import tempfile
from unittest.mock import MagicMock, ANY
from unittest.mock import MagicMock
from .driver import Driver, Action as DA, Printer as DP
from pathlib import Path
import logging
import traceback
from .storage.database_test import DBTest
from .storage.database import DEFAULT_QUEUE, MODELS, populate_queues
from .storage import queries
Expand Down Expand Up @@ -362,7 +360,7 @@ def onupdate():
profile = dict(name="profile")
lq = LANQueue(
"LAN",
f"peer{i}:{12345+i}",
f"peer{i}:{12345 + i}",
logging.getLogger(f"peer{i}:LAN"),
Strategy.IN_ORDER,
onupdate,
Expand Down Expand Up @@ -403,8 +401,8 @@ def onupdate():
def test_ordered_acquisition(self):
logging.info("============ BEGIN TEST ===========")
self.assertEqual(len(self.peers), 2)
(d1, _, lq1, db1) = self.peers[0]
(d2, _, lq2, db2) = self.peers[1]
d1, _, lq1, db1 = self.peers[0]
d2, _, lq2, db2 = self.peers[1]
for name in ("j1", "j2", "j3"):
lq1.lan.q.setJob(
f"{name}_hash",
Expand Down Expand Up @@ -454,8 +452,8 @@ def test_ordered_acquisition(self):
self.assertEqual(d2.state.__name__, d2._state_idle.__name__)

def test_non_local_edit(self):
(d1, _, lq1, db1) = self.peers[0]
(d2, _, lq2, db2) = self.peers[1]
d1, _, lq1, db1 = self.peers[0]
d2, _, lq2, db2 = self.peers[1]
with tempfile.TemporaryDirectory() as tdir:
(Path(tdir) / "test.gcode").touch()
j = LANJobView(
Expand Down
Loading