Skip to content

Commit f9e1fec

Browse files
committed
Merge branch '2.7_stack_context' of github.com:Ultimaker/Uranium into 2.7
2 parents e4d3336 + 5e4e87f commit f9e1fec

16 files changed

+66
-19
lines changed

UM/Backend/Backend.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ class BackendState(IntEnum):
2727
Error = 4
2828
Disabled = 5
2929

30+
3031
## Base class for any backend communication (separate piece of software).
3132
# It makes use of the Socket class from libArcus for the actual communication bits.
3233
# The message_handlers dict should be filled with string (full name of proto message), function pairs.
@@ -71,7 +72,7 @@ def startEngine(self):
7172
self._process = self._runEngineProcess(command)
7273
if self._process is None: #Failed to start engine.
7374
return
74-
Logger.log("i", "Started engine process: %s" % (self.getEngineCommand()[0]))
75+
Logger.log("i", "Started engine process: %s", self.getEngineCommand()[0])
7576
self._backendLog(bytes("Calling engine with: %s\n" % self.getEngineCommand(), "utf-8"))
7677
t = threading.Thread(target = self._storeOutputToLogThread, args = (self._process.stdout,))
7778
t.daemon = True
@@ -80,7 +81,7 @@ def startEngine(self):
8081
t.daemon = True
8182
t.start()
8283
except FileNotFoundError as e:
83-
Logger.log("e", "Unable to find backend executable: %s" % (self.getEngineCommand()[0]))
84+
Logger.log("e", "Unable to find backend executable: %s", self.getEngineCommand()[0])
8485

8586
def close(self):
8687
if self._socket:

UM/Settings/ContainerRegistry.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -567,7 +567,7 @@ def isDirty(self) -> bool:
567567
def isReadOnly(self) -> bool:
568568
return True
569569

570-
def getProperty(self, key, property_name):
570+
def getProperty(self, key, property_name, context = None):
571571
return None
572572

573573
def setProperty(self, key, property_name, property_value, container = None, set_from_cache = False):

UM/Settings/ContainerStack.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from UM.MimeTypeDatabase import MimeTypeDatabase, MimeType
1515
from UM.Settings.DefinitionContainer import DefinitionContainer #For getting all definitions in this stack.
1616
from UM.Settings.Interfaces import ContainerInterface, ContainerRegistryInterface
17+
from UM.Settings.PropertyEvaluationContext import PropertyEvaluationContext
1718
from UM.Settings.SettingFunction import SettingFunction
1819
from UM.Settings.Validator import ValidatorState
1920

@@ -194,10 +195,14 @@ def setDirty(self, dirty: bool) -> None:
194195
# Note that if the property value is a function, this method will return the
195196
# result of evaluating that property with the current stack. If you need the
196197
# actual function, use getRawProperty()
197-
def getProperty(self, key: str, property_name: str):
198+
def getProperty(self, key: str, property_name: str, context: Optional[PropertyEvaluationContext] = None):
198199
value = self.getRawProperty(key, property_name)
199200
if isinstance(value, SettingFunction):
200-
return value(self)
201+
if context is not None:
202+
context.pushContainer(self)
203+
value = value(self, context)
204+
if context is not None:
205+
context.popContainer()
201206

202207
return value
203208

UM/Settings/DefinitionContainer.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ def getMetaDataEntry(self, entry, default = None):
167167
## \copydoc ContainerInterface::getProperty
168168
#
169169
# Reimplemented from ContainerInterface.
170-
def getProperty(self, key, property_name):
170+
def getProperty(self, key, property_name, context = None):
171171
definition = self._getDefinition(key)
172172
if not definition:
173173
return None

UM/Settings/InstanceContainer.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ def setDirty(self, dirty):
257257
## \copydoc ContainerInterface::getProperty
258258
#
259259
# Reimplemented from ContainerInterface
260-
def getProperty(self, key, property_name):
260+
def getProperty(self, key, property_name, context = None):
261261
self._instantiateCachedValues()
262262
if key in self._instances:
263263
try:

UM/Settings/Interfaces.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
import UM.Decorators
77
from UM.Signal import Signal
8+
from UM.Settings.PropertyEvaluationContext import PropertyEvaluationContext
89

910

1011
## Shared interface between setting container types
@@ -62,7 +63,7 @@ def getMetaDataEntry(self, entry: str, default: Any = None) -> Any:
6263
# \param name \type{string} The name of the property to retrieve.
6364
#
6465
# \return The specified property value of the container item corresponding to key, or None if not found.
65-
def getProperty(self, key: str, property_name: str) -> Any:
66+
def getProperty(self, key: str, property_name: str, context: Optional[PropertyEvaluationContext] = None) -> Any:
6667
pass
6768

6869
## Get whether the container item has a specific property.

UM/Settings/Models/SettingPropertyProvider.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,13 +275,17 @@ def _onPropertiesChanged(self, key, property_names):
275275
self.isValueUsedChanged.emit()
276276
return
277277

278+
has_values_changed = False
278279
for property_name in property_names:
279280
if property_name not in self._watched_properties:
280281
continue
281282

283+
has_values_changed = True
282284
self._property_map.insert(property_name, self._getPropertyValue(property_name))
283285

284286
self._updateStackLevels()
287+
if has_values_changed:
288+
self.propertiesChanged.emit()
285289

286290
def _update(self, container = None):
287291
if not self._stack or not self._watched_properties or not self._key:
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# Copyright (c) 2017 Ultimaker B.V.
2+
# Uranium is released under the terms of the AGPLv3 or higher.
3+
4+
from collections import deque
5+
6+
7+
## Context for evaluating a property value
8+
# It contains:
9+
# 1. a stack of containers during the evaluation in the function call stack fashion
10+
# 2. a context dictionary which contains all the current context
11+
#
12+
class PropertyEvaluationContext:
13+
14+
def __init__(self, source_stack = None):
15+
self.stack_of_containers = deque()
16+
if source_stack is not None:
17+
self.stack_of_containers.append(source_stack)
18+
self.context = {}
19+
20+
def rootStack(self):
21+
if self.stack_of_containers:
22+
return self.stack_of_containers[0]
23+
24+
def pushContainer(self, container):
25+
self.stack_of_containers.append(container)
26+
27+
def popContainer(self):
28+
return self.stack_of_containers.pop()

UM/Settings/SettingFunction.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@
33

44
import ast
55
import math # Imported here so it can be used easily by the setting functions.
6-
from typing import Any, Dict, Callable, Set, FrozenSet, NamedTuple
6+
from typing import Any, Dict, Callable, Set, FrozenSet, NamedTuple, Optional
77

88
from UM.Settings.Interfaces import ContainerInterface
9+
from UM.Settings.PropertyEvaluationContext import PropertyEvaluationContext
910
from UM.Logger import Logger
1011

1112
MYPY = False
@@ -54,16 +55,19 @@ def __init__(self, code: str) -> None:
5455
Logger.log("e", "Exception in function ({0}) for setting: {1}".format(str(e), self._code))
5556

5657
## Call the actual function to calculate the value.
57-
def __call__(self, value_provider: ContainerInterface) -> Any:
58+
def __call__(self, value_provider: ContainerInterface, context: Optional[PropertyEvaluationContext] = None) -> Any:
5859
if not value_provider:
5960
return None
6061

6162
if not self._valid:
6263
return None
6364

6465
locals = {} # type: Dict[str, Any]
66+
# if there is a context, evaluate the values from the perspective of the original caller
67+
if context is not None:
68+
value_provider = context.rootStack()
6569
for name in self._used_values:
66-
value = value_provider.getProperty(name, "value")
70+
value = value_provider.getProperty(name, "value", context)
6771
if value is None:
6872
continue
6973

UM/Settings/Validator.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@
22
# Uranium is released under the terms of the AGPLv3 or higher.
33

44
from enum import Enum
5-
from typing import Any
5+
from typing import Any, Optional
66

77
from UM.Settings.Interfaces import ContainerInterface
8+
from UM.Settings.PropertyEvaluationContext import PropertyEvaluationContext
89
from UM.Logger import Logger
910

1011
MYPY = False
@@ -39,7 +40,7 @@ def __init__(self, key: str) -> None:
3940
self._key = key # type: str
4041

4142
## Perform the actual validation.
42-
def __call__(self, value_provider: ContainerInterface) -> Any:
43+
def __call__(self, value_provider: ContainerInterface, context: Optional[PropertyEvaluationContext] = None) -> Any:
4344
if not value_provider:
4445
return
4546

@@ -53,6 +54,9 @@ def __call__(self, value_provider: ContainerInterface) -> Any:
5354
if minimum is not None and maximum is not None and minimum > maximum:
5455
raise ValueError("Cannot validate a state of setting {0} with minimum > maximum".format(self._key))
5556

57+
if context is not None:
58+
value_provider = context.rootStack()
59+
5660
value = value_provider.getProperty(self._key, "value")
5761
if value is None or value != value:
5862
raise ValueError("Cannot validate None, NaN or similar values in setting {0}, actual value: {1}".format(self._key, value))

0 commit comments

Comments
 (0)