Merge lp:~kissiel/checkbox/use-pyotherside1.4 into lp:checkbox

Proposed by Maciej Kisielewski
Status: Merged
Approved by: Zygmunt Krynicki
Approved revision: 3814
Merged at revision: 3813
Proposed branch: lp:~kissiel/checkbox/use-pyotherside1.4
Merge into: lp:checkbox
Diff against target: 417 lines (+97/-193)
6 files modified
checkbox-touch/checkbox-touch.qml (+3/-7)
checkbox-touch/components/CheckboxTouchApplication.qml (+5/-4)
checkbox-touch/components/PythonLogger.qml (+13/-7)
checkbox-touch/components/PythonObjectHandle.qml (+0/-76)
checkbox-touch/components/PythonObjectRef.qml (+69/-0)
checkbox-touch/py/checkbox_touch.py (+7/-99)
To merge this branch: bzr merge lp:~kissiel/checkbox/use-pyotherside1.4
Reviewer Review Type Date Requested Status
Zygmunt Krynicki (community) Approve
Review via email: mp+260106@code.launchpad.net

Description of the change

Migration from PythonObjectHandle to PythonObjectRef (pyotherside-1.4)

To post a comment you must log in.
3811. By Maciej Kisielewski

checkbox-touch: add PythonObjectRef component

This patch adds qml component that handles referencing python objects from qml
side.

Signed-off-by: Maciej Kisielewski <email address hidden>

3812. By Maciej Kisielewski

checkbox-touch: migrate CheckboxTouchApplication class to PythonObjectRef

This patch moves main CheckboxTouchApplication class to be used through
PythonObjectRef from QML side.

3813. By Maciej Kisielewski

checkbox-touch: migrate PythonLogger to PythonObjectRef

Signed-off-by: Maciej Kisielewski <email address hidden>

3814. By Maciej Kisielewski

checkbox-touch: remove PythonObjectHandle

Signed-off-by: Maciej Kisielewski <email address hidden>

Revision history for this message
Zygmunt Krynicki (zyga) wrote :

Looks good, thanks for changing this. +1

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'checkbox-touch/checkbox-touch.qml'
2--- checkbox-touch/checkbox-touch.qml 2015-05-20 07:56:01 +0000
3+++ checkbox-touch/checkbox-touch.qml 2015-05-27 06:52:22 +0000
4@@ -23,7 +23,7 @@
5 import Ubuntu.Components 1.1
6 import Ubuntu.Components.Popups 0.1
7 import QtQuick.Layouts 1.1
8-import io.thp.pyotherside 1.2
9+import io.thp.pyotherside 1.4
10 import "components"
11 import "components/ErrorLogic.js" as ErrorLogic
12 import "components/CbtDialogLogic.js" as CbtDialogLogic
13@@ -110,9 +110,7 @@
14 // create_app_object() function and assign the resulting handle
15 // back to the application component.
16 py.importModule("checkbox_touch", function() {
17- call("checkbox_touch.create_app_object", [], function(handle) {
18- app.handle = handle;
19- });
20+ app.construct("checkbox_touch.create_app_object", [])
21 });
22 }
23 onError: {
24@@ -146,9 +144,7 @@
25 Component.onCompleted: {
26 py.Component.onCompleted.connect(function() {
27 py.importModule("checkbox_touch", function() {
28- py.call("checkbox_touch.get_qml_logger", [], function(handle) {
29- logger.handle = handle;
30- });
31+ construct("checkbox_touch.get_qml_logger", []);
32 });
33 });
34 }
35
36=== modified file 'checkbox-touch/components/CheckboxTouchApplication.qml'
37--- checkbox-touch/components/CheckboxTouchApplication.qml 2015-05-12 09:58:35 +0000
38+++ checkbox-touch/components/CheckboxTouchApplication.qml 2015-05-27 06:52:22 +0000
39@@ -1,10 +1,11 @@
40 /*
41 * This file is part of Checkbox
42 *
43- * Copyright 2014 Canonical Ltd.
44+ * Copyright 2014-2015 Canonical Ltd.
45 *
46 * Authors:
47 * - Zygmunt Krynicki <zygmunt.krynicki@canonical.com>
48+ * - Maciej Kisielewski <maciej.kisielewski@canonical.com>
49 *
50 * This program is free software; you can redistribute it and/or modify
51 * it under the terms of the GNU General Public License as published by
52@@ -24,7 +25,7 @@
53 import "ErrorLogic.js" as ErrorLogic
54
55
56-PythonObjectHandle {
57+PythonObjectRef {
58 id: app
59 // Version of the application
60 property string applicationVersion
61@@ -37,7 +38,7 @@
62 signal appReady();
63
64 // Signal sent when a session becomes ready
65- signal sessionReady();
66+ signal sessionReady()
67
68 // Create a new session
69 //
70@@ -168,7 +169,7 @@
71
72 // Internal handler that triggers a call to python to query for runtime and
73 // application versions.
74- onHandleReady: {
75+ onObjectReady: {
76 request("get_version_pair", [], function(result) {
77 app.applicationVersion = result.application_version;
78 app.plainboxVersion = result.plainbox_version;
79
80=== modified file 'checkbox-touch/components/PythonLogger.qml'
81--- checkbox-touch/components/PythonLogger.qml 2015-02-09 14:19:45 +0000
82+++ checkbox-touch/components/PythonLogger.qml 2015-05-27 06:52:22 +0000
83@@ -20,7 +20,7 @@
84 */
85
86 /*! \brief Python-driven logger
87- \inherits PythonObjectHandle
88+ \inherits PythonObjectRef
89
90 This component uses pyotherside to forward logging events to python.
91 It monkey-patches console.log and console.error to capture their calls
92@@ -28,7 +28,8 @@
93 */
94 import QtQuick 2.0
95
96-PythonObjectHandle {
97+PythonObjectRef {
98+ id: pythonLogger
99
100 function debug(msg) {
101 invoke('debug', [msg], function() {});
102@@ -63,13 +64,18 @@
103
104 /** Overridden invoke that doesn't log invoke calls */
105 function invoke(func, args, callback) {
106- if (py !== null && handle > 0) {
107- py.call("py_invoke", [handle, func, args], function(response) {
108+ if (py !== null && object !== null) {
109+ var callable = py.getattr(object, func);
110+ if (!callable) {
111+ console.error("Unable to invoke " + func + " on python logger");
112+ throw "trying to invoke not existing method"
113+ }
114+ py.call(callable, args, function(response) {
115 callback(response);
116 });
117 } else {
118- _original_console_error("unable to py_invoke: " + handle + ", " + func + ", " + JSON.stringify(args));
119- throw "py_invoke called without ready py and handle";
120+ _original_console_error("unable to invoke " + func + " on python logger");
121+ throw "invoke called without py initiated and/or object constructed";
122 }
123 }
124
125@@ -79,7 +85,7 @@
126 _original_console_error = console.error;
127 }
128
129- onHandleReady: {
130+ onObjectReady: {
131 /* monkey-patch console.log and console.error */
132 console.log = function() { debug(_argsToString(arguments)); };
133 console.error = function() { error(_argsToString(arguments)); };
134
135=== removed file 'checkbox-touch/components/PythonObjectHandle.qml'
136--- checkbox-touch/components/PythonObjectHandle.qml 2014-09-29 19:18:47 +0000
137+++ checkbox-touch/components/PythonObjectHandle.qml 1970-01-01 00:00:00 +0000
138@@ -1,76 +0,0 @@
139-/*
140- * This file is part of Checkbox
141- *
142- * Copyright 2014 Canonical Ltd.
143- *
144- * Authors:
145- * - Zygmunt Krynicki <zygmunt.krynicki@canonical.com>
146- *
147- * This program is free software; you can redistribute it and/or modify
148- * it under the terms of the GNU General Public License as published by
149- * the Free Software Foundation; version 3.
150- *
151- * This program is distributed in the hope that it will be useful,
152- * but WITHOUT ANY WARRANTY; without even the implied warranty of
153- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
154- * GNU General Public License for more details.
155- *
156- * You should have received a copy of the GNU General Public License
157- * along with this program. If not, see <http://www.gnu.org/licenses/>.
158- */
159-import QtQuick 2.0
160-
161-
162-QtObject {
163- // Reference to pyotherside's Python object
164- property var py: null
165- // Handle to a python object (via RemoteObjectLifecycleManager)
166- // if it is < 0 then it is an error of some kind
167- // if it is 0 (default) then the handle is just invalid
168- // if it is > 0 then it represents a valid object handle
169- property int handle: 0
170- // Flag set just prior to sending the handleReady() signal
171- property bool _ready: false
172-
173- // Signal sent when the handle is initially assigned *and* py is already
174- // assigned (so when the python object is ready to be interacted with)
175- signal handleReady();
176-
177- // Dereference the python object when we are shutting down
178- Component.onDestruction: _unref()
179- // Maybe send the handleReady signal (once) when handle is changed
180- onHandleChanged: _maybeReady()
181- // Maybe send the handleReady signal (once) when 'py' is changed
182- onPyChanged: _maybeReady()
183-
184- /** Send the handleReady() signal (once) if both py and handle are ready */
185- function _maybeReady() {
186- if (handle > 0 && py !== null && _ready == false) {
187- _ready = true;
188- handleReady()
189- }
190- }
191-
192- /** Dereference this object */
193- function _unref() {
194- if (py !== null && handle > 0) {
195- // console.log("py_unref: " + handle);
196- py.call("py_unref", [handle]);
197- handle = 0;
198- }
199- }
200-
201- /** Call a method on this object */
202- function invoke(func, args, callback) {
203- if (py !== null && handle > 0) {
204- console.log("py_invoke(" + handle + ", " + func + ", " + JSON.stringify(args) + ") ...");
205- py.call("py_invoke", [handle, func, args], function(response) {
206- console.log("py_invoke(" + handle + ", " + func + ", " + JSON.stringify(args) + ") -> " + JSON.stringify(response));
207- callback(response);
208- });
209- } else {
210- console.error("unable to py_invoke: " + handle + ", " + func + ", " + JSON.stringify(args));
211- throw "py_invoke called without ready py and handle";
212- }
213- }
214-}
215
216=== added file 'checkbox-touch/components/PythonObjectRef.qml'
217--- checkbox-touch/components/PythonObjectRef.qml 1970-01-01 00:00:00 +0000
218+++ checkbox-touch/components/PythonObjectRef.qml 2015-05-27 06:52:22 +0000
219@@ -0,0 +1,69 @@
220+/*
221+ * This file is part of Checkbox
222+ *
223+ * Copyright 2015 Canonical Ltd.
224+ *
225+ * Authors:
226+ * - Maciej Kisielewski <maciej.kisielewski@canonical.com>
227+ *
228+ * This program is free software; you can redistribute it and/or modify
229+ * it under the terms of the GNU General Public License as published by
230+ * the Free Software Foundation; version 3.
231+ *
232+ * This program is distributed in the hope that it will be useful,
233+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
234+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
235+ * GNU General Public License for more details.
236+ *
237+ * You should have received a copy of the GNU General Public License
238+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
239+ */
240+import QtQuick 2.0
241+
242+
243+QtObject {
244+ id: pythonObjectRef
245+
246+ // Reference to pyotherside's Python object
247+ property var py: null
248+
249+ // PyObjectRef to the object
250+ property var object: null
251+
252+ // Signal sent when the object reference is ready to use
253+ signal objectReady()
254+
255+ // Creation method name that were used to get the reference to the object
256+ property var creationMethodName: null
257+
258+ function construct(creationMethodName, args) {
259+ if (!py) {
260+ console.error("Trying to get reference to python object without py initiated!");
261+ } else {
262+ console.info("Getting reference to python object via " + creationMethodName);
263+ py.call(creationMethodName, args, function(result) {
264+ object = result;
265+ pythonObjectRef.creationMethodName = creationMethodName;
266+ objectReady();
267+ });
268+ }
269+ }
270+ /** Call a method on this object */
271+ function invoke(func, args, callback) {
272+ if (py !== null && object !== null) {
273+ console.log("invoking " + func + " on object created with" + pythonObjectRef.creationMethodName + ", with args: " + JSON.stringify(args) + " ...");
274+ var callable = py.getattr(object, func);
275+ if (!callable) {
276+ console.log("Unable to invoke " + func + " on object " + JSON.stringify(pythonObjectRef));
277+ throw "trying to invoke inexistent method"
278+ }
279+ py.call(callable, args, function(response) {
280+ console.log(func + " on object created with" + pythonObjectRef.creationMethodName + ", with args: " + JSON.stringify(args) + " returned: " + JSON.stringify(response));
281+ callback(response);
282+ });
283+ } else {
284+ console.error("Unable to invoke " + func + " on object " + JSON.stringify(pythonObjectRef));
285+ throw "invoke called without py initiated and/or object constructed";
286+ }
287+ }
288+}
289
290=== modified file 'checkbox-touch/py/checkbox_touch.py'
291--- checkbox-touch/py/checkbox_touch.py 2015-05-18 11:20:02 +0000
292+++ checkbox-touch/py/checkbox_touch.py 2015-05-27 06:52:22 +0000
293@@ -65,72 +65,9 @@
294 from embedded_providers import EmbeddedProvider1PlugInCollection
295
296 _logger = logging.getLogger('checkbox.touch')
297-_manager = None
298-
299-
300-class VerboseLifecycle:
301- """
302- Mix-in class for verbose lifecycle reporting
303- """
304-
305- def __new__(cls, *args, **kwargs):
306- self = super().__new__(cls, *args, **kwargs)
307- _logger.debug("new %s %x", cls.__name__, id(self))
308- return self
309-
310- def __del__(self):
311- _logger.debug("del %s %x", self.__class__.__name__, id(self))
312-
313-
314-class RemoteObjectLifecycleManager(VerboseLifecycle):
315- """
316- Remote object life-cycle manager
317-
318- This class aids in handling non-trivial objects that are referenced from
319- QML (via pyotherside) but really stored on the python side.
320- """
321-
322- def __init__(self):
323- self._count = 0
324- self._handle_map = {}
325-
326- def unref(self, handle: int):
327- """
328- Remove a reference represented by the specified handle
329- """
330- _logger.debug("unref %s", handle)
331- del self._handle_map[handle]
332-
333- def ref(self, obj: object) -> int:
334- """
335- Store a reference to an object and return the handle
336- """
337- self._count += 1
338- handle = self._count
339- self._handle_map[handle] = obj
340- _logger.debug("ref %r -> %s", obj, handle)
341- return handle
342-
343- def invoke(self, handle: int, func: str, args):
344- """
345- Call a method on a object represented by the handle
346-
347- :param handle:
348- A (numeric) handle to the objecet
349- :param func:
350- The (name of the) function to call
351- :param args:
352- A list of positional arguments to pass
353- :returns:
354- The value returned by the called method
355- """
356- obj = self._handle_map[handle]
357- impl = getattr(obj, func)
358- retval = impl(*args)
359- return retval
360-
361-
362-class PlainboxApplication(VerboseLifecycle, metaclass=abc.ABCMeta):
363+
364+
365+class PlainboxApplication(metaclass=abc.ABCMeta):
366 """
367 Base class for plainbox-based applications.
368
369@@ -141,16 +78,6 @@
370 def __repr__(self):
371 return "<{}>".format(self.__class__.__name__)
372
373- @classmethod
374- def create_and_get_handle(cls):
375- """
376- Create an instance of the high-level PlainBox object
377-
378- :returns:
379- A handle to a fresh instance of :class:`PlainBox`
380- """
381- return _manager.ref(cls())
382-
383 @abc.abstractmethod
384 def get_version_pair(self):
385 """
386@@ -820,27 +747,8 @@
387 self._password = password
388
389
390-def bootstrap():
391- logging.basicConfig(level=logging.INFO, stream=sys.stderr)
392- logging.info("environ: %r", os.environ)
393- logging.info("path: %r", sys.path)
394- # from plainbox.impl.logging import adjust_logging
395- # from plainbox.impl.logging import setup_logging
396- # Setup logging
397- # setup_logging()
398- # adjust_logging(logging.DEBUG, ['checkbox.stack'], True)
399- # Create the Javascript <=> Python remote object lifecycle manager
400- manager = RemoteObjectLifecycleManager()
401- # Expose top-level functions for pyotherside's simplicity
402- builtins.py_ref = manager.ref
403- builtins.py_unref = manager.unref
404- builtins.py_invoke = manager.invoke
405- return manager
406-
407-
408 def get_qml_logger():
409- return _manager.ref(logging.getLogger('checkbox.touch.qml'))
410-
411-
412-create_app_object = CheckboxTouchApplication.create_and_get_handle
413-_manager = bootstrap()
414+ return logging.getLogger('checkbox.touch.qml')
415+
416+
417+create_app_object = CheckboxTouchApplication

Subscribers

People subscribed via source and target branches