Fix remaining failing tests

This brings schematic i/o compatiblity up to 9.0.5.
This commit is contained in:
jaseg 2025-11-18 23:11:22 +01:00
parent c2679260a0
commit 46df184358
5 changed files with 85 additions and 7 deletions

View file

@ -1,6 +1,8 @@
import string
import math
import base64
import textwrap
from .sexp import *
from .base_types import *
@ -325,13 +327,28 @@ class DimensionStyle:
keep_text_aligned: Flag() = False
@sexp_type('data')
class Base64Blob:
@classmethod
def __map__(kls, obj, parent=None, path=''):
_data, *content = obj
for x in content[:10]:
print(str(x))
return base64.b64decode(''.join(map(str, content)))
@classmethod
def __sexp__(kls, value):
encoded = base64.b64encode(value).decode()
yield [Atom.data, *textwrap.wrap(encoded, 76)]
@sexp_type('image')
class Image:
at: AtPos = field(default_factory=AtPos)
scale: Named(float) = None
layer: Named(str) = None
uuid: UUID = field(default_factory=UUID)
data: str = ''
data: Base64Blob = ''
def offset(self, x=0, y=0):
self.at = self.at.with_offset(x, y)

View file

@ -85,6 +85,12 @@ class NoConnect:
fill='none', stroke_width='0.254', stroke=colorscheme.no_connect)
@sexp_type('bus_alias')
class BusAlias:
name: str = ''
members: Named(Array(str)) = field(default_factory=list)
@sexp_type('bus_entry')
class BusEntry:
at: AtPos = field(default_factory=AtPos)
@ -171,6 +177,24 @@ class Polyline:
yield _polyline_svg(self, colorscheme.lines)
@sexp_type('circle')
class Circle:
center: Rename(XYCoord) = field(default_factory=XYCoord)
radius: Named(float) = 0.0
stroke: Stroke = field(default_factory=Stroke)
fill: OmitDefault(Fill) = None
uuid: UUID = field(default_factory=UUID)
@sexp_type('rectangle')
class Rectangle:
start: Rename(XYCoord) = field(default_factory=XYCoord)
end: Rename(XYCoord) = field(default_factory=XYCoord)
stroke: Stroke = field(default_factory=Stroke)
fill: OmitDefault(Fill) = None
uuid: UUID = field(default_factory=UUID)
def label_shape_path_d(shape, w, h):
l, r = {
Atom.input: '<]',
@ -578,12 +602,28 @@ class TextBox(TextMixin):
yield from gr.TextBox.render(self, variables=variables)
@sexp_type('comment')
class TitleComment:
@classmethod
def __map__(kls, obj, parent=None, path=''):
return '\n'.join(obj[2::2])
@classmethod
def __sexp__(kls, value):
l = [Atom.comment]
for i, line in enumerate(value.splitlines(), start=1):
l.append(i)
l.append(line.rstrip('\n'))
return l
@sexp_type('title_block')
class TitleBlock:
title: Named(str) = ''
date: Named(str) = ''
rev: Named(str) = ''
company: Named(str) = ''
comment: TitleComment = None
@sexp_type('lib_symbols')
@ -597,8 +637,11 @@ class Schematic:
_version: Named(int, name='version') = 20230620
generator: Named(str) = 'gerbonara'
generator_version: Named(str) = __version__
legacy_generator: Named(Array(str), name='host') = None
uuid: UUID = field(default_factory=UUID)
page_settings: PageSettings = field(default_factory=PageSettings)
legacy_page: Named(Array(int), name='page') = None
legacy_paper: Named(str, name='paper') = None
title_block: TitleBlock = None
# The doc says this is expected, but eeschema barfs when it's there.
# path: SheetPath = field(default_factory=SheetPath)
@ -607,12 +650,14 @@ class Schematic:
no_connects: List(NoConnect) = field(default_factory=list)
rule_areas: List(RuleArea) = field(default_factory=list)
netclass_flags: List(NetclassFlag) = field(default_factory=list)
bus_aliases: List(BusAlias) = field(default_factory=list)
bus_entries: List(BusEntry) = field(default_factory=list)
wires: List(Wire) = field(default_factory=list)
buses: List(Bus) = field(default_factory=list)
images: List(gr.Image) = field(default_factory=list)
polylines: List(Polyline) = field(default_factory=list)
circles: List(gr.Circle) = field(default_factory=list)
circles: List(Circle) = field(default_factory=list)
rectangles: List(Rectangle) = field(default_factory=list)
texts: List(Text) = field(default_factory=list)
text_boxes: List(TextBox) = field(default_factory=list)
local_labels: List(LocalLabel) = field(default_factory=list)
@ -620,7 +665,7 @@ class Schematic:
hierarchical_labels: List(HierarchicalLabel) = field(default_factory=list)
symbols: List(SymbolInstance) = field(default_factory=list)
subsheets: List(Subsheet) = field(default_factory=list)
sheet_instances: Named(List(SubsheetCrosslinkSheet)) = field(default_factory=list)
sheet_instances: Named(Array(SubsheetCrosslinkSheet)) = field(default_factory=list)
symbol_instances: Named(Array(SymbolCrosslinkProject)) = field(default_factory=list)
embedded_fonts: Named(YesNoAtom()) = False
_ : SEXP_END = None

View file

@ -64,7 +64,7 @@ term_regex = r"""(?mx)
(\))|
([+-]?\d+\.\d+(?=[\s\)]))|
(\-?\d+(?=[\s\)]))|
([^0-9"\s()][^"\s)]*)
([^"\s()][^"\s)]*)
)"""

View file

@ -26,7 +26,7 @@ from .primitives import kicad_mid_to_center_arc, Margins
PIN_ETYPE = AtomChoice(Atom.input, Atom.output, Atom.bidirectional, Atom.tri_state, Atom.passive, Atom.free,
Atom.unspecified, Atom.power_in, Atom.power_out, Atom.open_collector, Atom.open_emitter,
Atom.no_connect)
Atom.no_connect, Atom.unconnected)
PIN_STYLE = AtomChoice(Atom.line, Atom.inverted, Atom.clock, Atom.inverted_clock, Atom.input_low, Atom.clock_low,
@ -251,11 +251,19 @@ class Circle:
**self.stroke.svg_attrs(colorscheme.lines))
@sexp_type('radius')
class ArcRadius:
at: AtPos = field(default_factory=AtPos)
length: Named(float) = 0.0
angles: Rename(XYCoord) = field(default_factory=XYCoord)
@sexp_type('arc')
class Arc:
start: Rename(XYCoord) = field(default_factory=XYCoord)
mid: Rename(XYCoord) = field(default_factory=XYCoord)
end: Rename(XYCoord) = field(default_factory=XYCoord)
radius: ArcRadius = None
stroke: Stroke = field(default_factory=Stroke)
fill: Fill = field(default_factory=Fill)
@ -419,13 +427,13 @@ class Property(TextMixin):
@sexp_type('pin_numbers')
class PinNumberSpec:
hide: Flag() = False
hide: Named(YesNoAtom()) = False
@sexp_type('pin_names')
class PinNameSpec:
offset: OmitDefault(Named(float)) = 0.508
hide: Flag() = False
hide: OmitDefault(Named(YesNoAtom())) = False
@sexp_type('text_box')
class TextBox:

View file

@ -98,6 +98,14 @@ def test_round_trip(kicad_library_file, tmpfile):
j += 1
continue
if original != stage1:
# Make pytest output more useful error messages
context_original = original_lines[max(0, i-10):min(i+10, len(original_lines)-1)]
context_original = '\n'.join(context_original)
context_stage1 = stage1_lines[max(0, i-10):min(i+10, len(stage1_lines)-1)]
context_stage1 = '\n'.join(context_stage1)
assert context_original == context_stage1
assert original == stage1
i, j = i+1, j+1