From a404444e392cee0a05d31c32b9fc9140d49c2695 Mon Sep 17 00:00:00 2001 From: "Salvador E. Tropea" Date: Fri, 3 Dec 2021 17:55:20 -0300 Subject: [PATCH] Moved SCH/PCB replace common code to a base class - Also added the missing file (oops!) --- kibot/pre_any_replace.py | 115 +++++++++++++++++++++++++++++++++++++++ kibot/pre_pcb_replace.py | 56 +++++++++++++++++++ kibot/pre_sch_replace.py | 107 +++++------------------------------- 3 files changed, 185 insertions(+), 93 deletions(-) create mode 100644 kibot/pre_any_replace.py create mode 100644 kibot/pre_pcb_replace.py diff --git a/kibot/pre_any_replace.py b/kibot/pre_any_replace.py new file mode 100644 index 00000000..4ed44f73 --- /dev/null +++ b/kibot/pre_any_replace.py @@ -0,0 +1,115 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2021 Salvador E. Tropea +# Copyright (c) 2021 Instituto Nacional de TecnologĂ­a Industrial +# License: GPL-3.0 +# Project: KiBot (formerly KiPlot) +import os +import re +import sys +from subprocess import run, PIPE +from .error import KiPlotConfigurationError +from .misc import FAILED_EXECUTE, W_EMPTREP, W_BADCHARS +from .optionable import Optionable +from .pre_base import BasePreFlight +from .macros import macros, document, pre_class # noqa: F401 +from . import log + +logger = log.get_logger() + + +class TagReplaceBase(Optionable): + """ Tags to be replaced """ + def __init__(self): + super().__init__() + self._unkown_is_error = True + with document: + self.tag = '' + """ Name of the tag to replace. Use `version` for a tag named `@version@` """ + self.tag_delimiter = '@' + """ Character used to indicate the beginning and the end of a tag. + Don't change it unless you really know about KiCad's file formats """ + self.text = '' + """ Text to insert instead of the tag """ + self.command = '' + """ Command to execute to get the text, will be used only if `text` is empty """ + self.before = '' + """ Text to add before the output of `command` """ + self.after = '' + """ Text to add after the output of `command` """ + self._relax_check = False + + def config(self, parent): + super().config(parent) + if not self.tag: + raise KiPlotConfigurationError("No tag to replace specified ({})".format(str(self._tree))) + self.tag = self.tag_delimiter + re.escape(self.tag) + self.tag_delimiter + + +class Base_ReplaceOptions(Optionable): + """ PCB/SCH replacement options """ + def __init__(self): + super().__init__() + with document: + self.date_command = '' + """ Command to get the date to use in the schematic. + git log -1 --format='%as' -- $KIBOT_PCB_NAME + Will return the date in YYYY-MM-DD format. + date -d @`git log -1 --format='%at' -- $KIBOT_PCB_NAME` +%Y-%m-%d_%H-%M-%S + Will return the date in YYYY-MM-DD_HH-MM-SS format """ + self.replace_tags = TagReplaceBase + """ [dict|list(dict)] Tag or tags to replace """ + + def config(self, parent): + super().config(parent) + if isinstance(self.replace_tags, type): + self.replace_tags = [] + elif isinstance(self.replace_tags, TagReplaceBase): + self.replace_tags = [self.replace_tags] + + +class Base_Replace(BasePreFlight): # noqa: F821 + """ [dict] Replaces tags in the PCB/schematic. I.e. to insert the git hash or last revision date """ + def __init__(self, name, value): + super().__init__(name, value) + self._context = '' # PCB/SCH + + @classmethod + def get_example(cls): + """ Returns a YAML value for the example config """ + return ("\n date_command: \"git log -1 --format='%as' -- $KIBOT_{}_NAME\"" + "\n replace_tags:" + "\n - tag: '@git_hash@'" + "\n command: 'git log -1 --format=\"%h\" $KIBOT_{}_NAME'" + "\n before: 'Git hash: <'" + "\n after: '>'\n".format(cls._context, cls._context)) + + def replace(self, file): + logger.debug('Applying replacements to `{}`'.format(file)) + with open(file, 'rt') as f: + content = f.read() + os.environ['KIBOT_' + type(self)._context + '_NAME'] = file + o = self._value + for r in o.replace_tags: + text = r.text + if not text: + cmd = ['/bin/bash', '-c', r.command] + result = run(cmd, stdout=PIPE, stderr=PIPE, universal_newlines=True) + if result.returncode: + logger.error('Failed to execute:\n{}\nreturn code {}'.format(r.command, result.returncode)) + sys.exit(FAILED_EXECUTE) + if not result.stdout: + logger.warning(W_EMPTREP+"Empty value from `{}` skipping it".format(r.command)) + continue + text = result.stdout.strip() + text = r.before + text + r.after + if not r._relax_check: + new_text = re.sub(r'["\\\\\s]', '_', text) + if new_text != text: + logger.warning(W_BADCHARS+"Replace text can't contain double quotes, backslashes or white spaces ({})". + format(text)) + text = new_text + logger.debug('- ' + r.tag + ' -> ' + text) + content = re.sub(r.tag, text, content, flags=re.MULTILINE) + os.rename(file, file + '-bak') + with open(file, 'wt') as f: + f.write(content) diff --git a/kibot/pre_pcb_replace.py b/kibot/pre_pcb_replace.py new file mode 100644 index 00000000..c466e213 --- /dev/null +++ b/kibot/pre_pcb_replace.py @@ -0,0 +1,56 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2021 Salvador E. Tropea +# Copyright (c) 2021 Instituto Nacional de TecnologĂ­a Industrial +# License: GPL-3.0 +# Project: KiBot (formerly KiPlot) +from .gs import GS +from .pre_any_replace import TagReplaceBase, Base_ReplaceOptions, Base_Replace +from .macros import macros, document, pre_class # noqa: F401 +from . import log + +logger = log.get_logger() + + +class TagReplacePCB(TagReplaceBase): + """ Tags to be replaced for an PCB """ + def __init__(self): + super().__init__() + self._help_command += ".\nKIBOT_PCB_NAME variable is the name of the current PCB" + + +class PCB_ReplaceOptions(Base_ReplaceOptions): + """ PCB replacement options """ + def __init__(self): + super().__init__() + self.replace_tags = TagReplacePCB + + +@pre_class +class PCB_Replace(Base_Replace): # noqa: F821 + """ [dict] Replaces tags in the schematic. I.e. to insert the git hash or last revision date """ + _context = 'PCB' + + def __init__(self, name, value): + o = PCB_ReplaceOptions() + o.set_tree(value) + o.config(self) + super().__init__(name, o) + + @classmethod + def get_doc(cls): + return cls.__doc__, PCB_ReplaceOptions + + def apply(self): + o = self._value + if o.date_command: + # Convert it into another replacement + t = TagReplacePCB() + t.tag = r'^ \(date (\S+|"(?:[^"]|\\")+")\)$' + t.command = o.date_command + t.before = ' (date "' + t.after = '")' + t._relax_check = True + o.replace_tags.append(t) + self.replace(GS.pcb_file) + # Force the schematic reload + GS.board = None diff --git a/kibot/pre_sch_replace.py b/kibot/pre_sch_replace.py index dd736445..43454fc9 100644 --- a/kibot/pre_sch_replace.py +++ b/kibot/pre_sch_replace.py @@ -4,130 +4,51 @@ # License: GPL-3.0 # Project: KiBot (formerly KiPlot) import os -import re -import sys -from subprocess import run, PIPE from .gs import GS -from .error import KiPlotConfigurationError -from .optionable import Optionable from .kiplot import load_sch -from .misc import W_EMPTREP, FAILED_EXECUTE, W_BADCHARS +from .pre_any_replace import TagReplaceBase, Base_ReplaceOptions, Base_Replace from .macros import macros, document, pre_class # noqa: F401 from . import log logger = log.get_logger() -class TagReplace(Optionable): - """ Tags to be replaced """ +class TagReplaceSCH(TagReplaceBase): + """ Tags to be replaced for an SCH """ def __init__(self): super().__init__() - self._unkown_is_error = True - with document: - self.tag = '' - """ Name of the tag to replace. Use `version` for a tag named `@version@` """ - self.tag_delimiter = '@' - """ Character used to indicate the beginning and the end of a tag. - Don't change it unless you really know about KiCad's file formats """ - self.text = '' - """ Text to insert instead of the tag """ - self.command = '' - """ Command to execute to get the text, will be used only if `text` is empty. - KIBOT_SCH_NAME variable is the name of the current sheet. - KIBOT_TOP_SCH_NAME variable is the name of the top sheet """ - self.before = '' - """ Text to add before the output of `command` """ - self.after = '' - """ Text to add after the output of `command` """ - self._relax_check = False - - def config(self, parent): - super().config(parent) - if not self.tag: - raise KiPlotConfigurationError("No tag to replace specified ({})".format(str(self._tree))) - self.tag = self.tag_delimiter + re.escape(self.tag) + self.tag_delimiter + self._help_command += (".\nKIBOT_SCH_NAME variable is the name of the current sheet." + "\nKIBOT_TOP_SCH_NAME variable is the name of the top sheet") -class SCH_ReplaceOptions(Optionable): - """ A list of filter entries """ +class SCH_ReplaceOptions(Base_ReplaceOptions): + """ SCH replacement options """ def __init__(self): super().__init__() - with document: - self.date_command = '' - """ Command to get the date to use in the schematic. - git log -1 --format='%as' -- $KIBOT_SCH_NAME - Will return the date in YYYY-MM-DD format. - date -d @`git log -1 --format='%at' -- $KIBOT_SCH_NAME` +%Y-%m-%d_%H-%M-%S - Will return the date in YYYY-MM-DD_HH-MM-SS format """ - self.replace_tags = TagReplace - """ [dict|list(dict)] Tag or tags to replace """ - - def config(self, parent): - super().config(parent) - if isinstance(self.replace_tags, type): - self.replace_tags = [] - elif isinstance(self.replace_tags, TagReplace): - self.replace_tags = [self.replace_tags] + self._help_date_command = self._help_date_command.replace('PCB', 'SCH') + self.replace_tags = TagReplaceSCH @pre_class -class SCH_Replace(BasePreFlight): # noqa: F821 +class SCH_Replace(Base_Replace): # noqa: F821 """ [dict] Replaces tags in the schematic. I.e. to insert the git hash or last revision date """ + _context = 'SCH' + def __init__(self, name, value): o = SCH_ReplaceOptions() o.set_tree(value) o.config(self) super().__init__(name, o) - def get_example(): - """ Returns a YAML value for the example config """ - return ("\n date_command: \"git log -1 --format='%as' -- $KIBOT_SCH_NAME\"" - "\n replace_tags:" - "\n - tag: '@git_hash@'" - "\n command: 'git log -1 --format=\"%h\" $KIBOT_SCH_NAME'" - "\n before: 'Git hash: <'" - "\n after: '>'\n") - @classmethod def get_doc(cls): return cls.__doc__, SCH_ReplaceOptions - def replace_in_sch(self, file): - logger.debug('Applying replacements to `{}`'.format(file)) - with open(file, 'rt') as f: - content = f.read() - os.environ['KIBOT_SCH_NAME'] = file - o = self._value - for r in o.replace_tags: - text = r.text - if not text: - cmd = ['/bin/bash', '-c', r.command] - result = run(cmd, stdout=PIPE, stderr=PIPE, universal_newlines=True) - if result.returncode: - logger.error('Failed to execute:\n{}\nreturn code {}'.format(r.command, result.returncode)) - sys.exit(FAILED_EXECUTE) - if not result.stdout: - logger.warning(W_EMPTREP+"Empty value from `{}` skipping it".format(r.command)) - continue - text = result.stdout.strip() - text = r.before + text + r.after - if not r._relax_check: - new_text = re.sub(r'["\\\\\s]', '_', text) - if new_text != text: - logger.warning(W_BADCHARS+"Replace text can't contain double quotes, backslashes or white spaces ({})". - format(text)) - text = new_text - logger.debug('- ' + r.tag + ' -> ' + text) - content = re.sub(r.tag, text, content, flags=re.MULTILINE) - os.rename(file, file + '-bak') - with open(file, 'wt') as f: - f.write(content) - def apply(self): o = self._value if o.date_command: # Convert it into another replacement - t = TagReplace() + t = TagReplaceSCH() t.tag = '^Date \"(.*)\"$' t.command = o.date_command t.before = 'Date "' @@ -137,6 +58,6 @@ class SCH_Replace(BasePreFlight): # noqa: F821 load_sch() os.environ['KIBOT_TOP_SCH_NAME'] = GS.sch_file for file in GS.sch.get_files(): - self.replace_in_sch(file) + self.replace(file) # Force the schematic reload GS.sch = None