Git Product home page Git Product logo

kiutils's Introduction

KiUtils

PyPI version PyPI downloads Python version License Last commit Documentation Status

Simple and SCM-friendly KiCad file parser based on Python dataclasses for KiCad 6.0 and up. The following KiCad-related files are currently supported:

  • .kicad_pcb - Board layouts
  • .kicad_sch - Schematics
  • .kicad_mod - Footprints
  • .kicad_sym - Symbols and symbol libraries
  • .kicad_wks - Worksheets
  • .kicad_dru - Custom design rules
  • fp-lib-table & sym-lib-table - Library tables

KiUtils implements a "pythonic" abstraction of the documentation found at the KiCad Developer Reference and is intended to work with an SCM like Git or SVN without breaking the layout of the files when the Python script ran.

Parsing of the files is based on the S-Expression parser found in this library: GitLab: KiCad Library utilities

Prerequisites

The following is required to use kiutils:

  • Python 3.7 or higher

Installation

kiutils is available on PyPI. Use Python's pip to install it:

pip install kiutils

If kiutils is already installed, upgrade it to the latest version using:

pip install --no-cache-dir --upgrade kiutils

Documentation

Visit the kiutils documentation for more information on how to install, use and develop kiutils, as well as examples and general module documentation.

Donate

If you found this module helpful for your project consider donating via PayPal. Thanks!

kiutils's People

Contributors

aphera avatar eeintech avatar frzoen avatar hlprasu avatar hvraven avatar jjendryka avatar mvnmgrx avatar pingpongun avatar salexander avatar sapristi avatar zardini123 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

kiutils's Issues

KiCad 7: Implement TextBox

Problems with KiCad 7

Hi,
thank you for the great parser, it helped a lot! I used it to load schematics and symbols generated by kicad 7, manipulate them, and save them again. This is what I noticed:

Symbols Library
An exception was thrown when I loaded the .kicad_sym file, because "stroke" has only 3 parameters in kicad 7 instead of 4 in kicad 6.
After removing "or len(exp) != 4" from line 236 in common.py the import worked fine.
But there are some differences between the read and the written file:

  • In the written file, every property has the parameter (id 0) attatched, like (property "Reference" "R?" (id 0) (at 1.27 0.635 0) instead of (property "Reference" "R?" (at 1.27 0.635 0). But kicad 7 does not complain about that when I open the file.
  • Every stroke has the "color" parameter attached, like (stroke (width 0.254) (type default) (color 0 0 0 0)) instead of (stroke (width 0.254) (type default))

Schematic
Reading and writing worked fine, but there also are some differences between read and written file:

  • Unfortunatelly the symbols "do not populate" option is not supported yet, so (dnp yes) is missing in the written file
  • (id 0) is inserted at any property

Do you have any idea when "do not populate" will be supported? Unfortunatelly it is very important for my project.

Edit: These are my test files, renamed to .txt because I could not upload files with kicad extensions here.

symbollib_in.txt
symbollib_out.txt

schematic_in.txt
schematic_out.txt

Compare KiCad Library Utils with KiUtils

Hello @mvnmgrx

For Ki-nTree I have been relying on KiCad Library Utils, developed and maintained by the KiCad team, to merge two symbol libraries together (eg. adding a symbol into an existing library, after updating its fields).
However, this method result in improper formatting with the resulting library and I had to indicate that opening the library and saving it in KiCad's symbol editor is necessary for diff-ing.

I just found out about KiUtils and gave it a shot. Well I'm impressed as the resulting library library file has the right formatting!

Now, before to moving on and replacing the "KiCad Library Utils" with yours, I would like to understand what is different...

For my test, I used the following code with KiUtils:

from kiutils.symbol import SymbolLib

res_0 = SymbolLib.from_file('resistor_0.kicad_sym')
res_1 = SymbolLib.from_file('resistor_1.kicad_sym')

for symbol in res_1.symbols:
    res_0.symbols.append(symbol)

res_0.to_file()

and here's the code using "KiCad Library Utils":

from lib_utils.kicad_sym import KicadLibrary

res_0 = KicadLibrary.from_file('resistor_0.kicad_sym')
res_1 = KicadLibrary.from_file('resistor_1.kicad_sym')

for symbol in res_1.symbols:
    res_0.symbols.append(symbol)

res_0.write()

(lib_utils folder contains the latest sexpr.py and kicad_sym.py from KiCad Library Utils common folder)

Here are the original files (change extension back to .kicad_sym):

The result of each code snippet is:

If you have time to review this, would you let me know what you are doing differently?

Thanks!

EDIT: Found an issue with updating symbol name in Python dataset, will post a PR.

Test report does not generate expected/produced output for failing tests in Python 3.11

The HTML test report does not generate the expected/produced output when ran in a Python 3.11 environment.

To reproduce:

  1. Use Python 3.11
  2. Make at least one test fail
  3. Run the unittests with test report generation: python3 test.py

Expected behavior:
The test report shows both the expected output as well as the produced output.

Actual behavior:
grafik

This works just fine in older versions of Python (3.10 used here):
grafik

No ability to set library table entry as inactive

KiCad's GUI provides the functionality of setting a symbol or footprint library entry to inactive. When a symbol or footprint library table entry is set to inactive, the token (disabled) is added to the corresponding entry in sym-lib-table or fp-lib-table. When the entry is active, there is no token, and therefore is assumed to be active.

Example below shows symbol_lib_1 as active, and symbol_lib_2 as inactive:

(sym_lib_table
  (lib (name "symbol_lib_1")(type "Legacy")(uri "${KIPRJMOD}/legacy_libs/legacy_lib_1.lib")(options "")(descr ""))
  (lib (name "symbol_lib_2")(type "Legacy")(uri "${KIPRJMOD}/legacy_libs/legacy_lib_2.lib")(options "")(descr "")(disabled))
)

As of currently, kiutils does not provide an API or functionality for setting a library in a library table as active or not. I would suggest adding a member variable active to kiutils.libraries.Library as to reflect the KiCad naming convention in the symbol and footprint library managers.

Thank you all for kiutils!

Python 3.10 required

I tried to use your library with Python 3.6.1 and since union types written as X | Y is a Python 3.10 feature (PEP604), I can't use it with my old Python version.

I don't think this is a real issue, my suggestion is to simply add in the prerequisite something like Python version 3.10 or greater or thinking to handle this with older Python versions.

Missing "lib_name" field in schematicSymbols

Description: The optional field "lib_name" is missing in schematicSymbols and is used to create alternative symbols directly in the schematic. As a result, components modified in the schematic are not displayed.

Example from schematic file: (symbol (lib_id "") (lib_name "USBLC6-4SC6_1") (at 43.18 135.89 0) (unit 1)

Solution: The field can be added by adding the following code to the class SchematicSymbol() in schitems.py:

libraryName: str = ""

if item[0] == 'lib_name': object.libraryName = item[1] 
# in from_sexpr

f'(lib_name "{dequote(self.libraryName)}") 
# after f'{indents}(symbol (lib_id "{dequote(self.libraryIdentifier)}")

List index out of range when parsing a footprint with empty 'attr' field

When parsing this footprint, the empty attr token triggers a exception:

(footprint "Battery_CR1225"
  (layer "F.Cu")
  (tedit 5B93F51B)
  (descr "CR1225 battery")
  (tags "battery CR1225 coin cell")
  (attr)
  (fp_text reference "REF**" (at 0 -1) (layer "F.SilkS") hide
    (effects (font (size 1 1) (thickness 0.15)))
  )
  (fp_text value "Battery_CR1225" (at 0 0.5) (layer "F.Fab")
    (effects (font (size 1 1) (thickness 0.15)))
  )
  (fp_circle (center 0 0) (end 6.25 0) (layer "F.Fab") (width 0.1))
  (model "${KICAD6_3DMODEL_DIR}/Battery.3dshapes/Battery_CR1225.wrl"
    (offset (xyz 0 0 0))
    (scale (xyz 1 1 1))
    (rotate (xyz 0 0 0))
  )
)

image

Footprint: Pad.layers token are quoted, but missing after parsing

Input

(footprint "JST_EH_B2B-EH-A_1x02_P2.50mm_Vertical" (version 20221018) (generator pcbnew)
  (pad "1" thru_hole roundrect (at 0 0) (size 1.7 2) (drill 1) (layers "*.Cu" "*.Mask") (roundrect_rratio 0.147059) (tstamp b9960672-3af1-4d13-99fc-2f62dd24a4bc))
  (pad "2" thru_hole oval (at 2.5 0) (size 1.7 2) (drill 1) (layers "*.Cu" "*.Mask") (tstamp 94cccd64-f718-491b-83c2-3fc50ed44c7e))
)

Output

(footprint "JST_EH_B2B-EH-A_1x02_P2.50mm_Vertical" (version 20221018) (generator pcbnew)
  (pad "1" thru_hole roundrect (at 0 0) (size 1.7 2) (drill 1) (layers *.Cu *.Mask) (roundrect_rratio 0.147059) (tstamp b9960672-3af1-4d13-99fc-2f62dd24a4bc))
  (pad "2" thru_hole oval (at 2.5 0) (size 1.7 2) (drill 1) (layers *.Cu *.Mask) (tstamp 94cccd64-f718-491b-83c2-3fc50ed44c7e))
)

KiCad 7 feature collection

A collection of new stuff KiCad 7 seems to implement that may be important in the future.

April developer update:

  • Symbol: New pin types, such as Unit, Orientation, etc
  • Board: Inverse text objects

February developer update:

  • DRC: Mechanical clearance rules: Two new constraints (mechanical_clearance and mechanical_hole_clearance)
  • DRC: Custom rule severities (new severity token added!)
  • DRC: Pad to zone rules (thermal_relief_gap, thermal_spoke_width and zone_connection)
  • Dimension: Radial dimension tool
  • Schematic: Rectangle and circle primitives added
  • Schematic: Custom fonts in development
  • Schematic: Text boxes added

December developer update

  • Do not populate field
  • Simulation model editor

New things in the KiCad developer reference:

Tests: Make report only show differences in parsed code

The HTML test report shows the expected and produced output for each test. It would be nice to see only the differences between those two, as it is not easy to pinpoint where the issue is looking at the report itself. Maybe some kind of github-like diff or something ..

KiCad v7: Check where render_cache is used

Currently only the text boxes make use of the new render_cache token. I've noticed now that gr_text also uses this token when changing the font to a custom font. Maybe some other text-based tokens to so too ..

API: Do not allow multiple library table entries of same nickname

With library tables in KiCad (sym-lib-table and fp-lib-table), KiCad cannot accept more than one entry with the same nickname (see screenshot below). With this, it seems KiCad processes the library table as a dictionary where the key is the nickname.

As of currently KiUtil's API for library tables does not organize library table entries by a key. Instead it is an array of library table entries. Assuming all users of KiUtils intend to import their symbol tables to be used by KiCad >= 6.0, maybe it would be of use to have libraries be a dictionary, where the key is the nickname?

This change in API would enforce invariants imposed by KiCad.

image

Creating a design rule only for inner/outer layers is not possible

When I create a custom Rule in KiCad, I can use the syntax

(rule "HV to HV (IL)"
	(layer inner)
	(constraint clearance (min 2.4mm))
	(condition "..."))

to limit its scope to all inner layers.

This is not possible with kiutils, because the layers member is a string, which gets parsed to

(rule "HV to HV (IL)"
	(layer "inner")
	(constraint clearance (min 2.4mm))
	(condition "..."))

with the word inner in appostrophs, which is not understood by kicad.

Rework API for SchematicSymbol's libraryIdentifier + libraryName

As done in #41, add some code to split the libraryIdentifier into its parts:

  • Remove token libraryIdentifier
  • Add token libraryNickname & entryName
  • Add a setter def id() that accepts some string in the following format: <libraryNickname:>entryName and splits it into its parts via regex
  • Add a getter def id() that returns the library identifier combined from its parts

Furthermore, it was noticed in #46 that a schematic symbol may have a lib_name token assigned to it. This has nothing to do with the library identifier and may be set depending on the number of variations of a modified library symbol in the schematic.

E.g. when copying a symbol and editing it directly in the schematic, the lib_name token will be set to the symbol's <entryName>_X where X is a unique number that specifies which variation this symbol is of its original.

The following has to be done:

  • Add token libraryName

Footprint attributes is None when no "Manufacturing Attributes" are selected in layout

Problem:

When adding a footprint to the layout that has all its manufacturing attributes all unset / set to "other", the parser will not create the corresponding footprint.attributes token.

Manufacturing attributes set:
image

Expected behavior:

footprint.attributes token created with its members all set accordingly.

Observed behavior:

In this case, the footprint.attributes token will be None, as the S-Expression may not be created from KiCad when selecting the manufacturing attributes as shown above.
image

Setting at least the footprint type to something other than "Other", the parser will parse the footprint.attributes token correctly:
image

Possbible fix:

Always create footprint.attributes token, but check that this does not create redundant S-Expression when creating a footprint like described above.

common.Justify may not return an empty string

When neither horizontal nor vertical justification is given, the Justify().to_sexpr() function may return an none-empty string if the params supply indentations or a line break. The documentation sugests that it always returns an empty string.

Currently this is no bug, but check the test cases if its okay to return an empty string every time and adjust the function accordingly to be consistent with the documentation.

Current implementation in src/kiutils/items/common.py:

class Justify():
    # (...)
    def to_sexpr(self, indent=0, newline=False) -> str:
        indents = ' '*indent
        endline = '\n' if newline else ''
        # (...)
        if self.horizontally is None and self.vertically is None and self.mirror == False:
            return f'{indents}{endline}';
    # (...)

Footprint with `fp_arc` versioning issue

I found exporting a footprint with a fp_arc from KiUtils, KiCad fails to import it due to a syntax error in the fp_arc. From further investigation, I found that without specifying a footprint version number, KiCad imports the file using legacy syntax. Specifically, the version number evaluates to <= LEGACY_ARC_FORMATTING (fp_arc parsing source), where LEGACY_ARC_FORMATTING is of date 20210925 (date source). In the source of KiCad I found that they consider both (modules and (footprint as .kicad_mod files as modern due to first versions of .kicad_mod being (modules. Therefore the main differentiation is the version number provided in the footprint header.

The goal is to have footprint library files freshly exported from KiUtils be importable by KiCad. Currently this is not possible in whole due to files with fp_arc and the default version of None not importing in KiCad under the version KiUtils expects.

After exploring KiUtil's source for a couple days, I find fixing this issue might be a bit more loaded than I hoped. Footprints can be embedded in board files, but when as a standalone footprint library includes the version and generator token (footprint library documentation). Therefore attempting to set the default date in KiUtils to 20210926 (day after LEGACY_ARC_FORMATTING) would result in embedded footprints in boards with version numbers, which is against what the documentation suggests. My suggested "quick fix" is this:

  • Make a FootprintLib object (similar to SymbolLib) that houses the footprint object (Footprint) and its version and generator.
  • Set the default version number of FootprintLib to 20210926 (day after LEGACY_ARC_FORMATTING)

Additional layers on stackup page are not parsed

The PCB stackup can be expanded with additional dielectric layers, as seen below, which will not be picked up by the parser.

image

TODO:

  • Add support for sublayers in PCB stackup section
  • Write test cases

Possible test case:

    (stackup
      (layer "F.SilkS" (type "Top Silk Screen") (color "White"))
      (layer "F.Paste" (type "Top Solder Paste"))
      (layer "F.Mask" (type "Top Solder Mask") (color "Green") (thickness 0.01) (material "Epoxy") (epsilon_r 3.3) (loss_tangent 0))
      (layer "F.Cu" (type "copper") (thickness 0.035))
      (layer "dielectric 1" (type "prepreg") (thickness 0.073) (material "FR4") (epsilon_r 4.3) (loss_tangent 0.02))
      (layer "In1.Cu" (type "copper") (thickness 0.035))
      (layer "dielectric 2" (type "core") (thickness 0.12) (material "FR4") (epsilon_r 4.5) (loss_tangent 0.02))
      (layer "In2.Cu" (type "copper") (thickness 0.035))
      (layer "dielectric 3" (type "prepreg") (thickness 0.195) (material "FR4") (epsilon_r 4.6) (loss_tangent 0.02)
        addsublayer (thickness 0.195))
      (layer "In3.Cu" (type "copper") (thickness 0.035))
      (layer "dielectric 4" (type "core") (thickness 0.12) (material "FR4") (epsilon_r 4.5) (loss_tangent 0.02))
      (layer "In4.Cu" (type "copper") (thickness 0.035))
      (layer "dielectric 5" (type "prepreg") (thickness 0.195) (material "FR4") (epsilon_r 4.6) (loss_tangent 0.02)
        addsublayer (thickness 0.195) (material "FR4") (epsilon_r 4.6) (loss_tangent 0.02)
        addsublayer (thickness 0)
        addsublayer (thickness 0))
      (layer "In5.Cu" (type "copper") (thickness 0.035))
      (layer "dielectric 6" (type "core") (thickness 0.12) (material "FR4") (epsilon_r 4.5) (loss_tangent 0.02))
      (layer "In6.Cu" (type "copper") (thickness 0.035))
      (layer "dielectric 7" (type "prepreg") (thickness 0.073) (material "FR4") (epsilon_r 4.3) (loss_tangent 0.02)
        addsublayer (thickness 0))
      (layer "B.Cu" (type "copper") (thickness 0.035))
      (layer "B.Mask" (type "Bottom Solder Mask") (color "Green") (thickness 0.01) (material "Epoxy") (epsilon_r 3.3) (loss_tangent 0))
      (layer "B.Paste" (type "Bottom Solder Paste"))
      (layer "B.SilkS" (type "Bottom Silk Screen") (color "White"))
      (copper_finish "None")
      (dielectric_constraints no)
    )

Schematic can't be load if Position.angle is None for a certain Property

Starting from an existing schematic I tried to add a new property with the following script:

from kiutils.schematic import Schematic
from kiutils.items.schitems import SchematicSymbol
from kiutils.items.common import Property

sch = Schematic().from_file("DummyKiCAD/DummyKiCAD.kicad_sch")
new_property = Property(key="MyProperty", id=5)
symbol[0].properties.append()
sch.to_file()

The script runs smoothly, but when I try to open the .kicad_sch file I got the following error from KiCAD:

Error loading schematic: Need a number for 'text angle' in ...path/to/file/file_name.kicad_sch

This is the line inside the file that generates the error:

(property "MyProperty" "" (id 4) (at 0.0 0.0))

If I change it like this:

(property "MyProperty" "" (id 4) (at 0.0 0.0 0))

KiCAD is able to load the schematic correctly.

The error is due to the default None value of [ANGLE] parameter in kiutils.items.common.Position class, however I think KiCAD documentation is not too clear on this topic because the angle attribute should be optional.

Maybe it can be considered a KiCAD bug, but a quick and dirty fix to the kiutils.items.common.Property class definition could be as follow:

@dataclass
class Property():
    """The `property` token defines a symbol property when used inside a `symbol` definition.

    Documentation:
        https://dev-docs.kicad.org/en/file-formats/sexpr-intro/index.html#_symbol_property
    """

    key: str = ""
    """The `key` string defines the name of the property and must be unique"""

    value: str = ""
    """The `value` string defines the value of the property"""

    id: int = 0
    """The id token defines an integer ID for the property and must be unique"""

    position: Position = Position(angle=0)
    """The `position` defines the X and Y coordinates and rotation angle of the property"""

    effects: Effects | None = None
    """The `effects` section defines how the text is displayed"""

Additional info:

  • kiutils 1.1.3
  • KiCAD 6.0.6 (build)

Expecting 'end' in fp_arc

This error message occured:
image

Line of intrest:

  (fp_arc (start 3.25 -0.8) (mid 0 0) (end 3.25 -1.35) (layer "F.Fab") (width 0.12))

Error reading the sch and pcb file (and possibly not only) due to a unicode error

Problem: if there are characters in a file (for example, in a text field) that are not contained in the standard encoding, then a UnicodeDecodeError error occurs: 'charmap' codec can't decode byte: character maps to

Solution: when opening a file and writing to a file, you can use encoding='cp437'

For example in schematic.py:

with open(filepath, 'w', encoding='cp437') as outfile:
   outfile.write(self.to_sexpr())

Support for bus_alias

bus_alias seems to be an S-Expression which is introduction KiCad 7.0 version which is used to store the names of different member names within a bus. Unfortunately, this is documented in kicad-dev-docs website for which I've raised another merge request here -
https://gitlab.com/kicad/services/kicad-dev-docs/-/merge_requests/53/diffs

bus_alias is used for tapping out named bus_entry + wire + label from a bus in eeschema tool.

I've raised a pull request for adding support bus_alias here - #92

@mvnmgrx Kindly review the same and accept if ok. Otherwise, please let me know of any corrections needed.

Can't parse board with length-matched traces due to Arc() not being implemented

The class kiutils.items.brditems.Arc() is currently not implemented as it is not used in the Pcbnew GUI, thus thinking it was obsolete.

But when parsing a board with length-matched traces, the Arc() token is used for the resulting trace meanders, making it not possible to parse such a board.

TODO:

Arc examples for test case (need to be put in a .kicad_pcb file):

  (arc (start 94.690001 121.156476) (mid 95.022341 121.018816) (end 95.160001 120.686476) (width 0.14) (layer "F.Cu") (net 281) (tstamp 10bd161e-71bd-4712-9f3a-fa8b15cf90b0))
  (arc (start 96.560001 119.723522) (mid 96.627366 119.560887) (end 96.790001 119.493522) (width 0.14) (layer "F.Cu") (net 281) (tstamp 12c97758-a647-4496-950b-8932b3dce42a))
  (arc (start 93.990001 119.493522) (mid 94.152636 119.560887) (end 94.220001 119.723522) (width 0.14) (layer "F.Cu") (net 281) (tstamp 25ee11b7-028c-4f84-ad93-7330a5cd0ea1))
  (arc (start 95.160001 119.723522) (mid 95.227366 119.560887) (end 95.390001 119.493522) (width 0.14) (layer "F.Cu") (net 281) (tstamp 2b06ac3b-81d0-46e3-bec3-5650f184c514))
  (arc (start 93.290001 120.324999) (mid 93.622341 120.187339) (end 93.760001 119.854999) (width 0.14) (layer "F.Cu") (net 281) (tstamp 30841a89-efd5-48af-ab5d-31600ca2a0c5))
  (arc (start 95.620001 120.686476) (mid 95.757661 121.018816) (end 96.090001 121.156476) (width 0.14) (layer "F.Cu") (net 281) (tstamp 5f21b05e-0563-43fc-aadd-e3a865ed4015))
  (arc (start 95.390001 119.493522) (mid 95.552636 119.560887) (end 95.620001 119.723522) (width 0.14) (layer "F.Cu") (net 281) (tstamp 7a4db92d-93a0-4dd2-ada5-fd92d16fcef7))
  (arc (start 96.090001 121.156476) (mid 96.422341 121.018816) (end 96.560001 120.686476) (width 0.14) (layer "F.Cu") (net 281) (tstamp 864fabad-0a66-4562-8515-0213dfd4d397))
  (arc (start 94.220001 120.686476) (mid 94.357661 121.018816) (end 94.690001 121.156476) (width 0.14) (layer "F.Cu") (net 281) (tstamp aa460c6e-5d8b-448a-a81d-2e0314c2ebee))
  (arc (start 97.960001 120.554999) (mid 98.027366 120.392364) (end 98.190001 120.324999) (width 0.14) (layer "F.Cu") (net 281) (tstamp c1f01119-31c5-41eb-94d8-552a7ddea952))
  (arc (start 97.490001 121.156476) (mid 97.822341 121.018816) (end 97.960001 120.686476) (width 0.14) (layer "F.Cu") (net 281) (tstamp c5af3e10-3aab-4516-9ce9-8b0935cf1d00))
  (arc (start 96.790001 119.493522) (mid 96.952636 119.560887) (end 97.020001 119.723522) (width 0.14) (layer "F.Cu") (net 281) (tstamp d24abddf-b37f-403c-bf5a-aa828ec0ebe9))
  (arc (start 97.020001 120.686476) (mid 97.157661 121.018816) (end 97.490001 121.156476) (width 0.14) (layer "F.Cu") (net 281) (tstamp d7d0b556-7d30-4828-b903-7a9ff784ffdb))
  (arc (start 93.760001 119.723522) (mid 93.827366 119.560887) (end 93.990001 119.493522) (width 0.14) (layer "F.Cu") (net 281) (tstamp dc6176fc-89a7-4910-a133-f804d595d492))

Dataclass member defaults may be shared by more than one instance

I've noticed that default arguments for mutable members of a dataclass may be shared through more than one instance when doing something like this:

from dataclasses import dataclass

@dataclass
class SubClass():
    some_pretty_int: int = 0

@dataclass
class MainClass():
    some_value: SubClass() = SubClass()

obj1 = MainClass()
obj2 = MainClass()

# Check identity
print(f'{id(obj1.some_value)}')
print(f'{id(obj2.some_value)}')

# Change one instance's value reflects to the other instance as well
obj2.some_value.some_pretty_int = 7
print(obj1.some_value)
print(obj2.some_value)

In this code, obj1 and obj2 share the same reference to some_value, as seen by the MWE's output:

PS E:\.....> py mwe.py
2100960016608
2100960016608
SubClass(some_pretty_int=7)            # Expected to be 0
SubClass(some_pretty_int=7)

Possible workaround:
Create mutable members with default values using the field directive with a lamda function:

@dataclass
class MainClass():
    some_value: SubClass() = field(default_factory=lambda: SubClass())

This will yield the desired functionality:

PS E:\.....> py mwe.py
2393500270256
2393500269776
SubClass(some_pretty_int=0)
SubClass(some_pretty_int=7)

Although this wasn't an issue in kiutils yet, it uses the same programming style as described above.

It needs to be checked if this could be come a problem.

`libraryLink` attribute in Footprint class missing

This MWE does throw an attribute exception:

from kiutils.kiutils.footprint import Footprint

footprint = Footprint(version='20220512', generator='mygen')

footprint.to_file("test.kicad_mod")
Traceback (most recent call last):
  File "e:\etc\projects\py-pingen\pingen.py", line 8, in <module>
    footprint.to_file("test.kicad_mod")
  File "e:\etc\projects\py-pingen\kiutils\kiutils\footprint.py", line 854, in to_file
    outfile.write(self.to_sexpr())
  File "e:\etc\projects\py-pingen\kiutils\kiutils\footprint.py", line 876, in to_sexpr
    expression =  f'{indents}(footprint "{dequote(self.libraryLink)}"{locked}{placed}{version}{generator}'
AttributeError: 'Footprint' object has no attribute 'libraryLink'

Fix:
In footprint.py -> class Footprint() the token version is defined twice. One of them should be libraryLink, as already documented there.

Error: Dimensions are not yet handled! Please report this bug along with the file being parsed.

The error occurs when reading board layers.
"""
ERROR: failed to read C:\Users\valte\PycharmProjects\pcbpr_v2\data\pcb_repos\maxlab-io_tokay-lite-pcb\ai-camera-rev3\ai-camera-rev3.kicad_pcb (v6) using PCBParserKiCadV6Plus()
Error: Dimensions are not yet handled! Please report this bug along with the file being parsed.

"""

For example, the error occurs with this file:
https://github.com/maxlab-io/tokay-lite-pcb ai-camera-rev3/ai-camera-rev3.kicad_pcb

e.g.

from kiutils.board import Board

board = Board.from_file(layout_path, encoding="utf-8")
print(board.layers)

Footprints don't support net_tie_pad_groups

Footprint objects don't support net_tie_pad_groups, which causes them to be removed from the PCB file whenever it is parsed/saved, and you can't edit them in python either.

Footprint pad inserts unwanted line break

When parsing a footprint in a board with a schematic symbol set (aka net, pinfunction or pintype set) as well as at least the solder_paste_margin_ratio token set, the resulting S-Expression is added a line break like shown below:

image

KiCad is on the left, parsed is on the right.

Broken as of KiCAD 8?

It seems KiCAD 8 changed the file format enough that KiUtils opening, parsing, and saving a PCB will corrupt it and prevent it from being opened.

To replicate:

  1. Open a KiCAD 7 PCB in KiCAD 8, you will get a notice about upgrading the file format upon save.
  2. Save the PCB to change to new format
  3. Board().from_file("foo.kicad_pcb").to_file()
  4. Try to open it in KiCAD 8, no dice!

KiCad 7: Mark symbols as DNP

Symbols can now be marked as "do not populate". This adds another token called dnp to the symbol, which does not seem to be optional in v7, but does not exist in v6.

Example:

  (symbol (lib_id "74xx:74AHC240") (at 180.34 101.6 0) (unit 1)
    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
    (uuid 587a4c9d-0749-472e-ae82-ada578716f31)
    (property "Reference" "U1" (at 182.2959 82.55 0)
      (effects (font (size 1.27 1.27)) (justify left))
    )
  )

Bug: 3D model with `hide` property won't parse correctly

3D-models may have a hide token which is currently not parsed. The Model class throws an exception as the S-Expression does not have the correct type.

Some example:

  (footprint "footprints:DP9700" (layer "F.Cu")
    (tstamp 71a3d5a8-0576-436c-9616-dfd44ac398e3)
    (model "${KIPRJMOD}/etherlight-librarys/3dmodels/DP9700.step" hide
      (offset (xyz 6.35 -0.3 0))
      (scale (xyz 1 1 1))
      (rotate (xyz 0 0 0))
    )
  )

Footprint: private_layer token is not parsed

The new private_layers token is not parsed. Example:

(footprint "my_footprint" (version 20221018) (generator pcbnew)
  (layer "F.Cu")
  (descr "test descr")
  (tags "test tags")
  (private_layers "Eco2.User" "User.1" "User.2" "User.3" "User.5")
)

Valid private_layers: User.[1-9], User.Drawings, User.Comments, User.Eco[1-2]

FpItems: `stroke` token may be None when used to generate S-Expression

In src/kiutils/items/fpitems.py:

The stroke token is used when the width token is None, but without prior checking if it was even set during parsing. The following snippet is used several times in this file:

        if self.width is not None:
            width = f' (width {self.width})'
        else:
            width = f' {self.stroke.to_sexpr(indent=0, newline=False)}'

The self.stroke member is optional and may be None at this point. Add a check before using it to fix this.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.