diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..39181eb --- /dev/null +++ b/.editorconfig @@ -0,0 +1,19 @@ +; https://EditorConfig.org +root = true + +[*.{py, css, scss, sass, less, js, ts, html, xhtml, json, txt, ini, yml, env, sh, cfg}] +charset = utf-8 +max_line_length = 140 + +[*.py] +indent_style = space +indent_size = 4 +trim_trailing_whitespace = true +insert_final_newline = true + +[Makefile] +indent_style = tab + +[*.{css, scss, sass, less, js, ts, html, xhtml, json}] +indent_style = space +indent_size = 2 \ No newline at end of file diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..bb7675c --- /dev/null +++ b/.flake8 @@ -0,0 +1,5 @@ +[flake8] +max-line-length = 140 +exclude = venv*/*, + build/*, + dist/*, diff --git a/CHANGELOG.md b/CHANGELOG.md index 9db2b72..ce32ee2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,6 @@ * Removed macshim.py * Removed coldshot support - ## Modifications since the Fork * Python3 compatibility @@ -19,3 +18,17 @@ * Replaced SafeConfigParser with ConfigParser * Replaced logger.warn with logger.warning +## [2.1.0] - 2023-02-05 + +* wxPython 4.2.0 compatibility +* Fixed deprecation warning + +## [2.2.0] - 2023-02-07 + +* Fixed top-level code environment +* Modernized `setup.py`: updated and fixed installation meta +* Modernized `version` control: rename `version.py` -> `vertion.txt` +* Added `requirements.txt` +* Added `.flake8` and fixed the code +* Added `.editorconfig` +* Updated application resource diff --git a/MANIFEST.in b/MANIFEST.in index 6cc9c9c..49daaf1 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,6 +1,7 @@ include MANIFEST.in -include license.txt -include readme.txt +include LICENSE +include CHANGELOG.md +include README.md include setup.py include *.desktop include runsnakerun *.png diff --git a/README.md b/README.md index 711aa8d..758c0bc 100644 --- a/README.md +++ b/README.md @@ -17,8 +17,8 @@ and up to current best practices and standards. ### Requirements -* Python3 -* wxpython 4 +* Python: 3.7+ +* wxPython: 4.2+ ### Modifications since the Fork @@ -33,6 +33,12 @@ and up to current best practices and standards. [RunSnakeRun]: http://www.vrplumber.com/programming/runsnakerun/ +### Start + +```shell +python -m snakerunner +``` + ### Screenshot ![Screenshot of runsnakerun on macOS Mojave with Python 3.7](screenshot.png) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..af51873 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +setuptools~=65.5.1 +wxPython~=4.2.0 \ No newline at end of file diff --git a/setup.py b/setup.py index 16cf194..804ca69 100755 --- a/setup.py +++ b/setup.py @@ -1,22 +1,22 @@ from setuptools import setup, find_packages +from os.path import abspath, dirname, join -meta = {} -exec(open('./snakerunner/version.py').read(), meta) -meta['long_description'] = open('./README.md').read() +_root = dirname(abspath(__file__)) +_version = open(join(_root, 'snakerunner', 'version.txt'), 'r').read() +_long_description = open(join(_root, 'README.md'), 'r').read() +_install_requires = open(join(_root, 'requirements.txt'), 'r').readlines() setup( name='snakerunner', - version=meta['__version__'], + version=_version, author="Bastian Venthur", author_email="venthur@debian.org", description="GUI Viewer for Python profiling runs", - long_description=meta['long_description'], + long_description=_long_description, keywords='profile,gui,wxPython,squaremap', url="https://github.com/venthur/snakerunner", packages=find_packages(exclude=['tests', "venv"]), - install_requires=[ - 'wxpython', - ], + install_requires=_install_requires, license="BSD", zip_safe=False, entry_points={ diff --git a/snakerunner/snakerunner.py b/snakerunner/__main__.py similarity index 98% rename from snakerunner/snakerunner.py rename to snakerunner/__main__.py index 0d0d447..5b051db 100755 --- a/snakerunner/snakerunner.py +++ b/snakerunner/__main__.py @@ -300,7 +300,7 @@ def LoadRSRIcon(self): try: from snakerunner.resources import rsricon_png return getIcon(rsricon_png.data) - except Exception as err: + except Exception: return None sourceCodeControl = None @@ -488,7 +488,7 @@ def OnBackView(self, event): self.historyIndex -= 1 try: self.RestoreHistory(self.history[self.historyIndex]) - except IndexError as err: + except IndexError: self.SetStatusText(_('No further history available')) def OnRootView(self, event): @@ -514,7 +514,7 @@ def SourceShowFile(self, node): if filename and self.sourceFileShown != filename: try: data = open(filename).read() - except Exception as err: + except Exception: # TODO: load from zips/eggs? What about .pyc issues? return None else: @@ -570,7 +570,7 @@ def RecordHistory(self): if self.historyIndex < -1: try: del self.history[self.historyIndex+1:] - except AttributeError as err: + except AttributeError: pass if (not self.history) or record != self.history[-1]: self.history.append(record) @@ -666,10 +666,10 @@ def LoadState(self, config_parser): ] self.SetPosition((x, y)) self.SetSize((width, height)) - except configparser.NoSectionError as err: + except configparser.NoSectionError: # the file isn't written yet, so don't even warn... pass - except Exception as err: + except Exception: # this is just convenience, if it breaks in *any* way, ignore it... log.error( "Unable to load window preferences, ignoring: %s", traceback.format_exc() @@ -698,7 +698,7 @@ def OnCloseWindow(self, event=None): temp = config + '~' self.config.write(open(temp, 'w')) os.rename(temp, config) - except Exception as err: + except Exception: log.error("Unable to write window preferences, ignoring: %s", traceback.format_exc()) self.Destroy() @@ -706,7 +706,7 @@ def OnCloseWindow(self, event=None): class RunSnakeRunApp(wx.App): """Basic application for holding the viewing Frame""" - #handler = wx.PNGHandler() + # handler = wx.PNGHandler() def OnInit(self): """Initialise the application""" diff --git a/snakerunner/listviews.py b/snakerunner/listviews.py index c892293..4aebe48 100644 --- a/snakerunner/listviews.py +++ b/snakerunner/listviews.py @@ -133,7 +133,7 @@ def OnNodeActivated(self, event): node = self.sorted[event.GetIndex()] except AttributeError: pass - except IndexError as err: + except IndexError: log.warning(_('Invalid index in node activated: %(index)s'), index=event.GetIndex()) else: @@ -149,7 +149,7 @@ def OnNodeSelected(self, event): node = self.sorted[event.GetIndex()] except AttributeError: pass - except IndexError as err: + except IndexError: log.warning(_('Invalid index in node selected: %(index)s'), index=event.GetIndex()) else: @@ -166,7 +166,7 @@ def OnMouseMove(self, event): if item > -1: try: node = self.sorted[item] - except IndexError as err: + except IndexError: log.warning(_('Invalid index in mouse move.')) else: wx.PostEvent( @@ -211,7 +211,7 @@ def OnReorder(self, event): def ReorderByColumn(self, column): """Reorder the set of records by column""" # TODO: store current selection and re-select after sorting... - single_column = self.SetNewOrder(column) + self.SetNewOrder(column) self.reorder(single_column=True) self.Refresh() @@ -273,7 +273,7 @@ def OnGetItemText(self, item, col): try: column = self.columns[col] value = column.get(self.sorted[item]) - except IndexError as err: + except IndexError: return '' else: if value is None: @@ -283,7 +283,7 @@ def OnGetItemText(self, item, col): if column.format: try: return column.format % (value,) - except Exception as err: + except Exception: log.warning('Column %s could not format %r value: %r', column.name, type(value), value) value = column.get(self.sorted[item]) diff --git a/snakerunner/pstatsloader.py b/snakerunner/pstatsloader.py index e454091..5526ba4 100644 --- a/snakerunner/pstatsloader.py +++ b/snakerunner/pstatsloader.py @@ -19,7 +19,7 @@ def __init__(self, *filenames): self.location_rows = {} self.stats = pstats.Stats(*filenames) self.tree = self.load(self.stats.stats) - self.location_tree = l = self.load_location() + self.location_tree = self.load_location() ROOTS = ['functions', 'location'] @@ -54,7 +54,7 @@ def load(self, stats): for func, raw in stats.items(): try: rows[func] = row = PStatRow(func, raw) - except ValueError as err: + except ValueError: log.info('Null row: %s', func) for row in rows.values(): row.weave(rows) @@ -177,7 +177,7 @@ def __init__(self, key, raw): file, line, func = self.key = key try: dirname, basename = os.path.dirname(file), os.path.basename(file) - except ValueError as err: + except ValueError: dirname = '' basename = file nc, cc, tt, ct, callers = raw @@ -220,7 +220,7 @@ def child_cumulative_time(self, child): if total: try: (cc, nc, tt, ct) = child.callers[self.key] - except TypeError as err: + except TypeError: ct = child.callers[self.key] return float(ct)/total return 0 diff --git a/snakerunner/resources/__init__.py b/snakerunner/resources/__init__.py index 72b86cb..7b71b9d 100644 --- a/snakerunner/resources/__init__.py +++ b/snakerunner/resources/__init__.py @@ -17,8 +17,8 @@ # CUSTOMISATION POINT # import specialised generators here, such as for wxPython - #from resourcepackage import wxgenerators - #generators.update( wxgenerators.generators ) + # from resourcepackage import wxgenerators + # generators.update( wxgenerators.generators ) except ImportError: pass else: diff --git a/snakerunner/resources/rsricon_png.py b/snakerunner/resources/rsricon_png.py index 740fd72..bcf85cc 100644 --- a/snakerunner/resources/rsricon_png.py +++ b/snakerunner/resources/rsricon_png.py @@ -1,31 +1,7 @@ # -*- coding: ISO-8859-1 -*- """Resource rsricon_png (from file rsricon.png)""" # written by resourcepackage: (1, 0, 1) +# flake8: noqa source = 'rsricon.png' package = 'runsnakerun.resources' -data = "PNG\015\012\032\012\000\000\000\015IHDR\000\000\000 \000\000\000 \010\006\000\000\000szz\000\000\000\004sBIT\010\010\010\010|\010d\000\000\000\011pHYs\000\000\015\ -\000\000\015\001B(x\000\000\000\031tEXtSoftware\000www.inkscape.org<\032\000\000\005NIDATXm\ -lSU\030;e\033[p\033\014Xx\021\007\010\010d\012Q\003:#/JBH4|1~\000_\023/hLHԠ\031D\ -?\020\015\001\004\011\014\004\030\006sۺ[o^Y[/=}_{s\025RJ\020¥(5\005\ -\\\031\012p8\024w)\010A\034!\002)KJxa6f\025V6`T2pi=\034\014z\";\000۶\ -r\001E3gNl*\007\012v0_Ji\013\017ؚ4h\0160\015/-W>\035-\031\ -\035\017\000\010!Դ46l\005+J1,SBbm7&\000eeLqe\007NCnZp:2\014'{\ -\004`B&=5kJȏ(\032\024)\022@O%\015XTW;v\031Q*\021)\000Y\030\0004C\ -lϛA9si6;Ȼ\"ﶓb1ٺ\036|\000\000R=O\035d;&bh\006:\002](\ -(@\015\025\011\026\034SY\010Z0&\000\003\011!\0009·/ngW'\\\023OI\011\027\016RcE}5\ -˾\031-@åץǦ!pzts\033;\011>{IErSJՔ\006\000\020Bh\013p٣܎\0216\ -J)\000R{{eʛc\002\000ds5>JI8ɣ\034\002IJ\007\011\032\010 bI\026\003IǗ-3\ -5@g\007SWaDNM@\030\021\001H)ͺj\000\023\016\001B\001$\031\017z\020.\0345\000:L\013ң\ -(6H']31K\021{Ԥsn\\8\"1,e_c\023&(\002a0\031='\ -z\001\004qi\006g21\0112}?\014|\004lu\013!'и5RzKdEzh\ -7Xb&L(@Ʃoi62CJ\033\004R\023\0340\003\011B؋_ cdf\026G7\007\ -]L\000\000\026;Z4\025\026M\001>\007Sr\005y\025Eh6M\003BޘK\037C\ -6lb߄s\031|t՝gEau\011t\020qʐ6O\024\033\017\004-ts\035\ -&Զ•f0Ñ\035\015X\017p>>RZB4.-B\031n`6]\021\001A\030z2fNf\ -co\013-]o;X\012s${R֡z-AIh\000\0166Yayv\000C@4`\ -`D(n\037\020\007\012@\000\007\022o~\000\000\000\000IENDB`" -# end +data = b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00 \x00\x00\x00 \x08\x06\x00\x00\x00szz\xf4\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\x00\x00\tpHYs\x00\x00\r\xd7\x00\x00\r\xd7\x01B(\x9bx\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\x9b\xee<\x1a\x00\x00\x05NIDATX\x85\xad\x96mlSU\x18\xc7\x7f\xe7\xf6\xde\xde;\xb6\xb5e\x1b[\xc7\xc6p\x1b\x0c\xc6Xx\x11\x07\x08\x08d\nQ\x03:#/J\xe2\x8cBH4\x98\x88\xa2|1~\x00_\x13\xa2\x89/hLH\xd4\xa0\x19D?\x10\r\x01\x04\x82\t\x0c\xc3\xeb\x82\x04\xd8\x18\xd9\xc6\x06s\xac\xdb\xba\x8e\xb6[o\xef\xf1\xc3^Y[\xd8\xe8\x9e/=}\xce\xf3\xbf\xff_{\x9es\xce\x15RJ\x86\x87\x10\xc2\xa5\xd8(5\x9c\xac\x05\\\x80\x19\np8\x14\xe0w)\xe5\xcd\x08A\x1c!\x86\x02\xec\xdc)\xf2KJx\xfb\xe0a6f\x15\xa0V6`\xebT\xc02\xa1\xbd\x9ep\xfdi\xee\xf8=\x1c\x0cz\xd9"\xa5\xbc;\xa6\x00\xdb\xb6\x89\xf4\xf2r\x8e\x01E3g\xf6N\xdel\x86\xf7\x7f\x82\x8e\xf0\xa0\xa0\xe5*\xa1\xcb\x07\xa8\nv0_Ji\xc5\x0b\xa0\xf4\x0f\xca\xca\xd8\x9a\x944h\x0e0\xc9\r/-\xbcW\x90>\x1d-\x7f\x19\x85\x86\x83\x1d\xf1\x9a\x0f\x00\x08!\xd4\xb446\xd8l\x91\x05+J\xc01,\x97SBb\xb2\x9b\xd7m\x9a\x987&\x00eeL\xad\xa9qe\x07\x83N\x86\xf7\xa4\xaeCnZ\xa4p\xd6:2\x0c\'{\xc6\x04`\xe1B&\xed\xde=\x835kJ\xa9\xa8\xc8\x8f(\x1a\x9f\x14)\xd4\x12@O\xc6%\x84\xb0\xc7\r\xe0\xf5\xd2\xe3\xf1XTW;\xb8v\xcd\x19Q\xa4*\x11)\x00\x9cY\x18\xc0\xf4\xb8\x004C<\xfe\xe5\xf7\xecj\xf5^\x8cY\xd4\xde\xb7\xe1\x9a\xce\xc3\xd0\xbew\xe5\x90b\xd3x4.\x00\xc3\xc9/\x0b63w\xea\xfa \xb9\xb9\x9dL\x9b\xe6\x8d(j\xf4\xf4~\xa6\xe4A\xcb\xd5\xc1\xbc\xc3\x8dMOfi\\\x00\x19E\xb8\xec\x89\x80\x06\x1b\xde:\xca\xbau\xb5\xf7\x14\x9c\xba\x08\xcd\xa1\xdeq\x82\x0b\x82C\xf8BA\xb0\xc2x\xe2\x02\xb0L\xcc\xfe/U\x1d^j\x1b\x06\'\x83\xddPq\x12\x84\x80\xf6\x06h<\x07\xaa18\xefm"\x10\xf4\xf2w<\x00\xea\x9dk\xf8\n\x9e"UQ\xc1g\xc1\x8e\xfd\xb0z.8\x12\xe0P\x15\xd4t\xf6\x16\xfan\xc3\xc4\xd9\xa0\xea}\xc2\xb0\xc2\xc6\xd3\xd3\xcc\xad\\\xb9\x14\x0f\x80\xb0\'\xb2\xca\x91\xc9\x9ey\xaf\x92&bt{O\xb3\xc2\x17\x15Kh\xca\xe9\xe4\xf2#\x1e\xc2\x8adUe\x1e\xbf\xb5\xfd\xd7\xfeA\xd7\xd5T\x19\xedF\x1b)\x80\x94\x12\xc3!\xca\x9d\xd9\xec*.#\xd5>l\xcf\x9bA\xe89\x91\xc0si\xe9\xe46;\xc8\xbb\xed"\xef\xb6\x93\xb0b1\xcb\xfe\xd7\xd9\xba\xb6\xee\xc7\x1e\xd6|\x00\x00\xc0\xa6\x89R=\x99O\x1d\x99d;&bh\x06:\x02]((\xd9\xf3@\xb1\r\x15\t\x16\x1c\x9a\xc4\xfcSY\xff\xac\x08\x9dZ0&\x00\x03\t!\xa6\x009\xaa\xce\x87/ng\xb1W\'\xea\xc2\\\xf9\x13O\xdbI\xed\xd9\t\x17\x0e\xa9R\xe1cE\x88}5\xc5\xcb\xbe\x19-@\xc4\xc3\xa5\x94\xd7\xa5\x94\xc7\xa6\xcd!\xabpzts\x80\xb6\x1b\xf8;\t\x9d\xbd>{\xf9IErSJ\xf9\xd5\x94\xaa\xa3\xcb\xe3\x06\x00\x10Bh\x93\x0bp\xd9\xa3\xdc\x8e\xfd\x116\xf1J)\xc3\x00R\x91{{e\xca\x9bc\x02\x00d\xb8s\xd05\xf5>JI\xa8\x7f8\xc9\xa3\x1c\x91\x82\xc3\xc0\xf9\xd1\x02\xc4\xb2\xf0\x07\xbb\x90\t\x1a\x08 b\x8fI\x90\x16\x03\xefI\xc7\x97-3\x81\x95\xa35\x87\xd8\xff@g\x87\x07SW\xc1aDN\x86M@\xe0\x7f\x18\xc3\x11\x01H)\xcd\xbaj\xbc\x00\x13\x86\xbf\x0e\x01B\x01$\x19\x0fz\xb8\x10\xc2.\x84\xc8\x1c5\x00\xc0\xcd:\xaaL\x0b\xd2\xa3\xbc\x8c(6H\xcd\']3\xc4\xd31\x8c\xa7\x8cK\x11{\x92\xdd\xd4\xa4\xe6sn\\\x8a8"\x84\x88\xda\xd21\xdb,\xe0e_c\x13\xab\xb2&\xa2(\x02\xaca\x8d0\xfd\x19\xc6\xfb=\xfc\xa8\'\x8a\xcfz\xfc\xfc\x01\x04\xb5q\x94i\x06\xaf\xb8g21\xef\t2\x9c\xd9}?\xe6\x0c\xae\xda\xe3|\x04l\x8f\x80\x8du\x8c\x0b!\x8c\'\xd7\xd0\xb8\xfe5R\xab\x9a\xe0zKd\x8d\xb4\xe0\xd6Ez\xda\xeah7\x83X\xeeb&L(@\xed\xbf\xb0\x86\xc6\xa9oi\xf462CJ\xe9\x1b\x9a\x8f\xb9\x04R\xca\xe0\x99\x13\x1c\xf1\xfa\xa00\x03\xa2\x9d\tB\x81\xac\xb9\xd8\x8b_ c\xce\xcbdf\x16G7\x07\xc8]L\x86\x9e\xc4\xc6\xe1\xf9\x98\x00\x00\xde\x16\xde\xf8\xf5;Z4\x15\x16M\x01\xf5>\x07S\xb4\xb0\xc2\xd0r\x05y\xb7\x15\xdcEh6\x9dM\x03\xf0B\xb8\xec\xe3\xc4\xde\x98K\xd0\x1f\x86C\xac\x9d\xbb\x94\xef6lb\xbc\xdf\x84s\xf5\xd0\x19|\x80\xb3\x84\xfa\xd3t\xd5\x9d\xa4\xd1\xec\xe1gEau\xd1\xf3\xcc\tt\x10\xbcq\x82\xca\x90\x9f\xfd6\x9dO\x14\x1b\xd5\x0f\x04\xe8\xa3-t\xe7s\xa0\xfc\x1d\xf2\xf2&\xa3\xd4\xb6\xc2\x95f0\xc3\x91\xb5\x1d\rX\x97\x0fp\xbb\xdb\xc7\xd7\xdd>>\x97RZB\x88\xc4\xc44.-\xdaB\xae\xaf\x19\xdan`6]\xc0\xdb\xd5\xc2\xd2\x11\x01\xf4A\x18z2\xeffNf\xf3\xca\xf5\xa4\xa7\xa4co\xbc\x0b-]\xe0o\x85\xae;X\xf5\x95\xdc\n\xb4s$\xd0\xc1{R\xca\xd6\xa1z-A\x94\xeaI\xfc\xa0\xeah\xa1\x00\xad\x81\x0e6Yayv\xc4\x00C@4`\xa9\xe1`\xb9\xa2\xb2D(\xa8\xd2\xe2\xdfn\x1f\x95\xe1\x10\x07\xa5\x94\xcd\xf7\xd1\n@\xe9\xbf\xc4\x00\xfe\x07\x8b\x12\xf3\xe1o\x85~\x9d\x00\x00\x00\x00IEND\xaeB`\x82' diff --git a/snakerunner/squaremap.py b/snakerunner/squaremap.py index 0625d92..518cd18 100755 --- a/snakerunner/squaremap.py +++ b/snakerunner/squaremap.py @@ -9,7 +9,6 @@ import wx.lib.newevent log = logging.getLogger('squaremap') -#log.setLevel( logging.DEBUG ) SquareHighlightEvent, EVT_SQUARE_HIGHLIGHTED = wx.lib.newevent.NewEvent() SquareSelectionEvent, EVT_SQUARE_SELECTED = wx.lib.newevent.NewEvent() @@ -225,7 +224,7 @@ def SetModel(self, model, adapter=None): self.UpdateDrawing() def OnPaint(self, event): - dc = wx.BufferedPaintDC(self, self._buffer) + wx.BufferedPaintDC(self, self._buffer) def OnSize(self, event): # The buffer is initialized in here, so that the buffer is always @@ -415,9 +414,7 @@ def LayoutChildren(self, dc, children, parent, x, y, w, h, hot_map, depth=0, nod return (firstSize, firstNode) = nodes[-1] - fraction = firstSize/float(total) - head_coord, tail_coord = split_box( - firstSize/float(total), x, y, w, h) + head_coord, tail_coord = split_box(firstSize/float(total), x, y, w, h) if head_coord: self.DrawBox( dc, firstNode, head_coord[0], head_coord[1], head_coord[2], head_coord[3], @@ -461,7 +458,7 @@ def split_box(fraction, x, y, w, h): def split_by_value(total, nodes, headdivisor=2.0): """Produce, (sum,head),(sum,tail) for nodes to attempt binary partition""" - head_sum, tail_sum = 0, 0 + head_sum = 0 divider = 0 for node in nodes[::-1]: if head_sum < total/headdivisor: diff --git a/snakerunner/version.py b/snakerunner/version.py deleted file mode 100644 index a33997d..0000000 --- a/snakerunner/version.py +++ /dev/null @@ -1 +0,0 @@ -__version__ = '2.1.0' diff --git a/snakerunner/version.txt b/snakerunner/version.txt new file mode 100644 index 0000000..e3a4f19 --- /dev/null +++ b/snakerunner/version.txt @@ -0,0 +1 @@ +2.2.0 \ No newline at end of file