diff --git a/CHANGELOG.md b/CHANGELOG.md index 89bf68f2..9291c392 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,6 +38,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - **%sc**, **%sd**, **%sf**, **%sF**, **%sp** and **%sr** schematic data - Now patterns are also expanded in the out_dir name. - Global options to control the date format. +- Outputs can use the options of other outputs as base (extend them). ### Changed - Internal BoM: now components with different Tolerance, Voltage, Current diff --git a/README.md b/README.md index d1e1e553..dee22635 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,7 @@ * [Supported outputs](#supported-outputs) * [Consolidating BoMs](#consolidating-boms) * [Importing outputs from another file](#importing-outputs-from-another-file) + * [Using other output as base for a new one](#using-other-output-as-base-for-a-new-one) * [Importing filters and variants from another file](#importing-filters-and-variants-from-another-file) * [Usage](#usage) * [Installation](#installation) @@ -666,6 +667,7 @@ Next time you need this list just use an alias, like this: * Valid keys: - `comment`: [string=''] A comment for documentation purposes. - `dir`: [string='./'] Output directory for the generated files. If it starts with `+` the rest is concatenated to the default dir. + - `extends`: [string=''] Copy the options from the indicated output. - `name`: [string=''] Used to identify this particular output definition. - `options`: [dict] Options for the `boardview` output. * Valid keys: @@ -683,6 +685,7 @@ Next time you need this list just use an alias, like this: * Valid keys: - `comment`: [string=''] A comment for documentation purposes. - `dir`: [string='./'] Output directory for the generated files. If it starts with `+` the rest is concatenated to the default dir. + - `extends`: [string=''] Copy the options from the indicated output. - `name`: [string=''] Used to identify this particular output definition. - `options`: [dict] Options for the `bom` output. * Valid keys: @@ -808,6 +811,7 @@ Next time you need this list just use an alias, like this: * Valid keys: - `comment`: [string=''] A comment for documentation purposes. - `dir`: [string='./'] Output directory for the generated files. If it starts with `+` the rest is concatenated to the default dir. + - `extends`: [string=''] Copy the options from the indicated output. - `name`: [string=''] Used to identify this particular output definition. - `options`: [dict] Options for the `compress` output. * Valid keys: @@ -830,6 +834,7 @@ Next time you need this list just use an alias, like this: * Valid keys: - `comment`: [string=''] A comment for documentation purposes. - `dir`: [string='./'] Output directory for the generated files. If it starts with `+` the rest is concatenated to the default dir. + - `extends`: [string=''] Copy the options from the indicated output. - `layers`: [list(dict)|list(string)|string] [all,selected,copper,technical,user] List of PCB layers to plot. * Valid keys: @@ -874,6 +879,7 @@ Next time you need this list just use an alias, like this: * Valid keys: - `comment`: [string=''] A comment for documentation purposes. - `dir`: [string='./'] Output directory for the generated files. If it starts with `+` the rest is concatenated to the default dir. + - `extends`: [string=''] Copy the options from the indicated output. - `name`: [string=''] Used to identify this particular output definition. - `options`: [dict] Options for the `excellon` output. * Valid keys: @@ -907,6 +913,7 @@ Next time you need this list just use an alias, like this: * Valid keys: - `comment`: [string=''] A comment for documentation purposes. - `dir`: [string='./'] Output directory for the generated files. If it starts with `+` the rest is concatenated to the default dir. + - `extends`: [string=''] Copy the options from the indicated output. - `name`: [string=''] Used to identify this particular output definition. - `options`: [dict] Options for the `gerb_drill` output. * Valid keys: @@ -931,6 +938,7 @@ Next time you need this list just use an alias, like this: * Valid keys: - `comment`: [string=''] A comment for documentation purposes. - `dir`: [string='./'] Output directory for the generated files. If it starts with `+` the rest is concatenated to the default dir. + - `extends`: [string=''] Copy the options from the indicated output. - `layers`: [list(dict)|list(string)|string] [all,selected,copper,technical,user] List of PCB layers to plot. * Valid keys: @@ -980,6 +988,7 @@ Next time you need this list just use an alias, like this: * Valid keys: - `comment`: [string=''] A comment for documentation purposes. - `dir`: [string='./'] Output directory for the generated files. If it starts with `+` the rest is concatenated to the default dir. + - `extends`: [string=''] Copy the options from the indicated output. - `layers`: [list(dict)|list(string)|string] [all,selected,copper,technical,user] List of PCB layers to plot. * Valid keys: @@ -1026,6 +1035,7 @@ Next time you need this list just use an alias, like this: * Valid keys: - `comment`: [string=''] A comment for documentation purposes. - `dir`: [string='./'] Output directory for the generated files. If it starts with `+` the rest is concatenated to the default dir. + - `extends`: [string=''] Copy the options from the indicated output. - `name`: [string=''] Used to identify this particular output definition. - `options`: [dict] Options for the `ibom` output. * Valid keys: @@ -1088,6 +1098,7 @@ Next time you need this list just use an alias, like this: * Valid keys: - `comment`: [string=''] A comment for documentation purposes. - `dir`: [string='./'] Output directory for the generated files. If it starts with `+` the rest is concatenated to the default dir. + - `extends`: [string=''] Copy the options from the indicated output. - `name`: [string=''] Used to identify this particular output definition. - `options`: [dict] Options for the `kibom` output. * Valid keys: @@ -1179,6 +1190,7 @@ Next time you need this list just use an alias, like this: * Valid keys: - `comment`: [string=''] A comment for documentation purposes. - `dir`: [string='./'] Output directory for the generated files. If it starts with `+` the rest is concatenated to the default dir. + - `extends`: [string=''] Copy the options from the indicated output. - `name`: [string=''] Used to identify this particular output definition. - `options`: [dict] Options for the `kicost` output. * Valid keys: @@ -1216,6 +1228,7 @@ Next time you need this list just use an alias, like this: * Valid keys: - `comment`: [string=''] A comment for documentation purposes. - `dir`: [string='./'] Output directory for the generated files. If it starts with `+` the rest is concatenated to the default dir. + - `extends`: [string=''] Copy the options from the indicated output. - `name`: [string=''] Used to identify this particular output definition. - `options`: [dict] Options for the `pcbdraw` output. * Valid keys: @@ -1268,6 +1281,7 @@ Next time you need this list just use an alias, like this: - `edge_cut_extension`: [string=''] Used to configure the edge cuts layer extension for Protel mode. Include the dot. - `exclude_edge_layer`: [boolean=true] Do not include the PCB edge layer. - `exclude_pads_from_silkscreen`: [boolean=false] Do not plot the component pads in the silk screen (KiCad 5.x only). + - `extends`: [string=''] Copy the options from the indicated output. - `force_plot_invisible_refs_vals`: [boolean=false] Include references and values even when they are marked as invisible. - `inner_extension_pattern`: [string=''] Used to change the Protel style extensions for inner layers. The replacement pattern can contain %n for the inner layer number and %N for the layer number. @@ -1322,6 +1336,7 @@ Next time you need this list just use an alias, like this: * Valid keys: - `comment`: [string=''] A comment for documentation purposes. - `dir`: [string='./'] Output directory for the generated files. If it starts with `+` the rest is concatenated to the default dir. + - `extends`: [string=''] Copy the options from the indicated output. - `layers`: [list(dict)|list(string)|string] [all,selected,copper,technical,user] List of PCB layers to include in the PDF. * Valid keys: @@ -1354,6 +1369,7 @@ Next time you need this list just use an alias, like this: * Valid keys: - `comment`: [string=''] A comment for documentation purposes. - `dir`: [string='./'] Output directory for the generated files. If it starts with `+` the rest is concatenated to the default dir. + - `extends`: [string=''] Copy the options from the indicated output. - `name`: [string=''] Used to identify this particular output definition. - `options`: [dict] Options for the `pdf_sch_print` output. * Valid keys: @@ -1372,6 +1388,7 @@ Next time you need this list just use an alias, like this: * Valid keys: - `comment`: [string=''] A comment for documentation purposes. - `dir`: [string='./'] Output directory for the generated files. If it starts with `+` the rest is concatenated to the default dir. + - `extends`: [string=''] Copy the options from the indicated output. - `name`: [string=''] Used to identify this particular output definition. - `options`: [dict] Options for the `position` output. * Valid keys: @@ -1397,6 +1414,7 @@ Next time you need this list just use an alias, like this: * Valid keys: - `comment`: [string=''] A comment for documentation purposes. - `dir`: [string='./'] Output directory for the generated files. If it starts with `+` the rest is concatenated to the default dir. + - `extends`: [string=''] Copy the options from the indicated output. - `layers`: [list(dict)|list(string)|string] [all,selected,copper,technical,user] List of PCB layers to plot. * Valid keys: @@ -1445,6 +1463,7 @@ Next time you need this list just use an alias, like this: * Valid keys: - `comment`: [string=''] A comment for documentation purposes. - `dir`: [string='./'] Output directory for the generated files. If it starts with `+` the rest is concatenated to the default dir. + - `extends`: [string=''] Copy the options from the indicated output. - `name`: [string=''] Used to identify this particular output definition. - `options`: [dict] Options for the `render_3d` output. * Valid keys: @@ -1487,6 +1506,7 @@ Next time you need this list just use an alias, like this: * Valid keys: - `comment`: [string=''] A comment for documentation purposes. - `dir`: [string='./'] Output directory for the generated files. If it starts with `+` the rest is concatenated to the default dir. + - `extends`: [string=''] Copy the options from the indicated output. - `name`: [string=''] Used to identify this particular output definition. - `options`: [dict] Options for the `sch_variant` output. * Valid keys: @@ -1502,6 +1522,7 @@ Next time you need this list just use an alias, like this: * Valid keys: - `comment`: [string=''] A comment for documentation purposes. - `dir`: [string='./'] Output directory for the generated files. If it starts with `+` the rest is concatenated to the default dir. + - `extends`: [string=''] Copy the options from the indicated output. - `name`: [string=''] Used to identify this particular output definition. - `options`: [dict] Options for the `step` output. * Valid keys: @@ -1526,6 +1547,7 @@ Next time you need this list just use an alias, like this: * Valid keys: - `comment`: [string=''] A comment for documentation purposes. - `dir`: [string='./'] Output directory for the generated files. If it starts with `+` the rest is concatenated to the default dir. + - `extends`: [string=''] Copy the options from the indicated output. - `layers`: [list(dict)|list(string)|string] [all,selected,copper,technical,user] List of PCB layers to plot. * Valid keys: @@ -1568,6 +1590,7 @@ Next time you need this list just use an alias, like this: * Valid keys: - `comment`: [string=''] A comment for documentation purposes. - `dir`: [string='./'] Output directory for the generated files. If it starts with `+` the rest is concatenated to the default dir. + - `extends`: [string=''] Copy the options from the indicated output. - `name`: [string=''] Used to identify this particular output definition. - `options`: [dict] Options for the `svg_sch_print` output. * Valid keys: @@ -1866,6 +1889,18 @@ import: This will import all the outputs from the listed files. + +#### Using other output as base for a new one + +If you need to define an output that is similar to another, and you want to avoid copying the options from the former, you can *extend* an output. +To achieve it just specify the name of the base output in the `extends` value. +Note that this will use the `options` of the other output as base, not other data as the comment. + +Also note that you can use YAML anchors, but this won't work if you are importing the base output from other file. + +Additionally you must be aware that extending an output doesn't disable the base output. +For this reason you could need to deselect the base output from the command line. + #### Importing filters and variants from another file This is a more complex case of the previous [Importing outputs from another file](#importing-outputs-from-another-file). diff --git a/docs/README.in b/docs/README.in index 5446cb59..f4c2ebea 100644 --- a/docs/README.in +++ b/docs/README.in @@ -38,6 +38,7 @@ * [Supported outputs](#supported-outputs) * [Consolidating BoMs](#consolidating-boms) * [Importing outputs from another file](#importing-outputs-from-another-file) + * [Using other output as base for a new one](#using-other-output-as-base-for-a-new-one) * [Importing filters and variants from another file](#importing-filters-and-variants-from-another-file) * [Usage](#usage) * [Installation](#installation) @@ -806,6 +807,18 @@ import: This will import all the outputs from the listed files. + +#### Using other output as base for a new one + +If you need to define an output that is similar to another, and you want to avoid copying the options from the former, you can *extend* an output. +To achieve it just specify the name of the base output in the `extends` value. +Note that this will use the `options` of the other output as base, not other data as the comment. + +Also note that you can use YAML anchors, but this won't work if you are importing the base output from other file. + +Additionally you must be aware that extending an output doesn't disable the base output. +For this reason you could need to deselect the base output from the command line. + #### Importing filters and variants from another file This is a more complex case of the previous [Importing outputs from another file](#importing-outputs-from-another-file). diff --git a/kibot/config_reader.py b/kibot/config_reader.py index 0538c814..717b25c5 100644 --- a/kibot/config_reader.py +++ b/kibot/config_reader.py @@ -85,6 +85,7 @@ class CfgYamlReader(object): o_out.name = name o_out.type = otype o_out.comment = comment + o_out.extends = o_tree.get('extends', '') return o_out diff --git a/kibot/out_base.py b/kibot/out_base.py index f31b6c76..cfe35a2b 100644 --- a/kibot/out_base.py +++ b/kibot/out_base.py @@ -4,6 +4,7 @@ # License: GPL-3.0 # Project: KiBot (formerly KiPlot) import os +from copy import deepcopy from .gs import GS from .kiplot import load_sch, get_board_comps_data from .misc import Rect, KICAD_VERSION_5_99, W_WRONGPASTE @@ -16,6 +17,7 @@ from .registrable import RegOutput from .optionable import Optionable, BaseOptions from .fil_base import BaseFilter, apply_fitted_filter, reset_filters from .macros import macros, document # noqa: F401 +from .error import KiPlotConfigurationError from . import log logger = log.get_logger(__name__) @@ -33,6 +35,8 @@ class BaseOutput(RegOutput): """ Output directory for the generated files. If it starts with `+` the rest is concatenated to the default dir """ self.comment = '' """ A comment for documentation purposes """ + self.extends = '' + """ Copy the options from the indicated output """ if GS.global_dir: self.dir = GS.global_dir self._sch_related = False @@ -68,6 +72,21 @@ class BaseOutput(RegOutput): return [GS.pcb_file] def config(self, parent): + if self._tree and not self._configured and isinstance(self.extends, str) and self.extends: + logger.debug("Extending `{}` from `{}`".format(self.name, self.extends)) + # Copy the data from the base output + out = RegOutput.get_output(self.extends) + if out is None: + raise KiPlotConfigurationError('Unknown output `{}` in `extends`'.format(self.extends)) + if out._tree: + options = out._tree.get('options', None) + if options: + old_options = self._tree.get('options', {}) + # logger.error("Old options: "+str(old_options)) + options = deepcopy(options) + options.update(old_options) + self._tree['options'] = options + # logger.error("New options: "+str(options)) super().config(parent) if self.dir[0] == '+': self.dir = (GS.global_dir if GS.global_dir is not None else './') + self.dir[1:] diff --git a/kibot/registrable.py b/kibot/registrable.py index 07b6d72c..6ccf8ec6 100644 --- a/kibot/registrable.py +++ b/kibot/registrable.py @@ -94,6 +94,10 @@ class RegOutput(Optionable, Registrable): def get_outputs(): return RegOutput._def_outputs.values() + @staticmethod + def get_output(name): + return RegOutput._def_outputs.get(name, None) + @staticmethod def check_variant(variant): if variant: diff --git a/tests/test_plot/test_misc.py b/tests/test_plot/test_misc.py index 59aa953a..530c907d 100644 --- a/tests/test_plot/test_misc.py +++ b/tests/test_plot/test_misc.py @@ -810,6 +810,7 @@ def test_compress_fail_deps(test_dir, monkeypatch): def test_import_1(test_dir): + """ Import some outputs """ prj = 'test_v5' ctx = context.TestContext(test_dir, 'test_import_1', prj, 'import_test_1', '') ctx.run(extra=['-i']) @@ -819,9 +820,19 @@ def test_import_1(test_dir): def test_import_2(test_dir): + """ Import a global option """ prj = 'test_v5' ctx = context.TestContext(test_dir, 'test_import_2', prj, 'import_test_2', '') - ctx.run(extra=['-vvv']) + ctx.run() ctx.expect_out_file(POS_DIR+'/test_v5_(bottom_pos).pos') ctx.expect_out_file(POS_DIR+'/test_v5_(top_pos).pos') ctx.clean_up() + + +def test_import_3(test_dir): + """ Import an output and change it """ + prj = 'test_v5' + ctx = context.TestContext(test_dir, 'test_import_3', prj, 'import_test_3', '') + ctx.run(extra=['position_mine']) + ctx.expect_out_file(POS_DIR+'/test_v5_(both_pos).csv') + ctx.clean_up() diff --git a/tests/test_plot/test_yaml_errors.py b/tests/test_plot/test_yaml_errors.py index 8528556e..ea1e8b1d 100644 --- a/tests/test_plot/test_yaml_errors.py +++ b/tests/test_plot/test_yaml_errors.py @@ -720,3 +720,11 @@ def test_same_name_3(test_dir): ctx.run(EXIT_BAD_CONFIG) assert ctx.search_err(r"Output name `position` already defined, while importing from") ctx.clean_up() + + +def test_extends_1(test_dir): + """ Extend an undefined output """ + ctx = context.TestContext(test_dir, 'test_extends_1', PRJ, 'error_extends_1', '') + ctx.run(EXIT_BAD_CONFIG) + assert ctx.search_err(r"In section 'position_mine' \(position\): Unknown output `position2` in `extends`") + ctx.clean_up() diff --git a/tests/yaml_samples/error_extends_1.kibot.yaml b/tests/yaml_samples/error_extends_1.kibot.yaml new file mode 100644 index 00000000..f45fda49 --- /dev/null +++ b/tests/yaml_samples/error_extends_1.kibot.yaml @@ -0,0 +1,17 @@ +kibot: + version: 1 + +import: + # Here we change the global.output pattern + - file: global_import.kibot.yaml + - simple_position_csv.kibot.yaml + +outputs: + - name: 'position_mine' + comment: "Pick and place file" + type: position + dir: positiondir + extends: position2 + options: + separate_files_for_front_and_back: false + diff --git a/tests/yaml_samples/import_test_3.kibot.yaml b/tests/yaml_samples/import_test_3.kibot.yaml new file mode 100644 index 00000000..35b545d9 --- /dev/null +++ b/tests/yaml_samples/import_test_3.kibot.yaml @@ -0,0 +1,17 @@ +kibot: + version: 1 + +import: + # Here we change the global.output pattern + - file: global_import.kibot.yaml + - simple_position_csv.kibot.yaml + +outputs: + - name: 'position_mine' + comment: "Pick and place file" + type: position + dir: positiondir + extends: position + options: + separate_files_for_front_and_back: false +