Now the docstring for string options can specify a list of allowed values

This reduces the number of setters/getters we need to write.
On the other side the time to validate the YAML is increased.
This commit is contained in:
Salvador E. Tropea 2020-07-02 09:25:06 -03:00
parent ec35d2443f
commit 1ca21efe94
8 changed files with 54 additions and 76 deletions

View File

@ -289,7 +289,7 @@ Most options are the same you'll find in the KiCad dialogs.
- `blacklist`: [string=''] List of comma separated blacklisted components or prefixes with *. E.g. 'X1,MH*'. - `blacklist`: [string=''] List of comma separated blacklisted components or prefixes with *. E.g. 'X1,MH*'.
- `blacklist_empty_val`: [boolean=false] Blacklist components with empty value. - `blacklist_empty_val`: [boolean=false] Blacklist components with empty value.
- `board_rotation`: [number=0] Board rotation in degrees (-180 to 180). Will be rounded to multiple of 5. - `board_rotation`: [number=0] Board rotation in degrees (-180 to 180). Will be rounded to multiple of 5.
- `bom_view`: [string='left-right'] Default BOM view {bom-only,left-right,top-bottom}. - `bom_view`: [string='left-right'] [bom-only,left-right,top-bottom] Default BOM view.
- `checkboxes`: [string='Sourced,Placed'] Comma separated list of checkbox columns. - `checkboxes`: [string='Sourced,Placed'] Comma separated list of checkbox columns.
- `dark_mode`: [boolean=false] Default to dark mode. - `dark_mode`: [boolean=false] Default to dark mode.
- `dnp_field`: [string=''] Name of the extra field that indicates do not populate status. Components with this field not empty will be - `dnp_field`: [string=''] Name of the extra field that indicates do not populate status. Components with this field not empty will be
@ -300,7 +300,7 @@ Most options are the same you'll find in the KiCad dialogs.
- `highlight_pin1`: [boolean=false] Highlight pin1 by default. - `highlight_pin1`: [boolean=false] Highlight pin1 by default.
- `include_nets`: [boolean=false] Include netlist information in output.. - `include_nets`: [boolean=false] Include netlist information in output..
- `include_tracks`: [boolean=false] Include track/zone information in output. F.Cu and B.Cu layers only. - `include_tracks`: [boolean=false] Include track/zone information in output. F.Cu and B.Cu layers only.
- `layer_view`: [string='FB'] Default layer view {F,FB,B}. - `layer_view`: [string='FB'] [F,FB,B] Default layer view.
- `name_format`: [string='ibom'] Output file name format supports substitutions: - `name_format`: [string='ibom'] Output file name format supports substitutions:
%f : original pcb file name without extension. %f : original pcb file name without extension.
%p : pcb/project title from pcb metadata. %p : pcb/project title from pcb metadata.
@ -325,7 +325,7 @@ Most options are the same you'll find in the KiCad dialogs.
For more information: https://github.com/INTI-CMNB/KiBoM For more information: https://github.com/INTI-CMNB/KiBoM
This output is what you get from the 'Tools/Generate Bill of Materials' menu in eeschema. This output is what you get from the 'Tools/Generate Bill of Materials' menu in eeschema.
* Options: * Options:
- `format`: [string='HTML'] can be `HTML` or `CSV`. - `format`: [string='HTML'] [HTML,CSV] format for the BoM.
* PDF (Portable Document Format) * PDF (Portable Document Format)
* Type: `pdf` * Type: `pdf`
@ -366,10 +366,10 @@ Most options are the same you'll find in the KiCad dialogs.
* Description: Generates the file with position information for the PCB components, used by the pick and place machine. * Description: Generates the file with position information for the PCB components, used by the pick and place machine.
This output is what you get from the 'File/Fabrication output/Footprint poistion (.pos) file' menu in pcbnew. This output is what you get from the 'File/Fabrication output/Footprint poistion (.pos) file' menu in pcbnew.
* Options: * Options:
- `format`: [string='ASCII'] can be ASCII or CSV. - `format`: [string='ASCII'] [ASCII,CSV] format for the position file.
- `only_smd`: [boolean=true] only include the surface mount components. - `only_smd`: [boolean=true] only include the surface mount components.
- `separate_files_for_front_and_back`: [boolean=true] generate two separated files, one for the top and another for the bottom. - `separate_files_for_front_and_back`: [boolean=true] generate two separated files, one for the top and another for the bottom.
- `units`: [string='millimeters'] can be millimeters or inches. - `units`: [string='millimeters'] [millimeters,inches] units used for the positions.
* PS (Postscript) * PS (Postscript)
* Type: `ps` * Type: `ps`

View File

@ -363,7 +363,7 @@ outputs:
blacklist_empty_val: false blacklist_empty_val: false
# [number=0] Board rotation in degrees (-180 to 180). Will be rounded to multiple of 5 # [number=0] Board rotation in degrees (-180 to 180). Will be rounded to multiple of 5
board_rotation: 0 board_rotation: 0
# [string='left-right'] Default BOM view {bom-only,left-right,top-bottom} # [string='left-right'] [bom-only,left-right,top-bottom] Default BOM view
bom_view: 'left-right' bom_view: 'left-right'
# [string='Sourced,Placed'] Comma separated list of checkbox columns # [string='Sourced,Placed'] Comma separated list of checkbox columns
checkboxes: 'Sourced,Placed' checkboxes: 'Sourced,Placed'
@ -384,7 +384,7 @@ outputs:
include_nets: false include_nets: false
# [boolean=false] Include track/zone information in output. F.Cu and B.Cu layers only # [boolean=false] Include track/zone information in output. F.Cu and B.Cu layers only
include_tracks: false include_tracks: false
# [string='FB'] Default layer view {F,FB,B} # [string='FB'] [F,FB,B] Default layer view
layer_view: 'FB' layer_view: 'FB'
# [string='ibom'] Output file name format supports substitutions: # [string='ibom'] Output file name format supports substitutions:
# %f : original pcb file name without extension. # %f : original pcb file name without extension.
@ -422,7 +422,7 @@ outputs:
type: 'kibom' type: 'kibom'
dir: 'Example/kibom_dir' dir: 'Example/kibom_dir'
options: options:
# [string='HTML'] can be `HTML` or `CSV` # [string='HTML'] [HTML,CSV] format for the BoM
format: 'HTML' format: 'HTML'
# PDF (Portable Document Format): # PDF (Portable Document Format):
@ -607,13 +607,13 @@ outputs:
type: 'position' type: 'position'
dir: 'Example/position_dir' dir: 'Example/position_dir'
options: options:
# [string='ASCII'] can be ASCII or CSV # [string='ASCII'] [ASCII,CSV] format for the position file
format: 'ASCII' format: 'ASCII'
# [boolean=true] only include the surface mount components # [boolean=true] only include the surface mount components
only_smd: true only_smd: true
# [boolean=true] generate two separated files, one for the top and another for the bottom # [boolean=true] generate two separated files, one for the top and another for the bottom
separate_files_for_front_and_back: true separate_files_for_front_and_back: true
# [string='millimeters'] can be millimeters or inches # [string='millimeters'] [millimeters,inches] units used for the positions
units: 'millimeters' units: 'millimeters'
# PS (Postscript): # PS (Postscript):

View File

@ -23,6 +23,7 @@ class BaseOutput(object):
""" Map the options to class attributes """ """ Map the options to class attributes """
attrs = BaseOutput.get_attrs_for(self) attrs = BaseOutput.get_attrs_for(self)
num_range_re = compile(r"\[number=.*\] \[(-?\d+),(-?\d+)\]") num_range_re = compile(r"\[number=.*\] \[(-?\d+),(-?\d+)\]")
str_values_re = compile(r"\[string=.*\] \[([^\]]+)\]")
for k, v in self._options.items(): for k, v in self._options.items():
# Map known attributes and avoid mapping private ones # Map known attributes and avoid mapping private ones
if (k[0] == '_') or (k not in attrs): if (k[0] == '_') or (k not in attrs):
@ -42,7 +43,6 @@ class BaseOutput(object):
# If the docstring specifies a range in the form [from-to] enforce it # If the docstring specifies a range in the form [from-to] enforce it
m = num_range_re.match(cur_doc) m = num_range_re.match(cur_doc)
if m: if m:
logger.debug('Verificando')
min = float(m.group(1)) min = float(m.group(1))
max = float(m.group(2)) max = float(m.group(2))
if v < min or v > max: if v < min or v > max:
@ -50,6 +50,12 @@ class BaseOutput(object):
elif isinstance(cur_val, str): elif isinstance(cur_val, str):
if not isinstance(v, str): if not isinstance(v, str):
raise KiPlotConfigurationError("Option `{}` must be a string".format(k)) raise KiPlotConfigurationError("Option `{}` must be a string".format(k))
# If the docstring specifies the allowed values in the form [v1,v2...] enforce it
m = str_values_re.match(cur_doc)
if m:
vals = m.group(1).split(',')
if v not in vals:
raise KiPlotConfigurationError("Option `{}` must be any of {}".format(k, vals))
elif isinstance(v, list): elif isinstance(v, list):
raise KiPlotConfigurationError("list not yet supported for `{}`".format(k)) raise KiPlotConfigurationError("list not yet supported for `{}`".format(k))
# Seems to be ok, map it # Seems to be ok, map it

View File

@ -3,7 +3,6 @@ from subprocess import (check_output, STDOUT, CalledProcessError)
from .misc import (CMD_IBOM, URL_IBOM, BOM_ERROR) from .misc import (CMD_IBOM, URL_IBOM, BOM_ERROR)
from .gs import (GS) from .gs import (GS)
from .kiplot import (check_script) from .kiplot import (check_script)
from .error import KiPlotConfigurationError
from kiplot.macros import macros, document, output_class # noqa: F401 from kiplot.macros import macros, document, output_class # noqa: F401
from . import log from . import log
@ -37,10 +36,10 @@ class IBoM(BaseOutput): # noqa: F821
""" Board rotation in degrees (-180 to 180). Will be rounded to multiple of 5 """ """ Board rotation in degrees (-180 to 180). Will be rounded to multiple of 5 """
self.checkboxes = 'Sourced,Placed' self.checkboxes = 'Sourced,Placed'
""" Comma separated list of checkbox columns """ """ Comma separated list of checkbox columns """
self._bom_view = 'left-right' self.bom_view = 'left-right'
""" Default BOM view {bom-only,left-right,top-bottom} """ """ [bom-only,left-right,top-bottom] Default BOM view """
self._layer_view = 'FB' self.layer_view = 'FB'
""" Default layer view {F,FB,B} """ """ [F,FB,B] Default layer view """
self.name_format = 'ibom' self.name_format = 'ibom'
""" Output file name format supports substitutions: """ Output file name format supports substitutions:
%f : original pcb file name without extension. %f : original pcb file name without extension.
@ -78,28 +77,6 @@ class IBoM(BaseOutput): # noqa: F821
""" Name of the extra field that indicates do not populate status. Components with this field not empty will be """ Name of the extra field that indicates do not populate status. Components with this field not empty will be
blacklisted """ # pragma: no cover blacklisted """ # pragma: no cover
@property
def bom_view(self):
return self._bom_view
@bom_view.setter
def bom_view(self, val):
valid = ['bom-only', 'left-right', 'top-bottom']
if val not in valid:
raise KiPlotConfigurationError("`bom_view` must be any of "+str(valid))
self._bom_view = val
@property
def layer_view(self):
return self._layer_view
@layer_view.setter
def layer_view(self, val):
valid = ['F', 'FB', 'B']
if val not in valid:
raise KiPlotConfigurationError("`layer_view` must be any of "+str(valid))
self._layer_view = val
def run(self, output_dir, board): def run(self, output_dir, board):
check_script(CMD_IBOM, URL_IBOM) check_script(CMD_IBOM, URL_IBOM)
logger.debug('Doing Interactive BoM') logger.debug('Doing Interactive BoM')

View File

@ -1,7 +1,6 @@
import os import os
from glob import (glob) from glob import (glob)
from subprocess import (check_output, STDOUT, CalledProcessError) from subprocess import (check_output, STDOUT, CalledProcessError)
from .error import KiPlotConfigurationError
from .misc import (CMD_KIBOM, URL_KIBOM, BOM_ERROR) from .misc import (CMD_KIBOM, URL_KIBOM, BOM_ERROR)
from .kiplot import (check_script) from .kiplot import (check_script)
from .gs import (GS) from .gs import (GS)
@ -22,18 +21,8 @@ class KiBoM(BaseOutput): # noqa: F821
self._sch_related = True self._sch_related = True
# Options # Options
with document: with document:
self._format = 'HTML' self.format = 'HTML'
""" can be `HTML` or `CSV` """ # pragma: no cover """ [HTML,CSV] format for the BoM """ # pragma: no cover
@property
def format(self):
return self._format
@format.setter
def format(self, val):
if val not in ['HTML', 'CSV']:
raise KiPlotConfigurationError("`format` must be either `HTML` or `CSV`")
self._format = val
def run(self, output_dir, board): def run(self, output_dir, board):
check_script(CMD_KIBOM, URL_KIBOM) check_script(CMD_KIBOM, URL_KIBOM)

View File

@ -2,7 +2,6 @@ import os
import operator import operator
from datetime import datetime from datetime import datetime
from pcbnew import (IU_PER_MM, IU_PER_MILS) from pcbnew import (IU_PER_MM, IU_PER_MILS)
from .error import KiPlotConfigurationError
from kiplot.macros import macros, document, output_class # noqa: F401 from kiplot.macros import macros, document, output_class # noqa: F401
@ -15,34 +14,14 @@ class Position(BaseOutput): # noqa: F821
super(Position, self).__init__(name, type, description) super(Position, self).__init__(name, type, description)
# Options # Options
with document: with document:
self._format = 'ASCII' self.format = 'ASCII'
""" can be ASCII or CSV """ """ [ASCII,CSV] format for the position file """
self.separate_files_for_front_and_back = True self.separate_files_for_front_and_back = True
""" generate two separated files, one for the top and another for the bottom """ """ generate two separated files, one for the top and another for the bottom """
self.only_smd = True self.only_smd = True
""" only include the surface mount components """ """ only include the surface mount components """
self._units = 'millimeters' self.units = 'millimeters'
""" can be millimeters or inches """ # pragma: no cover """ [millimeters,inches] units used for the positions """ # pragma: no cover
@property
def format(self):
return self._format
@format.setter
def format(self, val):
if val not in ['ASCII', 'CSV']:
raise KiPlotConfigurationError("`format` must be either `ASCII` or `CSV`")
self._format = val
@property
def units(self):
return self._units
@units.setter
def units(self, val):
if val not in ['millimeters', 'inches']:
raise KiPlotConfigurationError("`units` must be either `millimeters` or `inches`")
self._units = val
def _do_position_plot_ascii(self, board, output_dir, columns, modulesStr, maxSizes): def _do_position_plot_ascii(self, board, output_dir, columns, modulesStr, maxSizes):
name = os.path.splitext(os.path.basename(board.GetFileName()))[0] name = os.path.splitext(os.path.basename(board.GetFileName()))[0]

View File

@ -37,6 +37,7 @@ Tests various errors in the config file
- YAML syntax - YAML syntax
- Unknown section - Unknown section
- HPGL wrong pen_number - HPGL wrong pen_number
- KiBoM wrong format
For debug information use: For debug information use:
pytest-3 --log-cli-level debug pytest-3 --log-cli-level debug
@ -328,3 +329,10 @@ def test_error_hpgl_pen_num():
ctx.run(EXIT_BAD_CONFIG) ctx.run(EXIT_BAD_CONFIG)
assert ctx.search_err("Option .?pen_number.? outside its range") assert ctx.search_err("Option .?pen_number.? outside its range")
ctx.clean_up() ctx.clean_up()
def test_error_bom_wrong_format():
ctx = context.TestContext('BoMWrongFormat', PRJ, 'error_bom_wrong_format', '')
ctx.run(EXIT_BAD_CONFIG)
assert ctx.search_err("Option .?format.? must be any of")
ctx.clean_up()

View File

@ -0,0 +1,19 @@
# Example KiPlot config file
kiplot:
version: 1
outputs:
- name: 'bom_html'
comment: "Bill of Materials in HTML format"
type: kibom
dir: BoM
options:
format: HTM # HTML or CSV
- name: 'bom_csv'
comment: "Bill of Materials in CSV format"
type: kibom
dir: BoM
options:
format: CSV # HTML or CSV