Example #1
0
def load_sch():
    if GS.sch:  # Already loaded
        return
    GS.kicad_version = pcbnew.GetBuildVersion()
    logger.debug('KiCad: ' + GS.kicad_version)
    GS.check_sch()
    # We can't yet load the new format
    if GS.sch_file[-9:] == 'kicad_sch':
        return
    GS.sch = Schematic()
    try:
        GS.sch.load(GS.sch_file)
        GS.sch.load_libs(GS.sch_file)
        if GS.debug_level > 1:
            logger.debug('Schematic dependencies: ' + str(GS.sch.get_files()))
    except SchFileError as e:
        trace_dump()
        logger.error('At line {} of `{}`: {}'.format(e.line, e.file, e.msg))
        logger.error('Line content: `{}`'.format(e.code))
        exit(CORRUPTED_SCH)
    except KiConfError as e:
        trace_dump()
        logger.error('At line {} of `{}`: {}'.format(e.line, e.file, e.msg))
        logger.error('Line content: `{}`'.format(e.code))
        exit(EXIT_BAD_CONFIG)
Example #2
0
    def __init__(self):
        super(KiBuzzardPlugin, self).__init__()

        self.InitLogger()
        self.logger = logging.getLogger(__name__)

        self.name = "Create Labels"
        self.category = "Modify PCB"
        self.pcbnew_icon_support = hasattr(self, "show_toolbar_button")
        self.show_toolbar_button = True
        icon_dir = os.path.dirname(os.path.dirname(__file__))
        self.icon_file_name = os.path.join(icon_dir, 'icon.png')
        self.description = "Create Labels"
        self.config = FileConfig(localFilename=self.config_file)

        self._pcbnew_frame = None

        self.kicad_build_version = pcbnew.GetBuildVersion()
        if '5.1' in self.kicad_build_version or '5.0' in self.kicad_build_version:
            # Library location for KiCad 5.1
            self.filepath = os.path.join(tempfile.mkdtemp(),
                                         'buzzard_labels.pretty',
                                         'label.kicad_mod')
            try:  # Use try/except here because python 2.7 doesn't support exist_ok
                os.makedirs(os.path.dirname(self.filepath))
            except:
                pass
Example #3
0
 def __init__(self, test_name, prj_name):
     ng_ver = os.environ.get('KIAUS_USE_NIGHTLY')
     if ng_ver:
         # Path to the Python module
         sys.path.insert(
             0, '/usr/lib/kicad-nightly/lib/python3/dist-packages')
         self.kicad_cfg_dir = os.path.join(os.environ['HOME'],
                                           '.config/kicadnightly/' + ng_ver)
     else:
         self.kicad_cfg_dir = os.path.join(os.environ['HOME'],
                                           '.config/kicad')
     import pcbnew
     # Detect version
     m = re.match(r'(\d+)\.(\d+)\.(\d+)', pcbnew.GetBuildVersion())
     major = int(m.group(1))
     minor = int(m.group(2))
     patch = int(m.group(3))
     self.kicad_version = major * 1000000 + minor * 1000 + patch
     logging.debug('Detected KiCad v{}.{}.{} ({})'.format(
         major, minor, patch, self.kicad_version))
     if self.kicad_version < KICAD_VERSION_5_99:
         self.board_dir = '../kicad5'
         self.sch_ext = '.sch'
         self.ref_dir = 'tests/reference/5'
         self.pro_ext = '.pro'
         self.pcbnew_conf = os.path.join(self.kicad_cfg_dir, 'pcbnew')
         self.eeschema_conf = os.path.join(self.kicad_cfg_dir, 'eeschema')
         self.kicad_conf = os.path.join(self.kicad_cfg_dir, 'kicad_common')
     else:
         self.board_dir = '../kicad6'
         self.sch_ext = '.kicad_sch'
         self.ref_dir = 'tests/reference/6'
         self.pro_ext = '.kicad_pro'
         self.pcbnew_conf = os.path.join(self.kicad_cfg_dir, 'pcbnew.json')
         self.eeschema_conf = os.path.join(self.kicad_cfg_dir,
                                           'eeschema.json')
         self.kicad_conf = os.path.join(self.kicad_cfg_dir,
                                        'kicad_common.json')
     # We are using PCBs
     self.mode = MODE_PCB
     # The name used for the test output dirs and other logging
     self.test_name = test_name
     # The name of the PCB board file
     self.prj_name = prj_name
     # The actual board file that will be loaded
     self._get_board_name()
     # The actual output dir for this run
     self._set_up_output_dir(pytest.config.getoption('test_dir'))
     # stdout and stderr from the run
     self.out = None
     self.err = None
     self.proc = None
Example #4
0
 def __init__(self):
     super(KiBuzzardPlugin, self).__init__()
     self.name = "Create Labels"
     self.category = "Modify PCB"
     self.pcbnew_icon_support = hasattr(self, "show_toolbar_button")
     self.show_toolbar_button = True
     icon_dir = os.path.dirname(os.path.dirname(__file__))
     self.icon_file_name = os.path.join(icon_dir, 'icon.png')
     self.description = "Create Labels"
     self.config = FileConfig(localFilename=self.config_file)
     self._pcbnew_frame = None
     self.kicad_version = pcbnew.GetBuildVersion()
     self.buzzard_label_library_path = "/buzzard_labels.pretty"
     self.buzzard_label_module_name = "buzzard_labels"
Example #5
0
def detect_kicad():
    # Check if we have to run the nightly KiCad build
    nightly = False
    if os.environ.get('KIAUS_USE_NIGHTLY'):
        # Path to the Python module
        sys_path.insert(0, '/usr/lib/kicad-nightly/lib/python3/dist-packages')
        nightly = True
    try:
        import pcbnew
    except ImportError:
        logger.error("Failed to import pcbnew Python module."
                     " Is KiCad installed?"
                     " Do you need to add it to PYTHONPATH?")
        sys.exit(NO_PCBNEW_MODULE)
    try:
        GS.kicad_version = pcbnew.GetBuildVersion()
    except AttributeError:
        logger.warning(
            W_NOKIVER +
            "Unknown KiCad version, please install KiCad 5.1.6 or newer")
        # Assume the best case
        GS.kicad_version = '5.1.5'
    m = re.search(r'(\d+)\.(\d+)\.(\d+)', GS.kicad_version)
    GS.kicad_version_major = int(m.group(1))
    GS.kicad_version_minor = int(m.group(2))
    GS.kicad_version_patch = int(m.group(3))
    GS.kicad_version_n = GS.kicad_version_major * 1000000 + GS.kicad_version_minor * 1000 + GS.kicad_version_patch
    logger.debug('Detected KiCad v{}.{}.{} ({} {})'.format(
        GS.kicad_version_major, GS.kicad_version_minor, GS.kicad_version_patch,
        GS.kicad_version, GS.kicad_version_n))
    if GS.kicad_version_n >= KICAD_VERSION_5_99:
        GS.kicad_conf_path = pcbnew.GetSettingsManager().GetUserSettingsPath()
        if nightly:
            # Nightly Debian packages uses `/usr/share/kicad-nightly/kicad-nightly.env` as an environment extension
            # This script defines KICAD_CONFIG_HOME="$HOME/.config/kicadnightly"
            # So we just patch it, as we patch the name of the binaries
            GS.kicad_conf_path = GS.kicad_conf_path.replace(
                '/kicad/', '/kicadnightly/')
    else:
        logger.debug(
            'Ignore the next message about creating a wxApp, is a KiCad 5 bug (6989)'
        )
        GS.kicad_conf_path = pcbnew.GetKicadConfigPath()
    if GS.debug_level > 1:
        logger.debug('KiCad config path {}'.format(GS.kicad_conf_path))
Example #6
0
logger = log.get_logger(__name__)
# Cache to avoid running external many times to check their versions
script_versions = {}
# Check if we have to run the nightly KiCad build
if os.environ.get('KIAUS_USE_NIGHTLY'):
    # Path to the Python module
    sys_path.insert(0, '/usr/lib/kicad-nightly/lib/python3/dist-packages')
try:
    import pcbnew
except ImportError:
    log.init()
    logger.error("Failed to import pcbnew Python module."
                 " Is KiCad installed?"
                 " Do you need to add it to PYTHONPATH?")
    exit(NO_PCBNEW_MODULE)
m = re.search(r'(\d+)\.(\d+)\.(\d+)', pcbnew.GetBuildVersion())
GS.kicad_version_major = int(m.group(1))
GS.kicad_version_minor = int(m.group(2))
GS.kicad_version_patch = int(m.group(3))
GS.kicad_version_n = GS.kicad_version_major * 1000000 + GS.kicad_version_minor * 1000 + GS.kicad_version_patch
logger.debug('Detected KiCad v{}.{}.{} ({})'.format(GS.kicad_version_major,
                                                    GS.kicad_version_minor,
                                                    GS.kicad_version_patch,
                                                    GS.kicad_version_n))


def _import(name, path):
    # Python 3.4+ import mechanism
    spec = spec_from_file_location("kibot." + name, path)
    mod = module_from_spec(spec)
    try:
Example #7
0
try:
    from urllib import urlretrieve
except:
    from urllib.request import urlretrieve

logger = logging.getLogger(__name__)

# get version information
version_filename = os.path.join(os.path.dirname(os.path.realpath(__file__)),
                                "version.txt")
with open(version_filename) as f:
    VERSION = f.readline().strip()

# > V5.1.5 and V 5.99 build information
if hasattr(pcbnew, 'GetBuildVersion'):
    BUILD_VERSION = pcbnew.GetBuildVersion()
else:
    BUILD_VERSION = "Unknown"


def balanced_braces(args):
    if isinstance(args, str):
        args = [args]
    parts = []
    for arg in args:
        if '(' not in arg:
            continue
        chars = []
        n = 0
        for c in arg:
            if c == '(':
Example #8
0
    sys.path.insert(0, prev_dir)
from kibot.misc import (error_level_to_name)

COVERAGE_SCRIPT = 'python3-coverage'
KICAD_PCB_EXT = '.kicad_pcb'
KICAD_VERSION_5_99 = 5099000
KICAD_VERSION_5_1_7 = 5001007
MODE_SCH = 1
MODE_PCB = 0

ng_ver = os.environ.get('KIAUS_USE_NIGHTLY')
if ng_ver:
    # Path to the Python module
    sys.path.insert(0, '/usr/lib/kicad-nightly/lib/python3/dist-packages')
import pcbnew
m = re.search(r'(\d+)\.(\d+)\.(\d+)', pcbnew.GetBuildVersion())
logging.debug(pcbnew.GetBuildVersion())
kicad_major = int(m.group(1))
kicad_minor = int(m.group(2))
kicad_patch = int(m.group(3))
kicad_version = kicad_major * 1000000 + kicad_minor * 1000 + kicad_patch
if kicad_version >= KICAD_VERSION_5_99:
    BOARDS_DIR = '../board_samples/kicad_6'
    REF_DIR = 'tests/reference/6_0_0'
    KICAD_SCH_EXT = '.kicad_sch'
    # Now these layers can be renamed.
    # KiCad 6 takes the freedom to give them more descriptive names ...
    DEF_ADHES = 'Adhesive'
    DEF_CRTYD = 'Courtyard'
    DEF_SILKS = 'Silkscreen'
    DEF_CMTSU = 'User_Comments'
Example #9
0
def detect_kicad():
    # Check if we have to run the nightly KiCad build
    nightly = False
    if os.environ.get('KIAUS_USE_NIGHTLY'):  # pragma: no cover (Ki6)
        # Path to the Python module
        sys_path.insert(0, '/usr/lib/kicad-nightly/lib/python3/dist-packages')
        nightly = True
    try:
        import pcbnew
    except ImportError:
        logger.error("Failed to import pcbnew Python module."
                     " Is KiCad installed?"
                     " Do you need to add it to PYTHONPATH?")
        sys.exit(NO_PCBNEW_MODULE)
    try:
        GS.kicad_version = pcbnew.GetBuildVersion()
    except AttributeError:
        logger.warning(
            W_NOKIVER +
            "Unknown KiCad version, please install KiCad 5.1.6 or newer")
        # Assume the best case
        GS.kicad_version = '5.1.5'
    try:
        # Debian sid may 2021 mess:
        really_index = GS.kicad_version.index('really')
        GS.kicad_version = GS.kicad_version[really_index + 6:]
    except ValueError:
        pass

    m = re.search(r'(\d+)\.(\d+)\.(\d+)', GS.kicad_version)
    GS.kicad_version_major = int(m.group(1))
    GS.kicad_version_minor = int(m.group(2))
    GS.kicad_version_patch = int(m.group(3))
    GS.kicad_version_n = GS.kicad_version_major * 1000000 + GS.kicad_version_minor * 1000 + GS.kicad_version_patch
    logger.debug('Detected KiCad v{}.{}.{} ({} {})'.format(
        GS.kicad_version_major, GS.kicad_version_minor, GS.kicad_version_patch,
        GS.kicad_version, GS.kicad_version_n))
    # Used to look for plug-ins.
    # KICAD_PATH isn't good on my system.
    # The kicad-nightly package overwrites the regular package!!
    GS.kicad_share_path = '/usr/share/kicad'
    if GS.kicad_version_n >= KICAD_VERSION_5_99:  # pragma: no cover (Ki6)
        GS.kicad_conf_path = pcbnew.GetSettingsManager().GetUserSettingsPath()
        if nightly:
            # Nightly Debian packages uses `/usr/share/kicad-nightly/kicad-nightly.env` as an environment extension
            # This script defines KICAD_CONFIG_HOME="$HOME/.config/kicadnightly"
            # So we just patch it, as we patch the name of the binaries
            GS.kicad_conf_path = GS.kicad_conf_path.replace(
                '/kicad/', '/kicadnightly/')
            GS.kicad_share_path = GS.kicad_share_path.replace(
                '/kicad/', '/kicadnightly/')
    else:
        # Bug in KiCad (#6989), prints to stderr:
        # `../src/common/stdpbase.cpp(62): assert "traits" failed in Get(test_dir): create wxApp before calling this`
        # Found in KiCad 5.1.8, 5.1.9
        # So we temporarily supress stderr
        with hide_stderr():
            GS.kicad_conf_path = pcbnew.GetKicadConfigPath()
    # Dirs to look for plugins
    GS.kicad_plugins_dirs = []
    # /usr/share/kicad/*
    GS.kicad_plugins_dirs.append(os.path.join(GS.kicad_share_path,
                                              'scripting'))
    GS.kicad_plugins_dirs.append(
        os.path.join(GS.kicad_share_path, 'scripting', 'plugins'))
    # ~/.config/kicad/*
    GS.kicad_plugins_dirs.append(os.path.join(GS.kicad_conf_path, 'scripting'))
    GS.kicad_plugins_dirs.append(
        os.path.join(GS.kicad_conf_path, 'scripting', 'plugins'))
    # ~/.kicad_plugins and ~/.kicad
    if 'HOME' in os.environ:
        home = os.environ['HOME']
        GS.kicad_plugins_dirs.append(os.path.join(home, '.kicad_plugins'))
        GS.kicad_plugins_dirs.append(os.path.join(home, '.kicad', 'scripting'))
        GS.kicad_plugins_dirs.append(
            os.path.join(home, '.kicad', 'scripting', 'plugins'))
    if GS.debug_level > 1:
        logger.debug('KiCad config path {}'.format(GS.kicad_conf_path))
Example #10
0
    def __init__(self, logger, input_file=None, args=None):
        self.export_format = 'pdf'
        if input_file:
            self.input_file = input_file
            self.input_no_ext = os.path.splitext(input_file)[0]
            #
            # As soon as we init pcbnew the following files are modified:
            #
            if os.path.isfile(self.input_no_ext+'.pro'):
                self.start_pro_stat = os.stat(self.input_no_ext+'.pro')
            else:
                self.start_pro_stat = None
            if os.path.isfile(self.input_no_ext+'.kicad_pro'):
                self.start_kicad_pro_stat = os.stat(self.input_no_ext+'.kicad_pro')
            else:
                self.start_kicad_pro_stat = None
            if os.path.isfile(self.input_no_ext+'.kicad_prl'):
                self.start_kicad_prl_stat = os.stat(self.input_no_ext+'.kicad_prl')
            else:
                self.start_kicad_prl_stat = None
        if args:
            # Session debug
            self.use_wm = args.use_wm  # Use a Window Manager, dialogs behaves in a different way
            self.start_x11vnc = args.start_x11vnc
            self.rec_width = args.rec_width
            self.rec_height = args.rec_height
            self.record = args.record
            self.video_dir = args.output_dir
            self.wait_for_key = args.wait_key
            # Others
            if hasattr(args, 'file_format'):
                self.export_format = args.file_format.lower()
        else:
            # Session debug
            self.use_wm = False
            self.start_x11vnc = False
            self.rec_width = REC_W
            self.rec_height = REC_H
            self.record = False
            self.video_dir = None
            self.wait_for_key = False
        self.colordepth = 24
        self.video_name = None
        # Executable and dirs
        self.eeschema = 'eeschema'
        self.pcbnew = 'pcbnew'
        self.kicad_conf_dir = 'kicad'
        ng_ver = os.environ.get('KIAUS_USE_NIGHTLY')
        if ng_ver:
            self.eeschema += '-'+NIGHTLY
            self.pcbnew += '-'+NIGHTLY
            self.kicad_conf_dir += os.path.join(NIGHTLY, ng_ver)
            # Path to the Python module
            path.insert(0, '/usr/lib/kicad-nightly/lib/python3/dist-packages')
        # Detect KiCad version
        try:
            import pcbnew
        except ImportError:
            logger.error("Failed to import pcbnew Python module."
                         " Is KiCad installed?"
                         " Do you need to add it to PYTHONPATH?")
            exit(NO_PCBNEW_MODULE)

        # GetBuildVersion is missing in new versions of pcbnew
        try:
            m = re.match(r'(\d+)\.(\d+)\.(\d+)', pcbnew.GetBuildVersion())
            self.kicad_version_major = int(m.group(1))
            self.kicad_version_minor = int(m.group(2))
            self.kicad_version_patch = int(m.group(3))
        except Exception:
            logger.debug("Newer version")
            self.kicad_version_major = 5
            self.kicad_version_minor = 99
            self.kicad_version_patch = 99

        self.kicad_version = self.kicad_version_major*1000000+self.kicad_version_minor*1000+self.kicad_version_patch
        logger.debug('Detected KiCad v{}.{}.{} ({})'.format(self.kicad_version_major, self.kicad_version_minor,
                     self.kicad_version_patch, self.kicad_version))
        # Config file names
        self.kicad_conf_path = os.path.join(os.environ['HOME'], '.config/'+self.kicad_conf_dir)
        # - eeschema config
        self.conf_eeschema = os.path.join(self.kicad_conf_path, 'eeschema')
        self.conf_eeschema_bkp = None
        # - pcbnew config
        self.conf_pcbnew = os.path.join(self.kicad_conf_path, 'pcbnew')
        self.conf_pcbnew_bkp = None
        # - kicad config
        self.conf_kicad = os.path.join(self.kicad_conf_path, 'kicad_common')
        self.conf_kicad_bkp = None
        # Config files that migrated to JSON
        # Note that they remain in the old format until saved
        if self.kicad_version >= KICAD_VERSION_5_99:
            self.conf_eeschema += '.json'
            self.conf_pcbnew += '.json'
            self.conf_kicad += '.json'
            self.conf_kicad_json = True
            self.conf_eeschema_json = True
            self.conf_pcbnew_json = True
            self.pro_ext = 'kicad_pro'
            self.prl_ext = 'kicad_prl'
        else:
            self.conf_kicad_json = False
            self.conf_eeschema_json = False
            self.conf_pcbnew_json = False
            self.pro_ext = 'pro'
            self.prl_ext = None
        # - hotkeys
        self.conf_hotkeys = os.path.join(self.kicad_conf_path, 'user.hotkeys')
        self.conf_hotkeys_bkp = None
        # - sym-lib-table
        self.user_sym_lib_table = os.path.join(self.kicad_conf_path, 'sym-lib-table')
        self.user_fp_lib_table = os.path.join(self.kicad_conf_path, 'fp-lib-table')
        self.sys_sym_lib_table = [KICAD_SHARE+'template/sym-lib-table']
        self.sys_fp_lib_table = [KICAD_SHARE+'template/fp-lib-table']
        if ng_ver:
            # 20200912: sym-lib-table is missing
            self.sys_sym_lib_table.insert(0, KICAD_NIGHTLY_SHARE+'template/sym-lib-table')
            self.sys_fp_lib_table.insert(0, KICAD_NIGHTLY_SHARE+'template/fp-lib-table')
        # Some details about the UI
        if self.kicad_version >= KICAD_VERSION_5_99:
            # KiCad 5.99.0
            self.ee_window_title = r'\[/.*\] — Eeschema$'  # "PROJECT [HIERARCHY_PATH] - Eeschema"
        else:
            # KiCad 5.1.6
            self.ee_window_title = r'Eeschema.*\.sch'  # "Eeschema - file.sch"
        # Collected errors and unconnecteds (warnings)
        self.errs = []
        self.wrns = []
        # Error filters
        self.err_filters = []
Example #11
0
logger = log.get_logger(__name__)
# Cache to avoid running external many times to check their versions
script_versions = {}
# Check if we have to run the nightly KiCad build
if os.environ.get('KIAUS_USE_NIGHTLY'):
    # Path to the Python module
    sys_path.insert(0, '/usr/lib/kicad-nightly/lib/python3/dist-packages')
try:
    import pcbnew
except ImportError:  # pragma: no cover
    log.init(False, False)
    logger.error("Failed to import pcbnew Python module."
                 " Is KiCad installed?"
                 " Do you need to add it to PYTHONPATH?")
    exit(NO_PCBNEW_MODULE)
m = re.match(r'(\d+)\.(\d+)\.(\d+)', pcbnew.GetBuildVersion())
GS.kicad_version_major = int(m.group(1))
GS.kicad_version_minor = int(m.group(2))
GS.kicad_version_patch = int(m.group(3))
GS.kicad_version_n = GS.kicad_version_major * 1000000 + GS.kicad_version_minor * 1000 + GS.kicad_version_patch
logger.debug('Detected KiCad v{}.{}.{} ({})'.format(GS.kicad_version_major,
                                                    GS.kicad_version_minor,
                                                    GS.kicad_version_patch,
                                                    GS.kicad_version_n))


def _import(name, path):
    # Python 3.4+ import mechanism
    spec = spec_from_file_location("kibot." + name, path)
    mod = module_from_spec(spec)
    try:
Example #12
0
 def __init__(self, logger, input_file=None, args=None):
     self.export_format = 'pdf'
     if input_file:
         self.input_file = input_file
         self.input_no_ext = os.path.splitext(input_file)[0]
         #
         # As soon as we init pcbnew the following files are modified:
         #
         if os.path.isfile(self.input_no_ext+'.pro'):
             self.start_pro_stat = os.stat(self.input_no_ext+'.pro')
         else:
             self.start_pro_stat = None
         if os.path.isfile(self.input_no_ext+'.kicad_pro'):
             self.start_kicad_pro_stat = os.stat(self.input_no_ext+'.kicad_pro')
         else:
             self.start_kicad_pro_stat = None
         if os.path.isfile(self.input_no_ext+'.kicad_prl'):
             self.start_kicad_prl_stat = os.stat(self.input_no_ext+'.kicad_prl')
         else:
             self.start_kicad_prl_stat = None
     if args:
         # Session debug
         self.use_wm = args.use_wm  # Use a Window Manager, dialogs behaves in a different way
         self.start_x11vnc = args.start_x11vnc
         self.rec_width = args.rec_width
         self.rec_height = args.rec_height
         self.record = args.record
         self.video_dir = args.output_dir
         self.wait_for_key = args.wait_key
         self.time_out_scale = args.time_out_scale
         # Others
         if hasattr(args, 'file_format'):
             self.export_format = args.file_format.lower()
     else:
         # Session debug
         self.use_wm = False
         self.start_x11vnc = False
         self.rec_width = REC_W
         self.rec_height = REC_H
         self.record = False
         self.video_dir = None
         self.wait_for_key = False
         self.time_out_scale = 1.0
     self.colordepth = 24
     self.video_name = None
     self.video_dir = self.output_dir = ''
     # Executable and dirs
     self.eeschema = 'eeschema'
     self.pcbnew = 'pcbnew'
     self.kicad2step = 'kicad2step'
     self.kicad_conf_dir = 'kicad'
     ng_ver = os.environ.get('KIAUS_USE_NIGHTLY')
     if ng_ver:
         self.eeschema += '-'+NIGHTLY
         self.pcbnew += '-'+NIGHTLY
         self.kicad2step += '-'+NIGHTLY
         self.kicad_conf_dir += os.path.join(NIGHTLY, ng_ver)
         # Path to the Python module
         path.insert(0, '/usr/lib/kicad-nightly/lib/python3/dist-packages')
     # Detect KiCad version
     try:
         import pcbnew
     except ImportError:
         logger.error("Failed to import pcbnew Python module."
                      " Is KiCad installed?"
                      " Do you need to add it to PYTHONPATH?")
         exit(NO_PCBNEW_MODULE)
     kicad_version = pcbnew.GetBuildVersion()
     try:
         # Debian sid may 2021 mess:
         really_index = kicad_version.index('really')
         kicad_version = kicad_version[really_index+6:]
     except ValueError:
         pass
     m = re.search(r'(\d+)\.(\d+)\.(\d+)', kicad_version)
     if m is None:
         logger.error("Unable to detect KiCad version, got: `{}`".format(kicad_version))
         exit(NO_PCBNEW_MODULE)
     self.kicad_version_major = int(m.group(1))
     self.kicad_version_minor = int(m.group(2))
     self.kicad_version_patch = int(m.group(3))
     self.kicad_version = self.kicad_version_major*1000000+self.kicad_version_minor*1000+self.kicad_version_patch
     logger.debug('Detected KiCad v{}.{}.{} ({} {})'.format(self.kicad_version_major, self.kicad_version_minor,
                  self.kicad_version_patch, kicad_version, self.kicad_version))
     self.ki5 = self.kicad_version < KICAD_VERSION_5_99
     # Config file names
     if not self.ki5:
         self.kicad_conf_path = pcbnew.GetSettingsManager().GetUserSettingsPath()
         # No longer needed for 202112021512+6.0.0+rc1+287+gbb08ef2f41+deb11
         # if ng_ver:
         #    self.kicad_conf_path = self.kicad_conf_path.replace('/kicad/', '/kicadnightly/')
     else:
         # Bug in KiCad (#6989), prints to stderr:
         # `../src/common/stdpbase.cpp(62): assert "traits" failed in Get(test_dir): create wxApp before calling this`
         # Found in KiCad 5.1.8, 5.1.9
         # So we temporarily supress stderr
         with hide_stderr():
             self.kicad_conf_path = pcbnew.GetKicadConfigPath()
     logger.debug('Config path {}'.format(self.kicad_conf_path))
     # First we solve kicad_common because it can redirect to another config dir
     self.conf_kicad = os.path.join(self.kicad_conf_path, 'kicad_common')
     self.conf_kicad_bkp = None
     if not self.ki5:
         self.conf_kicad += '.json'
         self.conf_kicad_json = True
     else:
         self.conf_kicad_json = False
     # Read the environment redefinitions used by KiCad
     if os.path.isfile(self.conf_kicad):
         self.load_kicad_environment(logger)
         if 'KICAD_CONFIG_HOME' in self.env and self.ki5:
             # The user is redirecting the configuration
             # KiCad 5 unintentionally allows it, is a bug, and won't be fixed:
             # https://forum.kicad.info/t/kicad-config-home-inconsistencies-and-detail/26875
             self.kicad_conf_path = self.env['KICAD_CONFIG_HOME']
             logger.debug('Redirecting KiCad config path to: '+self.kicad_conf_path)
     else:
         logger.warning('Missing KiCad main config file '+self.conf_kicad)
     # - eeschema config
     self.conf_eeschema = os.path.join(self.kicad_conf_path, 'eeschema')
     self.conf_eeschema_bkp = None
     # - pcbnew config
     self.conf_pcbnew = os.path.join(self.kicad_conf_path, 'pcbnew')
     self.conf_pcbnew_bkp = None
     # Config files that migrated to JSON
     # Note that they remain in the old format until saved
     if not self.ki5:
         self.conf_eeschema += '.json'
         self.conf_pcbnew += '.json'
         self.conf_eeschema_json = True
         self.conf_pcbnew_json = True
         self.pro_ext = 'kicad_pro'
         self.prl_ext = 'kicad_prl'
         self.conf_colors = os.path.join(self.kicad_conf_path, 'colors', 'user.json')
         self.conf_colors_bkp = None
         self.conf_3dview = os.path.join(self.kicad_conf_path, '3d_viewer.json')
         self.conf_3dview_bkp = None
     else:
         self.conf_eeschema_json = False
         self.conf_pcbnew_json = False
         self.pro_ext = 'pro'
         self.prl_ext = None
         self.conf_colors = self.conf_colors_bkp = None
         self.conf_3dview = self.conf_3dview_bkp = None
     # - hotkeys
     self.conf_hotkeys = os.path.join(self.kicad_conf_path, 'user.hotkeys')
     self.conf_hotkeys_bkp = None
     # - sym-lib-table
     self.user_sym_lib_table = os.path.join(self.kicad_conf_path, 'sym-lib-table')
     self.user_fp_lib_table = os.path.join(self.kicad_conf_path, 'fp-lib-table')
     self.sys_sym_lib_table = [KICAD_SHARE+'template/sym-lib-table']
     self.sys_fp_lib_table = [KICAD_SHARE+'template/fp-lib-table']
     if ng_ver:
         # 20200912: sym-lib-table is missing
         self.sys_sym_lib_table.insert(0, KICAD_NIGHTLY_SHARE+'template/sym-lib-table')
         self.sys_fp_lib_table.insert(0, KICAD_NIGHTLY_SHARE+'template/fp-lib-table')
     # Some details about the UI
     if not self.ki5:
         # KiCad 5.99.0
         # self.ee_window_title = r'\[.*\] — Eeschema$'  # "PROJECT [HIERARCHY_PATH] - Eeschema"
         # KiCad 6.0.0 rc1
         self.ee_window_title = r'\[.*\] — Schematic Editor$'  # "PROJECT [HIERARCHY_PATH] - Schematic Editor"
         self.pn_window_title = r'.* — PCB Editor$'  # "PROJECT - PCB Editor"
     else:
         # KiCad 5.1.6
         self.ee_window_title = r'Eeschema.*\.sch'  # "Eeschema - file.sch"
         self.pn_window_title = r'^Pcbnew'
     # Collected errors and unconnecteds (warnings)
     self.errs = []
     self.wrns = []
     # Error filters
     self.err_filters = []