From 414c7bd6c5d383b9e31e6e53d39b6ec0cd742a4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Gotor?= Date: Thu, 14 Dec 2023 23:15:50 +0100 Subject: [PATCH 1/3] Create main.yml --- .github/workflows/main.yml | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 .github/workflows/main.yml diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..383e65c --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,23 @@ +name: Pylint + +on: [push] + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ["3.8", "3.9", "3.10"] + steps: + - uses: actions/checkout@v3 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v3 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install pylint + - name: Analysing the code with pylint + run: | + pylint $(git ls-files '*.py') From 74a3f4d339c083ff001a9943b43e8fdfad3e24c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Gotor?= Date: Sun, 17 Dec 2023 22:54:08 +0100 Subject: [PATCH 2/3] refactor: addressed pylint warnings --- .github/workflows/main.yml | 2 +- area_view.py | 42 ++++-- labels.py | 19 ++- linkerscope.py | 25 ++-- links.py | 6 +- map_drawer.py | 164 ++++++++++++++--------- map_file_parser.py => map_file_loader.py | 29 ++-- map_parser.py | 25 ++-- section.py | 8 +- sections.py | 24 ++-- style.py | 13 +- 11 files changed, 229 insertions(+), 128 deletions(-) rename map_file_parser.py => map_file_loader.py (55%) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 383e65c..542dde0 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -20,4 +20,4 @@ jobs: pip install pylint - name: Analysing the code with pylint run: | - pylint $(git ls-files '*.py') + pylint $(git ls-files '*.py') --disable=C0114,R0902,C0116 --fail-under=9 diff --git a/area_view.py b/area_view.py index 3856c51..fbc1a4d 100644 --- a/area_view.py +++ b/area_view.py @@ -6,6 +6,10 @@ class AreaView: + """ + AreaView provides the container for a given set of sections and the methods to process + and transform the information they contain into useful data for graphical representation + """ pos_y: int pos_x: int zoom: int @@ -37,18 +41,38 @@ def __init__(self, if self.config is not None: self._process() - def get_processed_section_views(self): + def get_split_area_views(self): + """ + Get current area view split in multiple area views around break sections + :return: List of AreaViews + """ return self.processed_section_views - def to_pixels(self, value): + def to_pixels(self, value) -> float: + """ + Convert a given address to pixels in an absolute manner, + according to the address / pixel size ratio of current area + + :param value: Address to be converted to pixels + :return: Conversion result + """ return value / self.address_to_pxl - def to_pixels_relative(self, value): - a = self.size_y - ((value - self.start_address) / self.address_to_pxl) - return a + def to_pixels_relative(self, value) -> float: + """ + Convert a given address to pixels in a relative manner, + according to the address / pixel size ratio of current area - def _overwrite_sections_info(self): + Relative in this context means relative to the start address of the Area view. If Area View + starts at 0x20000 and ends at 0x30000, passing these values to this function for an area + with a height of 1000 pixels, will result in 0 and 1000 respectively + :param value: Address to be converted to pixels + :return: Conversion result + """ + return self.size_y - ((value - self.start_address) / self.address_to_pxl) + + def _overwrite_sections_info(self): for section in self.sections.get_sections(): section_style = copy.deepcopy(self.style) @@ -80,7 +104,8 @@ def _process(self): if area_has_breaks: total_breaks_size_y_px = self._get_break_total_size_px() - total_non_breaks_size_y_px = self._get_non_breaks_total_size_px(self.sections.get_sections()) + total_non_breaks_size_y_px = self._get_non_breaks_total_size_px( + self.sections.get_sections()) expandable_size_px = total_breaks_size_y_px - (breaks_section_size_y_px * breaks_count) last_area_pos = self.pos_y + self.size_y @@ -90,7 +115,8 @@ def _process(self): if section_group.is_break_section_group(): corrected_size = breaks_section_size_y_px else: - split_section_size_px = self._get_non_breaks_total_size_px(section_group.get_sections()) + split_section_size_px = self._get_non_breaks_total_size_px( + section_group.get_sections()) corrected_size = (split_section_size_px / total_non_breaks_size_y_px) * ( total_non_breaks_size_y_px + expandable_size_px) diff --git a/labels.py b/labels.py index 658e2cd..79683ea 100644 --- a/labels.py +++ b/labels.py @@ -1,9 +1,13 @@ import copy - +from dataclasses import dataclass from style import Style +@dataclass class Labels: + """ + Container for labels, and methods to build them from a yaml label specification + """ sections: [] addresses: [] style: Style @@ -12,7 +16,12 @@ def __init__(self, labels, style): self.style = style self.labels = self.build_labels(labels) - def build_labels(self, labels_yaml): + def build_labels(self, labels_yaml) -> []: + """ + Build a list of labels (`[Label]`) from a list of labels in a yaml format + :param labels_yaml: List of labels in a yaml format + :return: list of labels (`[Label]`) + """ labels = [] for element in labels_yaml: @@ -28,8 +37,12 @@ def build_labels(self, labels_yaml): return labels +@dataclass class Label: - + """ + Stores single label information for a given address. + Additionally, provides style information for drawing the link + """ def __init__(self, style): self.style = style self.address = 0 diff --git a/linkerscope.py b/linkerscope.py index 52a2030..f7dc0f4 100755 --- a/linkerscope.py +++ b/linkerscope.py @@ -2,15 +2,16 @@ import argparse import copy +import sys + import yaml from area_view import AreaView from helpers import safe_element_get -from labels import Labels from links import Links from map_drawer import Map from style import Style -from map_file_parser import MapFileParser +from map_file_loader import MapFileLoader from sections import Sections parser = argparse.ArgumentParser() @@ -20,25 +21,25 @@ default='map.svg') parser.add_argument('--input', '-i', - help='Name of the map file, can be either linker .map files or .yaml descriptor', + help='Name of the map file,' + 'can be either linker .map files or .yaml descriptor', default='map.yaml') parser.add_argument('--configuration', '-c', - help='Configuration file (.yml). If not specified, will use config.yaml as default', + help='Configuration file (.yml). If not specified,' + 'will use config.yaml as default', default='config.yaml') args = parser.parse_args() areas = [] -with open(args.configuration, 'r') as file: +with open(args.configuration, 'r', encoding='utf-8') as file: config = yaml.safe_load(file) if config['areas'] is None: print('No information to show on current configuration file') - exit(-1) - - # TODO: linked sections compatibility + sys.exit(-1) base_style = Style().get_default() base_style.override_properties_from(Style(style=config.get('style', None))) @@ -50,7 +51,7 @@ section_size = area.get('section-size', {}) _range = area.get('range', {}) area_view = AreaView( - sections=(Sections(sections=MapFileParser(args.input).parse()) + sections=(Sections(sections=MapFileLoader(args.input).parse()) .filter_address_min(safe_element_get(_range, 0)) .filter_address_max(safe_element_get(_range, 1)) .filter_size_min(safe_element_get(section_size, 0)) @@ -60,11 +61,13 @@ global_config=config, style=area_style.override_properties_from(Style(style=area.get('style'))) ) - areas.extend(area_view.get_processed_section_views()) + areas.extend(area_view.get_split_area_views()) yaml_links = config.get('links', None) links_style = copy.deepcopy(base_style) -links = Links(config.get('links', {}), style=links_style.override_properties_from(Style(style=yaml_links.get('style') if yaml_links is not None else None))) +links = Links(config.get('links', {}), + style=links_style.override_properties_from( + Style(style=yaml_links.get('style') if yaml_links is not None else None))) Map(area_view=areas, links=links, style=base_style, diff --git a/links.py b/links.py index fa700db..88390cf 100644 --- a/links.py +++ b/links.py @@ -2,11 +2,15 @@ class Links: + """ + Stores the link information between given section or address + Additionally, provides style information for drawing the link + """ sections: [] addresses: [] style: Style - def __init__(self, links={}, style=None): + def __init__(self, links, style=None): self.addresses = links.get('addresses', []) self.sections = links.get('sections', []) self.style = style diff --git a/map_drawer.py b/map_drawer.py index 7719aa4..5be9b24 100755 --- a/map_drawer.py +++ b/map_drawer.py @@ -1,18 +1,21 @@ -import copy from math import cos -import svgwrite from svgwrite import Drawing - -from area_view import AreaView +import svgwrite from section import Section from style import Style class Map: + """ + This class does the actual drawing of the map. + + Takes all the graphical information stored at the different sections and areas, together + with their style and configuration, and convert them to SVG objects (see `draw()` function) + """ dwg: Drawing pointer_y: int - def __init__(self, area_view=[], links={}, file='map.svg', **kwargs): + def __init__(self, area_view, links, file='map.svg', **kwargs): self.style = kwargs.get('style') self.type = type self.area_views = area_view @@ -28,8 +31,8 @@ def _get_valid_linked_sections(self, linked_sections): """ Get a valid list of linked sections to draw, given a list of wished sections to be linked - For a link to be valid, the starting and ending addresses of the linked section/s must be visible and available - inside of at least one single area + For a link to be valid, the starting and ending addresses of the linked section/s must be + visible and available inside of at least one single area :param linked_sections: List of sections or pair of sections to be linked :return: List of valid (start, end) addresses for sections @@ -42,13 +45,14 @@ def _get_valid_linked_sections(self, linked_sections): appended = False multi_section = False - # Check if we are dealing with a link for a single section or for many of them. That is, user passed a - # string or a list of two strings + # Check if we are dealing with a link for a single section or for many of them. + # That is, user passed a string or a list of two strings if isinstance(linked_section, list): multi_section = True - # Iterate through all available areas checking if this is a valid link: i.e, the starting and ending - # addresses of the linked section/s is visible and available inside of a single area + # Iterate through all available areas checking if this is a valid link: i.e, the + # starting and ending addresses of the linked section/s is visible and available + # inside of a single area for area in self.area_views: start = None end = None @@ -58,32 +62,35 @@ def _get_valid_linked_sections(self, linked_sections): break for section in area.sections.get_sections(): - # If single section, the start and end address of the linked section equals those of the section + # If single section, the start and end address of the linked section equals + # those of the section if not multi_section: if section.name == linked_section: l_sections.append([section.address, section.address + section.size]) appended = True break - # If multiple section, the start and end address of the linked section are the start of the first - # provided section and the end of the second provided section respectively + # If multiple section, the start and end address of the linked section are the + # start of the first provided section and the end of the second provided section + # respectively else: if section.name == linked_section[0]: start = section.address elif section.name == linked_section[1]: end = section.address + section.size - # If before finishing the iteration on this area, we found a valid start and end address, - # we can append this linked section to the list + # If before finishing the iteration on this area, we found a valid start and + # end address, we can append this linked section to the list if start is not None and end is not None: l_sections.append([start, end]) appended = True break - # If we finish iterating the area, and we have a valid start (or end) address but the section was not - # appended, means that the other end of the section is at another area, and that is not valid + # If we finish iterating the area, and we have a valid start (or end) address but + # the section was not appended, means that the other end of the section is at + # another area, and that is not valid if multi_section and not appended and (start is not None or end is not None): - print("WARNING: A multisection zoom region was specified for two sections of different areas," - "which is not supported") + print("WARNING: A multisection zoom region was specified for two sections" + "of different areas, which is not supported") break return l_sections @@ -104,7 +111,11 @@ def _draw_area(_area_view): group.translate(_area_view.pos_x, _area_view.pos_y) - dwg.add(dwg.rect(insert=(0, 0), size=('100%', '100%'), rx=None, ry=None, fill=self.style.background)) + dwg.add(dwg.rect(insert=(0, 0), + size=('100%', '100%'), + rx=None, + ry=None, + fill=self.style.background)) lines_group = dwg.g() growths_group = dwg.g() @@ -134,18 +145,23 @@ def _draw_area(_area_view): zoom[1] <= area_view.sections.highest_memory and \ zoom[0] >= self.area_views[0].sections.lowest_memory and \ zoom[1] <= self.area_views[0].sections.highest_memory: - linked_sections_group.add(self._make_poly(area_view, zoom[0], zoom[1], self.links.style)) + + linked_sections_group.add(self._make_poly(area_view, + zoom[0], + zoom[1], + self.links.style)) is_drawn = True if not is_drawn: - print("WARNING: Starting or ending point of the zoom region is outside the shown areas") - + print("WARNING: Starting or ending point of the zoom region is outside the shown" + "areas") for area_view in self.area_views: _draw_area(area_view) - # We need to do another pass once all areas are drawn in order to be able to properly draw the growth arrows - # without the break areas hiding them. Also, as we do stuff outside the loop where the areas are drawn, we - # loose the reference for translation, and we have to manually translate the grows here + # We need to do another pass once all areas are drawn in order to be able to properly draw + # the growth arrows without the break areas hiding them. Also, as we do stuff outside the + # loop where the areas are drawn, we loose the reference for translation, and we have to + # manually translate the grows here for area_view in self.area_views: area_growth = dwg.g() for section in area_view.sections.get_sections(): @@ -162,11 +178,10 @@ def _make_title(self, area_view): title_pos_x = area_view.size_x / 2 title_pos_y = -20 return self._make_text(area_view.area.get('title', ''), - title_pos_x, - title_pos_y, + (title_pos_x, title_pos_y), style=area_view.style, anchor='middle', - title=True + text_type='title' ) def _make_growth(self, section: Section) -> svgwrite.container.Group: @@ -186,12 +201,18 @@ def _make_growth(self, section: Section) -> svgwrite.container.Group: def _make_growth_arrow_generic(arrow_start_y, direction): points_list = [(mid_point_x - arrow_tail_width, arrow_start_y), - (mid_point_x - arrow_tail_width, arrow_start_y - direction * arrow_length), - (mid_point_x - arrow_head_width, arrow_start_y - direction * arrow_head_height), - (mid_point_x, arrow_start_y - direction * (arrow_length + arrow_head_height)), - (mid_point_x + arrow_head_width, arrow_start_y - direction * arrow_head_height), - (mid_point_x + arrow_tail_width, arrow_start_y - direction * arrow_length), - (mid_point_x + arrow_tail_width, arrow_start_y)] + (mid_point_x - arrow_tail_width, + arrow_start_y - direction * arrow_length), + (mid_point_x - arrow_head_width, + arrow_start_y - direction * arrow_head_height), + (mid_point_x, + arrow_start_y - direction * (arrow_length + arrow_head_height)), + (mid_point_x + arrow_head_width, + arrow_start_y - direction * arrow_head_height), + (mid_point_x + arrow_tail_width, + arrow_start_y - direction * arrow_length), + (mid_point_x + arrow_tail_width, + arrow_start_y)] group.add(self.dwg.polyline(points_list, stroke=section.style.growth_arrow_stroke, @@ -222,8 +243,8 @@ def _make_break(self, section: Section) -> svgwrite.container.Group: """ Make a break representation for a given section. - Depending on the selected break type (at style/break_type), break can be wave (~), double wave(≈), diagonal(/) - or dots(...) + Depending on the selected break type (at style/break_type), break can be wave (~), double + wave(≈), diagonal(/) or dots(...) :param section: Section for which the break wants to be created :return: SVG group container with the breaks graphics """ @@ -239,7 +260,9 @@ def _make_break_dots(_section: Section) -> svgwrite.container.Group: :param _section: Section for which the break wants to be created :return: SVG group container with the breaks graphics """ - rectangle = self.dwg.rect((_section.pos_x, _section.pos_y), (_section.size_x, _section.size_y)) + rectangle = self.dwg.rect((_section.pos_x, _section.pos_y), + (_section.size_x, _section.size_y)) + rectangle.fill(style.fill) rectangle.stroke(style.stroke, width=style.stroke_width) @@ -270,7 +293,8 @@ def _make_break_wave(_section: Section) -> svgwrite.container.Group: points = [(i, mid_point_y + shift[0] + 2 * cos(i / 24)) for i in range(wave_len)] points.extend( [ - (_section.pos_x + _section.size_x, (_section.pos_y + _section.size_y) * shift[1]), + (_section.pos_x + _section.size_x, + (_section.pos_y + _section.size_y) * shift[1]), (_section.pos_x + _section.size_x, _section.pos_y + shift[2]), (_section.pos_x, _section.pos_y + shift[2]), (_section.pos_x, mid_point_y + shift[0] + 2 * cos(_section.pos_x / 24)), @@ -305,7 +329,8 @@ def _make_break_double_wave(_section: Section) -> svgwrite.container.Group: ] ] - rectangle = self.dwg.rect((_section.pos_x, _section.pos_y), (_section.size_x, _section.size_y)) + rectangle = self.dwg.rect((_section.pos_x, _section.pos_y), + (_section.size_x, _section.size_y)) rectangle.fill(section.style.fill) group.add(rectangle) @@ -341,12 +366,15 @@ def _make_break_diagonal(_section: Section) -> svgwrite.container.Group: """ points_list = [[(_section.pos_x, _section.pos_y), (_section.pos_x + _section.size_x, _section.pos_y), - (_section.pos_x + _section.size_x, (_section.pos_y + _section.size_y) * 3 / 10), + (_section.pos_x + _section.size_x, + (_section.pos_y + _section.size_y) * 3 / 10), (_section.pos_x, (_section.pos_y + _section.size_y) * 5 / 10), (_section.pos_x, _section.pos_y) ], [(_section.pos_x, _section.pos_y + _section.size_y), - (_section.pos_x + _section.size_x, _section.pos_y + _section.size_y), - (_section.pos_x + _section.size_x, (_section.pos_y + _section.size_y) * 5 / 10), + (_section.pos_x + _section.size_x, + _section.pos_y + _section.size_y), + (_section.pos_x + _section.size_x, + (_section.pos_y + _section.size_y) * 5 / 10), (_section.pos_x, (_section.pos_y + _section.size_y) * 7 / 10), (_section.pos_x, _section.pos_y + _section.size_y), ]] @@ -368,15 +396,21 @@ def _make_break_diagonal(_section: Section) -> svgwrite.container.Group: if style.break_type == _break[0]: return _break[1](section) - def _make_text(self, text, pos_x, pos_y, style, anchor, baseline='middle', small=False, title=False): - if title: + def _make_text(self, + text, + position, + style, + text_type='normal', + **kwargs): + + if text_type == 'title': size = '24px' - elif small: + elif text_type == 'small': size = '12px' else: size = style.font_size - return self.dwg.text(text, insert=(pos_x, pos_y), + return self.dwg.text(text, insert=(position[0], position[1]), stroke=style.text_stroke, # focusable='true', fill=style.text_fill, @@ -384,32 +418,29 @@ def _make_text(self, text, pos_x, pos_y, style, anchor, baseline='middle', small font_size=size, font_weight="normal", font_family=style.font_type, - text_anchor=anchor, - alignment_baseline=baseline + text_anchor=kwargs.get('anchor', 'middle'), + alignment_baseline=kwargs.get('baseline', 'middle') ) def _make_name(self, section): return self._make_text(section.name, - section.name_label_pos_x, - section.name_label_pos_y, + (section.name_label_pos_x,section.name_label_pos_y), style=section.style, anchor='middle', ) def _make_size_label(self, section): return self._make_text(hex(section.size), - section.size_label_pos[0], - section.size_label_pos[1], + (section.size_label_pos[0], section.size_label_pos[1]), section.style, - 'start', - 'hanging', - True, + anchor='start', + baseline='hanging', + text_type='small' ) def _make_address(self, section): return self._make_text(hex(section.address), - section.addr_label_pos_x, - section.addr_label_pos_y, + (section.addr_label_pos_x, section.addr_label_pos_y), anchor='start', style=section.style) @@ -454,10 +485,10 @@ def _get_points_for_address(self, address, area_view): def _make_poly(self, area_view, start_address, end_address, style): points = [] - reversed = self._get_points_for_address(end_address, area_view) - reversed.reverse() + _reversed = self._get_points_for_address(end_address, area_view) + _reversed.reverse() points.extend(self._get_points_for_address(start_address, area_view)) - points.extend(reversed) + points.extend(_reversed) return self.dwg.polyline(points, stroke=style.stroke, @@ -504,7 +535,7 @@ def _make_label(self, label, area_view): label_length = label.length if address is None: - return + raise KeyError("A label without address was found") if side == 1: pos_x_d = area_view.size_x @@ -520,14 +551,14 @@ def _make_label(self, label, area_view): points = [(0 + pos_x_d, pos_y), (direction*(label_length + pos_x_d), pos_y)] if 'right' in label.directions: - g.add(self._make_arrow_head(label, direction='right')).translate(direction*(label_length + pos_x_d), pos_y,) + g.add(self._make_arrow_head(label, direction='right')).translate( + direction*(label_length + pos_x_d), pos_y,) if 'left' in label.directions: g.add(self._make_arrow_head(label, direction='left')).translate(pos_x_d,pos_y,) g.add(self._make_text(text, - direction*(pos_x_d + label_length + line_label_spacer), - pos_y, + (direction*(pos_x_d + label_length + line_label_spacer), pos_y), label.style, anchor=anchor)) @@ -538,7 +569,6 @@ def _make_label(self, label, area_view): )) return g - def _make_links(self, address, style): hlines = self.dwg.g(id='hlines', stroke='grey') diff --git a/map_file_parser.py b/map_file_loader.py similarity index 55% rename from map_file_parser.py rename to map_file_loader.py index 6dc1249..a59e516 100644 --- a/map_file_parser.py +++ b/map_file_loader.py @@ -1,35 +1,49 @@ +import os +import sys + import yaml from section import Section from map_parser import MapParser -import os -class MapFileParser: + +class MapFileLoader: + """ + Takes input file provided by user and loads it in memory for further processing. + Depending on the type of file (.map or .yaml) will include an additional conversion step and + create a temporary .yaml file + """ def __init__(self, file): self.input_filename = file def parse(self): - filename, file_extension = os.path.splitext(self.input_filename) + _, file_extension = os.path.splitext(self.input_filename) if file_extension == '.map': self.parse_map(self.input_filename) return self.parse_yaml('examples/map.yaml') - elif file_extension in ['.yaml', '.yml']: + + if file_extension in ['.yaml', '.yml']: return self.parse_yaml(self.input_filename) + print("Wrong map file extension. Use .map or .yaml files") + sys.exit(-1) + @staticmethod def parse_yaml(filename): sections = [] - with open(filename, 'r') as file: + with open(filename, 'r', encoding='utf-8') as file: y = yaml.safe_load(file) for element in y['map']: sections.append(Section(address=element['address'], size=element['size'], name=element['name'], - parent=element.get('parent') if element.get('parent') is not None else 'none', - type=element.get('type') if element.get('type') is not None else 'area', + parent=element.get('parent') if element.get( + 'parent') is not None else 'none', + _type=element.get('type') if element.get( + 'type') is not None else 'area', )) return sections @@ -37,4 +51,3 @@ def parse_yaml(filename): @staticmethod def parse_map(input_filename): MapParser(input_filename=input_filename, output_filename='examples/map.yaml').parse() - diff --git a/map_parser.py b/map_parser.py index fe7052b..580a3c9 100644 --- a/map_parser.py +++ b/map_parser.py @@ -1,10 +1,13 @@ import re +import yaml from section import Section -import yaml class MapParser: + """ + Parse a linker map file and convert it to a yaml file for further processing + """ def __init__(self, input_filename, output_filename): self.areas = [] self.sections = [] @@ -12,18 +15,11 @@ def __init__(self, input_filename, output_filename): self.output_filename = output_filename def parse(self): - with open(self.input_filename, 'r') as file: + with open(self.input_filename, 'r', encoding='utf8') as file: - found_memory_map_section = False file_iterator = iter(file) prev_line = next(file_iterator) for line in file_iterator: - if 0:#not found_memory_map_section: - if 'Linker script and memory map' in line: - found_memory_map_section = True - else: - continue - self.process_areas(prev_line) multiple_line = prev_line + line self.process_sections(multiple_line) @@ -47,12 +43,12 @@ def parse(self): 'name': section.name, }) - with open(self.output_filename, 'w') as file: + with open(self.output_filename, 'w', encoding='utf8') as file: yaml_string = yaml.dump(my_dict) file.write(yaml_string) def process_areas(self, line): - pattern = ('([.][a-z]{1,})[ ]{1,}(0x[a-fA-F0-9]{1,})[ ]{1,}(0x[a-fA-F0-9]{1,})\n') + pattern = r'([.][a-z]{1,})[ ]{1,}(0x[a-fA-F0-9]{1,})[ ]{1,}(0x[a-fA-F0-9]{1,})\n' p = re.compile(pattern) result = p.search(line) @@ -62,12 +58,13 @@ def process_areas(self, line): name=result.group(1), address=int(result.group(2), 0), size=int(result.group(3), 0), - type='area' + _type='area' ) ) def process_sections(self, line): - pattern = ('\s(.[^.]+).([^. \n]+)[\n\r]\s+(0x[0-9a-fA-F]{16})\s+(0x[0-9a-fA-F]+)\s+[^\n]+[\n\r]{1}') + pattern = r'\s(.[^.]+).([^. \n]+)[\n\r]\s+(0x[0-9a-fA-F]{16})\s+' \ + r'(0x[0-9a-fA-F]+)\s+[^\n]+[\n\r]{1}' p = re.compile(pattern) result = p.search(line) @@ -77,6 +74,6 @@ def process_sections(self, line): name=result.group(2), address=int(result.group(3), 0), size=int(result.group(4), 0), - type='section' + _type='section' ) ) diff --git a/section.py b/section.py index 5270536..cbe4a44 100755 --- a/section.py +++ b/section.py @@ -2,6 +2,10 @@ class Section: + """ + Holds logical and graphical information for a given section, as well as other properties such as + style, visibility, type, etc... + """ size: int address: int name: str @@ -12,8 +16,8 @@ class Section: label_offset: int = 10 style: Style - def __init__(self, size, address, name, type, parent): - self.type = type + def __init__(self, size, address, name, _type, parent): + self.type = _type self.parent = parent self.size = size self.address = address diff --git a/sections.py b/sections.py index 4243471..f864bd6 100755 --- a/sections.py +++ b/sections.py @@ -2,6 +2,10 @@ class Sections: + """ + Provide methods and to select and filter sections according to their base address, size, parent, + type,... + """ sections: [Section] = [] def __init__(self, sections: [Section]): @@ -53,15 +57,16 @@ def filter_size_max(self, size_bytes: int): def filter_address_max(self, address_bytes: int): return Sections(self.sections) if address_bytes is None \ - else Sections(list(filter(lambda item: (item.address + item.size) <= address_bytes, self.sections))) + else Sections(list(filter(lambda item: (item.address + item.size) + <= address_bytes, self.sections))) def filter_address_min(self, address_bytes: int): return Sections(self.sections) if address_bytes is None \ else Sections(list(filter(lambda item: item.address >= address_bytes, self.sections))) - def filter_type(self, type: str): - return Sections(self.sections) if type is None \ - else Sections(list(filter(lambda item: item.filter_type == type, self.sections))) + def filter_type(self, _type: str): + return Sections(self.sections) if _type is None \ + else Sections(list(filter(lambda item: item.filter_type == _type, self.sections))) def filter_parent(self, parent: str): return Sections(self.sections) if parent is None \ @@ -84,8 +89,8 @@ def split_sections_around_breaks(self) -> []: for _break in breaks: # Section that covers from previous break till start of this break - # If it was the first break, will cover from begining of the whole area to this break. Only append if search - # returns more than 0 counts + # If it was the first break, will cover from begining of the whole area to this break. + # Only append if search returns more than 0 counts s = Sections(sections=self.sections)\ .filter_address_max(_break.address)\ .filter_address_min(previous_break_end_address) @@ -96,8 +101,8 @@ def split_sections_around_breaks(self) -> []: split_sections.append(Sections(sections=[_break])) previous_break_end_address = _break.address + _break.size - # Section that covers from the last break end address to the end of the whole area. Only append if search - # returns more than 0 counts + # Section that covers from the last break end address to the end of the whole area. Only + # append if search returns more than 0 counts last_group = Sections(sections=self.sections)\ .filter_address_max(self.highest_memory)\ .filter_address_min(previous_break_end_address) @@ -106,6 +111,3 @@ def split_sections_around_breaks(self) -> []: split_sections.append(last_group) return split_sections - - - diff --git a/style.py b/style.py index f81bcbc..4167c79 100644 --- a/style.py +++ b/style.py @@ -1,4 +1,7 @@ class Style: + """ + Holds style for different rendering objects + """ # Non SVG background: str break_type: str @@ -22,18 +25,19 @@ class Style: text_fill: str weigth: int + def __init__(self, style=None): if style is not None: for key, value in style.items(): setattr(self, key.replace('-','_'), style.get(key, value)) def override_properties_from(self, style): - ''' + """ Modify self by adding additional members available at the provided style :param style: Style whose members wants to be added :return: New merged styl - ''' + """ members = [attr for attr in dir(style) if not callable(getattr(style, attr)) and not attr.startswith("__") and getattr( style, attr) is not None] @@ -46,6 +50,11 @@ def override_properties_from(self, style): @staticmethod def get_default(): + """ + Get an initialized default Style instance + :return: A default initialized Style instance + """ + default_style = Style() default_style.break_type = '≈' default_style.break_size = 20 From b181de932e27ca3b67bd6ff8b170170db7bc14c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Gotor?= Date: Sun, 17 Dec 2023 23:05:42 +0100 Subject: [PATCH 3/3] docs: readme --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 3b3efda..767087b 100644 --- a/README.md +++ b/README.md @@ -164,14 +164,14 @@ At the folder examples, there are a series of configurations and map `.yaml` fil ## Roadmap -- [ ] Labels at specific memory addresses -- [ ] Area titles +- [x] Labels at specific memory addresses +- [x] Area titles - [ ] Links across specific areas - [ ] Choose side of the labels -- [ ] Memory direction +- [x] Memory direction - [ ] Hide specific elements - [ ] Memory size in bytes -- [ ] Sections links across breaks +- [ ] Section links across breaks - [ ] Friendly name and identifier - [ ] Legend ## References