From 3163199c5259179fe3172d27cdbe9965007ad6b6 Mon Sep 17 00:00:00 2001 From: Walter Bender Date: Mon, 13 Jan 2014 03:26:11 +0000 Subject: more recovery of rebase disaster --- diff --git a/pippy_app.py b/pippy_app.py index 5996dd2..005f21b 100644 --- a/pippy_app.py +++ b/pippy_app.py @@ -25,108 +25,160 @@ """Pippy Activity: A simple Python programming activity .""" from __future__ import with_statement -import gtk import logging -import pango -import vte import re import os -import gobject -import time +import subprocess +from random import uniform +import locale +import json +import sys + +import dbus +from dbus.mainloop.glib import DBusGMainLoop + +DBusGMainLoop(set_as_default=True) +bus = dbus.SessionBus() + +from gi.repository import Gtk +from gi.repository import Gdk +from gi.repository import GLib +from gi.repository import Pango +from gi.repository import Vte +from gi.repository import GObject from port.style import font_zoom from signal import SIGTERM from gettext import gettext as _ -from sugar.activity import activity +from sugar3.datastore import datastore +from sugar3.activity import activity +from sugar3.activity.widgets import EditToolbar +from sugar3.activity.widgets import StopButton +from sugar3.activity.activity import get_bundle_name +from sugar3.activity.activity import get_bundle_path +from sugar3.graphics.alert import Alert +from sugar3.graphics.alert import ConfirmationAlert +from sugar3.graphics.alert import NotifyAlert +from sugar3.graphics import style +from sugar3.graphics.icon import Icon +from sugar3.graphics.objectchooser import ObjectChooser +from sugar3.graphics.toggletoolbutton import ToggleToolButton + +from jarabe.view.customizebundle import generate_unique_id + from activity import ViewSourceActivity from activity import TARGET_TYPE_TEXT -from sugar.activity.activity import ActivityToolbox -from sugar.activity.activity import EditToolbar -from sugar.activity.activity import get_bundle_path -from sugar.activity.activity import get_bundle_name -from sugar.graphics import style -from sugar.graphics.toolbutton import ToolButton import groupthink.sugar_tools import groupthink.gtk_tools +from filedialog import FileDialog +from icondialog import IconDialog + text_buffer = None + # magic prefix to use utf-8 source encoding -PYTHON_PREFIX = """#!/usr/bin/python +PYTHON_PREFIX = '''#!/usr/bin/python # -*- coding: utf-8 -*- -""" +''' +# Force category names into Pootle +DEFAULT_CATEGORIES = [_('graphics'), _('math'), _('pyhton'), _('sound'), + _('string'), _('tutorials')] -OLD_TOOLBAR = False -try: - from sugar.graphics.toolbarbox import ToolbarBox - from sugar.graphics.toolbarbox import ToolbarButton - from sugar.activity.widgets import StopButton -except ImportError: - OLD_TOOLBAR = True +from sugar3.graphics.toolbarbox import ToolbarBox +from sugar3.graphics.toolbarbox import ToolbarButton +from sugar3.graphics.toolbutton import ToolButton +from sugar3.activity.widgets import ActivityToolbarButton # get screen sizes -SIZE_X = gtk.gdk.screen_width() -SIZE_Y = gtk.gdk.screen_height() +SIZE_X = Gdk.Screen.width() +SIZE_Y = Gdk.Screen.height() groupthink_mimetype = 'pickle/groupthink-pippy' +from notebook import SourceNotebook + +DISUTILS_SETUP_SCRIPT = """#!/usr/bin/python +# -*- coding: utf-8 -*- +from distutils.core import setup +setup(name='{modulename}', + version='1.0', + py_modules=[ + {filenames} + ], + ) +""" # This is .format()'ed with the list of the file names. + class PippyActivity(ViewSourceActivity, groupthink.sugar_tools.GroupActivity): - """Pippy Activity as specified in activity.info""" + '''Pippy Activity as specified in activity.info''' def early_setup(self): global text_buffer - import gtksourceview2 - text_buffer = gtksourceview2.Buffer() + from gi.repository import GtkSource + text_buffer = GtkSource.Buffer() + self.initial_text_buffer = GtkSource.Buffer() + self.loaded_from_journal = False + self.py_file = False + self.loaded_session = [] + self.session_data = [] + + sys.path.append(os.path.join(self.get_activity_root(), 'Library')) def initialize_display(self): self._logger = logging.getLogger('pippy-activity') # Activity toolbar with title input, share button and export buttons: - if OLD_TOOLBAR: - activity_toolbar = self.toolbox.get_activity_toolbar() - else: - activity_toolbar = self.activity_button.page - - # Hide keep button for Sugar versions prior to 0.94: - activity_toolbar.keep.hide() + activity_toolbar = self.activity_button.page - separator = gtk.SeparatorToolItem() + separator = Gtk.SeparatorToolItem() separator.show() activity_toolbar.insert(separator, -1) - export_doc_button = ToolButton('pippy-export_doc') - export_doc_button.set_tooltip(_("Export as Pippy Document")) + import_py_button = ToolButton('pippy-import-doc') + import_py_button.set_tooltip(_('Import Python file to new tab')) + import_py_button.connect('clicked', self._import_py_cb) + import_py_button.show() + activity_toolbar.insert(import_py_button, -1) + + export_doc_button = ToolButton('pippy-export-doc') + export_doc_button.set_tooltip(_('Export as Pippy document')) export_doc_button.connect('clicked', self._export_document_cb) export_doc_button.show() activity_toolbar.insert(export_doc_button, -1) - export_example_button = ToolButton('pippy-export_example') - export_example_button.set_tooltip(_("Export as Pippy Example")) + save_as_library = ToolButton('pippy-export-library') + save_as_library.set_tooltip(_('Save this file to the Pippy library')) + save_as_library.connect('clicked', self._save_as_library) + save_as_library.show() + activity_toolbar.insert(save_as_library, -1) + + export_example_button = ToolButton('pippy-export-example') + export_example_button.set_tooltip(_('Export as new Pippy example')) export_example_button.connect('clicked', self._export_example_cb) export_example_button.show() activity_toolbar.insert(export_example_button, -1) - create_bundle_button = ToolButton('pippy-create_bundle') - create_bundle_button.set_tooltip(_("Create Activity Bundle")) + create_bundle_button = ToolButton('pippy-create-bundle') + create_bundle_button.set_tooltip(_('Create a Sugar activity bundle')) create_bundle_button.connect('clicked', self._create_bundle_cb) create_bundle_button.show() activity_toolbar.insert(create_bundle_button, -1) - self._edit_toolbar = activity.EditToolbar() + export_disutils = ToolButton('pippy-create-disutils') + export_disutils.set_tooltip(_('Export as a disutils package')) + export_disutils.connect('clicked', self.__export_disutils_cb) + export_disutils.show() + activity_toolbar.insert(export_disutils, -1) - if OLD_TOOLBAR: - activity_toolbar = gtk.Toolbar() - self.toolbox.add_toolbar(_('Actions'), activity_toolbar) - self.toolbox.set_current_toolbar(1) - self.toolbox.add_toolbar(_('Edit'), self._edit_toolbar) - else: - edit_toolbar_button = ToolbarButton() - edit_toolbar_button.set_page(self._edit_toolbar) - edit_toolbar_button.props.icon_name = 'toolbar-edit' - edit_toolbar_button.props.label = _('Edit') - self.get_toolbar_box().toolbar.insert(edit_toolbar_button, -1) + self._edit_toolbar = EditToolbar() + + edit_toolbar_button = ToolbarButton() + edit_toolbar_button.set_page(self._edit_toolbar) + edit_toolbar_button.props.icon_name = 'toolbar-edit' + edit_toolbar_button.props.label = _('Edit') + self.get_toolbar_box().toolbar.insert(edit_toolbar_button, -1) self._edit_toolbar.show() @@ -135,196 +187,190 @@ class PippyActivity(ViewSourceActivity, groupthink.sugar_tools.GroupActivity): self._edit_toolbar.copy.connect('clicked', self.__copybutton_cb) self._edit_toolbar.paste.connect('clicked', self.__pastebutton_cb) - if OLD_TOOLBAR: - actions_toolbar = activity_toolbar - else: - actions_toolbar = self.get_toolbar_box().toolbar + actions_toolbar = self.get_toolbar_box().toolbar + + self.toggle_output = ToggleToolButton('tray-show') + self.toggle_output.set_tooltip(_('Show output panel')) + self.toggle_output.connect('toggled', self._toggle_output_cb) + actions_toolbar.insert(self.toggle_output, -1) # The "go" button - goicon_bw = gtk.Image() - goicon_bw.set_from_file("%s/icons/run_bw.svg" % os.getcwd()) - goicon_color = gtk.Image() - goicon_color.set_from_file("%s/icons/run_color.svg" % os.getcwd()) - gobutton = ToolButton(label=_("Run!")) + goicon_bw = Gtk.Image() + goicon_bw.set_from_file('%s/icons/run_bw.svg' % os.getcwd()) + goicon_color = Gtk.Image() + goicon_color.set_from_file('%s/icons/run_color.svg' % os.getcwd()) + gobutton = ToolButton(label=_('Run!')) gobutton.props.accelerator = _('r') gobutton.set_icon_widget(goicon_bw) - gobutton.set_tooltip(_("Run!")) - gobutton.connect('clicked', self.flash_cb, dict({'bw': goicon_bw, - 'color': goicon_color})) + gobutton.set_tooltip(_('Run!')) + gobutton.connect('clicked', self.flash_cb, + dict({'bw': goicon_bw, 'color': goicon_color})) gobutton.connect('clicked', self.gobutton_cb) actions_toolbar.insert(gobutton, -1) # The "stop" button - stopicon_bw = gtk.Image() - stopicon_bw.set_from_file("%s/icons/stopit_bw.svg" % os.getcwd()) - stopicon_color = gtk.Image() - stopicon_color.set_from_file("%s/icons/stopit_color.svg" % os.getcwd()) - stopbutton = ToolButton(label=_("Stop")) + stopicon_bw = Gtk.Image() + stopicon_bw.set_from_file('%s/icons/stopit_bw.svg' % os.getcwd()) + stopicon_color = Gtk.Image() + stopicon_color.set_from_file('%s/icons/stopit_color.svg' % os.getcwd()) + stopbutton = ToolButton(label=_('Stop')) stopbutton.props.accelerator = _('s') stopbutton.set_icon_widget(stopicon_bw) - stopbutton.connect('clicked', self.flash_cb, dict({'bw': stopicon_bw, - 'color': stopicon_color})) + stopbutton.connect('clicked', self.flash_cb, + dict({'bw': stopicon_bw, + 'color': stopicon_color})) stopbutton.connect('clicked', self.stopbutton_cb) - stopbutton.set_tooltip(_("Stop")) + stopbutton.set_tooltip(_('Stop')) actions_toolbar.insert(stopbutton, -1) # The "clear" button - clearicon_bw = gtk.Image() - clearicon_bw.set_from_file("%s/icons/eraser_bw.svg" % os.getcwd()) - clearicon_color = gtk.Image() - clearicon_color.set_from_file("%s/icons/eraser_color.svg" % + clearicon_bw = Gtk.Image() + clearicon_bw.set_from_file('%s/icons/eraser_bw.svg' % os.getcwd()) + clearicon_color = Gtk.Image() + clearicon_color.set_from_file('%s/icons/eraser_color.svg' % os.getcwd()) - clearbutton = ToolButton(label=_("Clear")) + clearbutton = ToolButton(label=_('Clear')) clearbutton.props.accelerator = _('c') clearbutton.set_icon_widget(clearicon_bw) clearbutton.connect('clicked', self.clearbutton_cb) - clearbutton.connect('clicked', self.flash_cb, dict({'bw': clearicon_bw, - 'color': clearicon_color})) - clearbutton.set_tooltip(_("Clear")) + clearbutton.connect('clicked', self.flash_cb, + dict({'bw': clearicon_bw, + 'color': clearicon_color})) + clearbutton.set_tooltip(_('Clear')) actions_toolbar.insert(clearbutton, -1) activity_toolbar.show() - if not OLD_TOOLBAR: - separator = gtk.SeparatorToolItem() - separator.props.draw = False - separator.set_expand(True) - self.get_toolbar_box().toolbar.insert(separator, -1) - separator.show() - - stop = StopButton(self) - self.get_toolbar_box().toolbar.insert(stop, -1) - - # Main layout. - self.hpane = gtk.HPaned() - self.vpane = gtk.VPaned() - - # The sidebar. - self.sidebar = gtk.VBox() - self.model = gtk.TreeStore(gobject.TYPE_PYOBJECT, gobject.TYPE_STRING) - treeview = gtk.TreeView(self.model) - cellrenderer = gtk.CellRendererText() - treecolumn = gtk.TreeViewColumn(_("Examples"), cellrenderer, text=1) - treeview.get_selection().connect("changed", self.selection_cb) - treeview.append_column(treecolumn) - treeview.set_size_request(int(SIZE_X * 0.3), SIZE_Y) - - # Create scrollbars around the view. - scrolled = gtk.ScrolledWindow() - scrolled.add(treeview) - self.sidebar.pack_start(scrolled) - self.hpane.add1(self.sidebar) - - root = os.path.join(get_bundle_path(), 'data') - for d in sorted(os.listdir(root)): - if not os.path.isdir(os.path.join(root, d)): - continue # skip non-dirs - direntry = {"name": _(d.capitalize()), - "path": os.path.join(root, d) + "/"} - olditer = self.model.insert_before(None, None) - self.model.set_value(olditer, 0, direntry) - self.model.set_value(olditer, 1, direntry["name"]) - - - for _file in sorted(os.listdir(os.path.join(root, d))): - if _file.endswith('~'): - continue # skip emacs backups - entry = {"name": _(_file.capitalize()), - "path": os.path.join(root, d, _file)} - _iter = self.model.insert_before(olditer, None) - self.model.set_value(_iter, 0, entry) - self.model.set_value(_iter, 1, entry["name"]) - # Adding local examples - root = os.path.join(os.environ['SUGAR_ACTIVITY_ROOT'],'data') - direntry_examples = { "name": _("My examples"), - "path": root + "/" } - self.example_iter = self.model.insert_before(None, None) - self.model.set_value(self.example_iter, 0, direntry_examples) - self.model.set_value(self.example_iter, 1, direntry_examples["name"]) - for _file in sorted(os.listdir(root)): - file_name = os.path.join(root, _file) - if os.path.isfile(file_name): - entry = {"name": _file, "path": file_name} - _iter = self.model.insert_before(self.example_iter, None) - self.model.set_value(_iter, 0, entry) - self.model.set_value(_iter, 1, entry["name"]) - - treeview.expand_all() - - # Source buffer - import gtksourceview2 - global text_buffer - lang_manager = gtksourceview2.language_manager_get_default() - if hasattr(lang_manager, 'list_languages'): - langs = lang_manager.list_languages() + examples = ToolButton('pippy-openoff') + examples.set_tooltip(_('Load example')) + examples.connect('clicked', self.load_example) + + self.get_toolbar_box().toolbar.insert(Gtk.SeparatorToolItem(), -1) + self.get_toolbar_box().toolbar.insert(examples, -1) + + separator = Gtk.SeparatorToolItem() + separator.props.draw = False + separator.set_expand(True) + self.get_toolbar_box().toolbar.insert(separator, -1) + separator.show() + + stop = StopButton(self) + self.get_toolbar_box().toolbar.insert(stop, -1) + + self.paths = [] + + vpane = Gtk.Paned.new(orientation=Gtk.Orientation.VERTICAL) + vpane.set_position(400) # setting initial position + + self.paths = [] + + data_path = os.path.join(get_bundle_path(), 'data') + + # get default language from locale + locale_lang = locale.getdefaultlocale()[0] + if locale_lang is None: + lang = 'en' else: - lang_ids = lang_manager.get_language_ids() - langs = [lang_manager.get_language(lang_id) - for lang_id in lang_ids] - for lang in langs: - for m in lang.get_mime_types(): - if m == "text/x-python": - text_buffer.set_language(lang) - - if hasattr(text_buffer, 'set_highlight'): - text_buffer.set_highlight(True) + lang = locale_lang.split('_')[0] + logging.debug(locale.getdefaultlocale()) + logging.debug(lang) + + # construct the path for both + lang_path = os.path.join(data_path, lang) + en_lang_path = os.path.join(data_path, 'en') + + # get all folders in lang examples + self.all_folders = [] + if os.path.exists(lang_path): + for d in sorted(os.listdir(lang_path)): + self.all_folders.append(d) + + # get all folders in English examples + for d in sorted(os.listdir(en_lang_path)): + # check if folder isn't already in list + if d not in self.all_folders: + self.all_folders.append(d) + + for folder in self.all_folders: + direntry = {} + # check if dir exists in pref language, if exists, add it + if os.path.exists(os.path.join(lang_path, folder)): + direntry = { + 'name': _(folder.capitalize()), + 'path': os.path.join(lang_path, folder) + '/'} + # if not try to see if it's in default English path + elif os.path.exists(os.path.join(en_lang_path, folder)): + direntry = { + 'name': _(folder.capitalize()), + 'path': os.path.join(en_lang_path, folder) + '/'} + self.paths.append([direntry['name'], direntry['path']]) + + # Adding local examples + root = os.path.join(os.environ['SUGAR_ACTIVITY_ROOT'], 'data') + self.paths.append([_('My examples'), root]) + + self.source_tabs = SourceNotebook(self) + self.source_tabs.connect('tab-added', self._add_source_cb) + if self.loaded_from_journal and self.py_file: + self.source_tabs.add_tab( + self.initial_title, + self.initial_text_buffer) + elif self.loaded_session: + for name, content in self.loaded_session: + self.source_tabs.add_tab(name, content) else: - text_buffer.set_highlight_syntax(True) - - # The GTK source view window - self.text_view = gtksourceview2.View(text_buffer) - self.text_view.set_size_request(0, int(SIZE_Y * 0.5)) - self.text_view.set_editable(True) - self.text_view.set_cursor_visible(True) - self.text_view.set_show_line_numbers(True) - self.text_view.set_wrap_mode(gtk.WRAP_CHAR) - self.text_view.set_insert_spaces_instead_of_tabs(True) - self.text_view.set_tab_width(2) - self.text_view.set_auto_indent(True) - self.text_view.modify_font(pango.FontDescription("Monospace " + - str(font_zoom(style.FONT_SIZE)))) - - # We could change the color theme here, if we want to. - #mgr = gtksourceview2.style_manager_get_default() - #style_scheme = mgr.get_scheme('kate') - #self.text_buffer.set_style_scheme(style_scheme) - - codesw = gtk.ScrolledWindow() - codesw.set_policy(gtk.POLICY_AUTOMATIC, - gtk.POLICY_AUTOMATIC) - codesw.add(self.text_view) - self.vpane.add1(codesw) - - # An hbox to hold the vte window and its scrollbar. - outbox = gtk.HBox() - - # The vte python window - self._vte = vte.Terminal() + self.source_tabs.add_tab() + + vpane.add1(self.source_tabs) + + outbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) + self._vte = Vte.Terminal() self._vte.set_encoding('utf-8') self._vte.set_size(30, 5) font = 'Monospace ' + str(font_zoom(style.FONT_SIZE)) - self._vte.set_font(pango.FontDescription(font)) - self._vte.set_colors(gtk.gdk.color_parse('#000000'), - gtk.gdk.color_parse('#E7E7E7'), + self._vte.set_font(Pango.FontDescription(font)) + self._vte.set_colors(Gdk.color_parse('#000000'), + Gdk.color_parse('#E7E7E7'), []) self._vte.connect('child_exited', self.child_exited_cb) + self._child_exited_handler = None - self._vte.drag_dest_set(gtk.DEST_DEFAULT_ALL, - [("text/plain", 0, TARGET_TYPE_TEXT)], - gtk.gdk.ACTION_COPY) self._vte.connect('drag_data_received', self.vte_drop_cb) - outbox.pack_start(self._vte) + outbox.pack_start(self._vte, True, True, 0) - outsb = gtk.VScrollbar(self._vte.get_adjustment()) + outsb = Gtk.Scrollbar(orientation=Gtk.Orientation.VERTICAL) + outsb.set_adjustment(self._vte.get_vadjustment()) outsb.show() outbox.pack_start(outsb, False, False, 0) - self.vpane.add2(outbox) - self.hpane.add2(self.vpane) - return self.hpane + vpane.add2(outbox) + self.outbox = outbox + + return vpane + + def after_init(self): + self.outbox.hide() + + def _toggle_output_cb(self, button): + shown = button.get_active() + if shown: + self.outbox.show_all() + self.toggle_output.set_tooltip(_('Hide output panel')) + self.toggle_output.set_icon_name('tray-hide') + else: + self.outbox.hide() + self.toggle_output.set_tooltip(_('Show output panel')) + self.toggle_output.set_icon_name('tray-show') + + def load_example(self, widget): + widget.set_icon_name('pippy-openon') + dialog = FileDialog(self.paths, self, widget) + dialog.run() + path = dialog.get_path() + if path: + self._select_func_cb(path) def when_shared(self): - self.hpane.remove(self.hpane.get_child1()) - global text_buffer + text_buffer = self.source_tabs.get_text_buffer() self.cloud.sharefield = \ groupthink.gtk_tools.TextBufferSharePoint(text_buffer) # HACK : There are issues with undo/redoing while in shared @@ -333,24 +379,56 @@ class PippyActivity(ViewSourceActivity, groupthink.sugar_tools.GroupActivity): self._edit_toolbar.undo.set_sensitive(False) self._edit_toolbar.redo.set_sensitive(False) + def _add_source_cb(self, button): + self.source_tabs.add_tab() + self.source_tabs.get_nth_page(-1).show_all() + def vte_drop_cb(self, widget, context, x, y, selection, targetType, time): if targetType == TARGET_TYPE_TEXT: self._vte.feed_child(selection.data) - def selection_cb(self, column): + def selection_cb(self, value): self.save() - model, _iter = column.get_selected() - value = model.get_value(_iter, 0) - self._logger.debug("clicked! %s" % value['path']) + self._logger.debug('clicked! %s' % value['path']) _file = open(value['path'], 'r') lines = _file.readlines() - global text_buffer - text_buffer.set_text("".join(lines)) + text_buffer = self.source_tabs.get_text_buffer() + text_buffer.set_text(''.join(lines)) + text_buffer.set_modified(False) self.metadata['title'] = value['name'] self.stopbutton_cb(None) self._reset_vte() self.text_view.grab_focus() + def _select_func_cb(self, path): + text_buffer = self.source_tabs.get_text_buffer() + if text_buffer.get_modified(): + alert = ConfirmationAlert() + alert.props.title = _('Example selection Warning') + alert.props.msg = \ + _('You have modified the currently selected file. ' + 'Discard changes?') + alert.connect('response', self._discard_changes_cb, path) + self.add_alert(alert) + return False + else: + values = {} + values['name'] = os.path.basename(path) + values['path'] = path + self.selection_cb(values) + + return False + + def _discard_changes_cb(self, alert, response_id, path): + self.remove_alert(alert) + if response_id is Gtk.ResponseType.OK: + values = {} + values['name'] = os.path.basename(path) + values['path'] = path + self.selection_cb(values) + text_buffer = self.source_tabs.get_text_buffer() + text_buffer.set_modified(False) + def timer_cb(self, button, icons): button.set_icon_widget(icons['bw']) button.show_all() @@ -359,187 +437,356 @@ class PippyActivity(ViewSourceActivity, groupthink.sugar_tools.GroupActivity): def flash_cb(self, button, icons): button.set_icon_widget(icons['color']) button.show_all() - gobject.timeout_add(400, self.timer_cb, button, icons) + GObject.timeout_add(400, self.timer_cb, button, icons) def clearbutton_cb(self, button): self.save() - global text_buffer - text_buffer.set_text("") + text_buffer = self.source_tabs.get_text_buffer() + text_buffer.set_text('') + text_buffer.set_modified(False) self.metadata['title'] = _('%s Activity') % get_bundle_name() self.stopbutton_cb(None) self._reset_vte() - self.text_view.grab_focus() - - def _write_text_buffer(self, filename): - global text_buffer - start, end = text_buffer.get_bounds() - text = text_buffer.get_text(start, end) - - with open(filename, 'w') as f: - # write utf-8 coding prefix if there's not already one - if re.match(r'coding[:=]\s*([-\w.]+)', - '\n'.join(text.splitlines()[:2])) is None: - f.write(PYTHON_PREFIX) - for line in text: - f.write(line) + self.source_tabs.get_text_view().grab_focus() + + def _write_all_buffers(self, tmp_dir): + data = self.source_tabs.get_all_data() + zipdata = zip(data[0], data[1]) + for name, content in zipdata: + with open(os.path.join(tmp_dir, name), 'w') as f: + # Write utf-8 coding prefix if there's not one already + if re.match(r'coding[:=]\s*([-\w.]+)', + '\n'.join(content.splitlines()[:2])) is None: + f.write(PYTHON_PREFIX) + f.write(content) def _reset_vte(self): self._vte.grab_focus() - self._vte.feed("\x1B[H\x1B[J\x1B[0;39m") + self._vte.feed('\x1B[H\x1B[J\x1B[0;39m') - def __undobutton_cb(self, button): - global text_buffer + def __undobutton_cb(self, butston): + text_buffer = self.source_tabs.get_text_buffer() if text_buffer.can_undo(): text_buffer.undo() def __redobutton_cb(self, button): - global text_buffer + text_buffer = self.source_tabs.get_text_buffer() if text_buffer.can_redo(): text_buffer.redo() def __copybutton_cb(self, button): - global text_buffer - text_buffer.copy_clipboard(gtk.Clipboard()) + clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD) + text_buffer.copy_clipboard(clipboard) def __pastebutton_cb(self, button): - global text_buffer - text_buffer.paste_clipboard(gtk.Clipboard(), None, True) + clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD) + text_buffer.paste_clipboard(clipboard, None, True) def gobutton_cb(self, button): from shutil import copy2 - self.stopbutton_cb(button) # try stopping old code first. + self.stopbutton_cb(button) # Try stopping old code first. self._reset_vte() # FIXME: We're losing an odd race here - # gtk.main_iteration(block=False) + # Gtk.main_iteration(block=False) + + if self.toggle_output.get_active() is False: + self.outbox.show_all() + self.toggle_output.set_active(True) + + pippy_tmp_dir = '%s/tmp/' % self.get_activity_root() + self._write_all_buffers(pippy_tmp_dir) - pippy_app_name = '%s/tmp/pippy_app.py' % self.get_activity_root() - self._write_text_buffer(pippy_app_name) - # write activity.py here too, to support pippy-based activities. + current_file = os.path.join( + pippy_tmp_dir, + self.source_tabs.get_current_file_name()) + + # Write activity.py here too, to support pippy-based activities. copy2('%s/activity.py' % get_bundle_path(), '%s/tmp/activity.py' % self.get_activity_root()) - self._pid = self._vte.fork_command( - command="/bin/sh", - argv=["/bin/sh", "-c", - "python %s; sleep 1" % pippy_app_name], - envv=["PYTHONPATH=%s/library:%s" % (get_bundle_path(), - os.getenv("PYTHONPATH", ""))], - directory=get_bundle_path()) + self._pid = self._vte.fork_command_full( + Vte.PtyFlags.DEFAULT, + get_bundle_path(), + ['/bin/sh', '-c', 'python %s; sleep 1' % current_file, + 'PYTHONPATH=%s/library:%s' % (get_bundle_path(), + os.getenv('PYTHONPATH', ''))], + ['PYTHONPATH=%s/library:%s' % (get_bundle_path(), + os.getenv('PYTHONPATH', ''))], + GLib.SpawnFlags.DO_NOT_REAP_CHILD, + None, + None,) def stopbutton_cb(self, button): try: - os.kill(self._pid, SIGTERM) + if self._pid is not None: + os.kill(self._pid[1], SIGTERM) except: - pass # process must already be dead. + pass # Process must already be dead. + + def _save_as_library(self, button): + library_dir = os.path.join(get_bundle_path(), 'library') + file_name = self.source_tabs.get_current_file_name() + text_buffer = self.source_tabs.get_text_buffer() + content = text_buffer.get_text( + *text_buffer.get_bounds(), + include_hidden_chars=True) + + if not os.path.isdir(library_dir): + os.mkdir(library_dir) + + with open(os.path.join(library_dir, file_name), 'w') as f: + f.write(content) + success = True + + if success: + alert = NotifyAlert(5) + alert.props.title = _('Python File added to Library') + IMPORT_MESSAGE = _('The file you selected has been added' + ' to the library. Use "import {importname}"' + ' to import the library for using.') + alert.props.msg = IMPORT_MESSAGE.format(importname=file_name[:-3]) + alert.connect('response', self.remove_alert_cb) + self.add_alert(alert) def _export_document_cb(self, __): self.copy() + def remove_alert_cb(self, alert, response_id): + self.remove_alert(alert) + + def _import_py_cb(self, button): + chooser = ObjectChooser() + result = chooser.run() + if result is Gtk.ResponseType.ACCEPT: + dsitem = chooser.get_selected_object() + if dsitem.metadata['mime_type'] != 'text/x-python': + alert = NotifyAlert(5) + alert.props.title = _('Error importing Python file') + alert.props.msg = _('The file you selected is not a ' + 'Python file.') + alert.connect('response', self.remove_alert_cb) + self.add_alert(alert) + elif dsitem.object_id in self.session_data: + alert = NotifyAlert(5) + alert.props.title = _('Error importing Python file') + alert.props.msg = _('The file you selected is already ' + 'open') + alert.connect('response', self.remove_alert_cb) + self.add_alert(alert) + else: + name = dsitem.metadata['title'] + file_path = dsitem.get_file_path() + content = open(file_path, 'r').read() + + self.source_tabs.add_tab(name, content) + self.session_data.append(dsitem.object_id) + + chooser.destroy() + alert = NotifyAlert() + alert.props.title = _('Saved') + alert.props.msg = _('The document has been saved to journal.') + alert.connect('response', lambda x, i: self.remove_alert(x)) + self.add_alert(alert) + def _create_bundle_cb(self, __): - from shutil import copytree - from shutil import copy2 from shutil import rmtree from tempfile import mkdtemp - # get the name of this pippy program. - title = self.metadata['title'] + + # Get the name of this pippy program. + title = self.metadata['title'].replace('.py', '') + title = title.replace('-', '') if title == 'Pippy Activity': - from sugar.graphics.alert import Alert - from sugar.graphics.icon import Icon alert = Alert() alert.props.title = _('Save as Activity Error') alert.props.msg = _('Please give your activity a meaningful name ' 'before attempting to save it as an activity.') ok_icon = Icon(icon_name='dialog-ok') - alert.add_button(gtk.RESPONSE_OK, _('Ok'), ok_icon) + alert.add_button(Gtk.ResponseType.OK, _('Ok'), ok_icon) alert.connect('response', self.dismiss_alert_cb) self.add_alert(alert) return - self.stopbutton_cb(None) # try stopping old code first. - self._reset_vte() - self._vte.feed(_("Creating activity bundle...")) - self._vte.feed("\r\n") - TMPDIR = 'instance' - app_temp = mkdtemp('.activity', 'Pippy', - os.path.join(self.get_activity_root(), TMPDIR)) - sourcefile = os.path.join(app_temp, 'xyzzy.py') - # invoke ourself to build the activity bundle. - try: - # write out application code + + alert_icon = Alert() + ok_icon = Icon(icon_name='dialog-ok') + alert_icon.add_button(Gtk.ResponseType.OK, _('Ok'), ok_icon) + alert_icon.props.title = _('Activity icon') + alert_icon.props.msg = _('Please select an activity icon.') + + def internal_callback(window=None, event=None): + icon = '%s/activity/activity-default.svg' % (get_bundle_path()) + if window: + icon = window.get_icon() + self.stopbutton_cb(None) # Try stopping old code first. + self._reset_vte() + self._vte.feed(_('Creating activity bundle...')) + self._vte.feed('\r\n') + + TMPDIR = 'instance' + app_temp = mkdtemp('.activity', 'Pippy', + os.path.join(self.get_activity_root(), TMPDIR)) + sourcefile = os.path.join(app_temp, 'xyzzy.py') + # Invoke ourself to build the activity bundle. + self._logger.debug('writing out source file: %s' % sourcefile) + + # Write out application code self._write_text_buffer(sourcefile) - # hook up a callback for when the bundle builder is done. - # we can't use gobject.child_watch_add because vte will reap our - # children before we can. - self._child_exited_handler = \ - lambda: self.bundle_cb(title, app_temp) - # invoke bundle builder - self._pid = self._vte.fork_command( - command="/usr/bin/python", - argv=["/usr/bin/python", - "%s/pippy_app.py" % get_bundle_path(), - '-p', '%s/library' % get_bundle_path(), - '-d', app_temp, - title, sourcefile], - directory=app_temp) - except: - rmtree(app_temp, ignore_errors=True) # clean up! - raise + + try: + # FIXME: vte invocation was raising errors. + # Switched to subprocss + output = subprocess.check_output( + ['/usr/bin/python', + '%s/pippy_app.py' % get_bundle_path(), + '-p', '%s/library' % get_bundle_path(), + '-d', app_temp, title, sourcefile, icon]) + self._vte.feed(output) + self._vte.feed('\r\n') + self.bundle_cb(title, app_temp) + except subprocess.CalledProcessError: + rmtree(app_temp, ignore_errors=True) # clean up! + self._vte.feed(_('Save as Activity Error')) + self._vte.feed('\r\n') + raise + + def alert_response(alert, response_id): + self.remove_alert(alert) + + def dialog(): + dialog = IconDialog() + dialog.connect('destroy', internal_callback) + + GObject.idle_add(dialog) + + alert_icon.connect('response', alert_response) + self.add_alert(alert_icon) + + def _write_text_buffer(self, filename): + text_buffer = self.source_tabs.get_text_buffer() + start, end = text_buffer.get_bounds() + text = text_buffer.get_text(start, end, True) + + with open(filename, 'w') as f: + # Write utf-8 coding prefix if there's not one already + if re.match(r'coding[:=]\s*([-\w.]+)', + '\n'.join(text.splitlines()[:2])) is None: + f.write(PYTHON_PREFIX) + for line in text: + f.write(line) + + def __export_disutils_cb(self, button): + app_temp = os.path.join(self.get_activity_root(), 'instance') + data = self.source_tabs.get_all_data() + for filename, content in zip(data[0], data[1]): + fileobj = open(os.path.join(app_temp, filename), 'w') + fileobj.write(content) + fileobj.close() + + filenames = ','.join([("'"+name[:-3]+"'") for name in data[0]]) + + title = self.metadata['title'] + if title is _('Pippy Activity'): + from sugar3.graphics.alert import Alert + from sugar3.graphics.icon import Icon + alert = Alert() + alert.props.title = _('Save as disutils package error') + alert.props.msg = _('Please give your activity a meaningful' + 'name before attempting to save it ' + 'as an disutils package.') + ok_icon = Icon(icon_name='dialog-ok') + alert.add_button(Gtk.ResponseType.OK, _('Ok'), ok_icon) + alert.connect('response', self.dismiss_alert_cb) + self.add_alert(alert) + return + + setup_script = DISUTILS_SETUP_SCRIPT.format(modulename=title, + filenames=filenames) + setupfile = open(os.path.join(app_temp, 'setup.py'), 'w') + setupfile.write(setup_script) + setupfile.close() + + os.chdir(app_temp) + + subprocess.check_output( + [ + '/usr/bin/python', + os.path.join(app_temp, 'setup.py'), + 'sdist', '-v' + ]) + + # Hand off to journal + os.chmod(app_temp, 0777) + jobject = datastore.create() + metadata = { + 'title': '%s disutils bundle' % title, + 'title_set_by_user': '1', + 'mime_type': 'application/x-gzip', + } + for k, v in metadata.items(): + # The dict.update method is missing =( + jobject.metadata[k] = v + tarname = 'dist/{modulename}-1.0.tar.gz'.format(modulename=title) + jobject.file_path = os.path.join(app_temp, tarname) + datastore.write(jobject) def _export_example_cb(self, __): - # get the name of this pippy program. + # Get the name of this pippy program. title = self.metadata['title'] if title == _('Pippy Activity'): - from sugar.graphics.alert import Alert - from sugar.graphics.icon import Icon - alert = Alert() - alert.props.title =_ ('Save as Example Error') - alert.props.msg = _('Please give your activity a meaningful name before attempting to save it as an example.') - ok_icon = Icon(icon_name='dialog-ok') - alert.add_button(gtk.RESPONSE_OK, _('Ok'), ok_icon) - alert.connect('response', self.dismiss_alert_cb) - self.add_alert(alert) - return - self.stopbutton_cb(None) # try stopping old code first. + from sugar3.graphics.alert import Alert + from sugar3.graphics.icon import Icon + alert = Alert() + alert.props.title = _('Save as Example Error') + alert.props.msg = \ + _('Please give your activity a meaningful ' + 'name before attempting to save it as an example.') + ok_icon = Icon(icon_name='dialog-ok') + alert.add_button(Gtk.ResponseType.OK, _('Ok'), ok_icon) + alert.connect('response', self.dismiss_alert_cb) + self.add_alert(alert) + return + self.stopbutton_cb(None) # Try stopping old code first. self._reset_vte() - self._vte.feed(_("Creating example...")) - self._vte.feed("\r\n") - local_data = os.path.join(os.environ['SUGAR_ACTIVITY_ROOT'],'data') - local_file = os.path.join(local_data,title) + self._vte.feed(_('Creating example...')) + self._vte.feed('\r\n') + local_data = os.path.join(os.environ['SUGAR_ACTIVITY_ROOT'], 'data') + local_file = os.path.join(local_data, title) if os.path.exists(local_file): - from sugar.graphics.alert import ConfirmationAlert alert = ConfirmationAlert() - alert.props.title =_ ('Save as Example Warning') - alert.props.msg = _('This example already exists. Do you want to overwrite it?') + alert.props.title = _('Save as Example Warning') + alert.props.msg = _('This example already exists. ' + 'Do you want to overwrite it?') alert.connect('response', self.confirmation_alert_cb, local_file) self.add_alert(alert) else: - self.write_file(local_file) - self._reset_vte() - self._vte.feed(_("Saved as example.")) - self._vte.feed("\r\n") - self.add_to_example_list(local_file) - + self.write_file(local_file) + self._reset_vte() + self._vte.feed(_('Saved as example.')) + self._vte.feed('\r\n') + self.add_to_example_list(local_file) def child_exited_cb(self, *args): - """Called whenever a child exits. If there's a handler, run it.""" + '''Called whenever a child exits. If there's a handler, run it.''' h, self._child_exited_handler = self._child_exited_handler, None if h is not None: h() def bundle_cb(self, title, app_temp): - """Called when we're done building a bundle for a source file.""" - from sugar import profile + '''Called when we're done building a bundle for a source file.''' + from sugar3 import profile from shutil import rmtree - from sugar.datastore import datastore try: - # find the .xo file: were we successful? - bundle_file = [f for f in os.listdir(app_temp) \ + # Find the .xo file: were we successful? + bundle_file = [f for f in os.listdir(app_temp) if f.endswith('.xo')] if len(bundle_file) != 1: self._logger.debug("Couldn't find bundle: %s" % str(bundle_file)) - return # something went wrong. - # hand off to journal + self._vte.feed('\r\n') + self._vte.feed(_('Error saving activity to journal.')) + self._vte.feed('\r\n') + return # Something went wrong. + # Hand off to journal os.chmod(app_temp, 0755) jobject = datastore.create() metadata = { @@ -551,12 +798,13 @@ class PippyActivity(ViewSourceActivity, groupthink.sugar_tools.GroupActivity): 'mime_type': 'application/vnd.olpc-sugar', } for k, v in metadata.items(): - jobject.metadata[k] = v # the dict.update method is missing =( + # The dict.update method is missing =( + jobject.metadata[k] = v jobject.file_path = os.path.join(app_temp, bundle_file[0]) datastore.write(jobject) - self._vte.feed("\r\n") - self._vte.feed(_("Activity saved to journal.")) - self._vte.feed("\r\n") + self._vte.feed('\r\n') + self._vte.feed(_('Activity saved to journal.')) + self._vte.feed('\r\n') self.journal_show_object(jobject.object_id) jobject.destroy() finally: @@ -565,33 +813,62 @@ class PippyActivity(ViewSourceActivity, groupthink.sugar_tools.GroupActivity): def dismiss_alert_cb(self, alert, response_id): self.remove_alert(alert) - - def confirmation_alert_cb(self, alert, response_id, local_file): #callback for conf alert + def confirmation_alert_cb(self, alert, response_id, local_file): + # Callback for conf alert self.remove_alert(alert) - if response_id is gtk.RESPONSE_OK: - self.write_file(local_file) - self._reset_vte() - self._vte.feed(_("Saved as example.")) - self._vte.feed("\r\n") + if response_id is Gtk.ResponseType.OK: + self.write_file(local_file) + self._reset_vte() + self._vte.feed(_('Saved as example.')) + self._vte.feed('\r\n') else: - self._reset_vte() + self._reset_vte() - def add_to_example_list(self,local_file): # def for add example - entry = { "name": _(os.path.basename(local_file)), - "path": local_file } + def add_to_example_list(self, local_file): # def for add example + entry = {'name': _(os.path.basename(local_file)), + 'path': local_file} _iter = self.model.insert_before(self.example_iter, None) self.model.set_value(_iter, 0, entry) - self.model.set_value(_iter, 1, entry["name"]) - + self.model.set_value(_iter, 1, entry['name']) def save_to_journal(self, file_path, cloudstring): _file = open(file_path, 'w') - if not self._shared_activity: - self.metadata['mime_type'] = 'text/x-python' - global text_buffer - start, end = text_buffer.get_bounds() - text = text_buffer.get_text(start, end) - _file.write(text) + if not self.shared_activity: + data = self.source_tabs.get_all_data() + zipped_data = zip(data[0], data[1]) + sessionlist = [] + app_temp = os.path.join(self.get_activity_root(), 'instance') + tmpfile = os.path.join(app_temp, + 'pippy-tempfile-storing.py') + for zipdata, dsid in map(None, zipped_data, self.session_data): + name, content = zipdata + + if dsid is not None: + dsitem = datastore.get(dsid) + __file = open(tmpfile, 'w') + __file.write(content) + __file.close() + dsitem.set_file_path(tmpfile) + dsitem.metadata['title'] = name + datastore.write(dsitem) + else: + dsobject = datastore.create() + dsobject.metadata['mime_type'] = 'text/x-python' + dsobject.metadata['title'] = name + __file = open(tmpfile, 'w') + __file.write(content) + __file.close() + dsobject.set_file_path(tmpfile) + datastore.write(dsobject) + dsitem = None + + if dsitem is not None: + sessionlist.append([name, dsitem.object_id]) + else: + sessionlist.append([name, dsobject.object_id]) + + self.metadata['mime_type'] = 'application/json' + _file.write(json.dumps(sessionlist)) else: self.metadata['mime_type'] = groupthink_mimetype _file.write(cloudstring) @@ -599,72 +876,55 @@ class PippyActivity(ViewSourceActivity, groupthink.sugar_tools.GroupActivity): def load_from_journal(self, file_path): if self.metadata['mime_type'] == 'text/x-python': text = open(file_path).read() - # discard the '#!/usr/bin/python' and 'coding: utf-8' lines, - # if present + # Discard the '#!/usr/bin/python' and 'coding: utf-8' lines, + # if present. text = re.sub(r'^' + re.escape(PYTHON_PREFIX), '', text) - global text_buffer - text_buffer.set_text(text) + + self.initial_text_buffer = text + self.initial_title = self.metadata['title'] + self.loaded_from_journal = self.py_file = True + + elif self.metadata['mime_type'] == 'application/json': + data = json.loads(open(file_path).read()) + for name, dsid in data: + dsitem = datastore.get(dsid) + content = open(dsitem.get_file_path()).read() + self.loaded_session.append([name, content]) + self.session_data.append(dsitem.object_id) + elif self.metadata['mime_type'] == groupthink_mimetype: return open(file_path).read() ############# TEMPLATES AND INLINE FILES ############## -ACTIVITY_INFO_TEMPLATE = """ +ACTIVITY_INFO_TEMPLATE = ''' [Activity] name = %(title)s bundle_id = %(bundle_id)s -service_name = %(bundle_id)s -class = %(class)s +exec = sugar-activity %(class)s icon = activity-icon activity_version = %(version)d mime_types = %(mime_types)s show_launcher = yes %(extra_info)s -""" +''' PIPPY_ICON = \ -""" - -]> - - - +""" + +]> + + + + -""" -PIPPY_DEFAULT_ICON = \ -""" - - - - -]> - - - - - - - """ ############# ACTIVITY META-INFORMATION ############### @@ -672,13 +932,13 @@ PIPPY_DEFAULT_ICON = \ def pippy_activity_version(): - """Returns the version number of the generated activity bundle.""" + '''Returns the version number of the generated activity bundle.''' return 39 def pippy_activity_extra_files(): - """Returns a map of 'extra' files which should be included in the - generated activity bundle.""" + '''Returns a map of 'extra' files which should be included in the + generated activity bundle.''' # Cheat here and generate the map from the fs contents. extra = {} bp = get_bundle_path() @@ -687,93 +947,101 @@ def pippy_activity_extra_files(): for name in files: fn = os.path.join(root, name).replace(bp + '/', '') extra[fn] = open(os.path.join(root, name), 'r').read() - extra['activity/activity-default.svg'] = PIPPY_DEFAULT_ICON return extra def pippy_activity_news(): - """Return the NEWS file for this activity.""" + '''Return the NEWS file for this activity.''' # Cheat again. return open(os.path.join(get_bundle_path(), 'NEWS')).read() def pippy_activity_icon(): - """Return an SVG document specifying the icon for this activity.""" + '''Return an SVG document specifying the icon for this activity.''' return PIPPY_ICON def pippy_activity_class(): - """Return the class which should be started to run this activity.""" + '''Return the class which should be started to run this activity.''' return 'pippy_app.PippyActivity' def pippy_activity_bundle_id(): - """Return the bundle_id for the generated activity.""" + '''Return the bundle_id for the generated activity.''' return 'org.laptop.Pippy' def pippy_activity_mime_types(): - """Return the mime types handled by the generated activity, as a list.""" + '''Return the mime types handled by the generated activity, as a list.''' return ['text/x-python', groupthink_mimetype] def pippy_activity_extra_info(): - return """ + return ''' license = GPLv2+ -update_url = http://activities.sugarlabs.org """ +update_url = http://activities.sugarlabs.org ''' ################# ACTIVITY BUNDLER ################ def main(): - """Create a bundle from a pippy-style source file""" + '''Create a bundle from a pippy-style source file''' from optparse import OptionParser from pyclbr import readmodule_ex from tempfile import mkdtemp from shutil import copytree, copy2, rmtree - from sugar import profile - from sugar.activity import bundlebuilder - import sys - parser = OptionParser(usage='%prog [options] [title] [sourcefile]') + from sugar3.activity import bundlebuilder + + parser = OptionParser(usage='%prog [options] [title] [sourcefile] [icon]') parser.add_option('-d', '--dir', dest='dir', default='.', metavar='DIR', help='Put generated bundle in the specified directory.') parser.add_option('-p', '--pythonpath', dest='path', action='append', default=[], metavar='DIR', help='Append directory to python search path.') + (options, args) = parser.parse_args() - if len(args) != 2: - parser.error('The title and sourcefile arguments are required.') + if len(args) < 3: + parser.error('The title, sourcefile and icon arguments are required.') + title = args[0] sourcefile = args[1] + icon_path = args[2] pytitle = re.sub(r'[^A-Za-z0-9_]', '', title) if re.match(r'[0-9]', pytitle) is not None: pytitle = '_' + pytitle # first character cannot be numeric - # first take a gander at the source file and see if it's got extra info + + # First take a gander at the source file and see if it's got extra info # for us. sourcedir, basename = os.path.split(sourcefile) if not sourcedir: sourcedir = '.' module, ext = os.path.splitext(basename) - # things we look for: + f = open(icon_path, 'r') + icon = f.read() + f.close() + # Things we look for: bundle_info = { 'version': 1, 'extra_files': {}, 'news': 'No news.', - 'icon': PIPPY_DEFAULT_ICON, + 'icon': icon, 'class': 'activity.VteActivity', - 'bundle_id': ('org.laptop.pippy.%s' % pytitle), + 'bundle_id': ('org.sugarlabs.pippy.%s%d' % + (generate_unique_id(), + int(round(uniform(1000, 9999), 0)))), 'mime_types': '', 'extra_info': '', } - # are any of these things in the module? + # Are any of these things in the module? try_import = False + info = readmodule_ex(module, [sourcedir] + options.path) for func in bundle_info.keys(): p_a_func = 'pippy_activity_%s' % func if p_a_func in info: try_import = True if try_import: - # yes, let's try to execute them to get better info about our bundle + # Yes, let's try to execute them to get better info about our bundle oldpath = list(sys.path) sys.path[0:0] = [sourcedir] + options.path modobj = __import__(module) @@ -783,9 +1051,9 @@ def main(): bundle_info[func] = modobj.__dict__[p_a_func]() sys.path = oldpath - # okay! We've done the hard part. Now let's build a bundle. - # create a new temp dir in which to create the bundle. - app_temp = mkdtemp('.activity', 'Pippy') # hope TMPDIR is set correctly! + # Okay! We've done the hard part. Now let's build a bundle. + # Create a new temp dir in which to create the bundle. + app_temp = mkdtemp('.activity', 'Pippy') # Hope TMPDIR is set correctly! bundle = get_bundle_path() try: copytree('%s/library' % bundle, '%s/library' % app_temp) @@ -809,39 +1077,36 @@ def main(): os.makedirs(dirname) with open(os.path.join(dirname, filename), 'w') as f: f.write(contents) - # put script into $app_temp/pippy_app.py + # Put script into $app_temp/pippy_app.py copy2(sourcefile, '%s/pippy_app.py' % app_temp) - # write MANIFEST file. - with open('%s/MANIFEST' % app_temp, 'w') as f: - for dirpath, dirnames, filenames in sorted(os.walk(app_temp)): - for name in sorted(filenames): - fn = os.path.join(dirpath, name) - fn = fn.replace(app_temp + '/', '') - if fn == 'MANIFEST': - continue - f.write('%s\n' % fn) - # invoke bundle builder + # Invoke bundle builder olddir = os.getcwd() oldargv = sys.argv os.chdir(app_temp) sys.argv = ['setup.py', 'dist_xo'] + print('\r\nStarting bundlebuilder\r\n') bundlebuilder.start() sys.argv = oldargv os.chdir(olddir) - # move to destination directory. - copy2('%s/dist/%s-%d.xo' % (app_temp, pytitle, bundle_info['version']), - '%s/%s-%d.xo' % (options.dir, pytitle, bundle_info['version'])) + # Move to destination directory. + src = '%s/dist/%s-%d.xo' % (app_temp, pytitle, bundle_info['version']) + dst = '%s/%s-%d.xo' % (options.dir, pytitle, bundle_info['version']) + if not os.path.exists(src): + print('Cannot find %s\r\n' % (src)) + else: + copy2(src, dst) finally: rmtree(app_temp, ignore_errors=True) + print('Finally\r\n') if __name__ == '__main__': - from gettext import gettext as _ import sys - if False: # change this to True to test within Pippy + from gettext import gettext as _ + if False: # Change this to True to test within Pippy sys.argv = sys.argv + ['-d', '/tmp', 'Pippy', '/home/olpc/pippy_app.py'] - #print _("Working..."), - #sys.stdout.flush() + print(_('Working...')) + sys.stdout.flush() main() - #print _("done!") + print(_('done!')) sys.exit(0) -- cgit v0.9.1