Exemple #1
0
def profile_memory():
    from pympler.classtracker import ClassTracker
    from pympler.asizeof import asizeof
    from soap.common import Flyweight, _cache_map
    from soap.expression import Expr
    tracker = ClassTracker()
    tracker.track_object(Flyweight._cache)
    tracker.track_class(Expr)
    yield
    tracker.create_snapshot()
    tracker.stats.print_summary()
    print('Flyweight cache size', asizeof(Flyweight._cache))
    print('Global cache size', asizeof(_cache_map))
Exemple #2
0
class TrackObjectTestCase(unittest.TestCase):

    def setUp(self):
        self.tracker = ClassTracker()

    def tearDown(self):
        self.tracker.detach_all()

    def test_track_object(self):
        """Test object registration.
        """
        foo = Foo()
        bar = Bar()

        self.tracker.track_object(foo)
        self.tracker.track_object(bar)

        self.assert_(id(foo) in self.tracker.objects)
        self.assert_(id(bar) in self.tracker.objects)

        self.assert_('Foo' in self.tracker.index)
        self.assert_('Bar' in self.tracker.index)

        self.assertEqual(self.tracker.objects[id(foo)].ref(),foo)
        self.assertEqual(self.tracker.objects[id(bar)].ref(),bar)

    def test_type_errors(self):
        """Test intrackable objects.
        """
        i = 42
        j = 'Foobar'
        k = [i,j]
        l = {i: j}

        self.assertRaises(TypeError, self.tracker.track_object, i)
        self.assertRaises(TypeError, self.tracker.track_object, j)
        self.assertRaises(TypeError, self.tracker.track_object, k)
        self.assertRaises(TypeError, self.tracker.track_object, l)

        self.assert_(id(i) not in self.tracker.objects)
        self.assert_(id(j) not in self.tracker.objects)
        self.assert_(id(k) not in self.tracker.objects)
        self.assert_(id(l) not in self.tracker.objects)

    def test_track_by_name(self):
        """Test registering objects by name.
        """
        foo = Foo()

        self.tracker.track_object(foo, name='Foobar')

        self.assert_('Foobar' in self.tracker.index        )
        self.assertEqual(self.tracker.index['Foobar'][0].ref(),foo)

    def test_keep(self):
        """Test lifetime of tracked objects.
        """
        foo = Foo()
        bar = Bar()

        self.tracker.track_object(foo, keep=1)
        self.tracker.track_object(bar)

        idfoo = id(foo)
        idbar = id(bar)

        del foo
        del bar

        self.assert_(self.tracker.objects[idfoo].ref() is not None)
        self.assert_(self.tracker.objects[idbar].ref() is None)

    def test_mixed_tracking(self):
        """Test mixed instance and class tracking.
        """
        foo = Foo()
        self.tracker.track_object(foo)
        self.tracker.create_snapshot()
        self.tracker.track_class(Foo)
        objs = []
        for _ in range(10):
            objs.append(Foo())
        self.tracker.create_snapshot()

    def test_recurse(self):
        """Test recursive sizing and saving of referents.
        """
        foo = Foo()

        self.tracker.track_object(foo, resolution_level=1)
        self.tracker.create_snapshot()

        fp = self.tracker.objects[id(foo)].snapshots[-1]
        refs = fp[1].refs
        dref = [r for r in refs if r.name == '__dict__']
        self.assertEqual(len(dref),1)
        dref = dref[0]
        self.assert_(dref.size > 0, dref.size)
        self.assert_(dref.flat > 0, dref.flat)
        self.assertEqual(dref.refs,())

        # Test track_change and more fine-grained resolution
        self.tracker.track_change(foo, resolution_level=2)
        self.tracker.create_snapshot()

        fp = self.tracker.objects[id(foo)].snapshots[-1]
        refs = fp[1].refs
        dref = [r for r in refs if r.name == '__dict__']
        self.assertEqual(len(dref),1)
        dref = dref[0]
        namerefs = [r.name for r in dref.refs]
        self.assert_('[K] foo' in namerefs, namerefs)
        self.assert_("[V] foo: 'foo'" in namerefs, namerefs)
Exemple #3
0
class SnapshotTestCase(unittest.TestCase):

    def setUp(self):
        self.tracker = ClassTracker()

    def tearDown(self):
        self.tracker.stop_periodic_snapshots()
        self.tracker.clear()

    def test_timestamp(self):
        """Test timestamp of snapshots.
        """
        foo = Foo()
        bar = Bar()

        self.tracker.track_object(foo)
        self.tracker.track_object(bar)

        self.tracker.create_snapshot()
        self.tracker.create_snapshot()
        self.tracker.create_snapshot()

        refts = [fp.timestamp for fp in self.tracker.snapshots]
        for to in self.tracker.objects.values():
            ts = [t for (t,sz) in to.snapshots[1:]]
            self.assertEqual(ts,refts)

    def test_snapshot_members(self):
        """Test existence and value of snapshot members.
        """
        foo = Foo()
        self.tracker.track_object(foo)
        self.tracker.create_snapshot()
        self.tracker.create_snapshot(compute_total=True)

        fp = self.tracker.snapshots[0]
        fp_with_total = self.tracker.snapshots[1]

        self.assert_(fp.overhead > 0, fp.overhead)
        self.assert_(fp.tracked_total > 0, fp.tracked_total)
        self.assertEqual(fp.asizeof_total, 0)

        self.assert_(fp_with_total.asizeof_total > 0, fp_with_total.asizeof_total)
        self.assert_(fp_with_total.asizeof_total >= fp_with_total.tracked_total)

        if pympler.process.is_available():
            procmem = fp.system_total
            self.assertEqual(fp.total, procmem.vsz)
            self.assert_(procmem.vsz > 0, procmem)
            self.assert_(procmem.rss > 0, procmem)
            self.assertTrue(procmem.vsz >= procmem.rss, procmem)
            self.assert_(procmem.vsz > fp.overhead, procmem)
            self.assert_(procmem.vsz > fp.tracked_total, procmem)
            self.assert_(fp_with_total.system_total.vsz > fp_with_total.asizeof_total)
        else:
            self.assertEqual(fp_with_total.total, fp_with_total.asizeof_total)
            self.assertEqual(fp.total, fp.tracked_total)


    def test_desc(self):
        """Test snapshot label.
        """
        self.tracker.create_snapshot()
        self.tracker.create_snapshot('alpha')
        self.tracker.create_snapshot(description='beta')
        self.tracker.create_snapshot(42)

        self.assertEqual(len(self.tracker.snapshots), 4)
        self.assertEqual(self.tracker.snapshots[0].desc, '')
        self.assertEqual(self.tracker.snapshots[1].desc, 'alpha')
        self.assertEqual(self.tracker.snapshots[2].desc, 'beta')
        self.assertEqual(self.tracker.snapshots[3].desc, '42')

        snapshot = self.tracker.snapshots[0]
        self.assertEqual(snapshot.label, '%.3fs' % snapshot.timestamp)
        snapshot = self.tracker.snapshots[1]
        self.assertEqual(snapshot.label, 'alpha (%.3fs)' % snapshot.timestamp)
        snapshot = self.tracker.snapshots[3]
        self.assertEqual(snapshot.label, '42 (%.3fs)' % snapshot.timestamp)


    def test_background_monitoring(self):
        """Test background monitoring.
        """
        self.tracker.start_periodic_snapshots(0.1)
        self.assertEqual(self.tracker._periodic_thread.interval, 0.1)
        self.assertEqual(self.tracker._periodic_thread.getName(), 'BackgroundMonitor')
        for x in range(10): # try to interfere
            self.tracker.create_snapshot(str(x))
        time.sleep(0.5)
        self.tracker.start_periodic_snapshots(0.2)
        self.assertEqual(self.tracker._periodic_thread.interval, 0.2)
        self.tracker.stop_periodic_snapshots()
        self.assert_(self.tracker._periodic_thread is None)
        self.assert_(len(self.tracker.snapshots) > 10)
Exemple #4
0
class GlobalContainer(box.BoxLayout):

    graph_list = prop.ListProperty([])
    active_graph = prop.ObjectProperty(None)
    name_manager = NameManager.Instance()
    show_sidepanel = prop.BooleanProperty(True)
    modestr = prop.StringProperty('insert')
    itemstr = prop.StringProperty('atom')

    modes = {'insert': asp.Mode.INSERT, 'select': asp.Mode.SELECT}
    items = {
        'atom': asp.Item.ATOM,
        'ellipse': asp.Item.ELLIPSE,
        'rectangle': asp.Item.SQUARE
    }

    def __init__(self, **kwargs):
        super(GlobalContainer, self).__init__(**kwargs)
        self._keyboard = None
        self.request_keyboard()
        self.working_dir = './'
        self.tutorial = None
        self.popup_stack = []
        window.Window.bind(on_resize=self.on_resize)

        if DEBUG:
            self.tracker = ClassTracker()
            self.tracker.track_object(MenuButton)
            self.all_objects = muppy.get_objects()

    def request_keyboard(self):
        self._keyboard = window.Window.request_keyboard(
            self._keyboard_release, self)
        self._keyboard.bind(on_key_down=self._on_keyboard_down)

    def _keyboard_catch(self):
        self._keyboard.bind(on_key_down=self._on_keyboard_down)

    def _keyboard_release(self):
        self._keyboard.unbind(on_key_down=self._on_keyboard_down)
        #self._keyboard = None

    def _on_keyboard_down(self, keyboard, keycode, text, modifiers):
        if keycode[1] == 'escape':
            base.stopTouchApp()
        elif keycode[1] == 'd':
            self.set_mode('insert')
        elif keycode[1] == 's':
            self.set_mode('select')
        elif keycode[1] == 'w':
            self.set_item('atom')
        elif keycode[1] == 'e':
            self.set_item('ellipse')
        elif keycode[1] == 'r':
            self.set_item('rectangle')
        elif keycode[1] == 'p':
            self.active_graph.show_tree(0)
        elif keycode[1] == 'o':
            self.view_symbolic_formula()
        elif keycode[1] == 'n':
            self.show_gringo_query()
        elif keycode[1] == 'g':
            self.show_save()
        elif keycode[1] == 'l':
            self.show_load()
        elif keycode[1] == 'y':
            print self.name_manager.get_all()
            print asp.Line.get_all_lines()
        elif keycode[1] == 't':
            self.toggle_sidepanel()
        elif keycode[1] == 'tab':
            if not self.show_sidepanel:
                self.toggle_sidepanel()
            self.ids.atom_input.focus = True
        elif keycode[1] == '.':
            if DEBUG:
                rb = refbrowser.InteractiveBrowser(self)
                rb.main()

                # self.all_objects = muppy.get_objects()
                # sum1 = summary.summarize(self.all_objects)
                # summary.print_(sum1)

                # self.tracker.create_snapshot()
                # self.tracker.stats.print_summary()
        return True

    def _focus_name_list(self, value):
        if value and (asp.AtomWidget.active_atom is not None):
            previous_name = asp.AtomWidget.active_atom.name
            for button in self.ids.name_list.children:
                if (button.text == previous_name) and (button.state != 'down'):
                    button.trigger_action()
        elif value:
            if self.ids.name_list.children:
                self.ids.name_list.children[-1].trigger_action()
        else:
            for button in self.ids.name_list.children:
                if button.state == 'down':
                    button.trigger_action()

    def on_resize(self, window, width, height):
        if self.show_sidepanel:
            self.ids.sidepanel.width = self.width * .15
        self.active_graph.size = self.ids.stencilview.size

    def toggle_sidepanel(self):
        self.show_sidepanel = not self.show_sidepanel
        if self.show_sidepanel:
            width = self.width * .15
        else:
            width = 0
        anim.Animation(width=width, d=.15,
                       t='out_quart').start(self.ids.sidepanel)
        if not self.show_sidepanel:
            self.ids.sidepanel.focus = False
            # Also release keyboard
        #self.update_sourcecode()

    def update_atom_editor(self, name):
        editor = self.ids.atom_editor
        if name == '':
            editor.disabled = True
        else:
            if editor.disabled:
                editor.disabled = False
            atom = self.name_manager.get(name)
            editor.update(atom)

    def update_atom(self,
                    name,
                    new_name='',
                    new_hook_points=[],
                    is_constant=False):
        atom = self.name_manager.get(name)
        if new_name <> '':
            atom.name = new_name
        if len(new_hook_points) == 4:
            atom.hook_points = new_hook_points
            # print id(atom.hook_points)
        atom.is_constant = is_constant

    def register_atom(self,
                      name,
                      hooks=[False, False, False, False],
                      is_constant=False):
        if name == '':
            return
        children = self.ids.name_list.children
        new_button = None
        name_to_insert = name
        i = len(children) - 1

        # If the name doesn't exist, register atom
        if self.name_manager.get(name) is None:
            self.name_manager.register(name, asp.Atom(name, hooks,
                                                      is_constant))
            # print id(self.name_manager.get(name).hook_points)

        # Insert in name_list sorted by name
        while i >= 0:
            if children[i].text < name_to_insert:
                #print children[i].text, '<',  name_to_insert
                pass
            elif children[i].text == name_to_insert:
                # Already exists
                if children[i].state != 'down':
                    children[i].trigger_action()
                return
            elif children[i].text > name_to_insert:
                #print children[i].text, '>',  name_to_insert
                temp = children[i].text
                children[i].text = name_to_insert
                name_to_insert = temp
                if new_button == None:
                    new_button = children[i]
            i -= 1

        self.ids.name_list.add_widget(AtomSelectionButton(text=name_to_insert))
        if new_button == None:
            new_button = children[0]
        if new_button.state == 'down':
            asp.AtomWidget.active_atom = self.name_manager.get(name)
            self.update_atom_editor(name)
            self.set_item('atom')
        else:
            new_button.trigger_action()

    def rename_atom(self, new_name):
        old_name = ''
        for button in self.ids.name_list.children:
            if button.state == 'down':
                old_name = button.text
                selected_button = button
        if (old_name != '') and (new_name != ''):
            try:
                atom = self.name_manager.get(old_name)
                exists = self.name_manager.get(new_name)
                assert atom is not None
                assert exists is None
            except AssertionError:
                #self.show_error('Name already exists.')
                print 'Name already exists.'
                return
            selected_button.text = new_name
            atom.name = new_name
            self.name_manager.unregister(old_name)
            self.name_manager.register(new_name, atom)
            self.update_atom_editor(new_name)

    def delete_atom(self):
        for button in self.ids.name_list.children:
            if button.state == 'down':
                self.name_manager.unregister(button.text)
                self.active_graph.delete_atom(button.text)
                asp.AtomWidget.active_atom = None
                self.ids.name_list.remove_widget(button)
                self.update_atom_editor('')

    def clear_atoms(self):
        self.ids.name_list.clear_widgets()
        self.name_manager.clear()
        self.update_atom_editor('')
        asp.AtomWidget.active_atom = None

    def new_graph(self):
        # TODO: Migrate to tab system
        asp.Line.clear_lines()
        self.clear_atoms()
        if self.active_graph is None:
            g = asp.RootWidget()
            self.graph_list.append(g)
            self.active_graph = g
            self.ids.stencilview.add_widget(g)
        else:
            self.active_graph.delete_tree()

    def close_graph(self):
        if self.active_graph is not None:
            asp.Line.clear_lines()
            self.active_graph.delete_tree()
            self.active_graph.delete_root()
            self.clear_atoms()
            self.graph_list.pop()
            self.active_graph = None

    def set_mode(self, mode):
        try:
            prev_mode = self.active_graph.mode
            new_mode = self.modes[mode]
            if new_mode == asp.Mode.INSERT:
                self.active_graph.show_hooks()
            else:
                if prev_mode == asp.Mode.INSERT:
                    self.active_graph.hide_hooks()
            self.active_graph.mode = new_mode
            self.modestr = mode
        except KeyError as err:
            print 'ERROR: Invalid mode {0} requested.'.format(str(err))

    def set_item(self, item):
        try:
            self.active_graph.item = self.items[item]
            self.itemstr = item
        except KeyError as err:
            print 'ERROR: Invalid item {0} requested.'.format(str(err))
        except AttributeError:
            pass
        if item == 'atom':
            self._focus_name_list(True)
        else:
            self._focus_name_list(False)

    def push_popup(self, popup):
        self.popup_stack.append(popup)
        popup.open()

    def dismiss_popup(self):
        popup = self.popup_stack.pop()
        popup.dismiss()

    def show_rename_atom(self):
        content = TextInputDialog(caption="Enter new name",
                                  validate_callback=self.rename_atom,
                                  cancel=self.dismiss_popup)
        p = CustomPopup(self,
                        title="Rename atom",
                        content=content,
                        size_hint=(0.4, 0.25))
        self.push_popup(p)

    def show_load(self):
        content = LoadDialog(load=self.load, cancel=self.dismiss_popup)
        content.ids.filechooser.path = self.working_dir
        p = CustomPopup(self,
                        title="Load file",
                        content=content,
                        size_hint=(0.9, 0.9))
        self.push_popup(p)

    def show_save(self):
        content = SaveDialog(save=self.save, cancel=self.dismiss_popup)
        content.ids.filechooser.path = self.working_dir
        p = CustomPopup(self,
                        title="Save file",
                        content=content,
                        size_hint=(0.9, 0.9))
        self.push_popup(p)

    def show_export(self):
        content = ExportDialog(export=self.export, cancel=self.dismiss_popup)
        content.ids.filechooser.path = self.working_dir
        p = CustomPopup(self,
                        title="Export file",
                        content=content,
                        size_hint=(0.9, 0.9))
        self.push_popup(p)

    def show_gringo_query(self):
        caption = "Enter desired predicates separated by commas"
        content = TextInputDialog(caption=caption,
                                  validate_callback=self.gringo_query,
                                  dismiss_on_validate=False,
                                  focus=False,
                                  cancel=self.dismiss_popup)
        p = CustomPopup(self,
                        title="Output predicates",
                        content=content,
                        size_hint=(0.4, 0.25))
        self.push_popup(p)

    def show_stable_models(self, solver):
        models = solver.get_models()
        content = None
        if len(models) == 0:
            content = ErrorDialog('Unsatisfiable', cancel=self.dismiss_popup)
        else:
            solver.generate_graph(models[0])
            content = StableModelDialog(solver, cancel=self.dismiss_popup)
            content.ids.img.reload()
        p = CustomPopup(self,
                        catch_keyboard=False,
                        title="Stable Models",
                        content=content,
                        size_hint=(0.9, 0.9))
        self.push_popup(p)

    def show_error(self, err_str):
        content = ErrorDialog(err_str, cancel=self.dismiss_popup)
        p = CustomPopup(self,
                        catch_keyboard=False,
                        title="Error",
                        content=content,
                        size_hint=(0.4, 0.3))
        self.push_popup(p)

    def show_about(self):
        content = AboutDialog(cancel=self.dismiss_popup)
        p = CustomPopup(self,
                        catch_keyboard=False,
                        title="About " + __title__,
                        content=content,
                        size_hint=(0.5, 0.5))
        self.push_popup(p)

    def load(self, path, filename):
        self.close_graph()
        self.working_dir = path

        try:
            f = os.path.join(path, filename[0])
            # Temporal line storage. Its contents are arranged as follows:
            # { line_id: (graph, hook_list) , ... }
            lines = {}
            with open(f, 'r') as stream:
                for line in stream:
                    if line.startswith(NameParser.TOKENS['name']):
                        name, hooks, is_constant = NameParser.parse_name(line)
                        self.register_atom(name, hooks, is_constant)
                    if line.startswith(NameParser.TOKENS['line']):
                        line_id, graph = NameParser.parse_line(line)
                        lines[line_id] = (graph, [None] * len(graph))

            new_graph = lang.Builder.load_file(f)
            self.ids.stencilview.add_widget(new_graph)
            self.active_graph = new_graph
            #self.graph_list.pop()
            self.graph_list.append(new_graph)
            for w in self.active_graph.walk(restrict=True):
                if isinstance(w, asp.AtomWidget):
                    w._deferred_init()
                    for i in w.get_line_info():
                        line_id = i[0]
                        hook_index = i[1]
                        lines[line_id][1][hook_index] = i[2]
                elif isinstance(w, asp.NexusWidget):
                    for i in w.line_info:
                        line_id = i[0]
                        hook_index = i[1]
                        lines[line_id][1][hook_index] = w

            for line, info in lines.iteritems():
                print line, info
                asp.Line.build_from_graph(info[0], info[1])

            self.set_mode(self.modestr)
            self.set_item(self.itemstr)
            self.dismiss_popup()
        except (KeyboardInterrupt, SystemExit):
            raise
        except:
            self.dismiss_popup()
            self.close_graph()
            self.new_graph()
            self.show_error('Corrupted file.')

    def save(self, path, filename):
        self.working_dir = path
        with open(os.path.join(path, filename), 'w') as stream:
            stream.write('#:kivy 1.0.9\n\n')
            for (name, atom) in self.name_manager.get_all():
                stream.write(
                    NameParser.get_name_str(name, atom.hook_points,
                                            atom.is_constant))
            for line in asp.Line.get_all_lines():
                stream.write(
                    NameParser.get_line_str(line.line_id,
                                            line.get_full_graph()))
            stream.write('\n')
            stream.write(self.active_graph.get_tree(0))
        self.dismiss_popup()

    def export(self, path, filename):
        _, ext = os.path.splitext(filename)
        if ext == '.png':
            self.active_graph.export_to_png(os.path.join(path, filename))
        elif ext == '.lp':
            rpn = self.active_graph.get_formula_RPN()
            constants = self.active_graph.get_constants()
            solver = eg_solver.Solver()
            solver.set_formula(rpn, constants)
            rules = solver.generate_asp_rules()
            with open(os.path.join(path, filename), 'w') as stream:
                for r in rules:
                    stream.write(r)
                    stream.write('\n')
        else:
            error_str = 'File extension not supported.'
            print error_str
            self.show_error(error_str)
            return
        self.dismiss_popup()

    def highlight_variables(self):
        self.active_graph.highlight_variables()

    def view_symbolic_formula(self):
        print self.active_graph.get_formula()

    def gringo_query(self, show_predicates):
        def generate_show_statements(predicates):
            pred_list = predicates.split(',')
            pred_list = map(lambda s: s.strip(), pred_list)
            n_args = (lambda name: self.name_manager.get(name).hook_points.
                      count(True))
            pred_list = [p + '/' + str(n_args(p)) for p in pred_list]
            show_list = ['#show {0}.'.format(p) for p in pred_list]
            return show_list

        self.dismiss_popup()

        rpn = self.active_graph.get_formula_RPN()
        rpn = norm.LIT.TRUE if rpn == '' else rpn
        constants = self.active_graph.get_constants()
        print 80 * '-'
        print 'RPN formula:\n', rpn

        solver = eg_solver.Solver()
        result = ''
        try:
            show_statements = []
            if show_predicates:
                try:
                    show_statements = generate_show_statements(show_predicates)
                except Exception:
                    pass
            solver.set_formula(rpn, constants)
            result = solver.solve(show=show_statements)
        except norm.MalformedFormulaError:
            self.show_error('Malformed formula.')
            return
        except RuntimeError, e:
            print e
            self.show_error(str(e))
            return
        self.show_stable_models(solver)
Exemple #5
0
class TrackObjectTestCase(unittest.TestCase):
    def setUp(self):
        self.tracker = ClassTracker()

    def tearDown(self):
        self.tracker.detach_all()

    def test_track_object(self):
        """Test object registration.
        """
        foo = Foo()
        bar = Bar()

        self.tracker.track_object(foo)
        self.tracker.track_object(bar)

        self.assertTrue(id(foo) in self.tracker.objects)
        self.assertTrue(id(bar) in self.tracker.objects)

        self.assertTrue('Foo' in self.tracker.index)
        self.assertTrue('Bar' in self.tracker.index)

        self.assertEqual(self.tracker.objects[id(foo)].ref(), foo)
        self.assertEqual(self.tracker.objects[id(bar)].ref(), bar)

    def test_type_errors(self):
        """Test intrackable objects.
        """
        i = 42
        j = 'Foobar'
        k = [i, j]
        l = {i: j}

        self.assertRaises(TypeError, self.tracker.track_object, i)
        self.assertRaises(TypeError, self.tracker.track_object, j)
        self.assertRaises(TypeError, self.tracker.track_object, k)
        self.assertRaises(TypeError, self.tracker.track_object, l)

        self.assertTrue(id(i) not in self.tracker.objects)
        self.assertTrue(id(j) not in self.tracker.objects)
        self.assertTrue(id(k) not in self.tracker.objects)
        self.assertTrue(id(l) not in self.tracker.objects)

    def test_track_by_name(self):
        """Test registering objects by name.
        """
        foo = Foo()

        self.tracker.track_object(foo, name='Foobar')

        self.assertTrue('Foobar' in self.tracker.index)
        self.assertEqual(self.tracker.index['Foobar'][0].ref(), foo)

    def test_keep(self):
        """Test lifetime of tracked objects.
        """
        foo = Foo()
        bar = Bar()

        self.tracker.track_object(foo, keep=1)
        self.tracker.track_object(bar)

        idfoo = id(foo)
        idbar = id(bar)

        del foo
        del bar

        self.assertTrue(self.tracker.objects[idfoo].ref() is not None)
        self.assertTrue(self.tracker.objects[idbar].ref() is None)

    def test_mixed_tracking(self):
        """Test mixed instance and class tracking.
        """
        foo = Foo()
        self.tracker.track_object(foo)
        self.tracker.create_snapshot()
        self.tracker.track_class(Foo)
        objs = []
        for _ in range(10):
            objs.append(Foo())
        self.tracker.create_snapshot()

    def test_recurse(self):
        """Test recursive sizing and saving of referents.
        """
        foo = Foo()

        self.tracker.track_object(foo, resolution_level=1)
        self.tracker.create_snapshot()

        fp = self.tracker.objects[id(foo)].snapshots[-1]
        refs = fp[1].refs
        dref = [r for r in refs if r.name == '__dict__']
        self.assertEqual(len(dref), 1)
        dref = dref[0]
        self.assertTrue(dref.size > 0, dref.size)
        self.assertTrue(dref.flat > 0, dref.flat)
        self.assertEqual(dref.refs, ())

        # Test track_change and more fine-grained resolution
        self.tracker.track_change(foo, resolution_level=2)
        self.tracker.create_snapshot()

        fp = self.tracker.objects[id(foo)].snapshots[-1]
        refs = fp[1].refs
        dref = [r for r in refs if r.name == '__dict__']
        self.assertEqual(len(dref), 1)
        dref = dref[0]
        namerefs = [r.name for r in dref.refs]
        self.assertTrue('[K] foo' in namerefs, namerefs)
        self.assertTrue("[V] foo: 'foo'" in namerefs, namerefs)
Exemple #6
0
class SnapshotTestCase(unittest.TestCase):
    def setUp(self):
        self.tracker = ClassTracker()

    def tearDown(self):
        self.tracker.stop_periodic_snapshots()
        self.tracker.clear()

    def test_timestamp(self):
        """Test timestamp of snapshots.
        """
        foo = Foo()
        bar = Bar()

        self.tracker.track_object(foo)
        self.tracker.track_object(bar)

        self.tracker.create_snapshot()
        self.tracker.create_snapshot()
        self.tracker.create_snapshot()

        refts = [fp.timestamp for fp in self.tracker.snapshots]
        for to in self.tracker.objects.values():
            ts = [t for (t, sz) in to.snapshots[1:]]
            self.assertEqual(ts, refts)

    def test_snapshot_members(self):
        """Test existence and value of snapshot members.
        """
        foo = Foo()
        self.tracker.track_object(foo)
        self.tracker.create_snapshot()
        self.tracker.create_snapshot(compute_total=True)

        fp = self.tracker.snapshots[0]
        fp_with_total = self.tracker.snapshots[1]

        self.assertTrue(fp.overhead > 0, fp.overhead)
        self.assertTrue(fp.tracked_total > 0, fp.tracked_total)
        self.assertEqual(fp.asizeof_total, 0)

        self.assertTrue(fp_with_total.asizeof_total > 0,
                        fp_with_total.asizeof_total)
        self.assertTrue(
            fp_with_total.asizeof_total >= fp_with_total.tracked_total)

        if pympler.process.is_available():
            procmem = fp.system_total
            self.assertEqual(fp.total, procmem.vsz)
            self.assertTrue(procmem.vsz > 0, procmem)
            self.assertTrue(procmem.rss > 0, procmem)
            self.assertTrue(procmem.vsz >= procmem.rss, procmem)
            self.assertTrue(procmem.vsz > fp.overhead, procmem)
            self.assertTrue(procmem.vsz > fp.tracked_total, procmem)
            self.assertTrue(
                fp_with_total.system_total.vsz > fp_with_total.asizeof_total)
        else:
            self.assertEqual(fp_with_total.total, fp_with_total.asizeof_total)
            self.assertEqual(fp.total, fp.tracked_total)

    def test_desc(self):
        """Test snapshot label.
        """
        self.tracker.create_snapshot()
        self.tracker.create_snapshot('alpha')
        self.tracker.create_snapshot(description='beta')
        self.tracker.create_snapshot(42)

        self.assertEqual(len(self.tracker.snapshots), 4)
        self.assertEqual(self.tracker.snapshots[0].desc, '')
        self.assertEqual(self.tracker.snapshots[1].desc, 'alpha')
        self.assertEqual(self.tracker.snapshots[2].desc, 'beta')
        self.assertEqual(self.tracker.snapshots[3].desc, '42')

        snapshot = self.tracker.snapshots[0]
        self.assertEqual(snapshot.label, '%.3fs' % snapshot.timestamp)
        snapshot = self.tracker.snapshots[1]
        self.assertEqual(snapshot.label, 'alpha (%.3fs)' % snapshot.timestamp)
        snapshot = self.tracker.snapshots[3]
        self.assertEqual(snapshot.label, '42 (%.3fs)' % snapshot.timestamp)

    def test_background_monitoring(self):
        """Test background monitoring.
        """
        self.tracker.start_periodic_snapshots(0.1)
        self.assertEqual(self.tracker._periodic_thread.interval, 0.1)
        self.assertEqual(self.tracker._periodic_thread.getName(),
                         'BackgroundMonitor')
        for x in range(10):  # try to interfere
            self.tracker.create_snapshot(str(x))
        time.sleep(0.5)
        self.tracker.start_periodic_snapshots(0.2)
        self.assertEqual(self.tracker._periodic_thread.interval, 0.2)
        self.tracker.stop_periodic_snapshots()
        self.assertTrue(self.tracker._periodic_thread is None)
        self.assertTrue(len(self.tracker.snapshots) > 10)
Exemple #7
0
class LogTestCase(unittest.TestCase):

    def setUp(self):
        self.out = StringIO()
        self.tracker = ClassTracker(stream=self.out)


    @property
    def output(self):
        """Return output recorded in `ClassTracker` output stream."""
        return self.out.getvalue()


    def tearDown(self):
        self.tracker.stop_periodic_snapshots()
        self.tracker.clear()


    def test_dump(self):
        """Test serialization of log data.
        """
        foo = Foo()
        foo.data = range(1000)
        bar = Bar()

        self.tracker.track_object(foo, resolution_level=4)
        self.tracker.track_object(bar)

        self.tracker.create_snapshot('Footest')

        f1 = StringIO()
        f2 = StringIO()

        ConsoleStats(tracker=self.tracker, stream=f1).print_stats()

        tmp = BytesIO()
        Stats(tracker=self.tracker).dump_stats(tmp, close=False)

        self.tracker.clear()

        stats = ConsoleStats(stream=f2)
        self.assertEqual(stats.index, None)
        self.assertEqual(stats.snapshots, None)
        tmp.seek(0)
        stats.load_stats(tmp)
        tmp.close()
        self.assert_('Foo' in stats.index)

        stats.print_stats()

        self.assertEqual(f1.getvalue(), f2.getvalue())

        # Test partial printing
        stats.stream = f3 = StringIO()
        stats.sort_stats()
        tolen = len(stats.sorted)
        stats.print_stats(clsname='Bar')
        self.assertEqual(len(stats.sorted), tolen)
        stats.print_summary()
        clsname = f3.getvalue().split('\n')[0]
        self.assertNotEqual(re.search('Bar', clsname), None, clsname)

        f1.close()
        f2.close()
        f3.close()


    def test_sort_stats(self):
        """Test sort_stats and reverse_order.
        """
        self.tracker.track_class(Bar, name='Bar')
        foo = Foo()
        foo.data = list(range(1000))
        bar1 = Bar()
        bar2 = Bar()
        self.tracker.track_object(foo, resolution_level=4)
        self.tracker.create_snapshot()

        stats = self.tracker.stats

        # Test sort_stats and reverse_order
        self.assertEqual(stats.sort_stats('size'), stats)
        self.assertEqual(stats.sorted[0].classname, 'Foo')
        stats.reverse_order()
        self.assertEqual(stats.sorted[0].classname, 'Bar')
        stats.sort_stats('classname', 'birth')
        self.assertEqual(stats.sorted[0].classname, 'Bar')
        self.assertRaises(ValueError, stats.sort_stats, 'name', 42, 'classn')
        stats.sort_stats('classname')


    def test_dump_load_with_filename(self):
        """Test serialization with filename.
        """
        foo = Foo()
        self.tracker.track_object(foo, resolution_level=2)
        self.tracker.create_snapshot()
        fhandle, fname = mkstemp(prefix='pympler_test_dump')
        os.close(fhandle)
        try:
            self.tracker.stats.dump_stats(fname)
            output = StringIO()
            stats = ConsoleStats(filename=fname, stream=output)
            stats.print_stats()
            self.assertTrue('<Foo>' in output.getvalue(), output.getvalue())
            # Check if a Stats loaded from a dump can be dumped again
            stats.dump_stats(fname)
        finally:
            os.unlink(fname)


    def test_tracked_classes(self):
        """Test listing tracked classes.
        """
        self.tracker.track_class(Foo, name='Foo')
        self.tracker.track_class(Bar, name='Bar')

        foo = Foo()
        self.tracker.create_snapshot()
        bar = Bar()
        self.tracker.create_snapshot()
        foo = FooNew()
        self.tracker.track_object(foo)
        self.tracker.create_snapshot()

        stats = self.tracker.stats
        self.assertEqual(stats.tracked_classes, ['Bar', 'Foo', 'FooNew'])
        stats.print_summary()


    def test_print_stats(self):
        """Test printing class-filtered statistics.
        """
        self.tracker.track_class(Foo, name='Foo', trace=True)
        self.tracker.track_class(Bar, name='Bar')

        foo = Foo()
        bar = Bar()

        self.tracker.create_snapshot()

        stats = self.tracker.stats
        stats.print_stats(clsname='Foo')
        self.assertTrue('Foo' in self.output, self.output)
        self.assertFalse('Bar' in self.output, self.output)
        self.assertTrue('foo = Foo()' in self.output, self.output)


    def test_print_stats_limit(self):
        """Test printing limited statistics.
        """
        self.tracker.track_class(Foo, name='Foo')

        foo = [Foo() for _ in range(10)]

        self.tracker.create_snapshot()

        stats = self.tracker.stats
        stats.print_stats(limit=3)
        self.assertEqual(self.output.count('<Foo>'), 3)

        self.out.seek(0)
        self.out.truncate()

        stats.print_stats(limit=0.5)
        self.assertEqual(self.output.count('<Foo>'), 5)


    def test_snapshots(self):
        """Test multiple snapshots.
        """
        self.tracker.track_class(Foo, name='Foo')
        self.tracker.track_class(Bar, name='Bar')
        self.tracker.track_class(FooNew, name='FooNew')

        self.tracker.create_snapshot()
        f1 = Foo()
        self.tracker.create_snapshot()
        f2 = Foo()
        f3 = FooNew()
        self.tracker.create_snapshot()
        b = Bar()
        del b
        self.tracker.create_snapshot()

        stats = self.tracker.stats
        stats.print_stats()
        stats.print_summary()


    def test_merge(self):
        """Test merging of reference trees.
        """
        self.tracker.track_class(FooNew, name='Foo', resolution_level=2)

        f1 = FooNew()
        f1.a = list(range(1000))
        f2 = FooNew()
        f2.a = list(range(100))
        f2.b = 'This is some stupid spam.'

        self.tracker.create_snapshot('Merge test')

        sizer = Asizer()
        sz1 = sizer.asized(f1)
        sz2 = sizer.asized(f2)

        stats = self.tracker.stats
        for fp in stats.snapshots:
            if fp.desc == 'Merge test':
                stats.annotate_snapshot(fp)
                self.assert_(hasattr(fp, 'classes'))
                classes = fp.classes
                stats.annotate_snapshot(fp)
                self.assertEqual(fp.classes, classes)
                self.assert_('Foo' in fp.classes, fp.classes)
                self.assert_('merged' in fp.classes['Foo'])
                fm = fp.classes['Foo']['merged']
                self.assertEqual(fm.size, sz1.size + sz2.size, (fm.size, str(sz1), str(sz2)))
                refs = {}
                for ref in fm.refs:
                    refs[ref.name] = ref
                self.assert_('__dict__' in refs.keys(), refs.keys())
                refs2 = {}
                for ref in refs['__dict__'].refs:
                    refs2[ref.name] = ref
                self.assert_('[V] a' in refs2.keys(), refs2.keys())
                self.assert_('[V] b' in refs2.keys(), refs2.keys())
                self.assertEqual(refs2['[V] a'].size, asizeof(f1.a, f2.a))


    def test_html(self):
        """Test emitting HTML statistics."""
        self.tracker.track_class(Foo, name='Foo', resolution_level=2)
        self.tracker.track_class(Bar, name='Bar', trace=True)

        f1 = Foo()
        f1.a = list(range(100000))
        f2 = Foo()
        f2.a = list(range(1000))
        f2.b = 'This is some stupid spam.'
        f1 = Bar()

        self.tracker.create_snapshot('Merge test')

        stats = HtmlStats(tracker=self.tracker)
        try:
            target = mkdtemp(prefix='pympler_test')
            output = os.path.join(target, 'footest.html')
            stats.create_html(output)

            source = open(output).read()
            # Ensure relative links are used
            fname = os.path.join('footest_files', 'Foo.html')
            self.assertTrue('<a href="%s">' % fname in source, (fname, source))
        finally:
            rmtree(target)


    def test_charts(self):
        """Test emitting graphic charts."""
        self.tracker.track_class(Foo, name='Foo', resolution_level=2)

        f1 = Foo()
        f1.a = list(range(1000))
        f2 = Foo()
        f2.a = list(range(100))
        f2.b = 'This is some stupid spam.'

        self.tracker.create_snapshot('Merge test')

        from pympler import charts
        try:
            target = mkdtemp(prefix='pympler_test')
            output = os.path.join(target, 'timespace.png')
            charts.tracker_timespace(output, self.tracker.stats)
        finally:
            rmtree(target)
class KademliaCrawlerProtocol(kademlia.KademliaProtocol):
    logger = logging.getLogger("KademliaCrawlerProtocol")

    def __init__(self, node: kademlia.Node, wire: 'DiscoveryProtocol') -> None:
        super(KademliaCrawlerProtocol, self).__init__(node, wire)
        self.logger.debug("Kademlia Crawler Created")

        self.tracker = ClassTracker()
        self.myTree = btree.Tree(256)
        self.tracker.track_object(self.myTree)

        self.MAX_CONCURRENT = 64

    async def bootstrap(self, bootstrap_nodes: List[kademlia.Node]) -> None:
        bonded = await asyncio.gather(*[self.bond(n) for n in bootstrap_nodes])
        if not any(bonded):
            self.logger.info("Failed to bond with bootstrap nodes {}".format(
                bootstrap_nodes))
            return
        await self.lookup(self.this_node.id)

    async def lookup(self,
                     node_id: int,
                     use_k_bucket=True) -> List[kademlia.Node]:
        """Lookup performs a network search for nodes close to the given target.

        It approaches the target by querying nodes that are closer to it on each 
        iteration. The given target does not need to be an actual node identifier.
        """
        self.logger.info("Kademlia Crawler Lookup Initiated")
        nodes_asked = set()  # type: Set[Node]
        nodes_seen = set()  # type: Set[Node]

        async def _find_node(node_id, remote, semaphore):
            async with semaphore:
                self.wire.send_find_node(remote, node_id)
                candidates = await self.wait_neighbours(remote)

                if len(candidates) == 0:
                    self.logger.debug(
                        "got no candidates from {}, returning".format(remote))
                    return candidates

                candidates = [c for c in candidates if c not in nodes_seen]
                self.logger.debug("got {} new candidates".format(
                    len(candidates)))
                # Add new candidates to nodes_seen so that we don't attempt to
                # bond with failing ones in the future.
                nodes_seen.update(candidates)
                bonded = await asyncio.gather(
                    *[self.bond(c) for c in candidates])
                self.logger.debug("bonded with {} candidates".format(
                    bonded.count(True)))

            return [c for c in candidates if bonded[candidates.index(c)]]

        def _exclude_if_asked(nodes):
            nodes_to_ask = list(set(nodes).difference(nodes_asked))
            return kademlia.sort_by_distance(
                nodes_to_ask, node_id)[:kademlia.k_find_concurrency]

        # Get all of the nodes in our list, not just the closest k_bucket nodes
        closest = self.routing.neighbours(node_id)
        self.logger.debug(
            "starting lookup; initial neighbours: {}".format(closest))
        nodes_to_ask = _exclude_if_asked(closest)
        mySem = asyncio.Semaphore(value=self.MAX_CONCURRENT)

        while nodes_to_ask:

            #summary.print_(summary.summarize(muppy.get_objects()))
            #print('Tasks count: ', len(asyncio.Task.all_tasks()))
            #print('Active tasks count: ', len(
            #[task for task in asyncio.Task.all_tasks() if not task.done()]))

            self.logger.debug("node lookup; querying {}".format(nodes_to_ask))
            nodes_asked.update(nodes_to_ask)

            tasks = [_find_node(node_id, n, mySem) for n in nodes_to_ask]

            results = await asyncio.gather(*tasks)

            for candidates in results:
                closest.extend(candidates)

            # Here we're keeping all of the nodes that were returned so that we
            # can get a better view of the entire network.
            if use_k_bucket:
                closest = kademlia.sort_by_distance(
                    closest, node_id)[:kademlia.k_bucket_size]
            else:
                closest = kademlia.sort_by_distance(closest, node_id)
            nodes_to_ask = _exclude_if_asked(closest)

        self.logger.info("lookup finished for {}: {} nodes discovered".format(
            node_id, len(closest)))
        return closest

    async def crawl(self):
        """
        Continue to look up random nodes until no new nodes are discovered. 
        """
        def _extract_new_nodes(
                nodes: List[kademlia.Node]) -> List[kademlia.Node]:
            """
            Returns a list of nodes that we havne't previously encountered
            """
            return [node.id for node in nodes if node.id not in known_nodes]

        known_nodes = []  # Will be a list of node ids

        # Populate our known nodes with nodes near us
        new_nodes = await self.lookup(self.this_node.id, False)
        print("Size of new nodes pre strip: " +
              str(asizeof.asizeof(new_nodes)))
        new_nodes = _extract_new_nodes(
            new_nodes)  # Reduced to list of node ids
        print("Size of new nodes post strip: " +
              str(asizeof.asizeof(new_nodes)))
        self.myTree.add_list(new_nodes)
        print("Size of myTree: " + str(asizeof.asizeof(self.myTree)))
        known_nodes += new_nodes

        while new_nodes is not []:

            #summary.print_(summary.summarize(muppy.get_objects()))
            #self.tracker.create_snapshot()
            #self.tracker.stats.print_summary()
            #print("Size of known nodes: " + str(asizeof.asizeof(known_nodes)))
            self.logger.debug(
                "In crawl loop... {} new nodes discovered...".format(
                    len(new_nodes)))
            self.logger.info("{} total nodes discovered...".format(
                len(known_nodes)))
            dump_new_nodes(known_nodes)
            #summary.print_(summary.summarize(muppy.get_objects()))

            try:
                new_nodes = await self.lookup(self.myTree.path_to_stump(),
                                              False)
                new_nodes = _extract_new_nodes(new_nodes)
                self.myTree.add_list(new_nodes)
                known_nodes += new_nodes
            except evm.p2p.kademlia.AlreadyWaiting as e:
                self.logger.error(e)

        self.logger.info("Crawled newtork... {} nodes found...".format(
            len(known_nodes)))
        return known_nodes

    async def targeted_crawl(self,
                             remote: kademlia.Node) -> List[kademlia.Node]:
        def print_neighbours_list(low, searched, high):
            def _get_neighbour(result, index):
                if index >= len(result["neighbours"]):
                    return ""
                else:
                    node = list(result["neighbours"])[index]
                    #return str(node) + " (" + str(hex(_xor_distance(node.id, remote.id))) + ")"
                    return str(node)

            result_lengths = [
                len(x["neighbours"]) for x in [low, searched, high]
            ]

            print(header_string("Search Result"))

            print("Low" + " " * 29 + "\t Searched " + " " * 29 + "\t High")
            for index in range(0, max(result_lengths) - 1):
                low_n = _get_neighbour(low, index)
                searched_n = _get_neighbour(searched, index)
                high_n = _get_neighbour(high, index)

                print(low_n + "\t" + searched_n + "\t" + high_n)
            print()

        async def _find_node_at_distance(distance: int):
            """
            Sends a find_node request to the remote node at a given distance from
            the node we're targeting. Returns all of the nodes given in the response. 
            """
            node_id = _node_at_distance(remote, distance)
            self.logger.debug("Searching for node: %d", node_id)

            self.wire.send_find_node(remote, node_id)
            candidates = await self.wait_neighbours(remote)
            if len(candidates) == 0:
                self.logger.warning(
                    "got no candidates from {}, returning".format(remote))

            return candidates


#            candidates = [c for c in candidates if c not in nodes_seen]
#            self.logger.debug("got {} new candidates".format(len(candidates)))
#            # Add new candidates to nodes_seen so that we don't attempt to bond with failing ones
#            # in the future.
#            nodes_seen.update(candidates)
#            bonded = await asyncio.gather(*[self.bond(c) for c in candidates])
#            self.logger.debug("bonded with {} candidates".format(bonded.count(True)))
#            return [c for c in candidates if bonded[candidates.index(c)]]

#def recurse_step(interest_low, interest_high):

        async def recurse_step(interest_low: 'DistanceResult',
                               interest_high: 'DistanceResult'):
            """
            {
                "distance": int,
                "neighbours": Set([kademlia.Node])
            }
            :d_start: The closest distance to the remote node
            :interest_high: The furthest distance from the remote node
            """

            d_interest = int(
                (interest_low["distance"] + interest_high["distance"]) // 2)

            self.logger.info(
                "Performing Recursive Search...\n" +
                "Max Distance: {}\n".format(hex(interest_high["distance"])) +
                "Mid Distance: {}\n".format(hex(d_interest)) +
                "Min Distance: {}".format(hex(interest_low["distance"])))

            if (interest_high["distance"] == d_interest):
                self.logger.warning("Error... high = mid\n")
                print_neighbours_list(interest_low, interest_high,
                                      interest_high)
                _compare_nodes_at_distance(remote, interest_low["distance"],
                                           interest_high["distance"])
                return interest_high["neighbours"]
            elif (interest_low["distance"] == d_interest):
                self.logger.warning("Error... low = mid\n")
                print_neighbours_list(interest_low, interest_low,
                                      interest_high)
                _compare_nodes_at_distance(remote, interest_low["distance"],
                                           interest_high["distance"])
                return interest_low["neighbours"]

            interest_result = make_distanceResult(
                d_interest, await _find_node_at_distance(d_interest))

            print_neighbours_list(interest_low, interest_result, interest_high)
            disjoint_up = interest_result["neighbours"].isdisjoint(
                interest_high["neighbours"])
            disjoint_down = interest_result["neighbours"].isdisjoint(
                interest_low["neighbours"])

            if not disjoint_up and not disjoint_down:
                # This is a BASE CASE
                # We overlap completely with both bounds. return this iteraiton.
                self.logger.info("Hit base case!\n")
                return interest_result["neighbours"]
            elif disjoint_up and not disjoint_down:
                # Overlapped bottom, but not top: check top
                # Check [d_interst, interest_high]
                self.logger.info(
                    "Overlapped bottom, but not top - Search up!\n")
                interest_result["neighbours"].update(\
                        await recurse_step(interest_result, interest_high))
                return interest_result["neighbours"]
            elif not disjoint_up and disjoint_down:
                # Overlapped top, but not bottom: check bottom
                # Check [interest_low, interest_result]
                self.logger.info(
                    "Overlapped top, but not bottom - Search down!\n")

                interest_result["neighbours"].update(\
                        await recurse_step(interest_low, interest_result))
                return interest_result["neighbours"]
            else:
                # Overlapped neither. search both.
                #recurse_tasks = [
                #        recurse_step(interest_low, interest_result),\
                #        recurse_step(interest_result, interest_high)]
                #responses = await asyncio.gather(*recurse_tasks)
                self.logger.info("No Overalp: search both\n")

                responses = await recurse_step(interest_low, interest_result)
                responses.update(await recurse_step(interest_result,
                                                    interest_high))
                interest_result["neighbours"].update(responses)
                return interest_result["neighbours"]

        furthest_node = make_distanceResult(
            _max_distance_to_node(), await
            _find_node_at_distance(_max_distance_to_node()))
        closest_node = make_distanceResult(0, await _find_node_at_distance(0))
        try:
            addr_book = await recurse_step(closest_node, furthest_node)
        except Exception as e:
            exc_type, exc_obj, exc_tb = sys.exc_info()
            traceback.print_tb(exc_tb)
            print(e)
        finally:
            addr_book = set()
        return addr_book
Exemple #9
0
class LogTestCase(unittest.TestCase):
    def setUp(self):
        self.out = StringIO()
        self.tracker = ClassTracker(stream=self.out)

    @property
    def output(self):
        """Return output recorded in `ClassTracker` output stream."""
        return self.out.getvalue()

    def tearDown(self):
        self.tracker.stop_periodic_snapshots()
        self.tracker.clear()

    def test_dump(self):
        """Test serialization of log data.
        """
        foo = Foo()
        foo.data = range(1000)
        bar = Bar()

        self.tracker.track_object(foo, resolution_level=4)
        self.tracker.track_object(bar)

        self.tracker.create_snapshot('Footest')

        f1 = StringIO()
        f2 = StringIO()

        ConsoleStats(tracker=self.tracker, stream=f1).print_stats()

        tmp = BytesIO()
        Stats(tracker=self.tracker).dump_stats(tmp, close=False)

        self.tracker.clear()

        stats = ConsoleStats(stream=f2)
        self.assertEqual(stats.index, None)
        self.assertEqual(stats.snapshots, None)
        tmp.seek(0)
        stats.load_stats(tmp)
        tmp.close()
        self.assert_('Foo' in stats.index)

        stats.print_stats()

        self.assertEqual(f1.getvalue(), f2.getvalue())

        # Test partial printing
        stats.stream = f3 = StringIO()
        stats.sort_stats()
        tolen = len(stats.sorted)
        stats.print_stats(clsname='Bar')
        self.assertEqual(len(stats.sorted), tolen)
        stats.print_summary()
        clsname = f3.getvalue().split('\n')[0]
        self.assertNotEqual(re.search('\.Bar', clsname), None, clsname)

        f1.close()
        f2.close()
        f3.close()

    def test_sort_stats(self):
        """Test sort_stats and reverse_order.
        """
        self.tracker.track_class(Bar, name='Bar')
        foo = Foo()
        foo.data = list(range(1000))
        bar1 = Bar()
        bar2 = Bar()
        self.tracker.track_object(foo, resolution_level=4)
        self.tracker.create_snapshot()

        stats = self.tracker.stats

        # Test sort_stats and reverse_order
        self.assertEqual(stats.sort_stats('size'), stats)
        self.assertEqual(stats.sorted[0].classname, 'Foo')
        stats.reverse_order()
        self.assertEqual(stats.sorted[0].classname, 'Bar')
        stats.sort_stats('classname', 'birth')
        self.assertEqual(stats.sorted[0].classname, 'Bar')
        self.assertRaises(ValueError, stats.sort_stats, 'name', 42, 'classn')
        stats.sort_stats('classname')

    def test_dump_load_with_filename(self):
        """Test serialization with filename.
        """
        foo = Foo()
        self.tracker.track_object(foo, resolution_level=2)
        self.tracker.create_snapshot()
        fhandle, fname = mkstemp(prefix='pympler_test_dump')
        os.close(fhandle)
        try:
            self.tracker.stats.dump_stats(fname)
            output = StringIO()
            stats = ConsoleStats(filename=fname, stream=output)
            stats.print_stats()
            self.assertTrue('<Foo>' in output.getvalue(), output.getvalue())
            # Check if a Stats loaded from a dump can be dumped again
            stats.dump_stats(fname)
        finally:
            os.unlink(fname)

    def test_tracked_classes(self):
        """Test listing tracked classes.
        """
        self.tracker.track_class(Foo, name='Foo')
        self.tracker.track_class(Bar, name='Bar')

        foo = Foo()
        self.tracker.create_snapshot()
        bar = Bar()
        self.tracker.create_snapshot()
        foo = FooNew()
        self.tracker.track_object(foo)
        self.tracker.create_snapshot()

        stats = self.tracker.stats
        self.assertEqual(stats.tracked_classes, ['Bar', 'Foo', 'FooNew'])
        stats.print_summary()

    def test_print_stats(self):
        """Test printing class-filtered statistics.
        """
        self.tracker.track_class(Foo, name='Foo', trace=True)
        self.tracker.track_class(Bar, name='Bar')

        foo = Foo()
        bar = Bar()

        self.tracker.create_snapshot()

        stats = self.tracker.stats
        stats.print_stats(clsname='Foo')
        self.assertTrue('Foo' in self.output, self.output)
        self.assertFalse('Bar' in self.output, self.output)
        self.assertTrue('foo = Foo()' in self.output, self.output)

    def test_print_stats_limit(self):
        """Test printing limited statistics.
        """
        self.tracker.track_class(Foo, name='Foo')

        foo = [Foo() for _ in range(10)]

        self.tracker.create_snapshot()

        stats = self.tracker.stats
        stats.print_stats(limit=3)
        self.assertEqual(self.output.count('<Foo>'), 3)

        self.out.seek(0)
        self.out.truncate()

        stats.print_stats(limit=0.5)
        self.assertEqual(self.output.count('<Foo>'), 5)

    def test_snapshots(self):
        """Test multiple snapshots.
        """
        self.tracker.track_class(Foo, name='Foo')
        self.tracker.track_class(Bar, name='Bar')
        self.tracker.track_class(FooNew, name='FooNew')

        self.tracker.create_snapshot()
        f1 = Foo()
        self.tracker.create_snapshot()
        f2 = Foo()
        f3 = FooNew()
        self.tracker.create_snapshot()
        b = Bar()
        del b
        self.tracker.create_snapshot()

        stats = self.tracker.stats
        stats.print_stats()
        stats.print_summary()

    def test_merge(self):
        """Test merging of reference trees.
        """
        self.tracker.track_class(FooNew, name='Foo', resolution_level=2)

        f1 = FooNew()
        f1.a = list(range(1000))
        f2 = FooNew()
        f2.a = list(range(100))
        f2.b = 'This is some stupid spam.'

        self.tracker.create_snapshot('Merge test')

        sizer = Asizer()
        sz1 = sizer.asized(f1)
        sz2 = sizer.asized(f2)

        stats = self.tracker.stats
        for fp in stats.snapshots:
            if fp.desc == 'Merge test':
                stats.annotate_snapshot(fp)
                self.assert_(hasattr(fp, 'classes'))
                classes = fp.classes
                stats.annotate_snapshot(fp)
                self.assertEqual(fp.classes, classes)
                self.assert_('Foo' in fp.classes, fp.classes)
                self.assert_('merged' in fp.classes['Foo'])
                fm = fp.classes['Foo']['merged']
                self.assertEqual(fm.size, sz1.size + sz2.size,
                                 (fm.size, str(sz1), str(sz2)))
                refs = {}
                for ref in fm.refs:
                    refs[ref.name] = ref
                self.assert_('__dict__' in refs.keys(), refs.keys())
                refs2 = {}
                for ref in refs['__dict__'].refs:
                    refs2[ref.name] = ref
                self.assert_('[V] a' in refs2.keys(), refs2.keys())
                self.assert_('[V] b' in refs2.keys(), refs2.keys())
                self.assertEqual(refs2['[V] a'].size, asizeof(f1.a, f2.a))

    def test_html(self):
        """Test emitting HTML statistics."""
        self.tracker.track_class(Foo, name='Foo', resolution_level=2)
        self.tracker.track_class(Bar, name='Bar', trace=True)

        f1 = Foo()
        f1.a = list(range(100000))
        f2 = Foo()
        f2.a = list(range(1000))
        f2.b = 'This is some stupid spam.'
        f1 = Bar()

        self.tracker.create_snapshot('Merge test')

        stats = HtmlStats(tracker=self.tracker)
        try:
            target = mkdtemp(prefix='pympler_test')
            output = os.path.join(target, 'footest.html')
            stats.create_html(output)

            source = open(output).read()
            # Ensure relative links are used
            fname = os.path.join('footest_files', 'Foo.html')
            self.assertTrue('<a href="%s">' % fname in source, (fname, source))
        finally:
            rmtree(target)

    def test_charts(self):
        """Test emitting graphic charts."""
        self.tracker.track_class(Foo, name='Foo', resolution_level=2)

        f1 = Foo()
        f1.a = list(range(1000))
        f2 = Foo()
        f2.a = list(range(100))
        f2.b = 'This is some stupid spam.'

        self.tracker.create_snapshot('Merge test')

        from pympler import charts
        try:
            target = mkdtemp(prefix='pympler_test')
            output = os.path.join(target, 'timespace.png')
            charts.tracker_timespace(output, self.tracker.stats)
        finally:
            rmtree(target)
Exemple #10
0
class GlobalContainer(box.BoxLayout):

    graph_list = prop.ListProperty([])
    active_graph = prop.ObjectProperty(None)
    name_manager = NameManager.Instance()
    show_sidepanel = prop.BooleanProperty(True)
    modestr = prop.StringProperty('insert')
    itemstr = prop.StringProperty('atom')

    modes = {'insert': asp.Mode.INSERT,
             'select': asp.Mode.SELECT}
    items = {'atom': asp.Item.ATOM,
             'ellipse': asp.Item.ELLIPSE,
             'rectangle': asp.Item.SQUARE}

    def __init__(self, **kwargs):
        super(GlobalContainer, self).__init__(**kwargs)
        self._keyboard = None
        self.request_keyboard()
        self.working_dir = './'
        self.tutorial = None
        self.popup_stack = []
        window.Window.bind(on_resize=self.on_resize)

        if DEBUG:
            self.tracker = ClassTracker()
            self.tracker.track_object(MenuButton)
            self.all_objects = muppy.get_objects()

    def request_keyboard(self):
        self._keyboard = window.Window.request_keyboard(self._keyboard_release,
                                                        self)
        self._keyboard.bind(on_key_down=self._on_keyboard_down)

    def _keyboard_catch(self):
        self._keyboard.bind(on_key_down=self._on_keyboard_down)

    def _keyboard_release(self):
        self._keyboard.unbind(on_key_down=self._on_keyboard_down)
        #self._keyboard = None

    def _on_keyboard_down(self, keyboard, keycode, text, modifiers):
        if keycode[1] == 'escape':
            base.stopTouchApp()
        elif keycode[1] == 'd':
            self.set_mode('insert')
        elif keycode[1] == 's':
            self.set_mode('select')
        elif keycode[1] == 'w':
            self.set_item('atom')
        elif keycode[1] == 'e':
            self.set_item('ellipse')
        elif keycode[1] == 'r':
            self.set_item('rectangle')
        elif keycode[1] == 'p':
            self.active_graph.show_tree(0)
        elif keycode[1] == 'o':
            self.view_symbolic_formula()
        elif keycode[1] == 'n':
            self.show_gringo_query()
        elif keycode[1] == 'g':
            self.show_save()
        elif keycode[1] == 'l':
            self.show_load()
        elif keycode[1] == 'y':
            print self.name_manager.get_all()
            print asp.Line.get_all_lines()
        elif keycode[1] == 't':
            self.toggle_sidepanel()
        elif keycode[1] == 'tab':
            if not self.show_sidepanel:
                self.toggle_sidepanel()
            self.ids.atom_input.focus = True
        elif keycode[1] == '.':
            if DEBUG:
                rb = refbrowser.InteractiveBrowser(self)
                rb.main()

                # self.all_objects = muppy.get_objects()
                # sum1 = summary.summarize(self.all_objects)
                # summary.print_(sum1)

                # self.tracker.create_snapshot()
                # self.tracker.stats.print_summary()
        return True

    def _focus_name_list(self, value):
        if value and (asp.AtomWidget.active_atom is not None):
            previous_name = asp.AtomWidget.active_atom.name
            for button in self.ids.name_list.children:
                if (button.text == previous_name) and (button.state != 'down'):
                    button.trigger_action()
        elif value:
            if self.ids.name_list.children:
                self.ids.name_list.children[-1].trigger_action()
        else:
            for button in self.ids.name_list.children:
                if button.state == 'down':
                    button.trigger_action()

    def on_resize(self, window, width, height):
        if self.show_sidepanel:
            self.ids.sidepanel.width = self.width * .15
        self.active_graph.size = self.ids.stencilview.size

    def toggle_sidepanel(self):
        self.show_sidepanel = not self.show_sidepanel
        if self.show_sidepanel:
            width = self.width * .15
        else:
            width = 0
        anim.Animation(width=width, d=.15, t='out_quart').start(
                self.ids.sidepanel)
        if not self.show_sidepanel:
            self.ids.sidepanel.focus = False
            # Also release keyboard
        #self.update_sourcecode()

    def update_atom_editor(self, name):
        editor = self.ids.atom_editor
        if name == '':
            editor.disabled = True
        else:
            if editor.disabled:
                editor.disabled = False
            atom = self.name_manager.get(name)
            editor.update(atom)

    def update_atom(self, name, new_name='', new_hook_points=[], is_constant=False):
        atom = self.name_manager.get(name)
        if new_name <> '':
            atom.name = new_name
        if len(new_hook_points) == 4:
            atom.hook_points = new_hook_points
            # print id(atom.hook_points)
        atom.is_constant = is_constant

    def register_atom(self, name,
                      hooks=[False, False, False, False], is_constant=False):
        if name == '':
            return
        children = self.ids.name_list.children
        new_button = None
        name_to_insert = name
        i = len(children) - 1

        # If the name doesn't exist, register atom
        if self.name_manager.get(name) is None:
            self.name_manager.register(name, asp.Atom(name, hooks, is_constant))
            # print id(self.name_manager.get(name).hook_points)

        # Insert in name_list sorted by name
        while i >= 0:
            if children[i].text < name_to_insert:
                #print children[i].text, '<',  name_to_insert
                pass
            elif children[i].text == name_to_insert:
                # Already exists
                if children[i].state != 'down':
                    children[i].trigger_action()
                return
            elif children[i].text > name_to_insert:
                #print children[i].text, '>',  name_to_insert
                temp = children[i].text
                children[i].text = name_to_insert
                name_to_insert = temp
                if new_button == None:
                    new_button = children[i]
            i -= 1

        self.ids.name_list.add_widget(AtomSelectionButton(text=name_to_insert))
        if new_button == None:
            new_button = children[0]
        if new_button.state == 'down':
            asp.AtomWidget.active_atom = self.name_manager.get(name)
            self.update_atom_editor(name)
            self.set_item('atom')
        else:
            new_button.trigger_action()

    def rename_atom(self, new_name):
        old_name = ''
        for button in self.ids.name_list.children:
            if button.state == 'down':
                old_name = button.text
                selected_button = button
        if (old_name != '') and (new_name != ''):
            try:
                atom = self.name_manager.get(old_name)
                exists = self.name_manager.get(new_name)
                assert atom is not None
                assert exists is None
            except AssertionError:
                #self.show_error('Name already exists.')
                print 'Name already exists.'
                return
            selected_button.text = new_name
            atom.name = new_name
            self.name_manager.unregister(old_name)
            self.name_manager.register(new_name, atom)
            self.update_atom_editor(new_name)

    def delete_atom(self):
        for button in self.ids.name_list.children:
            if button.state == 'down':
                self.name_manager.unregister(button.text)
                self.active_graph.delete_atom(button.text)
                asp.AtomWidget.active_atom = None
                self.ids.name_list.remove_widget(button)
                self.update_atom_editor('')

    def clear_atoms(self):
        self.ids.name_list.clear_widgets()
        self.name_manager.clear()
        self.update_atom_editor('')
        asp.AtomWidget.active_atom = None

    def new_graph(self):
        # TODO: Migrate to tab system
        asp.Line.clear_lines()
        self.clear_atoms()
        if self.active_graph is None:
            g = asp.RootWidget()
            self.graph_list.append(g)
            self.active_graph = g
            self.ids.stencilview.add_widget(g)
        else:
            self.active_graph.delete_tree()

    def close_graph(self):
        if self.active_graph is not None:
            asp.Line.clear_lines()
            self.active_graph.delete_tree()
            self.active_graph.delete_root()
            self.clear_atoms()
            self.graph_list.pop()
            self.active_graph = None

    def set_mode(self, mode):
        try:
            prev_mode = self.active_graph.mode
            new_mode = self.modes[mode]
            if new_mode == asp.Mode.INSERT:
                self.active_graph.show_hooks()
            else:
                if prev_mode == asp.Mode.INSERT:
                    self.active_graph.hide_hooks()
            self.active_graph.mode = new_mode
            self.modestr = mode
        except KeyError as err:
            print 'ERROR: Invalid mode {0} requested.'.format(str(err))

    def set_item(self, item):
        try:
            self.active_graph.item = self.items[item]
            self.itemstr = item
        except KeyError as err:
            print 'ERROR: Invalid item {0} requested.'.format(str(err))
        except AttributeError:
            pass
        if item == 'atom':
            self._focus_name_list(True)
        else:
            self._focus_name_list(False)

    def push_popup(self, popup):
        self.popup_stack.append(popup)
        popup.open()

    def dismiss_popup(self):
        popup = self.popup_stack.pop()
        popup.dismiss()

    def show_rename_atom(self):
        content = TextInputDialog(caption="Enter new name",
                                  validate_callback=self.rename_atom,
                                  cancel=self.dismiss_popup)
        p = CustomPopup(self, title="Rename atom", content=content,
                        size_hint=(0.4, 0.25))
        self.push_popup(p)

    def show_load(self):
        content = LoadDialog(load=self.load, cancel=self.dismiss_popup)
        content.ids.filechooser.path = self.working_dir
        p = CustomPopup(self, title="Load file", content=content,
                        size_hint=(0.9, 0.9))
        self.push_popup(p)

    def show_save(self):
        content = SaveDialog(save=self.save, cancel=self.dismiss_popup)
        content.ids.filechooser.path = self.working_dir
        p = CustomPopup(self, title="Save file", content=content,
                        size_hint=(0.9, 0.9))
        self.push_popup(p)

    def show_export(self):
        content = ExportDialog(export=self.export, cancel=self.dismiss_popup)
        content.ids.filechooser.path = self.working_dir
        p = CustomPopup(self, title="Export file", content=content,
                        size_hint=(0.9, 0.9))
        self.push_popup(p)

    def show_gringo_query(self):
        caption = "Enter desired predicates separated by commas"
        content = TextInputDialog(caption=caption,
                                  validate_callback=self.gringo_query,
                                  dismiss_on_validate=False,
                                  focus=False,
                                  cancel=self.dismiss_popup)
        p = CustomPopup(self, title="Output predicates", content=content,
                        size_hint=(0.4, 0.25))
        self.push_popup(p)

    def show_stable_models(self, solver):
        models = solver.get_models()
        content = None
        if len(models) == 0:
            content = ErrorDialog('Unsatisfiable', cancel=self.dismiss_popup)
        else:
            solver.generate_graph(models[0])
            content = StableModelDialog(solver, cancel=self.dismiss_popup)
            content.ids.img.reload()
        p = CustomPopup(self, catch_keyboard=False, title="Stable Models",
                        content=content, size_hint=(0.9, 0.9))
        self.push_popup(p)

    def show_error(self, err_str):
        content = ErrorDialog(err_str, cancel=self.dismiss_popup)
        p = CustomPopup(self, catch_keyboard=False, title="Error",
                        content=content, size_hint=(0.4, 0.3))
        self.push_popup(p)

    def show_about(self):
        content = AboutDialog(cancel=self.dismiss_popup)
        p = CustomPopup(self, catch_keyboard=False, title="About "+__title__,
                        content=content, size_hint=(0.5, 0.5))
        self.push_popup(p)

    def load(self, path, filename):
        self.close_graph()
        self.working_dir = path

        try:
            f = os.path.join(path, filename[0])
            # Temporal line storage. Its contents are arranged as follows:
            # { line_id: (graph, hook_list) , ... }
            lines = {}
            with open(f, 'r') as stream:
                for line in stream:
                    if line.startswith(NameParser.TOKENS['name']):
                        name, hooks, is_constant = NameParser.parse_name(line)
                        self.register_atom(name, hooks, is_constant)
                    if line.startswith(NameParser.TOKENS['line']):
                        line_id, graph = NameParser.parse_line(line)
                        lines[line_id] = (graph, [None] * len(graph))

            new_graph = lang.Builder.load_file(f)
            self.ids.stencilview.add_widget(new_graph)
            self.active_graph = new_graph
            #self.graph_list.pop()
            self.graph_list.append(new_graph)
            for w in self.active_graph.walk(restrict=True):
                if isinstance(w, asp.AtomWidget):
                    w._deferred_init()
                    for i in w.get_line_info():
                        line_id = i[0]
                        hook_index = i[1]
                        lines[line_id][1][hook_index] = i[2]
                elif isinstance(w, asp.NexusWidget):
                    for i in w.line_info:
                        line_id = i[0]
                        hook_index = i[1]
                        lines[line_id][1][hook_index] = w

            for line, info in lines.iteritems():
                print line, info
                asp.Line.build_from_graph(info[0], info[1])

            self.set_mode(self.modestr)
            self.set_item(self.itemstr)
            self.dismiss_popup()
        except (KeyboardInterrupt, SystemExit):
            raise
        except:
            self.dismiss_popup()
            self.close_graph()
            self.new_graph()
            self.show_error('Corrupted file.')

    def save(self, path, filename):
        self.working_dir = path
        with open(os.path.join(path, filename), 'w') as stream:
            stream.write('#:kivy 1.0.9\n\n')
            for (name, atom) in self.name_manager.get_all():
                stream.write(NameParser.get_name_str(name, atom.hook_points,
                                                     atom.is_constant))
            for line in asp.Line.get_all_lines():
                stream.write(NameParser.get_line_str(line.line_id,
                                                     line.get_full_graph()))
            stream.write('\n')
            stream.write(self.active_graph.get_tree(0))
        self.dismiss_popup()

    def export(self, path, filename):
        _, ext = os.path.splitext(filename)
        if ext == '.png':
            self.active_graph.export_to_png(os.path.join(path, filename))
        elif ext == '.lp':
            rpn = self.active_graph.get_formula_RPN()
            constants = self.active_graph.get_constants()
            solver = eg_solver.Solver()
            solver.set_formula(rpn, constants)
            rules = solver.generate_asp_rules()
            with open(os.path.join(path, filename), 'w') as stream:
                for r in rules:
                    stream.write(r)
                    stream.write('\n')
        else:
            error_str = 'File extension not supported.'
            print error_str
            self.show_error(error_str)
            return
        self.dismiss_popup()

    def highlight_variables(self):
        self.active_graph.highlight_variables()

    def view_symbolic_formula(self):
        print self.active_graph.get_formula()

    def gringo_query(self, show_predicates):
        def generate_show_statements(predicates):
            pred_list = predicates.split(',')
            pred_list = map(lambda s: s.strip(), pred_list)
            n_args = (lambda name:
                      self.name_manager.get(name).hook_points.count(True))
            pred_list = [p + '/' + str(n_args(p)) for p in pred_list]
            show_list = ['#show {0}.'.format(p) for p in pred_list]
            return show_list

        self.dismiss_popup()

        rpn = self.active_graph.get_formula_RPN()
        rpn = norm.LIT.TRUE if rpn == '' else rpn
        constants = self.active_graph.get_constants()
        print 80 * '-'
        print 'RPN formula:\n', rpn

        solver = eg_solver.Solver()
        result = ''
        try:
            show_statements = []
            if show_predicates:
                try:
                    show_statements = generate_show_statements(show_predicates)
                except Exception:
                    pass
            solver.set_formula(rpn, constants)
            result = solver.solve(show=show_statements)
        except norm.MalformedFormulaError:
            self.show_error('Malformed formula.')
            return
        except RuntimeError, e:
            print e
            self.show_error(str(e))
            return
        self.show_stable_models(solver)